summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore10
-rw-r--r--BitKeeper/etc/logging_ok6
-rwxr-xr-xBitKeeper/triggers/post-commit6
-rw-r--r--Docs/sp-imp-spec.txt1100
-rw-r--r--Docs/sp-implemented.txt112
-rw-r--r--VC++Files/libmysqld/libmysqld.dsp36
-rw-r--r--VC++Files/sql/mysqld.dsp38
-rw-r--r--client/Makefile.am2
-rw-r--r--client/mysqlbinlog.cc283
-rw-r--r--configure.in18
-rw-r--r--include/my_base.h8
-rw-r--r--include/my_bitmap.h2
-rw-r--r--include/my_global.h11
-rw-r--r--include/my_sys.h9
-rw-r--r--include/mysql.h13
-rw-r--r--include/mysql_com.h35
-rw-r--r--include/mysqld_error.h58
-rw-r--r--include/sql_state.h38
-rw-r--r--innobase/include/row0mysql.h4
-rw-r--r--innobase/row/row0sel.c31
-rw-r--r--libmysql/client_settings.h2
-rw-r--r--libmysql/libmysql.c79
-rw-r--r--libmysqld/Makefile.am4
-rw-r--r--libmysqld/lib_sql.cc4
-rw-r--r--myisam/myisamchk.c6
-rw-r--r--myisam/myisamdef.h2
-rw-r--r--myisam/sort.c3
-rwxr-xr-xmysql-test/create-test-result4
-rw-r--r--mysql-test/include/ps_modify.inc1
-rw-r--r--mysql-test/mysql-test-run.sh2
-rw-r--r--mysql-test/r/bdb.result6
-rw-r--r--mysql-test/r/bench_count_distinct.result2
-rw-r--r--mysql-test/r/case.result2
-rw-r--r--mysql-test/r/connect.result105
-rw-r--r--mysql-test/r/ctype_recoding.result20
-rw-r--r--mysql-test/r/ctype_ucs.result4
-rw-r--r--mysql-test/r/derived.result4
-rw-r--r--mysql-test/r/distinct.result6
-rw-r--r--mysql-test/r/drop.result4
-rw-r--r--mysql-test/r/drop_temp_table.result12
-rw-r--r--mysql-test/r/fulltext.result4
-rw-r--r--mysql-test/r/func_default.result2
-rw-r--r--mysql-test/r/func_gconcat.result6
-rw-r--r--mysql-test/r/func_group.result16
-rw-r--r--mysql-test/r/func_if.result2
-rw-r--r--mysql-test/r/func_in.result2
-rw-r--r--mysql-test/r/func_like.result4
-rw-r--r--mysql-test/r/func_regexp.result2
-rw-r--r--mysql-test/r/func_sapdb.result19
-rw-r--r--mysql-test/r/func_test.result4
-rw-r--r--mysql-test/r/func_time.result63
-rw-r--r--mysql-test/r/gis.result14
-rw-r--r--mysql-test/r/grant.result31
-rw-r--r--mysql-test/r/greedy_optimizer.result657
-rw-r--r--mysql-test/r/group_by.result2
-rw-r--r--mysql-test/r/having.result2
-rw-r--r--mysql-test/r/heap.result2
-rw-r--r--mysql-test/r/heap_btree.result2
-rw-r--r--mysql-test/r/heap_hash.result2
-rw-r--r--mysql-test/r/index_merge.result338
-rw-r--r--mysql-test/r/index_merge_bdb.result136
-rw-r--r--mysql-test/r/index_merge_innodb.result55
-rw-r--r--mysql-test/r/index_merge_innodb2.result136
-rw-r--r--mysql-test/r/index_merge_ror.result196
-rw-r--r--mysql-test/r/index_merge_ror_cpk.result120
-rw-r--r--mysql-test/r/innodb.result6
-rw-r--r--mysql-test/r/insert.result14
-rw-r--r--mysql-test/r/insert_select.result6
-rw-r--r--mysql-test/r/insert_update.result4
-rw-r--r--mysql-test/r/join_nested.result1323
-rw-r--r--mysql-test/r/join_outer.result8
-rw-r--r--mysql-test/r/key.result4
-rw-r--r--mysql-test/r/keywords.result16
-rw-r--r--mysql-test/r/lowercase_table.result8
-rw-r--r--mysql-test/r/lowercase_table2.result80
-rw-r--r--mysql-test/r/mix_innodb_myisam_binlog.result142
-rw-r--r--mysql-test/r/multi_update.result26
-rw-r--r--mysql-test/r/mysqlbinlog.result12
-rw-r--r--mysql-test/r/ndb_autodiscover.result2
-rw-r--r--mysql-test/r/null_key.result6
-rw-r--r--mysql-test/r/olap.result2
-rw-r--r--mysql-test/r/order_by.result4
-rw-r--r--mysql-test/r/ps_1general.result13
-rw-r--r--mysql-test/r/ps_2myisam.result5
-rw-r--r--mysql-test/r/ps_3innodb.result5
-rw-r--r--mysql-test/r/ps_4heap.result5
-rw-r--r--mysql-test/r/ps_5merge.result10
-rw-r--r--mysql-test/r/ps_6bdb.result5
-rw-r--r--mysql-test/r/query_cache.result26
-rw-r--r--mysql-test/r/range.result4
-rw-r--r--mysql-test/r/rename.result20
-rw-r--r--mysql-test/r/rowid_order_bdb.result186
-rw-r--r--mysql-test/r/rowid_order_innodb.result186
-rw-r--r--mysql-test/r/rpl000009.result18
-rw-r--r--mysql-test/r/rpl000015.result8
-rw-r--r--mysql-test/r/rpl_change_master.result4
-rw-r--r--mysql-test/r/rpl_charset.result116
-rw-r--r--mysql-test/r/rpl_error_ignored_table.result26
-rw-r--r--mysql-test/r/rpl_flush_log_loop.result2
-rw-r--r--mysql-test/r/rpl_flush_tables.result38
-rw-r--r--mysql-test/r/rpl_loaddata.result10
-rw-r--r--mysql-test/r/rpl_loaddata_rule_m.result4
-rw-r--r--mysql-test/r/rpl_loaddata_rule_s.result4
-rw-r--r--mysql-test/r/rpl_log.result112
-rw-r--r--mysql-test/r/rpl_log_pos.result14
-rw-r--r--mysql-test/r/rpl_max_relay_size.result14
-rw-r--r--mysql-test/r/rpl_relayrotate.result7
-rw-r--r--mysql-test/r/rpl_replicate_do.result2
-rw-r--r--mysql-test/r/rpl_reset_slave.result8
-rw-r--r--mysql-test/r/rpl_rotate_logs.result9
-rw-r--r--mysql-test/r/rpl_server_id1.result2
-rw-r--r--mysql-test/r/rpl_server_id2.result2
-rw-r--r--mysql-test/r/rpl_session_var.result43
-rw-r--r--mysql-test/r/rpl_temporary.result28
-rw-r--r--mysql-test/r/rpl_timezone.result16
-rw-r--r--mysql-test/r/rpl_trunc_binlog.result2
-rw-r--r--mysql-test/r/rpl_until.result34
-rw-r--r--mysql-test/r/rpl_user_variables.result58
-rw-r--r--mysql-test/r/select.result73
-rw-r--r--mysql-test/r/show_check.result3
-rw-r--r--mysql-test/r/sp-error.result465
-rw-r--r--mysql-test/r/sp-security.result121
-rw-r--r--mysql-test/r/sp-threads.result25
-rw-r--r--mysql-test/r/sp.result2013
-rw-r--r--mysql-test/r/sql_mode.result249
-rw-r--r--mysql-test/r/subselect.result102
-rw-r--r--mysql-test/r/sum_distinct.result203
-rw-r--r--mysql-test/r/system_mysql_db.result41
-rw-r--r--mysql-test/r/union.result4
-rw-r--r--mysql-test/r/user_var.result20
-rw-r--r--mysql-test/r/varbinary.result2
-rw-r--r--mysql-test/r/variables.result32
-rw-r--r--mysql-test/r/view.result1168
-rw-r--r--mysql-test/r/view_skip_grants.result6
-rw-r--r--mysql-test/t/derived.test2
-rw-r--r--mysql-test/t/distinct.test4
-rw-r--r--mysql-test/t/func_group.test1
-rw-r--r--mysql-test/t/func_sapdb.test3
-rw-r--r--mysql-test/t/func_time.test26
-rw-r--r--mysql-test/t/grant.test5
-rw-r--r--mysql-test/t/grant_cache.test14
-rw-r--r--mysql-test/t/greedy_optimizer.test313
-rw-r--r--mysql-test/t/index_merge.test281
-rw-r--r--mysql-test/t/index_merge_bdb.test52
-rw-r--r--mysql-test/t/index_merge_innodb.test54
-rw-r--r--mysql-test/t/index_merge_innodb2.test52
-rw-r--r--mysql-test/t/index_merge_ror.test249
-rw-r--r--mysql-test/t/index_merge_ror_cpk.test108
-rw-r--r--mysql-test/t/innodb.test6
-rw-r--r--mysql-test/t/insert.test32
-rw-r--r--mysql-test/t/join_nested.test754
-rw-r--r--mysql-test/t/keywords.test8
-rw-r--r--mysql-test/t/mix_innodb_myisam_binlog.test26
-rw-r--r--mysql-test/t/multi_update.test50
-rw-r--r--mysql-test/t/mysqlbinlog.test4
-rw-r--r--mysql-test/t/ps_1general.test5
-rw-r--r--mysql-test/t/query_cache.test19
-rw-r--r--mysql-test/t/rowid_order_bdb.test108
-rw-r--r--mysql-test/t/rowid_order_innodb.test108
-rw-r--r--mysql-test/t/rpl000010-slave.opt2
-rw-r--r--mysql-test/t/rpl000015.test10
-rw-r--r--mysql-test/t/rpl000017.test2
-rw-r--r--mysql-test/t/rpl000018.test2
-rw-r--r--mysql-test/t/rpl_change_master.test4
-rw-r--r--mysql-test/t/rpl_charset.test2
-rw-r--r--mysql-test/t/rpl_empty_master_crash.test2
-rw-r--r--mysql-test/t/rpl_error_ignored_table.test4
-rw-r--r--mysql-test/t/rpl_flush_log_loop.test2
-rw-r--r--mysql-test/t/rpl_heap.test2
-rw-r--r--mysql-test/t/rpl_loaddata.test8
-rw-r--r--mysql-test/t/rpl_loaddata_rule_m.test2
-rw-r--r--mysql-test/t/rpl_loaddata_rule_s.test2
-rw-r--r--mysql-test/t/rpl_log.test9
-rw-r--r--mysql-test/t/rpl_log_pos.test10
-rw-r--r--mysql-test/t/rpl_max_relay_size.test12
-rw-r--r--mysql-test/t/rpl_openssl.test4
-rw-r--r--mysql-test/t/rpl_redirect.test2
-rw-r--r--mysql-test/t/rpl_relayrotate-slave.opt3
-rw-r--r--mysql-test/t/rpl_relayrotate.test6
-rw-r--r--mysql-test/t/rpl_replicate_do.test2
-rw-r--r--mysql-test/t/rpl_reset_slave.test8
-rw-r--r--mysql-test/t/rpl_rotate_logs.test8
-rw-r--r--mysql-test/t/rpl_session_var.test42
-rw-r--r--mysql-test/t/rpl_trunc_binlog.test2
-rw-r--r--mysql-test/t/rpl_until.test10
-rw-r--r--mysql-test/t/rpl_user_variables.test2
-rw-r--r--mysql-test/t/show_check.test17
-rw-r--r--mysql-test/t/sp-error.test633
-rw-r--r--mysql-test/t/sp-security.test192
-rw-r--r--mysql-test/t/sp-threads.test54
-rw-r--r--mysql-test/t/sp.test2161
-rw-r--r--mysql-test/t/sql_mode.test95
-rw-r--r--mysql-test/t/subselect.test5
-rw-r--r--mysql-test/t/sum_distinct.test188
-rw-r--r--mysql-test/t/system_mysql_db_fix.test2
-rw-r--r--mysql-test/t/user_var.test2
-rw-r--r--mysql-test/t/variables.test16
-rw-r--r--mysql-test/t/view.test1108
-rw-r--r--mysql-test/t/view_skip_grants-master.opt1
-rw-r--r--mysql-test/t/view_skip_grants.test14
-rw-r--r--mysys/mf_getdate.c42
-rw-r--r--mysys/mf_iocache2.c7
-rw-r--r--mysys/my_bit.c5
-rw-r--r--mysys/my_bitmap.c66
-rw-r--r--netware/my_manage.h34
-rw-r--r--scripts/make_binary_distribution.sh14
-rw-r--r--scripts/mysql_create_system_tables.sh74
-rw-r--r--scripts/mysql_fix_privilege_tables.sh45
-rw-r--r--scripts/mysql_fix_privilege_tables.sql62
-rw-r--r--sql-bench/limits/mysql-4.0.cfg557
-rw-r--r--sql-common/client.c4
-rw-r--r--sql/Makefile.am10
-rw-r--r--sql/filesort.cc136
-rw-r--r--sql/ha_berkeley.cc27
-rw-r--r--sql/ha_berkeley.h2
-rw-r--r--sql/ha_heap.h7
-rw-r--r--sql/ha_innodb.cc76
-rw-r--r--sql/ha_innodb.h5
-rw-r--r--sql/ha_myisam.cc5
-rw-r--r--sql/ha_myisam.h1
-rw-r--r--sql/ha_myisammrg.cc4
-rw-r--r--sql/handler.cc1
-rw-r--r--sql/handler.h31
-rw-r--r--sql/item.cc292
-rw-r--r--sql/item.h176
-rw-r--r--sql/item_cmpfunc.cc8
-rw-r--r--sql/item_cmpfunc.h36
-rw-r--r--sql/item_create.cc31
-rw-r--r--sql/item_create.h1
-rw-r--r--sql/item_func.cc153
-rw-r--r--sql/item_func.h82
-rw-r--r--sql/item_subselect.cc109
-rw-r--r--sql/item_subselect.h5
-rw-r--r--sql/item_sum.cc135
-rw-r--r--sql/item_sum.h41
-rw-r--r--sql/item_timefunc.cc304
-rw-r--r--sql/item_timefunc.h27
-rw-r--r--sql/lex.h62
-rw-r--r--sql/lock.cc10
-rw-r--r--sql/log.cc146
-rw-r--r--sql/log_event.cc1343
-rw-r--r--sql/log_event.h575
-rw-r--r--sql/mysql_priv.h123
-rw-r--r--sql/mysqld.cc221
-rw-r--r--sql/net_serv.cc1
-rw-r--r--sql/opt_range.cc3791
-rw-r--r--sql/opt_range.h483
-rw-r--r--sql/opt_sum.cc4
-rw-r--r--sql/parse_file.cc791
-rw-r--r--sql/parse_file.h68
-rw-r--r--sql/protocol.cc82
-rw-r--r--sql/protocol.h13
-rw-r--r--sql/protocol_cursor.cc16
-rw-r--r--sql/records.cc2
-rw-r--r--sql/repl_failsafe.cc6
-rw-r--r--sql/set_var.cc54
-rw-r--r--sql/set_var.h20
-rw-r--r--sql/share/czech/errmsg.txt72
-rw-r--r--sql/share/danish/errmsg.txt56
-rw-r--r--sql/share/dutch/errmsg.txt56
-rw-r--r--sql/share/english/errmsg.txt56
-rw-r--r--sql/share/estonian/errmsg.txt56
-rw-r--r--sql/share/french/errmsg.txt56
-rw-r--r--sql/share/german/errmsg.txt56
-rw-r--r--sql/share/greek/errmsg.txt56
-rw-r--r--sql/share/hungarian/errmsg.txt56
-rw-r--r--sql/share/italian/errmsg.txt56
-rw-r--r--sql/share/japanese/errmsg.txt56
-rw-r--r--sql/share/korean/errmsg.txt56
-rw-r--r--sql/share/norwegian-ny/errmsg.txt56
-rw-r--r--sql/share/norwegian/errmsg.txt56
-rw-r--r--sql/share/polish/errmsg.txt56
-rw-r--r--sql/share/portuguese/errmsg.txt56
-rw-r--r--sql/share/romanian/errmsg.txt56
-rw-r--r--sql/share/russian/errmsg.txt56
-rw-r--r--sql/share/serbian/errmsg.txt56
-rw-r--r--sql/share/slovak/errmsg.txt56
-rw-r--r--sql/share/spanish/errmsg.txt72
-rw-r--r--sql/share/swedish/errmsg.txt94
-rw-r--r--sql/share/ukrainian/errmsg.txt110
-rw-r--r--sql/slave.cc674
-rw-r--r--sql/slave.h22
-rw-r--r--sql/sp.cc1183
-rw-r--r--sql/sp.h106
-rw-r--r--sql/sp_cache.cc166
-rw-r--r--sql/sp_cache.h107
-rw-r--r--sql/sp_head.cc1761
-rw-r--r--sql/sp_head.h835
-rw-r--r--sql/sp_pcontext.cc274
-rw-r--r--sql/sp_pcontext.h298
-rw-r--r--sql/sp_rcontext.cc254
-rw-r--r--sql/sp_rcontext.h253
-rw-r--r--sql/sql_acl.cc340
-rw-r--r--sql/sql_acl.h41
-rw-r--r--sql/sql_base.cc967
-rw-r--r--sql/sql_cache.cc29
-rw-r--r--sql/sql_class.cc145
-rw-r--r--sql/sql_class.h157
-rw-r--r--sql/sql_db.cc188
-rw-r--r--sql/sql_delete.cc145
-rw-r--r--sql/sql_derived.cc22
-rw-r--r--sql/sql_error.cc7
-rw-r--r--sql/sql_handler.cc23
-rw-r--r--sql/sql_help.cc24
-rw-r--r--sql/sql_insert.cc321
-rw-r--r--sql/sql_lex.cc502
-rw-r--r--sql/sql_lex.h136
-rw-r--r--sql/sql_list.h7
-rw-r--r--sql/sql_load.cc21
-rw-r--r--sql/sql_olap.cc3
-rw-r--r--sql/sql_parse.cc1452
-rw-r--r--sql/sql_prepare.cc332
-rw-r--r--sql/sql_rename.cc19
-rw-r--r--sql/sql_repl.cc224
-rw-r--r--sql/sql_repl.h2
-rw-r--r--sql/sql_select.cc2649
-rw-r--r--sql/sql_select.h86
-rw-r--r--sql/sql_show.cc178
-rw-r--r--sql/sql_sort.h1
-rw-r--r--sql/sql_string.cc13
-rw-r--r--sql/sql_string.h2
-rw-r--r--sql/sql_table.cc101
-rw-r--r--sql/sql_test.cc105
-rw-r--r--sql/sql_union.cc56
-rw-r--r--sql/sql_update.cc274
-rw-r--r--sql/sql_view.cc946
-rw-r--r--sql/sql_view.h35
-rw-r--r--sql/sql_yacc.yy1935
-rw-r--r--sql/structs.h4
-rw-r--r--sql/table.cc274
-rw-r--r--sql/table.h151
-rw-r--r--sql/udf_example.cc45
-rw-r--r--sql/uniques.cc477
-rw-r--r--sql/unireg.h1
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/client_test.c628
-rw-r--r--tests/udf_test3
-rw-r--r--tests/udf_test.res24
338 files changed, 44197 insertions, 4756 deletions
diff --git a/.bzrignore b/.bzrignore
index 162872e39cf..be7211af9e2 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -338,6 +338,7 @@ libmysqld/derror.cc
libmysqld/discover.cc
libmysqld/errmsg.c
libmysqld/examples/client_test.c
+libmysqld/examples/client_test.cc
libmysqld/examples/completion_hash.cc
libmysqld/examples/completion_hash.h
libmysqld/examples/link_sources
@@ -394,14 +395,21 @@ libmysqld/opt_ft.cc
libmysqld/opt_range.cc
libmysqld/opt_sum.cc
libmysqld/pack.c
+libmysqld/parse_file.cc
libmysqld/password.c
libmysqld/procedure.cc
libmysqld/protocol.cc
+libmysqld/protocol_cursor.cc
libmysqld/records.cc
libmysqld/repl_failsafe.cc
libmysqld/set_var.cc
libmysqld/simple-test
libmysqld/slave.cc
+libmysqld/sp.cc
+libmysqld/sp_cache.cc
+libmysqld/sp_head.cc
+libmysqld/sp_pcontext.cc
+libmysqld/sp_rcontext.cc
libmysqld/spatial.cc
libmysqld/sql_acl.cc
libmysqld/sql_analyse.cc
@@ -436,6 +444,7 @@ libmysqld/sql_udf.cc
libmysqld/sql_union.cc
libmysqld/sql_unions.cc
libmysqld/sql_update.cc
+libmysqld/sql_view.cc
libmysqld/sql_yacc.cc
libmysqld/stacktrace.c
libmysqld/strfunc.cc
@@ -511,6 +520,7 @@ mysql-test/std_data/*.pem
mysql-test/var/*
mysql.kdevprj
mysql.proj
+mysql_priv.h
mysqld.S
mysqld.sym
mysys/#mf_iocache.c#
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index 0eb4289c1d5..4ca73c35e40 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -68,6 +68,7 @@ jani@dsl-kpogw4gb5.dial.inet.fi
jani@hynda.(none)
jani@hynda.mysql.fi
jani@janikt.pp.saunalahti.fi
+jani@linux.local
jani@rhols221.adsl.netsonic.fi
jani@rhols221.arenanet.fi
jani@ua126d19.elisa.omakaista.fi
@@ -145,10 +146,14 @@ paul@kite-hub.kitebird.com
paul@teton.kitebird.com
pekka@mysql.com
pem@mysql.com
+pem@per-erik-martins-dator.local
peter@linux.local
peter@mysql.com
peterg@mysql.com
pgulutzan@linux.local
+pmartin@build.mysql2.com
+psergey@psergey-rh8.(none)
+psergey@psergey.(none)
ram@deer.(none)
ram@gw.mysql.r18.ru
ram@gw.udmsearch.izhnet.ru
@@ -182,6 +187,7 @@ tim@sand.box
tim@threads.polyesthetic.msg
tim@white.box
tim@work.mysql.com
+timour@mysql.com
tom@basil-firewall.home.com
tomas@mc05.(none)
tomas@poseidon.(none)
diff --git a/BitKeeper/triggers/post-commit b/BitKeeper/triggers/post-commit
index a1b6b819e63..47afc77118a 100755
--- a/BitKeeper/triggers/post-commit
+++ b/BitKeeper/triggers/post-commit
@@ -5,7 +5,7 @@ FROM=$USER@mysql.com
INTERNALS=internals@lists.mysql.com
DOCS=docs-commit@mysql.com
LIMIT=10000
-VERSION="4.1"
+VERSION="5.0"
if [ "$REAL_EMAIL" = "" ]
then
@@ -42,7 +42,7 @@ Subject: bk commit - $VERSION tree ($CHANGESET)$BUG
EOF
bk changes -v -r+
bk cset -r+ -d
- ) | head -n $LIMIT | /usr/sbin/sendmail -t
+ ) | /usr/sbin/sendmail -t
#++
# internals@ mail
@@ -85,7 +85,7 @@ Subject: bk commit - $VERSION tree (Manual) ($CHANGESET)
EOF
bk changes -v -r+
bk cset -r+ -d
- ) | head -n $LIMIT | /usr/sbin/sendmail -t
+ ) | /usr/sbin/sendmail -t
fi
else
diff --git a/Docs/sp-imp-spec.txt b/Docs/sp-imp-spec.txt
new file mode 100644
index 00000000000..ac17a375926
--- /dev/null
+++ b/Docs/sp-imp-spec.txt
@@ -0,0 +1,1100 @@
+
+ Implementation specification for Stored Procedures
+ ==================================================
+
+
+- How parsing and execution of queries work
+
+ In order to execute a query, the function sql_parse.cc:mysql_parse() is
+ called, which in turn calls the parser (yyparse()) with an updated Lex
+ structure as the result. mysql_parse() then calls mysql_execute_command()
+ which dispatches on the command code (in Lex) to the corresponding code for
+ executing that particular query.
+
+ There are three structures involved in the execution of a query which are of
+ interest to the stored procedure implementation:
+
+ - Lex (mentioned above) is the "compiled" query, that is the output from
+ the parser and what is then interpreted to do the actual work.
+ It constains an enum value (sql_command) which is the query type, and
+ all the data collected by the parser needed for the execution (table
+ names, fields, values, etc).
+ - THD is the "run-time" state of a connection, containing all that is
+ needed for a particular client connection, and, among other things, the
+ Lex structure currently being executed.
+ - Item_*: During parsing, all data is translated into "items", objects of
+ the subclasses of "Item", such as Item_int, Item_real, Item_string, etc,
+ for basic datatypes, and also various more specialized Item types for
+ expressions to be evaluated (Item_func objects).
+
+
+- How to fit Stored Procedure into this scheme
+
+ - An overview of the classes and files for stored procedures
+ (More detailed APIs at the end of this file)
+
+ - class sp_head (sp_head.{cc,h})
+ This contains, among other things, an array of "instructions" and the
+ method for executing the procedure.
+
+ - class sp_pcontext (sp_pcontext.{cc,h}
+ This is the parse context for the procedure. It's primarily used during
+ parsing to keep track of local parameters, variables and labels, but
+ it's also used at CALL time do find parameters mode (IN, OUT or INOUT)
+ and type when setting up the runtime context.
+
+ - class sp_instr (sp_head.{cc,h})
+ This is the base class for "instructions", that is, what is generated
+ by the parser. It turns out that we only need a minimum of 5 different
+ sub classes:
+ - sp_instr_stmt
+ Execute a statement. This is the "call-out" any normal SQL statement,
+ like a SELECT, INSERT etc. It contains the Lex structure for the
+ statement in question.
+ - sp_instr_set
+ Set the value of a local variable (or parameter)
+ - sp_instr_jump
+ An unconditional jump.
+ - sp_instr_jump_if_not
+ Jump if condition is not true. It turns out that the negative test is
+ most convenient when generating the code for the flow control
+ constructs.
+ - sp_instr_freturn
+ Return a value from a FUNCTION and exit.
+ For condition HANDLERs some special instructions are also needed, see
+ that section below.
+
+ - class sp_rcontext (sp_rcontext.h)
+ This is the runtime context in the THD structure.
+ It contains an array of items, the parameters and local variables for
+ the currently executing stored procedure.
+ This means that variable value lookup is in runtime is constant time,
+ a simple index operation.
+
+ - class Item_splocal (Item.{cc,h})
+ This is a subclass of Item. Its sole purpose is to hide the fact that
+ the real Item is actually in the current frame (runtime context).
+ It contains the frame offset and defers all methods to the real Item
+ in the frame. This is what the parser generates for local variables.
+
+ - Utility functions (sp.{cc,h})
+ This contains functions for creating, dropping and finding a stored
+ procedure in the mysql.proc table (or the internal cache).
+
+
+ - Parsing CREATE PROCEDURE ...
+
+ When parsing a CREATE PROCEDURE the parser first initializes the
+ sphead and spcont (runtime context) fields in the Lex.
+ The sql_command code for the result of parsing a is
+ SQLCOM_CREATE_PROCEDURE.
+
+ The parsing of the parameter list and body is relatively
+ straight-forward:
+
+ - Parameters:
+ name, type and mode (IN/OUT/INOUT) is pushed to spcont
+ - Declared local variables:
+ Same as parameters (mode is then IN)
+ - Local Variable references:
+ If an identifier is found in in spcont, an Item_splocal is created
+ with the variable's frame index, otherwise an Item_field or Item_ref
+ is created (as before).
+ - Statements:
+ The Lex in THD is replaced by a new Lex structure and the statement,
+ is parsed as usual. A sp_instr_stmt is created, containing the new
+ Lex, and added to added to the instructions in sphead.
+ Afterwards, the procedure's Lex is restored in THD.
+ - SET var:
+ Setting a local variable generates a sp_instr_set instruction,
+ containing the variable's frame offset, the expression (an Item),
+ and the type.
+ - Flow control:
+ Flow control constructs like, IF, WHILE, etc, generate a conditional
+ and unconditional jumps in the "obvious" way, but a few notes may
+ be required:
+ - Forward jumps: When jumping forward, the exact destination is not
+ known at the time of the creation of the jump instruction. The
+ sphead therefore contains list of instruction-label pairs for
+ each forward reference. When the position later is known, the
+ instructions in the list are updated with the correct location.
+ - Loop constructs have optional labels. If a loop doesn't have a
+ label, an anonymous label is generated to simplify the parsing.
+ - There are two types of CASE. The "simple" case is implemented
+ with an anonymous variable bound to the value to be tested.
+
+
+ - A simple example
+
+ Parsing the procedure:
+
+ create procedure a(s char(16))
+ begin
+ declare x int;
+ set x = 3;
+ while x > 0 do
+ set x = x-1;
+ insert into db.tab values (x, s);
+ end while;
+ end
+
+ would generate the following structures:
+ ______
+ thd: | | _________
+ | lex -+--->| | ___________________
+ |______| | spcont -+------------------->| "s",in,char(16):0 |
+ | sphead -+------ |("x",in,int :1)|
+ |_________| | |___________________|
+ ____V__________________
+ | m_name: "a" |
+ | m_defstr: "create ..."|
+ | m_instr: ... |
+ |_______________________|
+
+ Note that the contents of the spcont is changing during the parsing,
+ at all times reflecting the state of the would-be runtime frame.
+ The m_instr is an array of instructions:
+
+ Pos. Instruction
+ 0 sp_instr_set(1, '3')
+ 1 sp_instr_jump_if_not(5, 'x>0')
+ 2 sp_instr_set(1, 'x-1')
+ 3 sp_instr_stmt('insert into ...')
+ 4 sp_instr_jump(1)
+ 5 <end>
+
+ Here, '3', 'x>0', etc, represent the Items or Lex for the respective
+ expressions or statements.
+
+
+ - Parsing CREATE FUNCTION ...
+
+ Creating a functions is essensially the same thing as for a PROCEDURE,
+ with the addition that a FUNCTION has a return type and a RETURN
+ statement, but no OUT or INOUT parameters.
+
+ The main difference during parsing is that we store the result type
+ in the sp_head. However, there are big differences when it comes to
+ invoking a FUNCTION. (See below.)
+
+
+ - Storing, caching, dropping...
+
+ As seen above, the entired definition string, including the "CREATE
+ PROCEDURE" (or "FUNCTION") is kept. The procedure definition string is
+ stored in the table mysql.proc with the name and type as the key, the
+ type being one of the enum ("procedure","function").
+
+ A PROCEDURE is just stored in the mysql.proc table. A FUNCTION has an
+ additional requirement. They will be called in expressions with the same
+ syntax as UDFs, so UDFs and stored FUNCTIONs share the namespace. Thus,
+ we must make sure that we do not have UDFs and FUNCTIONs with the same
+ name (even if they are storded in different places).
+
+ This means that we can reparse the procedure as many time as we want.
+ The first time, the resulting Lex is used to store the procedure in
+ the database (using the function sp.c:sp_create_procedure()).
+
+ The simplest way would be to just leave it at that, and re-read the
+ procedure from the database each time it is called. (And in fact, that's
+ the way the earliest implementation will work.)
+ However, this is not very efficient, and we can do better. The full
+ implementation should work like this:
+
+ 1) Upon creation time, parse and store the procedure. Note that we still
+ need to parse it to catch syntax errors, but we can't check if called
+ procedures exists for instance.
+ 2) Upon first CALL, read from the database, parse it, and cache the
+ resulting Lex in memory. This time we can do more error checking.
+ 3) Upon subsequent CALLs, use the cached Lex.
+
+ Note that this implies that the Lex structure with its sphead must be
+ reentrant, that is, reusable and shareable between different threads
+ and calls. The runtime state for a procedure is kept in the sp_rcontext
+ in THD.
+
+ The mechanisms of storing, finding, and dropping procedures are
+ encapsulated in the files sp.{cc,h}.
+
+
+ - CALLing a procedure
+
+ A CALL is parsed just like any statement. The resulting Lex has the
+ sql_command SQLCOM_CALL, the procedure's name and the parameters are
+ pushed to the Lex' value_list.
+
+ sql_parse.cc:mysql_execute_command() then uses sp.cc:sp_find() to
+ get the sp_head for the procedure (which may have been read from the
+ database or feetched from the in-memory cache) and calls the sp_head's
+ method execute().
+ Note: It's important that substatements called by the procedure do not
+ do send_ok(). Fortunately, there is a flag in THD->net to disable
+ this during CALLs. If a substatement fails, it will however send
+ an error back to the client, so the CALL mechanism must return
+ immediately and without sending an error.
+
+ The sp_head::execute() method works as follows:
+
+ 1) Keep a pointer to the old runtime context in THD (if any)
+ 2) Create a new runtime context. The information about the required size
+ is in sp_head's parse time context.
+ 3) Push each parameter (from the CALL's Lex->value_list) to the new
+ context. If it's an OUT or INOUT parameter, the parameter's offset
+ in the caller's frame is set in the new context as well.
+ 4) For each instruction, call its execute() method.
+ The result is a pointer to the next instruction to execute (or NULL)
+ if an error occured.
+ 5) On success, set the new values of the OUT and INOUT parameters in
+ the caller's frame.
+
+ - USE database
+
+ Before executing the instruction we also keeps the current default
+ database (if any). If this was changed during execution (i.e. a "USE"
+ statement has been executed), we restore the current database to the
+ original.
+
+ This is the most useful way to handle USE in procedures. If we didn't,
+ the caller would find himself in a different database after calling
+ a function, which can be confusing.
+ Restoring the database also gives full freedom to the procedure writer:
+ - It's possible to write "general" procedures that are independent of
+ the actual database name.
+ - It's possible to write procedures that work on a particular database
+ by calling USE, without having to use fully qualified table names
+ everywhere (which doesn't help if you want to call other, "general",
+ procedures anyway).
+
+ - Evaluating Items
+
+ There are three occasions where we need to evaluate an expression:
+
+ - When SETing a variable
+ - When CALLing a procedure
+ - When testing an expression for a branch (in IF, WHILE, etc)
+
+ The semantics in stored procedures is "call-by-value", so we have to
+ evaluate any "func" Items at the point of the CALL or SET, otherwise
+ we would get a kind of "lazy" evaluation with unexpected results with
+ respect to OUT parameters for instance.
+ For this the support function, sp_head.cc:eval_func_item() is needed.
+
+
+ - Calling a FUNCTION
+
+ Functions don't have an explicit call keyword like procedures. Instead,
+ they appear in expressions with the conventional syntax "fun(arg, ...)".
+ The problem is that we already have User Defined Functions (UDFs) which
+ are called the same way. A UDF is detected by the lexical analyzer (not
+ the parser!), in the find_keyword() function, and returns a UDF_*_FUNC
+ or UDA_*_SUM token with the udf_func object as the yylval.
+
+ So, stored functions must be handled in a simpilar way, and as a
+ consequence, UDFs and functions must not have the same name.
+
+ - Detecting and parsing a FUNCTION invocation
+
+ The existance of UDFs are checked during the lexical analysis (in
+ sql_lex.cc:find_keyword()). This has the drawback that they must
+ exist before they are refered to, which was ok before SPs existed,
+ but then it becomes a problem. The first implementation of SP FUNCTIONs
+ will work the same way, but this should be fixed a.s.a.p. (This will
+ required some reworking of the way UDFs are handled, which is why it's
+ not done from the start.)
+ For the time being, a FUNCTION is detected the same way, and returns
+ the token SP_FUNC. During the parsing we only check for the *existance*
+ of the function, we don't parse it, since wa can't call the parser
+ recursively.
+
+ When encountering a SP_FUNC with parameters in the expression parser,
+ an instance of the new Item_func_sp class is created. Unlike UDFs, we
+ don't have different classes for different return types, since we at
+ this point don't know the type.
+
+ - Collecting FUNCTIONs to invoke
+
+ A FUNCTION differs from a PROCEDURE in one important aspect: Whereas a
+ PROCEDURE is CALLed as statement by itself, a FUNCTION is invoked
+ "on-the-fly" during the execution of *another* statement.
+ This makes things a lot more complicated compared to CALL:
+ - We can't read and parse the FUNCTION from the mysql.proc table at the
+ point of invocation; the server requires that all tables used are
+ opened and locked at the beginning of the query execution.
+ One "obvious" solution would be to simply push "mysql.proc" to the list
+ of tables used by the query, but this implies a "join" with this table
+ if the query is a select, so it doesn't work (and we can't exclude this
+ table easily; since a priviledged used might in fact want to search
+ the proc table).
+ Another solution would of course be to allow the opening and closing
+ of the mysql.proc table during a query execution, but this it not
+ possible at the present.
+
+ So, the solution is to collect the names of the refered FUNCTIONs during
+ parsing in the lex.
+ Then, before doing anything else in mysql_execute_command(), read all
+ functions from the database an keep them in the THD, where the function
+ sp_find_function() can find them during the execution.
+ Note: Even with an in-memory cache, we must still make sure that the
+ functions are indeed read and cached at this point.
+ The code that read and cache functions from the database must also be
+ invoked recursively for each read FUNCTION to make sure we have *all* the
+ functions we need.
+
+
+ - Parsing DROP PROCEDURE/FUNCTION
+
+ The procedure name is pushed to Lex->value_list.
+ The sql_command code for the result of parsing a is
+ SQLCOM_DROP_PROCEDURE/SQLCOM_DROP_FUNCTION.
+
+ Dropping is done by simply getting the procedure with the sp_find()
+ function and calling sp_drop() (both in sp.{cc,h}).
+
+ DROP PROCEDURE/FUNCTION also supports the non-standard "IF EXISTS",
+ analogous to other DROP statements in MySQL.
+
+
+ - Condition and Handlers
+
+ Condition names are lexical entities and are kept in the parser context
+ just like variables. But, condition are just "aliases" for SQLSTATE
+ strings, or mysqld error codes (which is a non-standard extension in
+ MySQL), and are only used during parsing.
+
+ Handlers comes in three types, CONTINUE, EXIT and UNDO. The latter is
+ like an EXIT handler with an implicit rollback, and is currently not
+ implemented.
+ The EXIT handler jumps to the end of its BEGIN-END block when finished.
+ The CONTINUE handler returns to the statement following that which
+ invoked the handler.
+
+ The handlers in effect at any point is part of each thread's runtime
+ state, so we need to push and pop handlers in the sp_rcontext during
+ execution. We use special instructions for this:
+ - sp_instr_hpush_jump
+ Push a handler. The instruction contains the necessary information,
+ like which conditions we handle and the location of the handler.
+ The jump takes us to the location after the handler code.
+ - sp_instr_hpop
+ Pop the handlers of the current frame (which we are just leaving).
+
+ It might seems strange to jump past the handlers like that, but there's
+ no extra cost in doing this, and for technical reasons it's easiest for
+ the parser to generate the handler instructions when they occur in the
+ source.
+
+ When an error occurs, one of the error routines is called and an error
+ message is normally sent back to the client immediately.
+ Catching a condition must be done in these error routines (there are
+ quite a few) to prevent them from doing this. We do this by calling
+ a method in the THD's sp_rcontext (if there is one). If a handler is
+ found, this is recorded in the context and the routine returns without
+ sending the error message.
+ The exectution loop (sp_head::execute()) checks for this after each
+ statement and invokes the handler that has been found. If several
+ errors or warnings occurs during one statement, only the first is
+ caught, the rest are ignored.
+
+ Invoking and returning from a handler is trivial in the EXIT case.
+ We simply jump to it, and it will have an sp_instr_jump as its last
+ instruction.
+
+ Calling and returning from a CONTINUE handler poses some special
+ problems. Since we need to return to the point after its invokation,
+ we push the return location on a stack in the sp_rcontext (this is
+ done by the exectution loop). The handler then ends with a special
+ instruction, sp_instr_hreturn, which returns to this location.
+
+ CONTINUE handlers have one additional problem: They are parsed at
+ the lexical level where they occur, so variable offsets will assume
+ that it's actually called at that level. However, a handler might be
+ invoked from a sub-block where additional local variables have been
+ declared, which will then share the location of any local variables
+ in the handler itself. So, when calling a CONTINUE handler, we need
+ to save any local variables above the handler's frame offset, and
+ restore them upon return. (This is not a problem for EXIT handlers,
+ since they will leave the block anyway.)
+ This is taken care of by the execution loop and the sp_instr_hreturn
+ instruction.
+
+ - Examples:
+
+ - EXIT handler
+ begin
+ declare x int default 0;
+
+ begin
+ declare exit handler for 'XXXXX' set x = 1;
+
+ (statement1);
+ (statement2);
+ end;
+ (statement3);
+ end
+
+ Pos. Instruction
+ 0 sp_instr_set(0, '0')
+ 1 sp_instr_hpush_jump(4, 1) # location and frame size
+ 2 sp_instr_set(0, '1')
+ 3 sp_instr_jump(6)
+ 4 sp_instr_stmt('statement1')
+ 5 sp_instr_stmt('statement2')
+ 6 sp_instr_hpop(1)
+ 7 sp_instr_stmt('statement3')
+
+ - CONTINUE handler
+ create procedure hndlr1(val int)
+ begin
+ declare x int default 0;
+ declare foo condition for 1146;
+ declare continue handler for foo set x = 1;
+
+ insert into t3 values ("hndlr1", val); # Non-existing table?
+ if x>0 then
+ insert into t1 values ("hndlr1", val); # This instead then
+ end if;
+ end|
+
+ Pos. Instruction
+ 0 sp_instr_set(1, '0')
+ 1 sp_instr_hpush_jump(4, 2)
+ 2 sp_instr_set(1, '1')
+ 3 sp_instr_hreturn(2) # frame size
+ 4 sp_instr_stmt('insert ... t3 ...')
+ 5 sp_instr_jump_if_not(7, 'x>0')
+ 6 sp_instr_stmt('insert ... t1 ...')
+ 7 sp_instr_hpop(2)
+
+
+ - Cursors
+
+ For stored procedures to be really useful, you want to have cursors.
+ MySQL doesn't yet have "real" cursor support (with API and ODBC support,
+ allowing updating, arbitrary scrolling, etc), but a simple asensitive,
+ non-scrolling, read-only cursor can be implemented in SPs using the
+ class Protocol_cursor.
+ This class intecepts the creation and sending of results sets and instead
+ stores it in-memory, as MYSQL_FIELDS and MYSQL_ROWS (as in the client API).
+
+ To support this, we need the usual name binding support in sp_pcontext
+ (similar to variables and conditions) to keep track on declared cursor
+ names, and a corresponding run-time mechanism in sp_rcontext.
+ Cursors are lexically scoped like everything with a body or BEGIN/END
+ block, so they are pushed and poped as usual (see conditions and variables
+ above).
+ The basic operations on a cursor are OPEN, FETCH and CLOSE, which will
+ each have a corresponding instruction. In addition, we need instructions
+ to push a new cursor (this will encapsulate the LEX of the SELECT statement
+ of the cursor), and a pop instruction:
+ - sp_instr_cpush
+ Push a cursor to the sp_rcontext. This instruction contains the LEX
+ for the select statement
+ - sp_instr_cpop
+ Pop a number of cursors from the sp_rcontext.
+ - sp_instr_copen
+ Open a cursor: This will execute the select and get the result set
+ in a sepeate memroot.
+ - sp_instr_cfetch
+ Fetch the next row from the in-memory result set. The instruction
+ contains a list of the variables (frame offsets) to set.
+ - sp_instr_cclose
+ Free the result set.
+
+ A cursor is a separate class, sp_cursor (defined in sp_rcontex.h) which
+ encapsulates the basic operations used by the above instructions.
+ This class contains the LEX, Protocol_cursor object, and its memroot,
+ as well as the cursor's current state.
+ Compiling and executing is fairly straight-forward. sp_instr_copen is
+ a subclass of sp_instr_stmt and uses its mechanism to execute a
+ substatement.
+
+ - Example:
+
+ begin
+ declare x int;
+ declare c cursor for select a from t1;
+
+ open c;
+ fetch c into x;
+ close c;
+ end
+
+ Pos. Instruction
+ 0 sp_instr_cpush('select a from ...')
+ 1 sp_instr_copen(0) # The 0'th cursor
+ 2 sp_instr_cfetch(0) # Contains the variable list
+ 3 sp_instr_cclose(0)
+ 4 sp_instr_cpop(1)
+
+
+
+ - The SP cache
+
+ There are two ways to cache SPs:
+
+ 1) one global cache, share by all threads/connections,
+ 2) one cache per thread.
+
+ There are pros and cons with both methods:
+
+ 1) Pros: Save memory, each SP only read from table once,
+ Cons: Needs locking (= serialization at access), requires thread-safe
+ data structures,
+ 2) Pros: Fast, no locking required (almost), limited thread-safe
+ requirement,
+ Cons: Uses more memory, each SP read from table once per thread.
+
+ Unfortunately, we cannot use alternative 1 for the time being, as most
+ of the datastructures to be cached (lex and items) are not reentrant
+ and thread-safe. (Things are modifed at execution, we have THD pointers
+ stored everywhere, etc.)
+ This leaves us with alternative 2, one cache per thread; or actually
+ two, since we keep FUNCTIONs and PROCEDUREs in separate caches.
+ This is not that terrible; the only case when it will perform
+ significantly worse than a global cache is when we have an application
+ where new threads are connecting, calling a procedure, and disconnecting,
+ over and over again.
+
+ The cache implementation itself is simple and straightforward, a hashtable
+ wrapped in a class and a C API (see APIs below).
+
+ There is however one issue with multiple caches: dropping and altering
+ procedures. Normally, this should be a very rare event in a running
+ system; it's typically something you do during development and testing,
+ so it's not unthinkable that we would simply ignore the issue and let
+ any threads running with a cached version of an SP keep doing so until
+ its disconnected.
+ But assuming we want to keep the caches consistent with respect to drop
+ and alter, it can be done:
+
+ 1) A global counter is needed, initialized to 0 at start.
+ 2) At each DROP or ALTER, increase the counter by one.
+ 3) Each cache has its own copy of the counter, copied at the last read.
+ 4) When looking up a name in the cache, first check if the global counter
+ is larger than the local copy.
+ If so, clear the cache and return "not found", and update the local
+ counter; otherwise, lookup as usual.
+
+ This minimizes the cost to a single brief lock for the access of an
+ integer when operating normally. Only in the event of an actual drop or
+ alter, is the cache cleared. This may seem to be drastic, but since we
+ assume that this is a rare event, it's not a problem.
+ It would of course be possible to have a much more fine-grained solution,
+ keeping track of each SP, but the overhead of doing so is not worth the
+ effort.
+
+
+ - Class and function APIs
+ This is an outline of the key types. Some types and other details
+ in the actual files have been omitted for readability.
+
+ - The parser context: sp_pcontext.h
+
+ typedef enum
+ {
+ sp_param_in,
+ sp_param_out,
+ sp_param_inout
+ } sp_param_mode_t;
+
+ typedef struct
+ {
+ LEX_STRING name;
+ enum enum_field_types type;
+ sp_param_mode_t mode;
+ uint offset; // Offset in current frame
+ my_bool isset;
+ } sp_pvar_t;
+
+ typedef struct sp_cond_type
+ {
+ enum { number, state, warning, notfound, exception } type;
+ char sqlstate[6];
+ uint mysqlerr;
+ } sp_cond_type_t;
+
+ class sp_pcontext
+ {
+ sp_pcontext();
+
+ // Return the maximum frame size
+ uint max_framesize();
+
+ // Return the current frame size
+ uint current_framesize();
+
+ // Return the number of parameters
+ uint params();
+
+ // Set the number of parameters to the current frame size
+ void set_params();
+
+ // Set type of the variable at offset 'i' in the frame
+ void set_type(uint i, enum enum_field_types type);
+
+ // Mark the i:th variable to "set" (i.e. having a value) with
+ // 'val' true.
+ void set_isset(uint i, my_bool val);
+
+ // Push the variable 'name' to the frame.
+ void push_var(LEX_STRING *name,
+ enum enum_field_types type, sp_param_mode_t mode);
+
+ // Pop 'num' variables from the frame.
+ void pop_var(uint num = 1);
+
+ // Find variable by name
+ sp_pvar_t *find_pvar(LEX_STRING *name);
+
+ // Find variable by index
+ sp_pvar_t *find_pvar(uint i);
+
+ // Push label 'name' of instruction index 'ip' to the label context
+ sp_label_t *push_label(char *name, uint ip);
+
+ // Find label 'name' in the context
+ sp_label_t *find_label(char *name);
+
+ // Return the last pushed label
+ sp_label_t *last_label();
+
+ // Return and remove the last pushed label.
+ sp_label_t *pop_label();
+
+ // Push a condition to the context
+ void push_cond(LEX_STRING *name, sp_cond_type_t *val);
+
+ // Pop a 'num' condition from the context
+ void pop_cond(uint num);
+
+ // Find a condition in the context
+ sp_cond_type_t *find_cond(LEX_STRING *name);
+
+ // Increase the handler count
+ void add_handler();
+
+ // Returns the handler count
+ uint handlers();
+
+ // Push a cursor
+ void push_cursor(LEX_STRING *name);
+
+ // Find a cursor
+ my_bool find_cursor(LEX_STRING *name, uint *poff);
+
+ // Pop 'num' cursors
+ void pop_cursor(uint num);
+
+ // Return the number of cursors
+ uint cursors();
+ }
+
+
+ - The run-time context (call frame): sp_rcontext.h
+
+ #define SP_HANDLER_NONE 0
+ #define SP_HANDLER_EXIT 1
+ #define SP_HANDLER_CONTINUE 2
+ #define SP_HANDLER_UNDO 3
+
+ typedef struct
+ {
+ struct sp_cond_type *cond;
+ uint handler; // Location of handler
+ int type;
+ uint foffset; // Frame offset for the handlers declare level
+ } sp_handler_t;
+
+ class sp_rcontext
+ {
+ // 'fsize' is the max size of the context, 'hmax' the number of handlers,
+ // 'cmax' the number of cursors
+ sp_rcontext(uint fsize, uint hmax, , uint cmax);
+
+ // Push value (parameter) 'i' to the frame
+ void push_item(Item *i);
+
+ // Set slot 'idx' to value 'i'
+ void set_item(uint idx, Item *i);
+
+ // Return the item in slot 'idx'
+ Item *get_item(uint idx);
+
+ // Set the "out" index 'oidx' for slot 'idx. If it's an IN slot,
+ // use 'oidx' -1.
+ void set_oindex(uint idx, int oidx);
+
+ // Return the "out" index for slot 'idx'
+ int get_oindex(uint idx);
+
+ // Set the FUNCTION result
+ void set_result(Item *i);
+
+ // Get the FUNCTION result
+ Item *get_result();
+
+ // Push handler at location 'h' for condition 'cond'. 'f' is the
+ // current variable frame size.
+ void push_handler(sp_cond_type_t *cond, uint h, int type, uint f);
+
+ // Pop 'count' handlers
+ void pop_handlers(uint count);
+
+ // Find a handler for this error. This sets the state for a found
+ // handler in the context. If called repeatedly without clearing,
+ // only the first call's state is kept.
+ int find_handler(uint sql_errno);
+
+ // Returns 1 if a handler has been found, with '*ip' and '*fp' set
+ // to the handler location and frame size respectively.
+ int found_handler(uint *ip, uint *fp);
+
+ // Clear the found handler state.
+ void clear_handler();
+
+ // Push a return address for a CONTINUE handler
+ void push_hstack(uint ip);
+
+ // Pop the CONTINUE handler return stack
+ uint pop_hstack();
+
+ // Save variables from frame index 'fp' and up.
+ void save_variables(uint fp);
+
+ // Restore saved variables from to frame index 'fp' and up.
+ void restore_variables(uint fp);
+
+ // Push a cursor for the statement (lex)
+ void push_cursor(LEX *lex);
+
+ // Pop 'count' cursors
+ void pop_cursors(uint count);
+
+ // Pop all cursors
+ void pop_all_cursors();
+
+ // Get the 'i'th cursor
+ sp_cursor *get_cursor(uint i);
+
+ }
+
+
+ - The procedure: sp_head.h
+
+ #define TYPE_ENUM_FUNCTION 1
+ #define TYPE_ENUM_PROCEDURE 2
+
+ class sp_head
+ {
+ int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
+
+ sp_head();
+
+ void init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid);
+
+ // Store this procedure in the database. This is a wrapper around
+ // the function sp_create_procedure().
+ int create(THD *);
+
+ // Invoke a FUNCTION
+ int
+ execute_function(THD *thd, Item **args, uint argcount, Item **resp);
+
+ // CALL a PROCEDURE
+ int
+ execute_procedure(THD *thd, List<Item> *args);
+
+ // Add the instruction to this procedure.
+ void add_instr(sp_instr *);
+
+ // Returns the number of instructions.
+ uint instructions();
+
+ // Returns the last instruction
+ sp_instr *last_instruction();
+
+ // Resets lex in 'thd' and keeps a copy of the old one.
+ void reset_lex(THD *);
+
+ // Restores lex in 'thd' from our copy, but keeps some status from the
+ // one in 'thd', like ptr, tables, fields, etc.
+ void restore_lex(THD *);
+
+ // Put the instruction on the backpatch list, associated with
+ // the label.
+ void push_backpatch(sp_instr *, struct sp_label *);
+
+ // Update all instruction with this label in the backpatch list to
+ // the current position.
+ void backpatch(struct sp_label *);
+
+ // Returns the SP name (with optional length in '*lenp').
+ char *name(uint *lenp = 0);
+
+ // Returns the result type for a function
+ Item_result result();
+
+ // Sets various attributes
+ void sp_set_info(char *creator, uint creatorlen,
+ longlong created, longlong modified,
+ bool suid, char *comment, uint commentlen);
+ }
+
+
+ - Instructions
+
+ - The base class:
+ class sp_instr
+ {
+ // 'ip' is the index of this instruction
+ sp_instr(uint ip);
+
+ // Execute this instrution.
+ // '*nextp' will be set to the index of the next instruction
+ // to execute. (For most instruction this will be the
+ // instruction following this one.)
+ // Returns 0 on success, non-zero if some error occured.
+ virtual int execute(THD *, uint *nextp)
+ }
+
+ - Statement instruction:
+ class sp_instr_stmt : public sp_instr
+ {
+ sp_instr_stmt(uint ip);
+
+ int execute(THD *, uint *nextp);
+
+ // Set the statement's Lex
+ void set_lex(LEX *);
+
+ // Return the statement's Lex
+ LEX *get_lex();
+ }
+
+ - SET instruction:
+ class sp_instr_set : public sp_instr
+ {
+ // 'offset' is the variable's frame offset, 'val' the value,
+ // and 'type' the variable type.
+ sp_instr_set(uint ip,
+ uint offset, Item *val, enum enum_field_types type);
+
+ int execute(THD *, uint *nextp);
+ }
+
+ - Unconditional jump
+ class sp_instr_jump : public sp_instr
+ {
+ // No destination, must be set.
+ sp_instr_jump(uint ip);
+
+ // 'dest' is the destination instruction index.
+ sp_instr_jump(uint ip, uint dest);
+
+ int execute(THD *, uint *nextp);
+
+ // Set the destination instruction 'dest'.
+ void set_destination(uint dest);
+ }
+
+ - Conditional jump
+ class sp_instr_jump_if_not : public sp_instr_jump
+ {
+ // Jump if 'i' evaluates to false. Destination not set yet.
+ sp_instr_jump_if_not(uint ip, Item *i);
+
+ // Jump to 'dest' if 'i' evaluates to false.
+ sp_instr_jump_if_not(uint ip, Item *i, uint dest)
+
+ int execute(THD *, uint *nextp);
+ }
+
+ - Return a function value
+ class sp_instr_freturn : public sp_instr
+ {
+ // Return the value 'val'
+ sp_instr_freturn(uint ip, Item *val, enum enum_field_types type);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Push a handler and jump
+ class sp_instr_hpush_jump : public sp_instr_jump
+ {
+ // Push handler of type 'htype', with current frame size 'fp'
+ sp_instr_hpush_jump(uint ip, int htype, uint fp);
+
+ int execute(THD *thd, uint *nextp);
+
+ // Add condition for this handler
+ void add_condition(struct sp_cond_type *cond);
+ }
+
+ - Pops handlers
+ class sp_instr_hpop : public sp_instr
+ {
+ // Pop 'count' handlers
+ sp_instr_hpop(uint ip, uint count);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Return from a CONTINUE handler
+ class sp_instr_hreturn : public sp_instr
+ {
+ // Return from handler, and restore variables to 'fp'.
+ sp_instr_hreturn(uint ip, uint fp);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Push a CURSOR
+ class sp_instr_cpush : public sp_instr_stmt
+ {
+ // Push a cursor for statement 'lex'
+ sp_instr_cpush(uint ip, LEX *lex)
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Pop CURSORs
+ class sp_instr_cpop : public sp_instr_stmt
+ {
+ // Pop 'count' cursors
+ sp_instr_cpop(uint ip, uint count)
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Open a CURSOR
+ class sp_instr_copen : public sp_instr_stmt
+ {
+ // Open the 'c'th cursor
+ sp_instr_copen(uint ip, uint c);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Close a CURSOR
+ class sp_instr_cclose : public sp_instr
+ {
+ // Close the 'c'th cursor
+ sp_instr_cclose(uint ip, uint c);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Fetch a row with CURSOR
+ class sp_instr_cfetch : public sp_instr
+ {
+ // Fetch next with the 'c'th cursor
+ sp_instr_cfetch(uint ip, uint c);
+
+ int execute(THD *thd, uint *nextp);
+
+ // Add a target variable for the fetch
+ void add_to_varlist(struct sp_pvar *var);
+ }
+
+
+ - Utility functions: sp.h
+
+ #define SP_OK 0
+ #define SP_KEY_NOT_FOUND -1
+ #define SP_OPEN_TABLE_FAILED -2
+ #define SP_WRITE_ROW_FAILED -3
+ #define SP_DELETE_ROW_FAILED -4
+ #define SP_GET_FIELD_FAILED -5
+ #define SP_PARSE_ERROR -6
+
+ // Finds a stored procedure given its name. Returns NULL if not found.
+ sp_head *sp_find_procedure(THD *, LEX_STRING *name);
+
+ // Store the procedure 'name' in the database. 'def' is the complete
+ // definition string ("create procedure ...").
+ int sp_create_procedure(THD *,
+ char *name, uint namelen,
+ char *def, uint deflen,
+ char *comment, uint commentlen, bool suid);
+
+ // Drop the procedure 'name' from the database.
+ int sp_drop_procedure(THD *, char *name, uint namelen);
+
+ // Finds a stored function given its name. Returns NULL if not found.
+ sp_head *sp_find_function(THD *, LEX_STRING *name);
+
+ // Store the function 'name' in the database. 'def' is the complete
+ // definition string ("create function ...").
+ int sp_create_function(THD *,
+ char *name, uint namelen,
+ char *def, uint deflen,
+ char *comment, uint commentlen, bool suid);
+
+ // Drop the function 'name' from the database.
+ int sp_drop_function(THD *, char *name, uint namelen);
+
+
+ - The cache: sp_cache.h
+
+ /* Initialize the SP caching once at startup */
+ void sp_cache_init();
+
+ /* Clear the cache *cp and set *cp to NULL */
+ void sp_cache_clear(sp_cache **cp);
+
+ /* Insert an SP to cache. If **cp points to NULL, it's set to a
+ new cache */
+ void sp_cache_insert(sp_cache **cp, sp_head *sp);
+
+ /* Lookup an SP in cache */
+ sp_head *sp_cache_lookup(sp_cache **cp, char *name, uint namelen);
+
+ /* Remove an SP from cache */
+ void sp_cache_remove(sp_cache **cp, sp_head *sp);
+
+
+ - The mysql.proc schema:
+
+ CREATE TABLE proc (
+ db char(64) binary DEFAULT '' NOT NULL,
+ name char(64) DEFAULT '' NOT NULL,
+ type enum('FUNCTION','PROCEDURE') NOT NULL,
+ specific_name char(64) DEFAULT '' NOT NULL,
+ language enum('SQL') DEFAULT 'SQL' NOT NULL,
+ sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL,
+ is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL,
+ security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL,
+ param_list blob DEFAULT '' NOT NULL,
+ returns char(64) DEFAULT '' NOT NULL,
+ body blob DEFAULT '' NOT NULL,
+ definer char(77) binary DEFAULT '' NOT NULL,
+ created timestamp,
+ modified timestamp,
+ sql_mode set(
+ 'REAL_AS_FLOAT',
+ 'PIPES_AS_CONCAT',
+ 'ANSI_QUOTES',
+ 'IGNORE_SPACE',
+ 'NOT_USED',
+ 'ONLY_FULL_GROUP_BY',
+ 'NO_UNSIGNED_SUBTRACTION',
+ 'NO_DIR_IN_CREATE',
+ 'POSTGRESQL',
+ 'ORACLE',
+ 'MSSQL',
+ 'DB2',
+ 'MAXDB',
+ 'NO_KEY_OPTIONS',
+ 'NO_TABLE_OPTIONS',
+ 'NO_FIELD_OPTIONS',
+ 'MYSQL323',
+ 'MYSQL40',
+ 'ANSI',
+ 'NO_AUTO_VALUE_ON_ZERO'
+ ) DEFAULT 0 NOT NULL,
+ comment char(64) binary DEFAULT '' NOT NULL,
+ PRIMARY KEY (db,name,type)
+ ) comment='Stored Procedures';
+
+ --
+ \ No newline at end of file
diff --git a/Docs/sp-implemented.txt b/Docs/sp-implemented.txt
new file mode 100644
index 00000000000..6f2cf49b3b0
--- /dev/null
+++ b/Docs/sp-implemented.txt
@@ -0,0 +1,112 @@
+Stored Procedures implemented 2004-01-29:
+
+
+Summary of what's implemented:
+
+ - SQL PROCEDUREs/FUNCTIONs (CREATE/DROP)
+ - CALL
+ - DECLARE of local variables
+ - BEGIN/END, SET, CASE, IF, LOOP, WHILE, REPEAT, ITERATE, LEAVE
+ - SELECT INTO local variables
+ - "Non-query" FUNCTIONs only
+ - Prepared SP caching
+ - CONDITIONs and HANDLERs
+ - Simple read-only CURSORs.
+ - SHOW CREATE PROCEDURE/FUNCTION and SHOW PROCEDURE/FUNCTION STATUS
+
+
+Summary of Not Yet Implemented:
+
+ - SQL statements using tables (like SELECT, INSERT, UPDATE etc) in FUNCTIONs
+ - External languages
+ - Access control
+ - SQL-99 COMMIT (related to BEGIN/END)
+ - FOR-loops
+ - CASCADE/RESTRICT for ALTER and DROP
+ - ALTER/DROP METHOD (as it implies User Defined Types)
+ - SIGNAL and RESIGNAL, and UNDO handlers
+
+
+List of what's implemented:
+
+ - CREATE PROCEDURE|FUNCTION name ( args ) characteristics body
+ where characteristics is:
+ LANGUAGE SQL |
+ [NOT] DETERMINISTIC |
+ SQL SECURITY [DEFINER|INVOKER] |
+ COMMENT string
+ However the DETERMINISTIC setting is not currently used.
+
+ - ALTER PROCEDURE|FUNCTION name characteristics
+ CASCADE/RESTRICT is not implemented.
+ characteristics is:
+ COMMENT string |
+ SQL SECURITY [DEFINER|INVOKER] |
+ NAME newname
+
+ - DROP PROCEDURE|FUNCTION [IF EXISTS] name
+ CASCADE/RESTRICT is not implemented.
+
+ - CALL name (args)
+ OUT and INOUT parameters are also works for user variables ("global"
+ variables) - i.e., if a procedure is defined as:
+ CREATE PROCEDURE foo(OUT p INT) ...;
+ a call like:
+ CALL foo(@x);
+ will set @x to the output value.
+
+ - Function/Procedure body:
+ - BEGIN/END
+ Is parsed, but not the real thing with (optional) transaction
+ control, it only serves as block syntax for multiple statements (and
+ local variable binding).
+ Note: Multiple statements requires a client that can send bodies
+ containing ";". This is handled in the CLI clients mysql and
+ mysqltest with the "delimiter" command. Changing the end-of-query
+ delimiter ";" to for instance "|" allows ";" to be used in the
+ routine body.
+ - SET of local variables
+ Implemented as part of the pre-existing SET syntax. This allows an
+ extended syntax of "SET a=x, b=y, ..." where different variable types
+ (SP local and global) can be mixed. This also allows combinations
+ of local variables and some options that only make sense for
+ global/system variables; in that case the options are accepted but
+ ignored.
+ - The flow control constructs: CASE, IF, LOOP, WHILE, ITERATE and LEAVE
+ are fully implemented.
+ - SELECT ... INTO local variables (as well as global session variables)
+ is implemented. (Note: This is not SQL-99 feature, but common in other
+ databases.)
+ - A FUNCTION can have flow control contructs, but must not contain
+ an SQL query/statement, like SELECT, INSERT, UPDATE, etc. The reason
+ is that it's hard to allow this is that a FUNCTION is executed as part
+ of another query (unlike a PROCEDURE, which is called as a statement).
+ The table locking scheme used makes it difficult to allow "subqueries"
+ during FUNCTION invokation.
+ - SPs are cached, but with a separate cache for each thread (THD).
+ There are still quite a few non-reentrant constructs in the lexical
+ context which makes sharing prepared SPs impossible. And, even when
+ this is resolved, it's not necessarily the case that it will be faster
+ than a cache per thread. A global cache requires locks, which might
+ become a bottleneck. (It would save memory though.)
+ - CONDITIONs and HANDLERs are implemented, but not the SIGNAL and
+ RESIGNAL statements. (It's unclear if these can be implemented.)
+ The semantics of CONDITIONs is expanded to allow catching MySQL error
+ codes as well. UNDO handlers are not implemented (since we don't have
+ SQL-99 style transaction control yet).
+ - Simple read-only CURSORs are implemented, but not yet any of the
+ optional arguments to DECLARE (SCROLL, SENSITIVE, etc) or FETCH
+ (NEXT, PRIOR, etc). Cursors are ASENSITIVE, READ-ONLY, non-SCROLLing.
+ (The additional syntax will be added for completeness, but for the
+ most part unsupported with the current underlying cursor mechanism.)
+ N.B. The current implementation is temporary and only works within a
+ stored procedure, and may not perform well for very large result sets.
+ A "real" cursor implementation is under development; this will replace
+ the current one when it's finished.
+
+ - SHOW procedures and functions
+ SHOW CREATE PROCEDURE|FUNCTION <name>
+ returns the definition of a routine.
+ SHOW PROCEDURE|FUNCTION STATUS [LIKE <pattern>]
+ returns characteristics of routines, like the name, type, creator,
+ creation and modification dates, etc.
diff --git a/VC++Files/libmysqld/libmysqld.dsp b/VC++Files/libmysqld/libmysqld.dsp
index 820ca30509c..6b0e29f08fd 100644
--- a/VC++Files/libmysqld/libmysqld.dsp
+++ b/VC++Files/libmysqld/libmysqld.dsp
@@ -351,6 +351,10 @@ SOURCE=..\mysys\my_alloc.c
SOURCE=..\mysys\my_getopt.c
# End Source File
# Begin Source File
+
+SOURCE=..\sql-common\my_time.c
+# End Source File
+# Begin Source File
SOURCE=..\sql\net_serv.cpp
# End Source File
@@ -364,11 +368,11 @@ SOURCE=..\sql\opt_sum.cpp
# End Source File
# Begin Source File
-SOURCE="..\sql-common\pack.c"
+SOURCE=..\sql-common\pack.c
# End Source File
# Begin Source File
-SOURCE=..\sql-common\my_time.c
+SOURCE=..\sql\parse_file.cpp
# End Source File
# Begin Source File
@@ -384,6 +388,10 @@ SOURCE=..\sql\protocol.cpp
# End Source File
# Begin Source File
+SOURCE=..\sql\protocol_cursor.cpp
+# End Source File
+# Begin Source File
+
SOURCE=..\sql\records.cpp
# End Source File
# Begin Source File
@@ -396,6 +404,26 @@ SOURCE=..\sql\set_var.cpp
# End Source File
# Begin Source File
+SOURCE=..\sql\sp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\sql\sp_cache.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\sql\sp_head.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\sql\sp_pcontext.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\sql\sp_rcontext.cpp
+# End Source File
+# Begin Source File
+
SOURCE=..\sql\spatial.cpp
# End Source File
# Begin Source File
@@ -525,6 +553,10 @@ SOURCE=..\sql\sql_union.cpp
# Begin Source File
SOURCE=..\sql\sql_update.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\sql\sql_view.cpp
# End Source File
# Begin Source File
diff --git a/VC++Files/sql/mysqld.dsp b/VC++Files/sql/mysqld.dsp
index 31c52009d9f..c4475427823 100644
--- a/VC++Files/sql/mysqld.dsp
+++ b/VC++Files/sql/mysqld.dsp
@@ -1025,8 +1025,12 @@ SOURCE=.\mf_iocache.cpp
!ELSEIF "$(CFG)" == "mysqld - Win32 pro nt"
-!ENDIF
+!ENDIF
+
+# End Source File
+# Begin Source File
+SOURCE=.\my_time.c
# End Source File
# Begin Source File
@@ -1136,7 +1140,7 @@ SOURCE=.\pack.c
# End Source File
# Begin Source File
-SOURCE=.\my_time.c
+SOURCE=.\parse_file.cpp
# End Source File
# Begin Source File
@@ -1199,6 +1203,10 @@ SOURCE=.\protocol.cpp
# End Source File
# Begin Source File
+SOURCE=.\protocol_cursor.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\records.cpp
!IF "$(CFG)" == "mysqld - Win32 Release"
@@ -1239,6 +1247,26 @@ SOURCE=.\slave.cpp
# End Source File
# Begin Source File
+SOURCE=.\sp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\sp_cache.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\sp_head.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\sp_pcontext.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\sp_rcontext.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\spatial.cpp
# End Source File
# Begin Source File
@@ -1782,8 +1810,12 @@ SOURCE=.\sql_update.cpp
!ELSEIF "$(CFG)" == "mysqld - Win32 pro nt"
-!ENDIF
+!ENDIF
+
+# End Source File
+# Begin Source File
+SOURCE=.\sql_view.cpp
# End Source File
# Begin Source File
diff --git a/client/Makefile.am b/client/Makefile.am
index 2db1e49850d..7400717731d 100644
--- a/client/Makefile.am
+++ b/client/Makefile.am
@@ -40,7 +40,7 @@ mysqlbinlog_SOURCES = mysqlbinlog.cc ../mysys/mf_tempdir.c
mysqlbinlog_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB)
mysqlmanagerc_SOURCES = mysqlmanagerc.c
mysqlmanagerc_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) $(DEPLIB)
-sql_src=log_event.h log_event.cc
+sql_src=log_event.h mysql_priv.h log_event.cc
# Fix for mit-threads
DEFS = -DUNDEF_THREADS_HACK
diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc
index 5f9a499bd31..538ec4982c1 100644
--- a/client/mysqlbinlog.cc
+++ b/client/mysqlbinlog.cc
@@ -14,11 +14,27 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/*
+
+ TODO: print the catalog (some USE catalog.db ????).
+
+ Standalone program to read a MySQL binary log (or relay log);
+ can read files produced by 3.23, 4.x, 5.0 servers.
+
+ Can read binlogs from 3.23/4.x/5.0 and relay logs from 4.x/5.0.
+ Should be able to read any file of these categories, even with --position.
+ An important fact: the Format_desc event of the log is at most the 3rd event
+ of the log; if it is the 3rd then there is this combination:
+ Format_desc_of_slave, Rotate_of_master, Format_desc_of_master.
+*/
+
#define MYSQL_CLIENT
#undef MYSQL_SERVER
#include "client_priv.h"
#include <my_time.h>
#include "log_event.h"
+/* That one is necessary for defines of OPTION_NO_FOREIGN_KEY_CHECKS etc */
+#include "mysql_priv.h"
#define BIN_LOG_HEADER_SIZE 4
#define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4)
@@ -66,6 +82,14 @@ static MYSQL* mysql = NULL;
static const char* dirname_for_local_load= 0;
static bool stop_passed= 0;
+/*
+ check_header() will set the pointer below.
+ Why do we need here a pointer on an event instead of an event ?
+ This is because the event will be created (alloced) in read_log_event()
+ (which returns a pointer) in check_header().
+*/
+Format_description_log_event* description_event;
+
static int dump_local_log_entries(const char* logname);
static int dump_remote_log_entries(const char* logname);
static int dump_log_entries(const char* logname);
@@ -310,16 +334,26 @@ Create_file event for file_id: %u\n",ae->file_id);
Load_log_processor load_processor;
+
/*
+ Process an event
+
+ SYNOPSIS
+ process_event()
+
RETURN
0 ok and continue
1 error and terminate
-1 ok and terminate
-
+
TODO
This function returns 0 even in some error cases. This should be changed.
*/
-int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
+
+
+
+int process_event(LAST_EVENT_INFO *last_event_info, Log_event *ev,
+ my_off_t pos)
{
char ll_buff[21];
DBUG_ENTER("process_event");
@@ -351,7 +385,7 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
if ((log_dbname != NULL) && (strcmp(log_dbname, database)))
goto end;
}
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, last_event_info);
break;
case CREATE_FILE_EVENT:
{
@@ -375,8 +409,9 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT'
below.
*/
- ce->print(result_file, short_form, last_db, TRUE);
- if (!old_format)
+ ce->print(result_file, short_form, last_event_info, TRUE);
+ // If this binlog is not 3.23 ; why this test??
+ if (description_event->binlog_version >= 3)
{
if (load_processor.process(ce))
break; // Error
@@ -385,13 +420,13 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
break;
}
case APPEND_BLOCK_EVENT:
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, last_event_info);
if (load_processor.process((Append_block_log_event*) ev))
break; // Error
break;
case EXEC_LOAD_EVENT:
{
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, last_event_info);
Execute_load_log_event *exv= (Execute_load_log_event*)ev;
Create_file_log_event *ce= load_processor.grab_event(exv->file_id);
/*
@@ -401,7 +436,7 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
*/
if (ce)
{
- ce->print(result_file, short_form, last_db, TRUE);
+ ce->print(result_file, short_form, last_event_info, TRUE);
my_free((char*)ce->fname,MYF(MY_WME));
delete ce;
}
@@ -410,8 +445,20 @@ int process_event(char *last_db, Log_event *ev, my_off_t pos, int old_format)
Create_file event for file_id: %u\n",exv->file_id);
break;
}
+ case FORMAT_DESCRIPTION_EVENT:
+ delete description_event;
+ description_event= (Format_description_log_event*) ev;
+ ev->print(result_file, short_form, last_event_info);
+ /*
+ We don't want this event to be deleted now, so let's hide it (I
+ (Guilhem) should later see if this triggers a non-serious Valgrind
+ error). Not serious error, because we will free description_event
+ later.
+ */
+ ev= 0;
+ break;
default:
- ev->print(result_file, short_form, last_db);
+ ev->print(result_file, short_form, last_event_info);
}
}
@@ -552,7 +599,7 @@ static void die(const char* fmt, ...)
static void print_version()
{
- printf("%s Ver 3.0 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
+ printf("%s Ver 3.1 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
NETWARE_SET_SCREEN_MODE(1);
}
@@ -695,12 +742,17 @@ static int dump_log_entries(const char* logname)
}
-static int check_master_version(MYSQL* mysql)
+/*
+ This is not as smart as check_header() (used for local log); it will not work
+ for a binlog which mixes format. TODO: fix this.
+*/
+static int check_master_version(MYSQL* mysql,
+ Format_description_log_event
+ **description_event)
{
MYSQL_RES* res = 0;
MYSQL_ROW row;
const char* version;
- int old_format = 0;
if (mysql_query(mysql, "SELECT VERSION()") ||
!(res = mysql_store_result(mysql)))
@@ -725,11 +777,18 @@ static int check_master_version(MYSQL* mysql)
switch (*version) {
case '3':
- old_format = 1;
+ *description_event= new Format_description_log_event(1);
break;
case '4':
+ *description_event= new Format_description_log_event(3);
case '5':
- old_format = 0;
+ /*
+ The server is soon going to send us its Format_description log
+ event, unless it is a 5.0 server with 3.23 or 4.0 binlogs.
+ So we first assume that this is 4.0 (which is enough to read the
+ Format_desc event if one comes).
+ */
+ *description_event= new Format_description_log_event(3);
break;
default:
sql_print_error("Master reported unrecognized MySQL version '%s'",
@@ -739,17 +798,17 @@ static int check_master_version(MYSQL* mysql)
return 1;
}
mysql_free_result(res);
- return old_format;
+ return 0;
}
static int dump_remote_log_entries(const char* logname)
+
{
char buf[128];
- char last_db[FN_REFLEN+1] = "";
+ LAST_EVENT_INFO last_event_info;
uint len, logname_len;
NET* net;
- int old_format;
int error= 0;
my_off_t old_off= start_position_mot;
char fname[FN_REFLEN+1];
@@ -762,7 +821,18 @@ static int dump_remote_log_entries(const char* logname)
*/
mysql= safe_connect();
net= &mysql->net;
- old_format = check_master_version(mysql);
+
+ if (check_master_version(mysql, &description_event))
+ {
+ fprintf(stderr, "Could not find server version");
+ DBUG_RETURN(1);
+ }
+ if (!description_event || !description_event->is_valid())
+ {
+ fprintf(stderr, "Invalid Format_description log event; \
+could be out of memory");
+ DBUG_RETURN(1);
+ }
/*
COM_BINLOG_DUMP accepts only 4 bytes for the position, so we are forced to
@@ -796,7 +866,8 @@ static int dump_remote_log_entries(const char* logname)
DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
len, net->read_pos[5]));
Log_event *ev = Log_event::read_log_event((const char*) net->read_pos + 1 ,
- len - 1, &error_msg, old_format);
+ len - 1, &error,
+ description_event);
if (!ev)
{
fprintf(stderr, "Could not construct log event object\n");
@@ -805,25 +876,27 @@ static int dump_remote_log_entries(const char* logname)
}
Log_event_type type= ev->get_type_code();
- if (!old_format || ( type != LOAD_EVENT && type != CREATE_FILE_EVENT))
+ if (description_event->binlog_version >= 3 ||
+ (type != LOAD_EVENT && type != CREATE_FILE_EVENT))
{
- if (ev->get_type_code() == ROTATE_EVENT)
+ /*
+ If this is a Rotate event, maybe it's the end of the requested binlog;
+ in this case we are done (stop transfer).
+ This is suitable for binlogs, not relay logs (but for now we don't read
+ relay logs remotely because the server is not able to do that). If one
+ day we read relay logs remotely, then we will have a problem with the
+ detection below: relay logs contain Rotate events which are about the
+ binlogs, so which would trigger the end-detection below.
+ */
+ if (type == ROTATE_EVENT)
{
Rotate_log_event *rev= (Rotate_log_event *)ev;
/*
- mysqld is sending us all its binlogs after the requested one, but we
- don't want them.
If this is a fake Rotate event, and not about our log, we can stop
transfer. If this a real Rotate event (so it's not about our log,
it's in our log describing the next log), we print it (because it's
part of our log) and then we will stop when we receive the fake one
soon.
- This is suitable for binlogs, not relay logs (but for now we don't
- read relay logs remotely because the server is not able to do
- that). If one day we read relay logs remotely, then we will have a
- problem with the detection below: relay logs contain Rotate events
- which are about the binlogs, so which would trigger the end-detection
- below.
*/
if (rev->when == 0)
{
@@ -846,7 +919,7 @@ static int dump_remote_log_entries(const char* logname)
len= 1; // fake Rotate, so don't increment old_off
}
}
- if ((error= process_event(last_db,ev,old_off,old_format)))
+ if ((error= process_event(&last_event_info,ev,old_off)))
{
error= ((error < 0) ? 0 : 1);
goto err;
@@ -858,64 +931,132 @@ static int dump_remote_log_entries(const char* logname)
const char *old_fname= le->fname;
uint old_len= le->fname_len;
File file;
-
+
if ((file= load_processor.prepare_new_file_for_old_format(le,fname)) < 0)
{
error= 1;
goto err;
}
-
- if ((error= process_event(last_db,ev,old_off,old_format)))
+
+ if ((error= process_event(&last_event_info,ev,old_off)))
{
- my_close(file,MYF(MY_WME));
+ my_close(file,MYF(MY_WME));
error= ((error < 0) ? 0 : 1);
goto err;
}
- if (load_processor.load_old_format_file(net,old_fname,old_len,file))
+ error= load_processor.load_old_format_file(net,old_fname,old_len,file);
+ my_close(file,MYF(MY_WME));
+ if (error)
{
- my_close(file,MYF(MY_WME));
error= 1;
goto err;
}
- my_close(file,MYF(MY_WME));
}
-
/*
Let's adjust offset for remote log as for local log to produce
similar text.
*/
old_off+= len-1;
}
+
err:
mysql_close(mysql);
DBUG_RETURN(error);
}
-static int check_header(IO_CACHE* file)
+static void check_header(IO_CACHE* file,
+ Format_description_log_event **description_event)
{
byte header[BIN_LOG_HEADER_SIZE];
byte buf[PROBE_HEADER_LEN];
- int old_format=0;
- DBUG_ENTER("check_header");
+ *description_event= new Format_description_log_event(3);
+ my_off_t tmp_pos;
my_off_t pos = my_b_tell(file);
my_b_seek(file, (my_off_t)0);
if (my_b_read(file, header, sizeof(header)))
die("Failed reading header; Probably an empty file");
if (memcmp(header, BINLOG_MAGIC, sizeof(header)))
die("File is not a binary log file");
- if (!my_b_read(file, buf, sizeof(buf)))
+
+ /*
+ Imagine we are running with --position=1000. We still need to know the
+ binlog format's. So we still need to find, if there is one, the Format_desc
+ event, or to know if this is a 3.23 binlog. So we need to first read the
+ first events of the log, those around offset 4.
+ Even if we are reading a 3.23 binlog from the start (no --position): we need
+ to know the header length (which is 13 in 3.23, 19 in 4.x) to be able to
+ successfully print the first event (Start_log_event_v3). So even in this
+ case, we need to "probe" the first bytes of the log *before* we do a real
+ read_log_event(). Because read_log_event() needs to know the header's length
+ to work fine.
+ */
+ for(;;)
{
- if (buf[4] == START_EVENT)
+ tmp_pos= my_b_tell(file); /* should be 4 the first time */
+ if (my_b_read(file, buf, sizeof(buf)))
+ {
+ if (file->error)
+ die("\
+Could not read entry at offset %lu : Error in log format or read error",
+ tmp_pos);
+ /*
+ Otherwise this is just EOF : this log currently contains 0-2 events.
+ Maybe it's going to be filled in the next milliseconds; then we are
+ going to have a problem if this a 3.23 log (imagine we are locally
+ reading a 3.23 binlog which is being written presently): we won't know
+ it in read_log_event() and will fail().
+ Similar problems could happen with hot relay logs if --position is used
+ (but a --position which is posterior to the current size of the log).
+ These are rare problems anyway (reading a hot log + when we read the
+ first events there are not all there yet + when we read a bit later
+ there are more events + using a strange --position).
+ */
+ break;
+ }
+ else
{
- uint event_len;
- event_len = uint4korr(buf + EVENT_LEN_OFFSET);
- old_format = (event_len < (LOG_EVENT_HEADER_LEN + START_HEADER_LEN));
+ DBUG_PRINT("info",("buf[4]=%d", buf[4]));
+ /* always test for a Start_v3, even if no --position */
+ if (buf[4] == START_EVENT_V3) /* This is 3.23 or 4.x */
+ {
+ if (uint4korr(buf + EVENT_LEN_OFFSET) <
+ (LOG_EVENT_MINIMAL_HEADER_LEN + START_V3_HEADER_LEN))
+ {
+ /* This is 3.23 (format 1) */
+ delete *description_event;
+ *description_event= new Format_description_log_event(1);
+ }
+ break;
+ }
+ else if (tmp_pos>=position)
+ break;
+ else if (buf[4] == FORMAT_DESCRIPTION_EVENT) /* This is 5.0 */
+ {
+ my_b_seek(file, tmp_pos); /* seek back to event's start */
+ if (!(*description_event= (Format_description_log_event*)
+ Log_event::read_log_event(file, *description_event)))
+ /* EOF can't be hit here normally, so it's a real error */
+ die("Could not read a Format_description_log_event event \
+at offset %lu ; this could be a log format error or read error",
+ tmp_pos);
+ DBUG_PRINT("info",("Setting description_event"));
+ }
+ else if (buf[4] == ROTATE_EVENT)
+ {
+ my_b_seek(file, tmp_pos); /* seek back to event's start */
+ if (!Log_event::read_log_event(file, *description_event))
+ /* EOF can't be hit here normally, so it's a real error */
+ die("Could not read a Rotate_log_event event \
+at offset %lu ; this could be a log format error or read error",
+ tmp_pos);
+ }
+ else
+ break;
}
}
my_b_seek(file, pos);
- DBUG_RETURN(old_format);
}
@@ -923,13 +1064,10 @@ static int dump_local_log_entries(const char* logname)
{
File fd = -1;
IO_CACHE cache,*file= &cache;
- char last_db[FN_REFLEN+1];
+ LAST_EVENT_INFO last_event_info;
byte tmp_buff[BIN_LOG_HEADER_SIZE];
- bool old_format = 0;
int error= 0;
- last_db[0]= 0;
-
if (logname && logname[0] != '-')
{
if ((fd = my_open(logname, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0)
@@ -938,16 +1076,16 @@ static int dump_local_log_entries(const char* logname)
MYF(MY_WME | MY_NABP)))
{
my_close(fd, MYF(MY_WME));
- exit(1);
+ return 1;
}
- old_format = check_header(file);
+ check_header(file, &description_event);
}
- else
+ else // reading from stdin; TODO: check that it works
{
if (init_io_cache(file, fileno(result_file), 0, READ_CACHE, (my_off_t) 0,
0, MYF(MY_WME | MY_NABP | MY_DONT_CHECK_FILESIZE)))
return 1;
- old_format = check_header(file);
+ check_header(file, &description_event);
if (start_position)
{
/* skip 'start_position' characters from stdout */
@@ -957,46 +1095,44 @@ static int dump_local_log_entries(const char* logname)
{
tmp=min(length,sizeof(buff));
if (my_b_read(file, buff, (uint) tmp))
- {
- error= 1;
- goto end;
- }
+ {
+ error= 1;
+ goto end;
+ }
}
}
file->pos_in_file= start_position_mot;
file->seek_not_done=0;
}
- if (!start_position)
+ if (!description_event || !description_event->is_valid())
+ die("Invalid Format_description log event; could be out of memory");
+
+ if (!start_position && my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
{
- // Skip header
- if (my_b_read(file, tmp_buff, BIN_LOG_HEADER_SIZE))
- {
- error= 1;
- goto end;
- }
+ error= 1;
+ goto end;
}
-
for (;;)
{
char llbuff[21];
my_off_t old_off = my_b_tell(file);
- Log_event* ev = Log_event::read_log_event(file, old_format);
+ Log_event* ev = Log_event::read_log_event(file, description_event);
if (!ev)
{
if (file->error)
{
fprintf(stderr,
- "Could not read entry at offset %s:"
- "Error in log format or read error\n",
- llstr(old_off,llbuff));
- error= 1;
+ "Could not read entry at offset %s:"
+ "Error in log format or read error\n",
+ llstr(old_off,llbuff));
+ error= 1;
}
// file->error == 0 means EOF, that's OK, we break in this case
break;
}
- if ((error= process_event(last_db,ev,old_off,false)))
+ if ((error= process_event(&last_event_info,ev,old_off)))
{
if (error < 0)
error= 0;
@@ -1008,6 +1144,7 @@ end:
if (fd >= 0)
my_close(fd, MYF(MY_WME));
end_io_cache(file);
+ delete description_event;
return error;
}
diff --git a/configure.in b/configure.in
index 788848ba4c2..6df20440c48 100644
--- a/configure.in
+++ b/configure.in
@@ -4,7 +4,7 @@ dnl Process this file with autoconf to produce a configure script.
AC_INIT(sql/mysqld.cc)
AC_CANONICAL_SYSTEM
# The Docs Makefile.am parses this line!
-AM_INIT_AUTOMAKE(mysql, 4.1.5-gamma)
+AM_INIT_AUTOMAKE(mysql, 5.0.2-alpha)
AM_CONFIG_HEADER(config.h)
PROTOCOL_VERSION=10
@@ -42,7 +42,7 @@ for i in $AVAILABLE_LANGUAGES
do
AVAILABLE_LANGUAGES_ERRORS="$AVAILABLE_LANGUAGES_ERRORS $i/errmsg.sys"
case $host_os in
- netware* | modesto*)
+ netware*)
echo "$i/errmsg.sys: $i/errmsg.txt
\$(top_builddir)/extra/comp_err.linux -C\$(srcdir)/charsets/ \$^ $i/errmsg.sys" \
>> $AVAILABLE_LANGUAGES_ERRORS_RULES
@@ -507,7 +507,7 @@ else
*cygwin*)
FIND_PROC="$PS -e | grep mysqld | grep \" \$\$PID \" > /dev/null"
;;
- *netware* | *modesto*)
+ *netware*)
FIND_PROC=
;;
*)
@@ -1629,7 +1629,7 @@ AC_SUBST(LIBDL)
# System characteristics
case $SYSTEM_TYPE in
- *netware* | *modesto*) ;;
+ *netware*) ;;
*)
AC_SYS_RESTARTABLE_SYSCALLS
;;
@@ -1659,10 +1659,12 @@ else
fi
if expr "$SYSTEM_TYPE" : ".*netware.*" > /dev/null; then
- DEBUG_CFLAGS="$DEBUG_CFLAGS -DDEBUG -sym internal,codeview4"
- DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -DDEBUG -sym internal,codeview4"
- OPTIMIZE_CFLAGS="$OPTIMIZE_CFLAGS -DNDEBUG"
- OPTIMIZE_CXXFLAGS="$OPTIMIZE_CXXFLAGS -DNDEBUG"
+ DEBUG_CFLAGS="-g -DDEBUG -sym internal,codeview4"
+ DEBUG_CXXFLAGS="-g -DDEBUG -sym internal,codeview4"
+ DEBUG_OPTIMIZE_CC="-DDEBUG"
+ DEBUG_OPTIMIZE_CXX="-DDEBUG"
+ OPTIMIZE_CFLAGS="-O3 -DNDEBUG"
+ OPTIMIZE_CXXFLAGS="-O3 -DNDEBUG"
fi
AC_ARG_WITH(debug,
diff --git a/include/my_base.h b/include/my_base.h
index 0ef66ef8123..583cd473936 100644
--- a/include/my_base.h
+++ b/include/my_base.h
@@ -146,7 +146,13 @@ enum ha_extra_function {
On-the-fly switching between unique and non-unique key inserting.
*/
HA_EXTRA_CHANGE_KEY_TO_UNIQUE,
- HA_EXTRA_CHANGE_KEY_TO_DUP
+ HA_EXTRA_CHANGE_KEY_TO_DUP,
+ /*
+ When using HA_EXTRA_KEYREAD, overwrite only key member fields and keep
+ other fields intact. When this is off (by default) InnoDB will use memcpy
+ to overwrite entire row.
+ */
+ HA_EXTRA_KEYREAD_PRESERVE_FIELDS
};
/* The following is parameter to ha_panic() */
diff --git a/include/my_bitmap.h b/include/my_bitmap.h
index a4511bf3414..fb1c3c69563 100644
--- a/include/my_bitmap.h
+++ b/include/my_bitmap.h
@@ -46,6 +46,8 @@ extern my_bool bitmap_is_set(const MY_BITMAP *map, uint bitmap_bit);
extern my_bool bitmap_is_set_all(const MY_BITMAP *map);
extern my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2);
extern uint bitmap_set_next(MY_BITMAP *map);
+extern uint bitmap_get_first(const MY_BITMAP *map);
+extern uint bitmap_bits_set(const MY_BITMAP *map);
extern void bitmap_clear_all(MY_BITMAP *map);
extern void bitmap_clear_bit(MY_BITMAP *map, uint bitmap_bit);
extern void bitmap_free(MY_BITMAP *map);
diff --git a/include/my_global.h b/include/my_global.h
index 89a5363dc4b..684617695a7 100644
--- a/include/my_global.h
+++ b/include/my_global.h
@@ -670,6 +670,17 @@ typedef SOCKET_SIZE_TYPE size_socket;
#define isinf(X) 0
#endif
+/* Define missing math constants. */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+#ifndef M_E
+#define M_E 2.7182818284590452354
+#endif
+#ifndef M_LN2
+#define M_LN2 0.69314718055994530942
+#endif
+
/*
Max size that must be added to a so that we know Size to make
adressable obj.
diff --git a/include/my_sys.h b/include/my_sys.h
index ad1966ba67f..54feb73d3e6 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -121,6 +121,13 @@ extern int NEAR my_errno; /* Last error in mysys */
#define MY_ERRNO_EDOM 33
#define MY_ERRNO_ERANGE 34
+ /* Bits for get_date timeflag */
+#define GETDATE_DATE_TIME 1
+#define GETDATE_SHORT_DATE 2
+#define GETDATE_HHMMSSTIME 4
+#define GETDATE_GMT 8
+#define GETDATE_FIXEDLENGTH 16
+
/* defines when allocating data */
#ifdef SAFEMALLOC
#define my_malloc(SZ,FLAG) _mymalloc((SZ), __FILE__, __LINE__, FLAG )
@@ -498,6 +505,7 @@ typedef int (*qsort2_cmp)(const void *, const void *, const void *);
/* tell write offset in the SEQ_APPEND cache */
my_off_t my_b_append_tell(IO_CACHE* info);
+my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */
#define my_b_bytes_in_cache(info) (uint) (*(info)->current_end - \
*(info)->current_pos)
@@ -746,6 +754,7 @@ extern byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen);
extern ha_checksum my_checksum(ha_checksum crc, const byte *mem, uint count);
extern uint my_bit_log2(ulong value);
extern uint my_count_bits(ulonglong v);
+extern uint my_count_bits_ushort(ushort v);
extern void my_sleep(ulong m_seconds);
extern ulong crc32(ulong crc, const uchar *buf, uint len);
extern uint my_set_max_open_files(uint files);
diff --git a/include/mysql.h b/include/mysql.h
index cf5af6ce189..9d08bf5aa57 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -580,6 +580,12 @@ typedef struct st_mysql_stmt
int (*read_row_func)(struct st_mysql_stmt *stmt,
unsigned char **row);
unsigned long stmt_id; /* Id for prepared statement */
+ unsigned long flags; /* i.e. type of cursor to open */
+ /*
+ Copied from mysql->server_status after execute/fetch to know
+ server-side cursor status for this statement.
+ */
+ unsigned int server_status;
unsigned int last_errno; /* error code */
unsigned int param_count; /* input parameter count */
unsigned int field_count; /* number of columns in result set */
@@ -608,7 +614,12 @@ enum enum_stmt_attr_type
In the new API we do that only by request because it slows down
mysql_stmt_store_result sufficiently.
*/
- STMT_ATTR_UPDATE_MAX_LENGTH
+ STMT_ATTR_UPDATE_MAX_LENGTH,
+ /*
+ unsigned long with combination of cursor flags (read only, for update,
+ etc)
+ */
+ STMT_ATTR_CURSOR_TYPE
};
diff --git a/include/mysql_com.h b/include/mysql_com.h
index 3b65d6f3fbc..3cf1a011e3c 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -36,6 +36,11 @@
#define MYSQL_SERVICENAME "MySQL"
#endif /* __WIN__ */
+/*
+ You should add new commands to the end of this list, otherwise old
+ servers won't be able to handle them as 'unsupported'.
+*/
+
enum enum_server_command
{
COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST,
@@ -44,7 +49,7 @@ enum enum_server_command
COM_TIME, COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP,
COM_TABLE_DUMP, COM_CONNECT_OUT, COM_REGISTER_SLAVE,
COM_PREPARE, COM_EXECUTE, COM_LONG_DATA, COM_CLOSE_STMT,
- COM_RESET_STMT, COM_SET_OPTION,
+ COM_RESET_STMT, COM_SET_OPTION, COM_FETCH,
COM_END /* Must be last */
};
@@ -127,12 +132,25 @@ enum enum_server_command
#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
#define SERVER_QUERY_NO_GOOD_INDEX_USED 16
#define SERVER_QUERY_NO_INDEX_USED 32
+/*
+ The server was able to fulfill client request and open read-only
+ non-scrollable cursor for the query. This flag comes in server
+ status with reply to COM_EXECUTE and COM_EXECUTE_DIRECT commands.
+*/
+#define SERVER_STATUS_CURSOR_EXISTS 64
+/*
+ This flag is sent with last row of read-only cursor, in reply to
+ COM_FETCH command.
+*/
+#define SERVER_STATUS_LAST_ROW_SENT 128
#define MYSQL_ERRMSG_SIZE 512
#define NET_READ_TIMEOUT 30 /* Timeout on read */
#define NET_WRITE_TIMEOUT 60 /* Timeout on write */
#define NET_WAIT_TIMEOUT 8*60*60 /* Wait for new query */
+#define ONLY_KILL_QUERY 1
+
struct st_vio; /* Only C */
typedef struct st_vio Vio;
@@ -158,7 +176,8 @@ typedef struct st_net {
unsigned int *return_status;
unsigned char reading_or_writing;
char save_char;
- my_bool no_send_ok;
+ my_bool no_send_ok; /* For SPs and other things that do multiple stmts */
+ my_bool no_send_eof; /* For SPs' first version read-only cursors */
/*
Pointer to query object in query cache, do not equal NULL (0) for
queries in cache that have not stored its results yet
@@ -256,6 +275,16 @@ enum mysql_enum_shutdown_level {
KILL_CONNECTION= 255
};
+
+enum enum_cursor_type
+{
+ CURSOR_TYPE_NO_CURSOR= 0,
+ CURSOR_TYPE_READ_ONLY= 1,
+ CURSOR_TYPE_FOR_UPDATE= 2,
+ CURSOR_TYPE_SCROLLABLE= 4
+};
+
+
/* options for mysql_set_option */
enum enum_mysql_set_option
{
@@ -316,6 +345,8 @@ typedef struct st_udf_args
char **args; /* Pointer to argument */
unsigned long *lengths; /* Length of string arguments */
char *maybe_null; /* Set to 1 for all maybe_null args */
+ char **attributes; /* Pointer to attribute name */
+ unsigned long *attribute_lengths; /* Length of attribute arguments */
} UDF_ARGS;
/* This holds information about the result */
diff --git a/include/mysqld_error.h b/include/mysqld_error.h
index 776869ff045..e1a0b352861 100644
--- a/include/mysqld_error.h
+++ b/include/mysqld_error.h
@@ -319,4 +319,60 @@
#define ER_INVALID_CHARACTER_STRING 1300
#define ER_WARN_ALLOWED_PACKET_OVERFLOWED 1301
#define ER_CONFLICTING_DECLARATIONS 1302
-#define ER_ERROR_MESSAGES 303
+#define ER_SP_NO_RECURSIVE_CREATE 1303
+#define ER_SP_ALREADY_EXISTS 1304
+#define ER_SP_DOES_NOT_EXIST 1305
+#define ER_SP_DROP_FAILED 1306
+#define ER_SP_STORE_FAILED 1307
+#define ER_SP_LILABEL_MISMATCH 1308
+#define ER_SP_LABEL_REDEFINE 1309
+#define ER_SP_LABEL_MISMATCH 1310
+#define ER_SP_UNINIT_VAR 1311
+#define ER_SP_BADSELECT 1312
+#define ER_SP_BADRETURN 1313
+#define ER_SP_BADSTATEMENT 1314
+#define ER_UPDATE_LOG_DEPRECATED_IGNORED 1315
+#define ER_UPDATE_LOG_DEPRECATED_TRANSLATED 1316
+#define ER_QUERY_INTERRUPTED 1317
+#define ER_SP_WRONG_NO_OF_ARGS 1318
+#define ER_SP_COND_MISMATCH 1319
+#define ER_SP_NORETURN 1320
+#define ER_SP_NORETURNEND 1321
+#define ER_SP_BAD_CURSOR_QUERY 1322
+#define ER_SP_BAD_CURSOR_SELECT 1323
+#define ER_SP_CURSOR_MISMATCH 1324
+#define ER_SP_CURSOR_ALREADY_OPEN 1325
+#define ER_SP_CURSOR_NOT_OPEN 1326
+#define ER_SP_UNDECLARED_VAR 1327
+#define ER_SP_WRONG_NO_OF_FETCH_ARGS 1328
+#define ER_SP_FETCH_NO_DATA 1329
+#define ER_SP_DUP_PARAM 1330
+#define ER_SP_DUP_VAR 1331
+#define ER_SP_DUP_COND 1332
+#define ER_SP_DUP_CURS 1333
+#define ER_SP_CANT_ALTER 1334
+#define ER_SP_SUBSELECT_NYI 1335
+#define ER_SP_NO_USE 1336
+#define ER_SP_VARCOND_AFTER_CURSHNDLR 1337
+#define ER_SP_CURSOR_AFTER_HANDLER 1338
+#define ER_SP_CASE_NOT_FOUND 1339
+#define ER_FPARSER_TOO_BIG_FILE 1340
+#define ER_FPARSER_BAD_HEADER 1341
+#define ER_FPARSER_EOF_IN_COMMENT 1342
+#define ER_FPARSER_ERROR_IN_PARAMETER 1343
+#define ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER 1344
+#define ER_VIEW_NO_EXPLAIN 1345
+#define ER_FRM_UNKNOWN_TYPE 1346
+#define ER_WRONG_OBJECT 1347
+#define ER_NONUPDATEABLE_COLUMN 1348
+#define ER_VIEW_SELECT_DERIVED 1349
+#define ER_VIEW_SELECT_PROCEDURE 1350
+#define ER_VIEW_SELECT_VARIABLE 1351
+#define ER_VIEW_SELECT_TMPTABLE 1352
+#define ER_VIEW_WRONG_LIST 1353
+#define ER_WARN_VIEW_MERGE 1354
+#define ER_WARN_VIEW_WITHOUT_KEY 1355
+#define ER_VIEW_INVALID 1356
+#define ER_SP_NO_DROP_SP 1357
+#define ER_SP_GOTO_IN_HNDLR 1358
+#define ER_ERROR_MESSAGES 359
diff --git a/include/sql_state.h b/include/sql_state.h
index 52a359405e1..7bf05203463 100644
--- a/include/sql_state.h
+++ b/include/sql_state.h
@@ -162,3 +162,41 @@ ER_WARN_DATA_TRUNCATED, "01000", "",
ER_WRONG_NAME_FOR_INDEX, "42000", "",
ER_WRONG_NAME_FOR_CATALOG, "42000", "",
ER_UNKNOWN_STORAGE_ENGINE, "42000", "",
+/* 5.0 */
+ER_SP_NO_RECURSIVE_CREATE, "2F003", "",
+ER_SP_ALREADY_EXISTS, "42000", "",
+ER_SP_DOES_NOT_EXIST, "42000", "",
+/*ER_SP_DROP_FAILED*/
+/*ER_SP_STORE_FAILED*/
+ER_SP_LILABEL_MISMATCH, "42000", "",
+ER_SP_LABEL_REDEFINE, "42000", "",
+ER_SP_LABEL_MISMATCH, "42000", "",
+ER_SP_UNINIT_VAR, "01000", "",
+ER_SP_BADSELECT, "0A000", "",
+ER_SP_BADRETURN, "42000", "",
+ER_SP_BADSTATEMENT, "0A000", "",
+ER_UPDATE_LOG_DEPRECATED_IGNORED, "42000", "",
+ER_UPDATE_LOG_DEPRECATED_TRANSLATED, "42000", "",
+ER_QUERY_INTERRUPTED, "70100", "",
+ER_SP_WRONG_NO_OF_ARGS, "42000", "",
+ER_SP_COND_MISMATCH, "42000", "",
+ER_SP_NORETURN, "42000", "",
+ER_SP_NORETURNEND, "2F005", "",
+ER_SP_BAD_CURSOR_QUERY, "42000", "",
+ER_SP_BAD_CURSOR_SELECT, "42000", "",
+ER_SP_CURSOR_MISMATCH, "42000", "",
+ER_SP_CURSOR_ALREADY_OPEN, "24000", "",
+ER_SP_CURSOR_NOT_OPEN, "24000", "",
+ER_SP_UNDECLARED_VAR, "42000", "",
+/*ER_SP_WRONG_NO_OF_FETCH_ARGS*/
+ER_SP_FETCH_NO_DATA, "02000", "",
+ER_SP_DUP_PARAM, "42000", "",
+ER_SP_DUP_VAR, "42000", "",
+ER_SP_DUP_COND, "42000", "",
+ER_SP_DUP_CURS, "42000", "",
+/*ER_SP_CANT_ALTER*/
+ER_SP_SUBSELECT_NYI, "0A000", "",
+ER_SP_NO_USE, "42000", "",
+ER_SP_VARCOND_AFTER_CURSHNDLR, "42000", "",
+ER_SP_CURSOR_AFTER_HANDLER, "42000", "",
+ER_SP_CASE_NOT_FOUND, "20000", "",
diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h
index 8f6264944ce..a576a637c79 100644
--- a/innobase/include/row0mysql.h
+++ b/innobase/include/row0mysql.h
@@ -577,6 +577,10 @@ struct row_prebuilt_struct {
allocated mem buf start, because
there is a 4 byte magic number at the
start and at the end */
+ ibool keep_other_fields_on_keyread; /* when using fetch
+ cache with HA_EXTRA_KEYREAD, don't
+ overwrite other fields in mysql row
+ row buffer.*/
ulint fetch_cache_first;/* position of the first not yet
fetched row in fetch_cache */
ulint n_fetch_cached; /* number of not yet fetched rows
diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c
index 2c0092adc6e..dc6694fc18c 100644
--- a/innobase/row/row0sel.c
+++ b/innobase/row/row0sel.c
@@ -2613,10 +2613,35 @@ row_sel_pop_cached_row_for_mysql(
row */
row_prebuilt_t* prebuilt) /* in: prebuilt struct */
{
- ut_ad(prebuilt->n_fetch_cached > 0);
+ ulint i;
+ mysql_row_templ_t* templ;
+ byte* cached_rec;
+ ut_ad(prebuilt->n_fetch_cached > 0);
+
+ if (prebuilt->keep_other_fields_on_keyread)
+ {
+ /* Copy cache record field by field, don't touch fields that
+ are not covered by current key */
+ cached_rec =
+ prebuilt->fetch_cache[prebuilt->fetch_cache_first];
+
+ for (i = 0; i < prebuilt->n_template; i++) {
+ templ = prebuilt->mysql_template + i;
+ ut_memcpy(
+ buf + templ->mysql_col_offset,
+ cached_rec + templ->mysql_col_offset,
+ templ->mysql_col_len);
- ut_memcpy(buf, prebuilt->fetch_cache[prebuilt->fetch_cache_first],
- prebuilt->mysql_row_len);
+ if (templ->mysql_null_bit_mask)
+ buf[templ->mysql_null_byte_offset] &=
+ cached_rec[templ->mysql_null_byte_offset];
+ }
+ }
+ else
+ {
+ ut_memcpy(buf, prebuilt->fetch_cache[prebuilt->fetch_cache_first],
+ prebuilt->mysql_row_len);
+ }
prebuilt->n_fetch_cached--;
prebuilt->fetch_cache_first++;
diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h
index 5857c0c84d6..a29f52ce366 100644
--- a/libmysql/client_settings.h
+++ b/libmysql/client_settings.h
@@ -42,7 +42,7 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename);
void mysql_read_default_options(struct st_mysql_options *options,
const char *filename,const char *group);
-MYSQL * STDCALL
+MYSQL *
cli_mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
const char *passwd, const char *db,
uint port, const char *unix_socket,ulong client_flag);
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 7d71998f37d..fcea9288283 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -1671,6 +1671,7 @@ myodbc_remove_escape(MYSQL *mysql,char *name)
static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row);
static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row);
+static int stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row);
static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row);
/*
@@ -2386,11 +2387,11 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length)
char buff[4 /* size of stmt id */ +
5 /* execution flags */];
DBUG_ENTER("execute");
- DBUG_PRINT("enter",("packet: %s, length :%d",packet ? packet :" ", length));
+ DBUG_DUMP("packet", packet, length);
mysql->last_used_con= mysql;
int4store(buff, stmt->stmt_id); /* Send stmt id to server */
- buff[4]= (char) 0; /* no flags */
+ buff[4]= (char) stmt->flags;
int4store(buff+5, 1); /* iteration count */
if (cli_advanced_command(mysql, COM_EXECUTE, buff, sizeof(buff),
packet, length, 1) ||
@@ -2400,6 +2401,7 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length)
DBUG_RETURN(1);
}
stmt->affected_rows= mysql->affected_rows;
+ stmt->server_status= mysql->server_status;
stmt->insert_id= mysql->insert_id;
DBUG_RETURN(0);
}
@@ -2555,6 +2557,59 @@ error:
return rc;
}
+
+/*
+ Fetch statement row using server side cursor.
+
+ SYNOPSIS
+ stmt_read_row_from_cursor()
+
+ RETURN VALUE
+ 0 success
+ 1 error
+ MYSQL_NO_DATA end of data
+*/
+
+static int
+stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row)
+{
+ if (stmt->data_cursor)
+ return stmt_read_row_buffered(stmt, row);
+ if (stmt->server_status & SERVER_STATUS_LAST_ROW_SENT)
+ stmt->server_status &= ~SERVER_STATUS_LAST_ROW_SENT;
+ else
+ {
+ MYSQL *mysql= stmt->mysql;
+ NET *net= &mysql->net;
+ MYSQL_DATA *result= &stmt->result;
+ char buff[4 /* statement id */ +
+ 4 /* number of rows to fetch */];
+
+ free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
+ result->data= NULL;
+ result->rows= 0;
+ /* Send row request to the server */
+ int4store(buff, stmt->stmt_id);
+ int4store(buff + 4, 1); /* number of rows to fetch */
+ if (cli_advanced_command(mysql, COM_FETCH, buff, sizeof(buff),
+ NullS, 0, 1))
+ {
+ set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate);
+ return 1;
+ }
+ stmt->server_status= mysql->server_status;
+ if (cli_read_binary_rows(stmt))
+ return 1;
+ stmt->server_status= mysql->server_status;
+
+ stmt->data_cursor= result->data;
+ return stmt_read_row_buffered(stmt, row);
+ }
+ *row= 0;
+ return MYSQL_NO_DATA;
+}
+
+
/*
Default read row function to not SIGSEGV in client in
case of wrong sequence of API calls.
@@ -2596,6 +2651,9 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt,
case STMT_ATTR_UPDATE_MAX_LENGTH:
stmt->update_max_length= value ? *(const my_bool*) value : 0;
break;
+ case STMT_ATTR_CURSOR_TYPE:
+ stmt->flags= value ? *(const unsigned long *) value : 0;
+ break;
default:
return TRUE;
}
@@ -2611,6 +2669,9 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt,
case STMT_ATTR_UPDATE_MAX_LENGTH:
*(unsigned long *) value= stmt->update_max_length;
break;
+ case STMT_ATTR_CURSOR_TYPE:
+ *(unsigned long *) value= stmt->flags;
+ break;
default:
return TRUE;
}
@@ -2714,9 +2775,17 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
stmt->state= MYSQL_STMT_EXECUTE_DONE;
if (stmt->field_count)
{
- stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
- stmt->unbuffered_fetch_cancelled= FALSE;
- stmt->read_row_func= stmt_read_row_unbuffered;
+ if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
+ {
+ mysql->status= MYSQL_STATUS_READY;
+ stmt->read_row_func= stmt_read_row_from_cursor;
+ }
+ else
+ {
+ stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
+ stmt->unbuffered_fetch_cancelled= FALSE;
+ stmt->read_row_func= stmt_read_row_unbuffered;
+ }
}
DBUG_RETURN(0);
}
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am
index 75a5ef7ff91..f0cda9ae524 100644
--- a/libmysqld/Makefile.am
+++ b/libmysqld/Makefile.am
@@ -57,7 +57,9 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \
sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \
unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \
- spatial.cc gstream.cc sql_help.cc tztime.cc
+ spatial.cc gstream.cc sql_help.cc tztime.cc protocol_cursor.cc \
+ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \
+ parse_file.cc sql_view.cc
libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources)
libmysqld_a_SOURCES=
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index 8092d87b97c..fdc1acea09b 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -591,7 +591,7 @@ err:
C_MODE_END
-bool Protocol::send_fields(List<Item> *list, uint flag)
+bool Protocol::send_fields(List<Item> *list, int flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
@@ -638,7 +638,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
if (INTERNAL_NUM_FIELD(client_field))
client_field->flags|= NUM_FLAG;
- if (flag & 2)
+ if (flags & (int) Protocol::SEND_DEFAULTS)
{
char buff[80];
String tmp(buff, sizeof(buff), default_charset_info), *res;
diff --git a/myisam/myisamchk.c b/myisam/myisamchk.c
index 98cbc838d31..bee248c0c65 100644
--- a/myisam/myisamchk.c
+++ b/myisam/myisamchk.c
@@ -1693,11 +1693,11 @@ err:
sorting
*/
-static my_bool not_killed= 0;
+static int not_killed= 0;
-volatile my_bool *killed_ptr(MI_CHECK *param)
+volatile int *killed_ptr(MI_CHECK *param)
{
- return &not_killed; /* always NULL */
+ return &not_killed; /* always NULL */
}
/* print warnings and errors */
diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h
index 736ce3f3869..12ce112dbe0 100644
--- a/myisam/myisamdef.h
+++ b/myisam/myisamdef.h
@@ -711,7 +711,7 @@ int mi_open_keyfile(MYISAM_SHARE *share);
void mi_setup_functions(register MYISAM_SHARE *share);
/* Functions needed by mi_check */
-volatile my_bool *killed_ptr(MI_CHECK *param);
+volatile int *killed_ptr(MI_CHECK *param);
void mi_check_print_error _VARARGS((MI_CHECK *param, const char *fmt,...));
void mi_check_print_warning _VARARGS((MI_CHECK *param, const char *fmt,...));
void mi_check_print_info _VARARGS((MI_CHECK *param, const char *fmt,...));
diff --git a/myisam/sort.c b/myisam/sort.c
index 3dc066e877c..5537ba55c7d 100644
--- a/myisam/sort.c
+++ b/myisam/sort.c
@@ -854,7 +854,8 @@ merge_buffers(MI_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
uchar *strpos;
BUFFPEK *buffpek,**refpek;
QUEUE queue;
- volatile my_bool *killed= killed_ptr(info->sort_info->param);
+ volatile int *killed= killed_ptr(info->sort_info->param);
+
DBUG_ENTER("merge_buffers");
count=error=0;
diff --git a/mysql-test/create-test-result b/mysql-test/create-test-result
index b9be2300976..ad19cdf08a1 100755
--- a/mysql-test/create-test-result
+++ b/mysql-test/create-test-result
@@ -6,7 +6,7 @@
# to start mysqld yourself and run mysqltest -r
RESULT_DIR=r
-if [ -z $EDITOR] ; then
+if [ -z "$EDITOR" ] ; then
EDITOR=vi
fi
@@ -24,7 +24,7 @@ function usage()
test_name=$1
-[ -z $test_name ] && usage
+[ -z "$test_name" ] && usage
result_file=$RESULT_DIR/$test_name.result
reject_file=$RESULT_DIR/$test_name.reject
diff --git a/mysql-test/include/ps_modify.inc b/mysql-test/include/ps_modify.inc
index 04c5cbaad6b..7202d857903 100644
--- a/mysql-test/include/ps_modify.inc
+++ b/mysql-test/include/ps_modify.inc
@@ -59,7 +59,6 @@ execute stmt1 using @arg00;
select a,b from t1 where b=@arg00;
## truncate a table
---error 1295
prepare stmt1 from 'truncate table t1' ;
diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh
index 41dc3c419f0..955bc12a43a 100644
--- a/mysql-test/mysql-test-run.sh
+++ b/mysql-test/mysql-test-run.sh
@@ -581,6 +581,8 @@ if [ -n "$DO_CLIENT_GDB" -o -n "$DO_GDB" ] ; then
XTERM=`which xterm`
fi
+export MYSQL MYSQL_DUMP MYSQL_BINLOG MYSQL_FIX_SYSTEM_TABLES CLIENT_BINDIR MASTER_MYSOCK
+
#++
# Function Definitions
#--
diff --git a/mysql-test/r/bdb.result b/mysql-test/r/bdb.result
index 25118702d09..d510ec53dfc 100644
--- a/mysql-test/r/bdb.result
+++ b/mysql-test/r/bdb.result
@@ -140,10 +140,10 @@ id parent_id level
1015 102 2
explain select level from t1 where level=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref level level 1 const 1 Using where; Using index
+1 SIMPLE t1 ref level level 1 const 1 Using index
explain select level,id from t1 where level=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref level level 1 const 1 Using where; Using index
+1 SIMPLE t1 ref level level 1 const 1 Using index
explain select level,id,parent_id from t1 where level=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref level level 1 const 1 Using where
@@ -625,7 +625,7 @@ id parent_id level
1016 102 2
explain select level from t1 where level=1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref level level 1 const 1 Using where; Using index
+1 SIMPLE t1 ref level level 1 const 1 Using index
select level,id from t1 where level=1;
level id
1 1004
diff --git a/mysql-test/r/bench_count_distinct.result b/mysql-test/r/bench_count_distinct.result
index fcc0c0948b3..62312870f59 100644
--- a/mysql-test/r/bench_count_distinct.result
+++ b/mysql-test/r/bench_count_distinct.result
@@ -7,5 +7,5 @@ explain extended select count(distinct n) from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 index NULL n 4 NULL 200 Using index
Warnings:
-Note 1003 select count(distinct test.t1.n) AS `count(distinct n)` from test.t1
+Note 1003 select count(distinct `test`.`t1`.`n`) AS `count(distinct n)` from `test`.`t1`
drop table t1;
diff --git a/mysql-test/r/case.result b/mysql-test/r/case.result
index 1aa838140fd..dba21877644 100644
--- a/mysql-test/r/case.result
+++ b/mysql-test/r/case.result
@@ -66,7 +66,7 @@ explain extended select case a when 1 then 2 when 2 then 3 else 0 end as fcase,
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 4 Using temporary; Using filesort
Warnings:
-Note 1003 select (case test.t1.a when 1 then 2 when 2 then 3 else 0 end) AS `fcase`,count(0) AS `count(*)` from test.t1 group by (case test.t1.a when 1 then 2 when 2 then 3 else 0 end)
+Note 1003 select (case `test`.`t1`.`a` when 1 then 2 when 2 then 3 else 0 end) AS `fcase`,count(0) AS `count(*)` from `test`.`t1` group by (case `test`.`t1`.`a` when 1 then 2 when 2 then 3 else 0 end)
select case a when 1 then "one" when 2 then "two" else "nothing" end as fcase, count(*) from t1 group by fcase;
fcase count(*)
nothing 2
diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result
index edf30e7f6e4..0e4583fbc96 100644
--- a/mysql-test/r/connect.result
+++ b/mysql-test/r/connect.result
@@ -1,43 +1,45 @@
show tables;
-Tables_in_mysql
-columns_priv
-db
-func
-help_category
-help_keyword
-help_relation
-help_topic
-host
-tables_priv
-time_zone
-time_zone_leap_second
-time_zone_name
-time_zone_transition
-time_zone_transition_type
-user
+Tables_in_mysql table_type
+columns_priv BASE TABLE
+db BASE TABLE
+func BASE TABLE
+help_category BASE TABLE
+help_keyword BASE TABLE
+help_relation BASE TABLE
+help_topic BASE TABLE
+host BASE TABLE
+proc BASE TABLE
+tables_priv BASE TABLE
+time_zone BASE TABLE
+time_zone_leap_second BASE TABLE
+time_zone_name BASE TABLE
+time_zone_transition BASE TABLE
+time_zone_transition_type BASE TABLE
+user BASE TABLE
show tables;
-Tables_in_test
+Tables_in_test table_type
grant ALL on *.* to test@localhost identified by "gambling";
grant ALL on *.* to test@127.0.0.1 identified by "gambling";
show tables;
-Tables_in_mysql
-columns_priv
-db
-func
-help_category
-help_keyword
-help_relation
-help_topic
-host
-tables_priv
-time_zone
-time_zone_leap_second
-time_zone_name
-time_zone_transition
-time_zone_transition_type
-user
+Tables_in_mysql table_type
+columns_priv BASE TABLE
+db BASE TABLE
+func BASE TABLE
+help_category BASE TABLE
+help_keyword BASE TABLE
+help_relation BASE TABLE
+help_topic BASE TABLE
+host BASE TABLE
+proc BASE TABLE
+tables_priv BASE TABLE
+time_zone BASE TABLE
+time_zone_leap_second BASE TABLE
+time_zone_name BASE TABLE
+time_zone_transition BASE TABLE
+time_zone_transition_type BASE TABLE
+user BASE TABLE
show tables;
-Tables_in_test
+Tables_in_test table_type
update mysql.user set password=old_password("gambling2") where user=_binary"test";
flush privileges;
set password="";
@@ -45,23 +47,24 @@ set password='gambling3';
ERROR HY000: Password hash should be a 41-digit hexadecimal number
set password=old_password('gambling3');
show tables;
-Tables_in_mysql
-columns_priv
-db
-func
-help_category
-help_keyword
-help_relation
-help_topic
-host
-tables_priv
-time_zone
-time_zone_leap_second
-time_zone_name
-time_zone_transition
-time_zone_transition_type
-user
+Tables_in_mysql table_type
+columns_priv BASE TABLE
+db BASE TABLE
+func BASE TABLE
+help_category BASE TABLE
+help_keyword BASE TABLE
+help_relation BASE TABLE
+help_topic BASE TABLE
+host BASE TABLE
+proc BASE TABLE
+tables_priv BASE TABLE
+time_zone BASE TABLE
+time_zone_leap_second BASE TABLE
+time_zone_name BASE TABLE
+time_zone_transition BASE TABLE
+time_zone_transition_type BASE TABLE
+user BASE TABLE
show tables;
-Tables_in_test
+Tables_in_test table_type
delete from mysql.user where user=_binary"test";
flush privileges;
diff --git a/mysql-test/r/ctype_recoding.result b/mysql-test/r/ctype_recoding.result
index be792c007fc..1ef185388a8 100644
--- a/mysql-test/r/ctype_recoding.result
+++ b/mysql-test/r/ctype_recoding.result
@@ -45,8 +45,8 @@ CREATE TABLE `ÔÁÂÌÉÃÁ`
ÐÏÌÅ CHAR(32) CHARACTER SET koi8r NOT NULL COMMENT "ËÏÍÍÅÎÔÁÒÉÊ ÐÏÌÑ"
) COMMENT "ËÏÍÍÅÎÔÁÒÉÊ ÔÁÂÌÉÃÙ";
SHOW TABLES;
-Tables_in_test
-ÔÁÂÌÉÃÁ
+Tables_in_test table_type
+ÔÁÂÌÉÃÁ BASE TABLE
SHOW CREATE TABLE ÔÁÂÌÉÃÁ;
Table Create Table
ÔÁÂÌÉÃÁ CREATE TABLE `ÔÁÂÌÉÃÁ` (
@@ -57,8 +57,8 @@ Field Type Null Key Default Extra
ÐÏÌÅ char(32)
SET CHARACTER SET cp1251;
SHOW TABLES;
-Tables_in_test
-òàáëèöà
+Tables_in_test table_type
+òàáëèöà BASE TABLE
SHOW CREATE TABLE òàáëèöà;
Table Create Table
òàáëèöà CREATE TABLE `òàáëèöà` (
@@ -69,8 +69,8 @@ Field Type Null Key Default Extra
ïîëå char(32)
SET CHARACTER SET utf8;
SHOW TABLES;
-Tables_in_test
-таблица
+Tables_in_test table_type
+таблица BASE TABLE
SHOW CREATE TABLE таблица;
Table Create Table
таблица CREATE TABLE `таблица` (
@@ -93,14 +93,14 @@ SET CHARACTER SET koi8r;
CREATE DATABASE ÔÅÓÔ;
USE ÔÅÓÔ;
SHOW TABLES;
-Tables_in_ÔÅÓÔ
+Tables_in_ÔÅÓÔ table_type
SHOW TABLES IN ÔÅÓÔ;
-Tables_in_ÔÅÓÔ
+Tables_in_ÔÅÓÔ table_type
SET CHARACTER SET cp1251;
SHOW TABLES;
-Tables_in_òåñò
+Tables_in_òåñò table_type
SHOW TABLES IN òåñò;
-Tables_in_òåñò
+Tables_in_òåñò table_type
SET CHARACTER SET koi8r;
DROP DATABASE ÔÅÓÔ;
SET NAMES koi8r;
diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result
index 1d3deb0b09a..a0ac29b7989 100644
--- a/mysql-test/r/ctype_ucs.result
+++ b/mysql-test/r/ctype_ucs.result
@@ -62,10 +62,10 @@ create table t1 (a varchar(10) character set ucs2, key(a));
insert into t1 values ("a"),("abc"),("abcd"),("hello"),("test");
explain select * from t1 where a like 'abc%';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 21 NULL 1 Using where; Using index
+1 SIMPLE t1 index a a 21 NULL 5 Using where; Using index
explain select * from t1 where a like concat('abc','%');
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 21 NULL 1 Using where; Using index
+1 SIMPLE t1 index a a 21 NULL 5 Using where; Using index
select * from t1 where a like "abc%";
a
abc
diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result
index 7e6b9b44566..ddd8f82de2c 100644
--- a/mysql-test/r/derived.result
+++ b/mysql-test/r/derived.result
@@ -318,11 +318,11 @@ create table t2 (a int, b int, primary key (a));
insert into t2 values (1,7),(2,7);
explain select a from t2 where a>1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 range PRIMARY PRIMARY 4 NULL 2 Using where; Using index
+1 SIMPLE t2 index PRIMARY PRIMARY 4 NULL 2 Using where; Using index
explain select a from (select a from t2 where a>1) tt;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> system NULL NULL NULL NULL 1
-2 DERIVED t2 range PRIMARY PRIMARY 4 NULL 2 Using where; Using index
+2 DERIVED t2 index PRIMARY PRIMARY 4 NULL 2 Using where; Using index
drop table t2;
CREATE TABLE `t1` ( `itemid` int(11) NOT NULL default '0', `grpid` varchar(15) NOT NULL default '', `vendor` int(11) NOT NULL default '0', `date_` date NOT NULL default '0000-00-00', `price` decimal(12,2) NOT NULL default '0.00', PRIMARY KEY (`itemid`,`grpid`,`vendor`,`date_`), KEY `itemid` (`itemid`,`vendor`), KEY `itemid_2` (`itemid`,`date_`));
insert into t1 values (128, 'rozn', 2, now(), 10),(128, 'rozn', 1, now(), 10);
diff --git a/mysql-test/r/distinct.result b/mysql-test/r/distinct.result
index 80af2dd0983..290cb9361f1 100644
--- a/mysql-test/r/distinct.result
+++ b/mysql-test/r/distinct.result
@@ -388,8 +388,8 @@ SELECT DISTINCTROW email, shipcode FROM t1, t2 WHERE t1.infoID=t2.infoID;
email shipcode
test1@testdomain.com Z001
test2@testdomain.com Z001
-test3@testdomain.com Z001
test2@testdomain.com R002
+test3@testdomain.com Z001
SELECT DISTINCTROW email FROM t1 ORDER BY dateentered DESC;
email
test1@testdomain.com
@@ -457,10 +457,10 @@ drop table t1,t2;
CREATE TABLE t1 (
html varchar(5) default NULL,
rin int(11) default '0',
-out int(11) default '0'
+rout int(11) default '0'
) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('1',1,0);
-SELECT DISTINCT html,SUM(out)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin;
+SELECT DISTINCT html,SUM(rout)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin;
html prod
1 0.00
drop table t1;
diff --git a/mysql-test/r/drop.result b/mysql-test/r/drop.result
index 8b919964163..e20b33223b5 100644
--- a/mysql-test/r/drop.result
+++ b/mysql-test/r/drop.result
@@ -52,6 +52,6 @@ ERROR HY000: Can't execute the query because you have a conflicting read lock
unlock tables;
create table t1(n int);
show tables;
-Tables_in_test
-t1
+Tables_in_test table_type
+t1 BASE TABLE
drop table t1;
diff --git a/mysql-test/r/drop_temp_table.result b/mysql-test/r/drop_temp_table.result
index 99ee0143c05..08793bcbd57 100644
--- a/mysql-test/r/drop_temp_table.result
+++ b/mysql-test/r/drop_temp_table.result
@@ -9,10 +9,10 @@ select get_lock("a",10);
get_lock("a",10)
1
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; create database `drop-temp+table-test`
-master-bin.000001 152 Query 1 152 use `drop-temp+table-test`; create temporary table `table:name` (a int)
-master-bin.000001 246 Query 1 246 use `drop-temp+table-test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `drop-temp+table-test`.`table:name`
-master-bin.000001 375 Query 1 375 use `drop-temp+table-test`; DO RELEASE_LOCK("a")
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 190 use `test`; create database `drop-temp+table-test`
+master-bin.000001 190 Query 1 306 use `drop-temp+table-test`; create temporary table `table:name` (a int)
+master-bin.000001 306 Query 1 457 use `drop-temp+table-test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `drop-temp+table-test`.`table:name`
+master-bin.000001 457 Query 1 550 use `drop-temp+table-test`; DO RELEASE_LOCK("a")
drop database `drop-temp+table-test`;
diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result
index 30c4c75f3d1..4447c20411f 100644
--- a/mysql-test/r/fulltext.result
+++ b/mysql-test/r/fulltext.result
@@ -17,7 +17,7 @@ explain extended select * from t1 where MATCH(a,b) AGAINST ("collections");
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 fulltext a a 0 1 Using where
Warnings:
-Note 1003 select test.t1.a AS `a`,test.t1.b AS `b` from test.t1 where (match test.t1.a,test.t1.b against (_latin1'collections'))
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where (match `test`.`t1`.`a`,`test`.`t1`.`b` against (_latin1'collections'))
select * from t1 where MATCH(a,b) AGAINST ("indexes");
a b
Full-text indexes are called collections
@@ -78,7 +78,7 @@ explain extended select * from t1 where MATCH(a,b) AGAINST("support -collections
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 fulltext a a 0 1 Using where
Warnings:
-Note 1003 select test.t1.a AS `a`,test.t1.b AS `b` from test.t1 where (match test.t1.a,test.t1.b against (_latin1'support -collections' in boolean mode))
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where (match `test`.`t1`.`a`,`test`.`t1`.`b` against (_latin1'support -collections' in boolean mode))
select * from t1 where MATCH(a,b) AGAINST("support collections" IN BOOLEAN MODE);
a b
MySQL has now support for full-text search
diff --git a/mysql-test/r/func_default.result b/mysql-test/r/func_default.result
index 2993d79a870..c7483027322 100644
--- a/mysql-test/r/func_default.result
+++ b/mysql-test/r/func_default.result
@@ -8,7 +8,7 @@ explain extended select default(str), default(strnull), default(intg), default(r
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 system NULL NULL NULL NULL 1
Warnings:
-Note 1003 select default(test.t1.str) AS `default(str)`,default(test.t1.strnull) AS `default(strnull)`,default(test.t1.intg) AS `default(intg)`,default(test.t1.rel) AS `default(rel)` from test.t1
+Note 1003 select default(`test`.`t1`.`str`) AS `default(str)`,default(`test`.`t1`.`strnull`) AS `default(strnull)`,default(`test`.`t1`.`intg`) AS `default(intg)`,default(`test`.`t1`.`rel`) AS `default(rel)` from `test`.`t1`
select * from t1 where str <> default(str);
str strnull intg rel
0 0
diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result
index e0883a6297e..665f9262dea 100644
--- a/mysql-test/r/func_gconcat.result
+++ b/mysql-test/r/func_gconcat.result
@@ -18,7 +18,7 @@ explain extended select grp,group_concat(c) from t1 group by grp;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 9 Using filesort
Warnings:
-Note 1003 select test.t1.grp AS `grp`,group_concat(test.t1.c separator ',') AS `group_concat(c)` from test.t1 group by test.t1.grp
+Note 1003 select `test`.`t1`.`grp` AS `grp`,group_concat(`test`.`t1`.`c` separator ',') AS `group_concat(c)` from `test`.`t1` group by `test`.`t1`.`grp`
select grp,group_concat(a,c) from t1 group by grp;
grp group_concat(a,c)
1 1a
@@ -93,7 +93,7 @@ explain extended select grp,group_concat(distinct c order by c desc) from t1 gro
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 9 Using filesort
Warnings:
-Note 1003 select test.t1.grp AS `grp`,group_concat(distinct test.t1.c order by test.t1.c separator ',') AS `group_concat(distinct c order by c desc)` from test.t1 group by test.t1.grp
+Note 1003 select `test`.`t1`.`grp` AS `grp`,group_concat(distinct `test`.`t1`.`c` order by `test`.`t1`.`c` separator ',') AS `group_concat(distinct c order by c desc)` from `test`.`t1` group by `test`.`t1`.`grp`
select grp,group_concat(c order by c separator ",") from t1 group by grp;
grp group_concat(c order by c separator ",")
1 a
@@ -113,7 +113,7 @@ explain extended select grp,group_concat(distinct c order by c separator ",") fr
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 9 Using filesort
Warnings:
-Note 1003 select test.t1.grp AS `grp`,group_concat(distinct test.t1.c order by test.t1.c separator ',') AS `group_concat(distinct c order by c separator ",")` from test.t1 group by test.t1.grp
+Note 1003 select `test`.`t1`.`grp` AS `grp`,group_concat(distinct `test`.`t1`.`c` order by `test`.`t1`.`c` separator ',') AS `group_concat(distinct c order by c separator ",")` from `test`.`t1` group by `test`.`t1`.`grp`
select grp,group_concat(distinct c order by c desc separator ",") from t1 group by grp;
grp group_concat(distinct c order by c desc separator ",")
1 a
diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result
index 06259ff4931..2ec389032f7 100644
--- a/mysql-test/r/func_group.result
+++ b/mysql-test/r/func_group.result
@@ -183,12 +183,12 @@ insert into t2 values('BBB', 20, 1.0);
select t1.a1, t1.a2, t2.a1, t2.a2 from t1,t2;
a1 a2 a1 a2
10 aaa AAA 10
-10 NULL AAA 10
-10 bbb AAA 10
-20 zzz AAA 10
10 aaa BBB 20
+10 NULL AAA 10
10 NULL BBB 20
+10 bbb AAA 10
10 bbb BBB 20
+20 zzz AAA 10
20 zzz BBB 20
select max(t1.a1), max(t2.a1) from t1, t2 where t2.a2=9;
max(t1.a1) max(t2.a1)
@@ -265,7 +265,7 @@ explain extended select SQL_BIG_RESULT a,count(b), sum(b), avg(b), std(b), min(b
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5 Using filesort
Warnings:
-Note 1003 select sql_big_result test.t1.a AS `a`,count(test.t1.b) AS `count(b)`,sum(test.t1.b) AS `sum(b)`,avg(test.t1.b) AS `avg(b)`,std(test.t1.b) AS `std(b)`,min(test.t1.b) AS `min(b)`,max(test.t1.b) AS `max(b)`,bit_and(test.t1.b) AS `bit_and(b)`,bit_or(test.t1.b) AS `bit_or(b)`,bit_xor(test.t1.b) AS `bit_xor(b)` from test.t1 group by test.t1.a
+Note 1003 select sql_big_result `test`.`t1`.`a` AS `a`,count(`test`.`t1`.`b`) AS `count(b)`,sum(`test`.`t1`.`b`) AS `sum(b)`,avg(`test`.`t1`.`b`) AS `avg(b)`,std(`test`.`t1`.`b`) AS `std(b)`,min(`test`.`t1`.`b`) AS `min(b)`,max(`test`.`t1`.`b`) AS `max(b)`,bit_and(`test`.`t1`.`b`) AS `bit_and(b)`,bit_or(`test`.`t1`.`b`) AS `bit_or(b)`,bit_xor(`test`.`t1`.`b`) AS `bit_xor(b)` from `test`.`t1` group by `test`.`t1`.`a`
drop table t1;
create table t1 (col int);
insert into t1 values (-1), (-2), (-3);
@@ -321,6 +321,7 @@ insert into t2 values('DEN','Denver','CO','BDL');
insert into t2 values('SDC','San Diego','CA','TWU');
insert into t2 values('NOL','New Orleans','LA','GTM');
insert into t2 values('LAK','Los Angeles','CA','TWU');
+insert into t2 values('AAA','AAA','AA','AME');
select * from t1;
a1 a2 a3 a4 a5
AME 0 SEA 0.1 1942-02-19
@@ -345,6 +346,7 @@ DEN Denver CO BDL
SDC San Diego CA TWU
NOL New Orleans LA GTM
LAK Los Angeles CA TWU
+AAA AAA AA AME
explain
select min(a1) from t1;
id select_type table type possible_keys key key_len ref rows Extra
@@ -572,11 +574,11 @@ AME AME
explain
select min(a1) from t1 where a1 > 'KKK' or a1 < 'XXX';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY PRIMARY 0 NULL 15 Using where; Using index
+1 SIMPLE t1 index PRIMARY PRIMARY 3 NULL 14 Using where; Using index
explain
select min(a1) from t1 where a1 != 'KKK';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY PRIMARY 3 NULL 14 Using where; Using index
+1 SIMPLE t1 index PRIMARY PRIMARY 3 NULL 14 Using where; Using index
explain
select max(a3) from t1 where a2 < 2 and a3 < 'SEA';
id select_type table type possible_keys key key_len ref rows Extra
@@ -584,7 +586,7 @@ id select_type table type possible_keys key key_len ref rows Extra
explain
select max(t1.a3), min(t2.a2) from t1, t2 where t1.a2 = 2 and t1.a3 < 'MIN' and t2.a3 > 'CA';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range k1 k1 7 NULL 1 Using where; Using index
+1 SIMPLE t1 ref k1 k1 3 const 2 Using where; Using index
1 SIMPLE t2 range k1 k1 3 NULL 4 Using where; Using index
explain
select min(a4 - 0.01) from t1;
diff --git a/mysql-test/r/func_if.result b/mysql-test/r/func_if.result
index dd916d06372..307d1021cdb 100644
--- a/mysql-test/r/func_if.result
+++ b/mysql-test/r/func_if.result
@@ -57,7 +57,7 @@ explain extended select nullif(u=0, 'test') from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 7
Warnings:
-Note 1003 select nullif((test.t1.u = 0),_latin1'test') AS `nullif(u=0, 'test')` from test.t1
+Note 1003 select nullif((`test`.`t1`.`u` = 0),_latin1'test') AS `nullif(u=0, 'test')` from `test`.`t1`
drop table t1;
select NULLIF(NULL,NULL), NULLIF(NULL,1), NULLIF(NULL,1.0), NULLIF(NULL,"test");
NULLIF(NULL,NULL) NULLIF(NULL,1) NULLIF(NULL,1.0) NULLIF(NULL,"test")
diff --git a/mysql-test/r/func_in.result b/mysql-test/r/func_in.result
index 374affce8c5..b132edab81d 100644
--- a/mysql-test/r/func_in.result
+++ b/mysql-test/r/func_in.result
@@ -146,7 +146,7 @@ explain extended select * from t1 where 'a' in (a,b,c collate latin1_bin);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where
Warnings:
-Note 1003 select test.t1.a AS `a`,test.t1.b AS `b`,test.t1.c AS `c` from test.t1 where (_latin1'a' in (test.t1.a,test.t1.b,(test.t1.c collate _latin1'latin1_bin')))
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where (_latin1'a' in (`test`.`t1`.`a`,`test`.`t1`.`b`,(`test`.`t1`.`c` collate _latin1'latin1_bin')))
drop table t1;
set names utf8;
create table t1 (a char(10) character set utf8 not null);
diff --git a/mysql-test/r/func_like.result b/mysql-test/r/func_like.result
index a58432cb06e..e9434b1749d 100644
--- a/mysql-test/r/func_like.result
+++ b/mysql-test/r/func_like.result
@@ -3,10 +3,10 @@ create table t1 (a varchar(10), key(a));
insert into t1 values ("a"),("abc"),("abcd"),("hello"),("test");
explain select * from t1 where a like 'abc%';
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 11 NULL 1 Using where; Using index
+1 SIMPLE t1 index a a 11 NULL 5 Using where; Using index
explain select * from t1 where a like concat('abc','%');
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 11 NULL 1 Using where; Using index
+1 SIMPLE t1 index a a 11 NULL 5 Using where; Using index
select * from t1 where a like "abc%";
a
abc
diff --git a/mysql-test/r/func_regexp.result b/mysql-test/r/func_regexp.result
index 8228d6982d3..787463c6aa3 100644
--- a/mysql-test/r/func_regexp.result
+++ b/mysql-test/r/func_regexp.result
@@ -40,7 +40,7 @@ explain extended select * from t1 where xxx regexp('is a test of some long text
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 system NULL NULL NULL NULL 1
Warnings:
-Note 1003 select test.t1.xxx AS `xxx` from test.t1 where (test.t1.xxx regexp _latin1'is a test of some long text to')
+Note 1003 select `test`.`t1`.`xxx` AS `xxx` from `test`.`t1` where (`test`.`t1`.`xxx` regexp _latin1'is a test of some long text to')
select * from t1 where xxx regexp('is a test of some long text to ');
xxx
this is a test of some long text to see what happens
diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result
index cf2bd687115..26fd688c312 100644
--- a/mysql-test/r/func_sapdb.result
+++ b/mysql-test/r/func_sapdb.result
@@ -194,15 +194,16 @@ ttt qqq
NULL NULL
NULL NULL
2001-01-01 02:02:02 26:02:02
-SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test;
-ttt qqq
--744:00:00 NULL
-26305:01:02 22:58:58
--26305:01:02 -22:58:58
-NULL 26:02:02
-NULL NULL
-NULL NULL
-00:00:00 -24:00:00
+SELECT TIMEDIFF(t1, t4) As ttt, TIMEDIFF(t2, t3) As qqq,
+TIMEDIFF(t3, t2) As eee, TIMEDIFF(t2, t4) As rrr from test;
+ttt qqq eee rrr
+-744:00:00 NULL NULL NULL
+26305:01:02 22:58:58 -22:58:58 NULL
+-26305:01:02 -22:58:58 22:58:58 NULL
+NULL 26:02:02 26:02:02 NULL
+NULL NULL NULL NULL
+NULL NULL NULL NULL
+00:00:00 -24:00:00 24:00:00 NULL
drop table t1, test;
select addtime("-01:01:01.01", "-23:59:59.1") as a;
a
diff --git a/mysql-test/r/func_test.result b/mysql-test/r/func_test.result
index c3fe1de15db..a969bf390bc 100644
--- a/mysql-test/r/func_test.result
+++ b/mysql-test/r/func_test.result
@@ -79,7 +79,7 @@ explain extended select * from t1 where 1 xor 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
Warnings:
-Note 1003 select test.t1.a AS `a` from test.t1 where (1 xor 1)
+Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (1 xor 1)
select - a from t1;
- a
-1
@@ -87,7 +87,7 @@ explain extended select - a from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 system NULL NULL NULL NULL 1
Warnings:
-Note 1003 select -(test.t1.a) AS `- a` from test.t1
+Note 1003 select -(`test`.`t1`.`a`) AS `- a` from `test`.`t1`
drop table t1;
select 5 between 0 and 10 between 0 and 1,(5 between 0 and 10) between 0 and 1;
5 between 0 and 10 between 0 and 1 (5 between 0 and 10) between 0 and 1
diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result
index 2dc6bffd071..64ace522fe6 100644
--- a/mysql-test/r/func_time.result
+++ b/mysql-test/r/func_time.result
@@ -530,6 +530,63 @@ date_add(date,INTERVAL "1 1:1" DAY_MINUTE)
select date_add(date,INTERVAL "1 1:1:1" DAY_SECOND) from t1;
date_add(date,INTERVAL "1 1:1:1" DAY_SECOND)
2003-01-03 01:01:01
+select date_add(date,INTERVAL "1" WEEK) from t1;
+date_add(date,INTERVAL "1" WEEK)
+2003-01-09 00:00:00
+select date_add(date,INTERVAL "1" QUARTER) from t1;
+date_add(date,INTERVAL "1" QUARTER)
+2003-04-02
+select timestampadd(MINUTE, 1, date) from t1;
+timestampadd(MINUTE, 1, date)
+2003-01-02 00:01:00
+select timestampadd(WEEK, 1, date) from t1;
+timestampadd(WEEK, 1, date)
+2003-01-09 00:00:00
+select timestampadd(SQL_TSI_SECOND, 1, date) from t1;
+timestampadd(SQL_TSI_SECOND, 1, date)
+2003-01-02 00:00:01
+select timestampadd(SQL_TSI_FRAC_SECOND, 1, date) from t1;
+timestampadd(SQL_TSI_FRAC_SECOND, 1, date)
+2003-01-02 00:00:00.000001
+select timestampdiff(MONTH, '2001-02-01', '2001-05-01') as a;
+a
+3
+select timestampdiff(YEAR, '2002-05-01', '2001-01-01') as a;
+a
+-1
+select timestampdiff(QUARTER, '2002-05-01', '2001-01-01') as a;
+a
+-5
+select timestampdiff(MONTH, '2000-03-28', '2000-02-29') as a;
+a
+0
+select timestampdiff(MONTH, '1991-03-28', '2000-02-29') as a;
+a
+107
+select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a;
+a
+12
+select timestampdiff(SQL_TSI_HOUR, '2001-02-01', '2001-05-01') as a;
+a
+2136
+select timestampdiff(SQL_TSI_DAY, '2001-02-01', '2001-05-01') as a;
+a
+89
+select timestampdiff(SQL_TSI_MINUTE, '2001-02-01 12:59:59', '2001-05-01 12:58:59') as a;
+a
+128159
+select timestampdiff(SQL_TSI_SECOND, '2001-02-01 12:59:59', '2001-05-01 12:58:58') as a;
+a
+7689539
+select timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a;
+a
+7689538999999
+select timestampdiff(SQL_TSI_DAY, '1986-02-01', '1986-03-01') as a1,
+timestampdiff(SQL_TSI_DAY, '1900-02-01', '1900-03-01') as a2,
+timestampdiff(SQL_TSI_DAY, '1996-02-01', '1996-03-01') as a3,
+timestampdiff(SQL_TSI_DAY, '2000-02-01', '2000-03-01') as a4;
+a1 a2 a3 a4
+28 28 29 29
select date_add(time,INTERVAL 1 SECOND) from t1;
date_add(time,INTERVAL 1 SECOND)
NULL
@@ -602,3 +659,9 @@ SELECT count(*) FROM t1 WHERE d>FROM_DAYS(TO_DAYS(@TMP)) AND d<=FROM_DAYS(TO_DAY
count(*)
3
DROP TABLE t1;
+explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1,
+timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 select timestamp_diff(WEEK,_latin1'2001-02-01',_latin1'2001-05-01') AS `a1`,timestamp_diff(SECOND_FRAC,_latin1'2001-02-01 12:59:59.120000',_latin1'2001-05-01 12:58:58.119999') AS `a2`
diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result
index 9f5dd286cf9..864d9d04ff5 100644
--- a/mysql-test/r/gis.result
+++ b/mysql-test/r/gis.result
@@ -228,7 +228,7 @@ explain extended select Dimension(g), GeometryType(g), IsEmpty(g), AsText(Envelo
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE gis_geometry ALL NULL NULL NULL NULL 21
Warnings:
-Note 1003 select dimension(test.gis_geometry.g) AS `Dimension(g)`,geometrytype(test.gis_geometry.g) AS `GeometryType(g)`,isempty(test.gis_geometry.g) AS `IsEmpty(g)`,astext(envelope(test.gis_geometry.g)) AS `AsText(Envelope(g))` from test.gis_geometry
+Note 1003 select dimension(`test`.`gis_geometry`.`g`) AS `Dimension(g)`,geometrytype(`test`.`gis_geometry`.`g`) AS `GeometryType(g)`,isempty(`test`.`gis_geometry`.`g`) AS `IsEmpty(g)`,astext(envelope(`test`.`gis_geometry`.`g`)) AS `AsText(Envelope(g))` from `test`.`gis_geometry`
SELECT fid, X(g) FROM gis_point;
fid X(g)
101 10
@@ -245,7 +245,7 @@ explain extended select X(g),Y(g) FROM gis_point;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE gis_point ALL NULL NULL NULL NULL 4
Warnings:
-Note 1003 select x(test.gis_point.g) AS `X(g)`,y(test.gis_point.g) AS `Y(g)` from test.gis_point
+Note 1003 select x(`test`.`gis_point`.`g`) AS `X(g)`,y(`test`.`gis_point`.`g`) AS `Y(g)` from `test`.`gis_point`
SELECT fid, AsText(StartPoint(g)) FROM gis_line;
fid AsText(StartPoint(g))
105 POINT(0 0)
@@ -280,7 +280,7 @@ explain extended select AsText(StartPoint(g)),AsText(EndPoint(g)),GLength(g),Num
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE gis_line ALL NULL NULL NULL NULL 3
Warnings:
-Note 1003 select astext(startpoint(test.gis_line.g)) AS `AsText(StartPoint(g))`,astext(endpoint(test.gis_line.g)) AS `AsText(EndPoint(g))`,glength(test.gis_line.g) AS `GLength(g)`,numpoints(test.gis_line.g) AS `NumPoints(g)`,astext(pointn(test.gis_line.g,2)) AS `AsText(PointN(g, 2))`,isclosed(test.gis_line.g) AS `IsClosed(g)` from test.gis_line
+Note 1003 select astext(startpoint(`test`.`gis_line`.`g`)) AS `AsText(StartPoint(g))`,astext(endpoint(`test`.`gis_line`.`g`)) AS `AsText(EndPoint(g))`,glength(`test`.`gis_line`.`g`) AS `GLength(g)`,numpoints(`test`.`gis_line`.`g`) AS `NumPoints(g)`,astext(pointn(`test`.`gis_line`.`g`,2)) AS `AsText(PointN(g, 2))`,isclosed(`test`.`gis_line`.`g`) AS `IsClosed(g)` from `test`.`gis_line`
SELECT fid, AsText(Centroid(g)) FROM gis_polygon;
fid AsText(Centroid(g))
108 POINT(15 15)
@@ -310,7 +310,7 @@ explain extended select AsText(Centroid(g)),Area(g),AsText(ExteriorRing(g)),NumI
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE gis_polygon ALL NULL NULL NULL NULL 3
Warnings:
-Note 1003 select astext(centroid(test.gis_polygon.g)) AS `AsText(Centroid(g))`,area(test.gis_polygon.g) AS `Area(g)`,astext(exteriorring(test.gis_polygon.g)) AS `AsText(ExteriorRing(g))`,numinteriorrings(test.gis_polygon.g) AS `NumInteriorRings(g)`,astext(interiorringn(test.gis_polygon.g,1)) AS `AsText(InteriorRingN(g, 1))` from test.gis_polygon
+Note 1003 select astext(centroid(`test`.`gis_polygon`.`g`)) AS `AsText(Centroid(g))`,area(`test`.`gis_polygon`.`g`) AS `Area(g)`,astext(exteriorring(`test`.`gis_polygon`.`g`)) AS `AsText(ExteriorRing(g))`,numinteriorrings(`test`.`gis_polygon`.`g`) AS `NumInteriorRings(g)`,astext(interiorringn(`test`.`gis_polygon`.`g`,1)) AS `AsText(InteriorRingN(g, 1))` from `test`.`gis_polygon`
SELECT fid, IsClosed(g) FROM gis_multi_line;
fid IsClosed(g)
114 0
@@ -349,7 +349,7 @@ explain extended SELECT fid, NumGeometries(g) from gis_multi_point;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE gis_multi_point ALL NULL NULL NULL NULL 3
Warnings:
-Note 1003 select test.gis_multi_point.fid AS `fid`,numgeometries(test.gis_multi_point.g) AS `NumGeometries(g)` from test.gis_multi_point
+Note 1003 select `test`.`gis_multi_point`.`fid` AS `fid`,numgeometries(`test`.`gis_multi_point`.`g`) AS `NumGeometries(g)` from `test`.`gis_multi_point`
SELECT fid, AsText(GeometryN(g, 2)) from gis_multi_point;
fid AsText(GeometryN(g, 2))
111 POINT(10 10)
@@ -377,7 +377,7 @@ explain extended SELECT fid, AsText(GeometryN(g, 2)) from gis_multi_point;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE gis_multi_point ALL NULL NULL NULL NULL 3
Warnings:
-Note 1003 select test.gis_multi_point.fid AS `fid`,astext(geometryn(test.gis_multi_point.g,2)) AS `AsText(GeometryN(g, 2))` from test.gis_multi_point
+Note 1003 select `test`.`gis_multi_point`.`fid` AS `fid`,astext(geometryn(`test`.`gis_multi_point`.`g`,2)) AS `AsText(GeometryN(g, 2))` from `test`.`gis_multi_point`
SELECT g1.fid as first, g2.fid as second,
Within(g1.g, g2.g) as w, Contains(g1.g, g2.g) as c, Overlaps(g1.g, g2.g) as o,
Equals(g1.g, g2.g) as e, Disjoint(g1.g, g2.g) as d, Touches(g1.g, g2.g) as t,
@@ -397,7 +397,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE g1 ALL NULL NULL NULL NULL 2 Using temporary; Using filesort
1 SIMPLE g2 ALL NULL NULL NULL NULL 2
Warnings:
-Note 1003 select test.g1.fid AS `first`,test.g2.fid AS `second`,within(test.g1.g,test.g2.g) AS `w`,contains(test.g1.g,test.g2.g) AS `c`,overlaps(test.g1.g,test.g2.g) AS `o`,equals(test.g1.g,test.g2.g) AS `e`,disjoint(test.g1.g,test.g2.g) AS `d`,touches(test.g1.g,test.g2.g) AS `t`,intersects(test.g1.g,test.g2.g) AS `i`,crosses(test.g1.g,test.g2.g) AS `r` from test.gis_geometrycollection g1 join test.gis_geometrycollection g2 order by test.g1.fid,test.g2.fid
+Note 1003 select `test`.`g1`.`fid` AS `first`,`test`.`g2`.`fid` AS `second`,within(`test`.`g1`.`g`,`test`.`g2`.`g`) AS `w`,contains(`test`.`g1`.`g`,`test`.`g2`.`g`) AS `c`,overlaps(`test`.`g1`.`g`,`test`.`g2`.`g`) AS `o`,equals(`test`.`g1`.`g`,`test`.`g2`.`g`) AS `e`,disjoint(`test`.`g1`.`g`,`test`.`g2`.`g`) AS `d`,touches(`test`.`g1`.`g`,`test`.`g2`.`g`) AS `t`,intersects(`test`.`g1`.`g`,`test`.`g2`.`g`) AS `i`,crosses(`test`.`g1`.`g`,`test`.`g2`.`g`) AS `r` from `test`.`gis_geometrycollection` `g1` join `test`.`gis_geometrycollection` `g2` order by `test`.`g1`.`fid`,`test`.`g2`.`fid`
DROP TABLE gis_point, gis_line, gis_polygon, gis_multi_point, gis_multi_line, gis_multi_polygon, gis_geometrycollection, gis_geometry;
CREATE TABLE t1 (
gp point,
diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result
index 35b90349804..866c19155db 100644
--- a/mysql-test/r/grant.result
+++ b/mysql-test/r/grant.result
@@ -10,8 +10,8 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3
GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
grant delete on mysqltest.* to mysqltest_1@localhost;
select * from mysql.user where user="mysqltest_1";
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections
-localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N SPECIFIED EDH-RSA-DES-CBC3-SHA 0 0 0
+Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections
+localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 0 0 0
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
@@ -62,7 +62,7 @@ revoke LOCK TABLES, ALTER on mysqltest.* from mysqltest_1@localhost;
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost'
-GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, CREATE VIEW, SHOW VIEW ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION
revoke all privileges on mysqltest.* from mysqltest_1@localhost;
delete from mysql.user where user='mysqltest_1';
flush privileges;
@@ -248,3 +248,28 @@ GRANT SELECT ON `ab%`.* TO 'test11'@'localhost'
GRANT SELECT ON `a%`.* TO 'test11'@'localhost'
delete from mysql.user where user='test11';
delete from mysql.db where user='test11';
+SHOW PRIVILEGES;
+Privilege Context Comment
+Alter Tables To alter the table
+Create Databases,Tables,Indexes To create new databases and tables
+Create temporary tables Databases To use CREATE TEMPORARY TABLE
+Create view Tables To create new views
+Delete Tables To delete existing rows
+Drop Databases,Tables To drop databases, tables, and views
+File File access on server To read and write files on the server
+Grant option Databases,Tables To give to other users those privileges you possess
+Index Tables To create or drop indexes
+Insert Tables To insert data into tables
+Lock tables Databases To use LOCK TABLES (together with SELECT privilege)
+Process Server Admin To view the plain text of currently executing queries
+References Databases,Tables To have references on tables
+Reload Server Admin To reload or refresh tables, logs and privileges
+Replication client Server Admin To ask where the slave or master servers are
+Replication slave Server Admin To read binary log events from the master
+Select Tables To retrieve rows from table
+Show databases Server Admin To see all databases with SHOW DATABASES
+Show view Tables To see views with SHOW CREATE VIEW
+Shutdown Server Admin To shut down the server
+Super Server Admin To use KILL thread, SET GLOBAL, CHANGE MASTER, etc.
+Update Tables To update existing rows
+Usage Server Admin No privileges - allow connect only
diff --git a/mysql-test/r/greedy_optimizer.result b/mysql-test/r/greedy_optimizer.result
new file mode 100644
index 00000000000..cf4f6e290d7
--- /dev/null
+++ b/mysql-test/r/greedy_optimizer.result
@@ -0,0 +1,657 @@
+drop table if exists t1,t2,t3,t4,t5,t6,t7;
+create table t1 (
+c11 integer,c12 integer,c13 integer,c14 integer,c15 integer,c16 integer,
+primary key (c11)
+);
+create table t2 (
+c21 integer,c22 integer,c23 integer,c24 integer,c25 integer,c26 integer
+);
+create table t3 (
+c31 integer,c32 integer,c33 integer,c34 integer,c35 integer,c36 integer,
+primary key (c31)
+);
+create table t4 (
+c41 integer,c42 integer,c43 integer,c44 integer,c45 integer,c46 integer
+);
+create table t5 (
+c51 integer,c52 integer,c53 integer,c54 integer,c55 integer,c56 integer,
+primary key (c51)
+);
+create table t6 (
+c61 integer,c62 integer,c63 integer,c64 integer,c65 integer,c66 integer
+);
+create table t7 (
+c71 integer,c72 integer,c73 integer,c74 integer,c75 integer,c76 integer,
+primary key (c71)
+);
+insert into t1 values (1,2,3,4,5,6);
+insert into t1 values (2,2,3,4,5,6);
+insert into t1 values (3,2,3,4,5,6);
+insert into t2 values (1,2,3,4,5,6);
+insert into t2 values (2,2,3,4,5,6);
+insert into t2 values (3,2,3,4,5,6);
+insert into t2 values (4,2,3,4,5,6);
+insert into t2 values (5,2,3,4,5,6);
+insert into t2 values (6,2,3,4,5,6);
+insert into t3 values (1,2,3,4,5,6);
+insert into t3 values (2,2,3,4,5,6);
+insert into t3 values (3,2,3,4,5,6);
+insert into t3 values (4,2,3,4,5,6);
+insert into t3 values (5,2,3,4,5,6);
+insert into t3 values (6,2,3,4,5,6);
+insert into t3 values (7,2,3,4,5,6);
+insert into t3 values (8,2,3,4,5,6);
+insert into t3 values (9,2,3,4,5,6);
+insert into t4 values (1,2,3,4,5,6);
+insert into t4 values (2,2,3,4,5,6);
+insert into t4 values (3,2,3,4,5,6);
+insert into t4 values (4,2,3,4,5,6);
+insert into t4 values (5,2,3,4,5,6);
+insert into t4 values (6,2,3,4,5,6);
+insert into t4 values (7,2,3,4,5,6);
+insert into t4 values (8,2,3,4,5,6);
+insert into t4 values (9,2,3,4,5,6);
+insert into t4 values (10,2,3,4,5,6);
+insert into t4 values (11,2,3,4,5,6);
+insert into t4 values (12,2,3,4,5,6);
+insert into t5 values (1,2,3,4,5,6);
+insert into t5 values (2,2,3,4,5,6);
+insert into t5 values (3,2,3,4,5,6);
+insert into t5 values (4,2,3,4,5,6);
+insert into t5 values (5,2,3,4,5,6);
+insert into t5 values (6,2,3,4,5,6);
+insert into t5 values (7,2,3,4,5,6);
+insert into t5 values (8,2,3,4,5,6);
+insert into t5 values (9,2,3,4,5,6);
+insert into t5 values (10,2,3,4,5,6);
+insert into t5 values (11,2,3,4,5,6);
+insert into t5 values (12,2,3,4,5,6);
+insert into t5 values (13,2,3,4,5,6);
+insert into t5 values (14,2,3,4,5,6);
+insert into t5 values (15,2,3,4,5,6);
+insert into t6 values (1,2,3,4,5,6);
+insert into t6 values (2,2,3,4,5,6);
+insert into t6 values (3,2,3,4,5,6);
+insert into t6 values (4,2,3,4,5,6);
+insert into t6 values (5,2,3,4,5,6);
+insert into t6 values (6,2,3,4,5,6);
+insert into t6 values (7,2,3,4,5,6);
+insert into t6 values (8,2,3,4,5,6);
+insert into t6 values (9,2,3,4,5,6);
+insert into t6 values (10,2,3,4,5,6);
+insert into t6 values (11,2,3,4,5,6);
+insert into t6 values (12,2,3,4,5,6);
+insert into t6 values (13,2,3,4,5,6);
+insert into t6 values (14,2,3,4,5,6);
+insert into t6 values (15,2,3,4,5,6);
+insert into t6 values (16,2,3,4,5,6);
+insert into t6 values (17,2,3,4,5,6);
+insert into t6 values (18,2,3,4,5,6);
+insert into t7 values (1,2,3,4,5,6);
+insert into t7 values (2,2,3,4,5,6);
+insert into t7 values (3,2,3,4,5,6);
+insert into t7 values (4,2,3,4,5,6);
+insert into t7 values (5,2,3,4,5,6);
+insert into t7 values (6,2,3,4,5,6);
+insert into t7 values (7,2,3,4,5,6);
+insert into t7 values (8,2,3,4,5,6);
+insert into t7 values (9,2,3,4,5,6);
+insert into t7 values (10,2,3,4,5,6);
+insert into t7 values (11,2,3,4,5,6);
+insert into t7 values (12,2,3,4,5,6);
+insert into t7 values (13,2,3,4,5,6);
+insert into t7 values (14,2,3,4,5,6);
+insert into t7 values (15,2,3,4,5,6);
+insert into t7 values (16,2,3,4,5,6);
+insert into t7 values (17,2,3,4,5,6);
+insert into t7 values (18,2,3,4,5,6);
+insert into t7 values (19,2,3,4,5,6);
+insert into t7 values (20,2,3,4,5,6);
+insert into t7 values (21,2,3,4,5,6);
+select @@optimizer_search_depth;
+@@optimizer_search_depth
+62
+select @@optimizer_prune_level;
+@@optimizer_prune_level
+1
+set optimizer_search_depth=63;
+select @@optimizer_search_depth;
+@@optimizer_search_depth
+63
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.838037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.838037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.838037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.838037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.838037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.838037
+set optimizer_prune_level=0;
+select @@optimizer_prune_level;
+@@optimizer_prune_level
+0
+set optimizer_search_depth=0;
+select @@optimizer_search_depth;
+@@optimizer_search_depth
+0
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.c21 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 274.418727
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.c21 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 274.418727
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.c21 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 274.418727
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.c21 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 274.418727
+set optimizer_search_depth=1;
+select @@optimizer_search_depth;
+@@optimizer_search_depth
+1
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+set optimizer_search_depth=62;
+select @@optimizer_search_depth;
+@@optimizer_search_depth
+62
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.c21 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 274.418727
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.c21 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 274.418727
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.c21 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 274.418727
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t1 eq_ref PRIMARY PRIMARY 4 test.t2.c21 1 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 274.418727
+set optimizer_prune_level=1;
+select @@optimizer_prune_level;
+@@optimizer_prune_level
+1
+set optimizer_search_depth=0;
+select @@optimizer_search_depth;
+@@optimizer_search_depth
+0
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+set optimizer_search_depth=1;
+select @@optimizer_search_depth;
+@@optimizer_search_depth
+1
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+set optimizer_search_depth=62;
+select @@optimizer_search_depth;
+@@optimizer_search_depth
+62
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t2.c22 1
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t4.c42 1
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t6.c62 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 821.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using index
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using index
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using index
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ALL PRIMARY NULL NULL NULL 3
+1 SIMPLE t2 ALL NULL NULL NULL NULL 6 Using where
+1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.c12 1 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
+1 SIMPLE t5 eq_ref PRIMARY PRIMARY 4 test.t1.c14 1 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 18 Using where
+1 SIMPLE t7 eq_ref PRIMARY PRIMARY 4 test.t1.c16 1 Using where
+show status like 'Last_query_cost';
+Variable_name Value
+Last_query_cost 794.837037
+drop table t1,t2,t3,t4,t5,t6,t7;
diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result
index 9af7304c167..4192a7701b2 100644
--- a/mysql-test/r/group_by.result
+++ b/mysql-test/r/group_by.result
@@ -288,7 +288,7 @@ explain extended select sql_big_result spid,sum(userid) from t1 group by spid de
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using filesort
Warnings:
-Note 1003 select sql_big_result test.t1.spID AS `spid`,sum(test.t1.userID) AS `sum(userid)` from test.t1 group by test.t1.spID desc
+Note 1003 select sql_big_result `test`.`t1`.`spID` AS `spid`,sum(`test`.`t1`.`userID`) AS `sum(userid)` from `test`.`t1` group by `test`.`t1`.`spID` desc
explain select sql_big_result spid,sum(userid) from t1 group by spid desc order by null;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using filesort
diff --git a/mysql-test/r/having.result b/mysql-test/r/having.result
index 218276406b1..2e94974e953 100644
--- a/mysql-test/r/having.result
+++ b/mysql-test/r/having.result
@@ -12,7 +12,7 @@ explain extended select count(a) as b from t1 where a=0 having b >=0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
Warnings:
-Note 1003 select count(test.t1.a) AS `b` from test.t1 where (test.t1.a = 0) having (count(test.t1.a) >= 0)
+Note 1003 select count(`test`.`t1`.`a`) AS `b` from `test`.`t1` where (`test`.`t1`.`a` = 0) having (count(`test`.`t1`.`a`) >= 0)
drop table t1;
CREATE TABLE t1 (
raw_id int(10) NOT NULL default '0',
diff --git a/mysql-test/r/heap.result b/mysql-test/r/heap.result
index c49c9abb368..a33d26f9efa 100644
--- a/mysql-test/r/heap.result
+++ b/mysql-test/r/heap.result
@@ -66,7 +66,7 @@ a
alter table t1 engine=myisam;
explain select * from t1 where a in (869751,736494,226312,802616);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range uniq_id uniq_id 4 NULL 4 Using where; Using index
+1 SIMPLE t1 index uniq_id uniq_id 4 NULL 4 Using where; Using index
drop table t1;
create table t1 (x int not null, y int not null, key x (x), unique y (y))
engine=heap;
diff --git a/mysql-test/r/heap_btree.result b/mysql-test/r/heap_btree.result
index 5c60c97d674..03625d19d04 100644
--- a/mysql-test/r/heap_btree.result
+++ b/mysql-test/r/heap_btree.result
@@ -66,7 +66,7 @@ a
alter table t1 engine=myisam;
explain select * from t1 where a in (869751,736494,226312,802616);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range uniq_id uniq_id 4 NULL 4 Using where; Using index
+1 SIMPLE t1 index uniq_id uniq_id 4 NULL 4 Using where; Using index
drop table t1;
create table t1 (x int not null, y int not null, key x using BTREE (x,y), unique y using BTREE (y))
engine=heap;
diff --git a/mysql-test/r/heap_hash.result b/mysql-test/r/heap_hash.result
index 7affbf788fb..ba02328d1ba 100644
--- a/mysql-test/r/heap_hash.result
+++ b/mysql-test/r/heap_hash.result
@@ -66,7 +66,7 @@ a
alter table t1 engine=myisam;
explain select * from t1 where a in (869751,736494,226312,802616);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range uniq_id uniq_id 4 NULL 4 Using where; Using index
+1 SIMPLE t1 index uniq_id uniq_id 4 NULL 4 Using where; Using index
drop table t1;
create table t1 (x int not null, y int not null, key x using HASH (x), unique y using HASH (y))
engine=heap;
diff --git a/mysql-test/r/index_merge.result b/mysql-test/r/index_merge.result
new file mode 100644
index 00000000000..f3654db030c
--- /dev/null
+++ b/mysql-test/r/index_merge.result
@@ -0,0 +1,338 @@
+drop table if exists t0, t1, t2, t3,t4;
+create table t0
+(
+key1 int not null,
+INDEX i1(key1)
+);
+alter table t0 add key2 int not null, add index i2(key2);
+alter table t0 add key3 int not null, add index i3(key3);
+alter table t0 add key4 int not null, add index i4(key4);
+alter table t0 add key5 int not null, add index i5(key5);
+alter table t0 add key6 int not null, add index i6(key6);
+alter table t0 add key7 int not null, add index i7(key7);
+alter table t0 add key8 int not null, add index i8(key8);
+update t0 set key2=key1,key3=key1,key4=key1,key5=key1,key6=key1,key7=key1,key8=1024-key1;
+analyze table t0;
+Table Op Msg_type Msg_text
+test.t0 analyze status OK
+explain select * from t0 where key1 < 3 or key1 > 1020;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 range i1 i1 4 NULL 55 Using where
+explain
+select * from t0 where key1 < 3 or key2 > 1020;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 31 Using sort_union(i1,i2); Using where
+select * from t0 where key1 < 3 or key2 > 1020;
+key1 key2 key3 key4 key5 key6 key7 key8
+1 1 1 1 1 1 1 1023
+2 2 2 2 2 2 2 1022
+1021 1021 1021 1021 1021 1021 1021 3
+1022 1022 1022 1022 1022 1022 1022 2
+1023 1023 1023 1023 1023 1023 1023 1
+1024 1024 1024 1024 1024 1024 1024 0
+explain select * from t0 where key1 < 3 or key2 <4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using sort_union(i1,i2); Using where
+explain
+select * from t0 where (key1 > 30 and key1<35) or (key2 >32 and key2 < 40);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 9 Using sort_union(i1,i2); Using where
+select * from t0 where (key1 > 30 and key1<35) or (key2 >32 and key2 < 40);
+key1 key2 key3 key4 key5 key6 key7 key8
+31 31 31 31 31 31 31 993
+32 32 32 32 32 32 32 992
+33 33 33 33 33 33 33 991
+34 34 34 34 34 34 34 990
+35 35 35 35 35 35 35 989
+36 36 36 36 36 36 36 988
+37 37 37 37 37 37 37 987
+38 38 38 38 38 38 38 986
+39 39 39 39 39 39 39 985
+explain select * from t0 ignore index (i2) where key1 < 3 or key2 <4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL i1 NULL NULL NULL 1024 Using where
+explain select * from t0 where (key1 < 3 or key2 <4) and key3 = 50;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ref i1,i2,i3 i3 4 const 1 Using where
+explain select * from t0 use index (i1,i2) where (key1 < 3 or key2 <4) and key3 = 50;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using sort_union(i1,i2); Using where
+explain select * from t0 where (key1 > 1 or key2 > 2);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL i1,i2 NULL NULL NULL 1024 Using where
+explain select * from t0 force index (i1,i2) where (key1 > 1 or key2 > 2);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 1024 Using sort_union(i1,i2); Using where
+explain
+select * from t0 where key1<3 or key2<3 or (key1>5 and key1<8) or
+(key1>10 and key1<12) or (key2>100 and key2<110);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 17 Using sort_union(i1,i2); Using where
+explain select * from t0 where key2 = 45 or key1 <=> null;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 range i1,i2 i2 4 NULL 1 Using where
+explain select * from t0 where key2 = 45 or key1 is not null;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL i1,i2 NULL NULL NULL 1024 Using where
+explain select * from t0 where key2 = 45 or key1 is null;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 range i1,i2 i2 4 NULL 1 Using where
+explain select * from t0 where key2=10 or key3=3 or key4 <=> null;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using union(i2,i3); Using where
+explain select * from t0 where key2=10 or key3=3 or key4 is null;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i2,i3,i4 i2,i3 4,4 NULL 2 Using union(i2,i3); Using where
+explain select key1 from t0 where (key1 <=> null) or (key2 < 5) or
+(key3=10) or (key4 <=> null);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3,i4 i2,i3 4,4 NULL 6 Using sort_union(i2,i3); Using where
+explain select key1 from t0 where (key1 <=> null) or (key1 < 5) or
+(key3=10) or (key4 <=> null);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i3,i4 i1,i3 4,4 NULL 5 Using sort_union(i1,i3); Using where
+explain select * from t0 where
+(key1 < 3 or key2 < 3) and (key3 < 4 or key4 < 4) and (key5 < 5 or key6 < 5);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3,i4,i5,i6 i1,i2 4,4 NULL 6 Using sort_union(i1,i2); Using where
+explain
+select * from t0 where (key1 < 3 or key2 < 6) and (key1 < 7 or key3 < 4);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3 i1,i2 4,4 NULL 9 Using sort_union(i1,i2); Using where
+select * from t0 where (key1 < 3 or key2 < 6) and (key1 < 7 or key3 < 4);
+key1 key2 key3 key4 key5 key6 key7 key8
+1 1 1 1 1 1 1 1023
+2 2 2 2 2 2 2 1022
+3 3 3 3 3 3 3 1021
+4 4 4 4 4 4 4 1020
+5 5 5 5 5 5 5 1019
+explain select * from t0 where
+(key1 < 3 or key2 < 3) and (key3 < 4 or key4 < 4) and (key5 < 2 or key6 < 2);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3,i4,i5,i6 i1,i2 4,4 NULL 6 Using sort_union(i1,i2); Using where
+explain select * from t0 where
+(key1 < 3 or key2 < 3) and (key3 < 100);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 range i1,i2,i3 i3 4 NULL 96 Using where
+explain select * from t0 where
+(key1 < 3 or key2 < 3) and (key3 < 1000);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL i1,i2,i3 NULL NULL NULL 1024 Using where
+explain select * from t0 where
+((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
+or
+key2 > 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL i1,i2,i3 NULL NULL NULL 1024 Using where
+explain select * from t0 where
+((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
+or
+key1 < 7;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3 i1,i2 4,4 NULL 10 Using sort_union(i1,i2); Using where
+select * from t0 where
+((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
+or
+key1 < 7;
+key1 key2 key3 key4 key5 key6 key7 key8
+1 1 1 1 1 1 1 1023
+2 2 2 2 2 2 2 1022
+3 3 3 3 3 3 3 1021
+4 4 4 4 4 4 4 1020
+5 5 5 5 5 5 5 1019
+6 6 6 6 6 6 6 1018
+explain select * from t0 where
+((key1 < 4 or key2 < 4) and (key3 <5 or key5 < 4))
+or
+((key5 < 5 or key6 < 6) and (key7 <7 or key8 < 4));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7,i8 i1,i2,i5,i6 4,4,4,4 NULL 19 Using sort_union(i1,i2,i5,i6); Using where
+explain select * from t0 where
+((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+or
+((key7 <7 or key8 < 4) and (key5 < 5 or key6 < 6));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7,i8 i3,i5,i7,i8 4,4,4,4 NULL 21 Using sort_union(i3,i5,i7,i8); Using where
+explain select * from t0 where
+((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+or
+((key3 <7 or key5 < 2) and (key5 < 5 or key6 < 6));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6 i3,i5 4,4 NULL 11 Using sort_union(i3,i5); Using where
+explain select * from t0 where
+((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+or
+(((key3 <7 and key7 < 6) or key5 < 2) and (key5 < 5 or key6 < 6));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6,i7 i3,i5 4,4 NULL 11 Using sort_union(i3,i5); Using where
+explain select * from t0 where
+((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+or
+((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL i1,i2,i3,i5,i6 NULL NULL NULL 1024 Using where
+explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
+((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+or
+((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2,i3,i5,i6 i3,i5 0,4 NULL 1024 Using sort_union(i3,i5); Using where
+select * from t0 where key1 < 5 or key8 < 4 order by key1;
+key1 key2 key3 key4 key5 key6 key7 key8
+1 1 1 1 1 1 1 1023
+2 2 2 2 2 2 2 1022
+3 3 3 3 3 3 3 1021
+4 4 4 4 4 4 4 1020
+1021 1021 1021 1021 1021 1021 1021 3
+1022 1022 1022 1022 1022 1022 1022 2
+1023 1023 1023 1023 1023 1023 1023 1
+1024 1024 1024 1024 1024 1024 1024 0
+explain
+select * from t0 where key1 < 5 or key8 < 4 order by key1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i8 i1,i8 4,4 NULL 9 Using sort_union(i1,i8); Using where; Using filesort
+create table t2 like t0;
+insert into t2 select * from t0;
+alter table t2 add index i1_3(key1, key3);
+alter table t2 add index i2_3(key2, key3);
+alter table t2 drop index i1;
+alter table t2 drop index i2;
+alter table t2 add index i321(key3, key2, key1);
+explain select key3 from t2 where key1 = 100 or key2 = 100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 index_merge i1_3,i2_3 i1_3,i2_3 4,4 NULL 2 Using sort_union(i1_3,i2_3); Using where
+explain select key3 from t2 where key1 <100 or key2 < 100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 index i1_3,i2_3 i321 12 NULL 1024 Using where; Using index
+explain select key7 from t2 where key1 <100 or key2 < 100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL i1_3,i2_3 NULL NULL NULL 1024 Using where
+create table t4 (
+key1a int not null,
+key1b int not null,
+key2 int not null,
+key2_1 int not null,
+key2_2 int not null,
+key3 int not null,
+index i1a (key1a, key1b),
+index i1b (key1b, key1a),
+index i2_1(key2, key2_1),
+index i2_2(key2, key2_1)
+);
+insert into t4 select key1,key1,key1 div 10, key1 % 10, key1 % 10, key1 from t0;
+select * from t4 where key1a = 3 or key1b = 4;
+key1a key1b key2 key2_1 key2_2 key3
+3 3 0 3 3 3
+4 4 0 4 4 4
+explain select * from t4 where key1a = 3 or key1b = 4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t4 index_merge i1a,i1b i1a,i1b 4,4 NULL 2 Using sort_union(i1a,i1b); Using where
+explain select * from t4 where key2 = 1 and (key2_1 = 1 or key3 = 5);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t4 ref i2_1,i2_2 i2_1 4 const 10 Using where
+explain select * from t4 where key2 = 1 and (key2_1 = 1 or key2_2 = 5);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t4 ref i2_1,i2_2 i2_1 4 const 10 Using where
+explain select * from t4 where key2_1 = 1 or key2_2 = 5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t4 ALL NULL NULL NULL NULL 1024 Using where
+create table t1 like t0;
+insert into t1 select * from t0;
+explain select * from t0 left join t1 on (t0.key1=t1.key1)
+where t0.key1=3 or t0.key2=4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
+1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1
+select * from t0 left join t1 on (t0.key1=t1.key1)
+where t0.key1=3 or t0.key2=4;
+key1 key2 key3 key4 key5 key6 key7 key8 key1 key2 key3 key4 key5 key6 key7 key8
+3 3 3 3 3 3 3 1021 3 3 3 3 3 3 3 1021
+4 4 4 4 4 4 4 1020 4 4 4 4 4 4 4 1020
+explain
+select * from t0,t1 where (t0.key1=t1.key1) and ( t0.key1=3 or t0.key2=4);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
+1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1
+explain
+select * from t0,t1 where (t0.key1=t1.key1) and
+(t0.key1=3 or t0.key2=4) and t1.key1<200;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
+1 SIMPLE t1 ref i1 i1 4 test.t0.key1 1 Using where
+explain
+select * from t0,t1 where (t0.key1=t1.key1) and
+(t0.key1=3 or t0.key2<4) and t1.key1=2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ref i1,i2 i1 4 const 1 Using where
+1 SIMPLE t1 ref i1 i1 4 const 1 Using where
+explain select * from t0,t1 where t0.key1 = 5 and
+(t1.key1 = t0.key1 or t1.key8 = t0.key1);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ref i1 i1 4 const 1 Using where
+1 SIMPLE t1 index_merge i1,i8 i1,i8 4,4 NULL 2 Using union(i1,i8); Using where
+explain select * from t0,t1 where t0.key1 < 3 and
+(t1.key1 = t0.key1 or t1.key8 = t0.key1);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 range i1 i1 4 NULL 3 Using where
+1 SIMPLE t1 ALL i1,i8 NULL NULL NULL 1024 Range checked for each record (index map: 0x81)
+explain select * from t1 where key1=3 or key2=4
+union select * from t1 where key1<4 or key3=5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
+2 UNION t1 index_merge i1,i3 i1,i3 4,4 NULL 5 Using sort_union(i1,i3); Using where
+NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL
+explain select * from (select * from t1 where key1 = 3 or key2 =3) as Z where key8 >5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> system NULL NULL NULL NULL 1
+2 DERIVED t1 index_merge i1,i2 i1,i2 4,4 NULL 2 Using union(i1,i2); Using where
+create table t3 like t0;
+insert into t3 select * from t0;
+alter table t3 add key9 int not null, add index i9(key9);
+alter table t3 add keyA int not null, add index iA(keyA);
+alter table t3 add keyB int not null, add index iB(keyB);
+alter table t3 add keyC int not null, add index iC(keyC);
+update t3 set key9=key1,keyA=key1,keyB=key1,keyC=key1;
+explain select * from t3 where
+key1=1 or key2=2 or key3=3 or key4=4 or
+key5=5 or key6=6 or key7=7 or key8=8 or
+key9=9 or keyA=10 or keyB=11 or keyC=12;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t3 index_merge i1,i2,i3,i4,i5,i6,i7,i8,i9,iA,iB,iC i1,i2,i3,i4,i5,i6,i7,i8,i9,iA,iB,iC 4,4,4,4,4,4,4,4,4,4,4,4 NULL 12 Using union(i1,i2,i3,i4,i5,i6,i7,i8,i9,iA,iB,iC); Using where
+select * from t3 where
+key1=1 or key2=2 or key3=3 or key4=4 or
+key5=5 or key6=6 or key7=7 or key8=8 or
+key9=9 or keyA=10 or keyB=11 or keyC=12;
+key1 key2 key3 key4 key5 key6 key7 key8 key9 keyA keyB keyC
+1 1 1 1 1 1 1 1023 1 1 1 1
+2 2 2 2 2 2 2 1022 2 2 2 2
+3 3 3 3 3 3 3 1021 3 3 3 3
+4 4 4 4 4 4 4 1020 4 4 4 4
+5 5 5 5 5 5 5 1019 5 5 5 5
+6 6 6 6 6 6 6 1018 6 6 6 6
+7 7 7 7 7 7 7 1017 7 7 7 7
+9 9 9 9 9 9 9 1015 9 9 9 9
+10 10 10 10 10 10 10 1014 10 10 10 10
+11 11 11 11 11 11 11 1013 11 11 11 11
+12 12 12 12 12 12 12 1012 12 12 12 12
+1016 1016 1016 1016 1016 1016 1016 8 1016 1016 1016 1016
+explain select * from t0 where key1 < 3 or key2 < 4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 index_merge i1,i2 i1,i2 4,4 NULL 7 Using sort_union(i1,i2); Using where
+select * from t0 where key1 < 3 or key2 < 4;
+key1 key2 key3 key4 key5 key6 key7 key8
+1 1 1 1 1 1 1 1023
+2 2 2 2 2 2 2 1022
+3 3 3 3 3 3 3 1021
+update t0 set key8=123 where key1 < 3 or key2 < 4;
+select * from t0 where key1 < 3 or key2 < 4;
+key1 key2 key3 key4 key5 key6 key7 key8
+1 1 1 1 1 1 1 123
+2 2 2 2 2 2 2 123
+3 3 3 3 3 3 3 123
+delete from t0 where key1 < 3 or key2 < 4;
+select * from t0 where key1 < 3 or key2 < 4;
+key1 key2 key3 key4 key5 key6 key7 key8
+select count(*) from t0;
+count(*)
+1021
+drop table t0, t1, t2, t3, t4;
diff --git a/mysql-test/r/index_merge_bdb.result b/mysql-test/r/index_merge_bdb.result
new file mode 100644
index 00000000000..3113bf95d3a
--- /dev/null
+++ b/mysql-test/r/index_merge_bdb.result
@@ -0,0 +1,136 @@
+drop table if exists t1;
+create table t1 (
+pk int primary key,
+key1 int,
+key2 int,
+filler char(200),
+filler2 char(200),
+index(key1),
+index(key2)
+) engine=bdb;
+select * from t1 where (key1 >= 2 and key1 <= 10) or (pk >= 4 and pk <=8 );
+pk key1 key2 filler filler2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+9 9 9 filler-data filler-data-2
+10 10 10 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+5 5 5 filler-data filler-data-2
+6 6 6 filler-data filler-data-2
+7 7 7 filler-data filler-data-2
+8 8 8 filler-data filler-data-2
+set @maxv=1000;
+select * from t1 where
+(pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+or key1=18 or key1=60;
+pk key1 key2 filler filler2
+18 18 18 filler-data filler-data-2
+60 60 60 filler-data filler-data-2
+1 1 1 filler-data filler-data-2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+11 11 11 filler-data filler-data-2
+12 12 12 filler-data filler-data-2
+13 13 13 filler-data filler-data-2
+14 14 14 filler-data filler-data-2
+50 50 50 filler-data filler-data-2
+51 51 51 filler-data filler-data-2
+52 52 52 filler-data filler-data-2
+53 53 53 filler-data filler-data-2
+54 54 54 filler-data filler-data-2
+991 991 991 filler-data filler-data-2
+992 992 992 filler-data filler-data-2
+993 993 993 filler-data filler-data-2
+994 994 994 filler-data filler-data-2
+995 995 995 filler-data filler-data-2
+996 996 996 filler-data filler-data-2
+997 997 997 filler-data filler-data-2
+998 998 998 filler-data filler-data-2
+999 999 999 filler-data filler-data-2
+1000 1000 1000 filler-data filler-data-2
+select * from t1 where
+(pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+or key1 < 3 or key1 > @maxv-11;
+pk key1 key2 filler filler2
+990 990 990 filler-data filler-data-2
+1 1 1 filler-data filler-data-2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+11 11 11 filler-data filler-data-2
+12 12 12 filler-data filler-data-2
+13 13 13 filler-data filler-data-2
+14 14 14 filler-data filler-data-2
+50 50 50 filler-data filler-data-2
+51 51 51 filler-data filler-data-2
+52 52 52 filler-data filler-data-2
+53 53 53 filler-data filler-data-2
+54 54 54 filler-data filler-data-2
+991 991 991 filler-data filler-data-2
+992 992 992 filler-data filler-data-2
+993 993 993 filler-data filler-data-2
+994 994 994 filler-data filler-data-2
+995 995 995 filler-data filler-data-2
+996 996 996 filler-data filler-data-2
+997 997 997 filler-data filler-data-2
+998 998 998 filler-data filler-data-2
+999 999 999 filler-data filler-data-2
+1000 1000 1000 filler-data filler-data-2
+select * from t1 where
+(pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+or
+(key1 < 5) or (key1 > 10 and key1 < 15) or (key1 >= 50 and key1 < 55 ) or (key1 > @maxv-10);
+pk key1 key2 filler filler2
+1 1 1 filler-data filler-data-2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+11 11 11 filler-data filler-data-2
+12 12 12 filler-data filler-data-2
+13 13 13 filler-data filler-data-2
+14 14 14 filler-data filler-data-2
+50 50 50 filler-data filler-data-2
+51 51 51 filler-data filler-data-2
+52 52 52 filler-data filler-data-2
+53 53 53 filler-data filler-data-2
+54 54 54 filler-data filler-data-2
+991 991 991 filler-data filler-data-2
+992 992 992 filler-data filler-data-2
+993 993 993 filler-data filler-data-2
+994 994 994 filler-data filler-data-2
+995 995 995 filler-data filler-data-2
+996 996 996 filler-data filler-data-2
+997 997 997 filler-data filler-data-2
+998 998 998 filler-data filler-data-2
+999 999 999 filler-data filler-data-2
+1000 1000 1000 filler-data filler-data-2
+select * from t1 where
+(pk > 10 and pk < 15) or (pk >= 50 and pk < 55 )
+or
+(key1 < 5) or (key1 > @maxv-10);
+pk key1 key2 filler filler2
+1 1 1 filler-data filler-data-2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+991 991 991 filler-data filler-data-2
+992 992 992 filler-data filler-data-2
+993 993 993 filler-data filler-data-2
+994 994 994 filler-data filler-data-2
+995 995 995 filler-data filler-data-2
+996 996 996 filler-data filler-data-2
+997 997 997 filler-data filler-data-2
+998 998 998 filler-data filler-data-2
+999 999 999 filler-data filler-data-2
+1000 1000 1000 filler-data filler-data-2
+11 11 11 filler-data filler-data-2
+12 12 12 filler-data filler-data-2
+13 13 13 filler-data filler-data-2
+14 14 14 filler-data filler-data-2
+50 50 50 filler-data filler-data-2
+51 51 51 filler-data filler-data-2
+52 52 52 filler-data filler-data-2
+53 53 53 filler-data filler-data-2
+54 54 54 filler-data filler-data-2
+drop table t1;
diff --git a/mysql-test/r/index_merge_innodb.result b/mysql-test/r/index_merge_innodb.result
new file mode 100644
index 00000000000..afa17c79a6e
--- /dev/null
+++ b/mysql-test/r/index_merge_innodb.result
@@ -0,0 +1,55 @@
+drop table if exists t1;
+create table t1
+(
+key1 int not null,
+key2 int not null,
+INDEX i1(key1),
+INDEX i2(key2)
+) engine=innodb;
+explain select * from t1 where key1 < 5 or key2 > 197;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using sort_union(i1,i2); Using where
+select * from t1 where key1 < 5 or key2 > 197;
+key1 key2
+0 200
+1 199
+2 198
+3 197
+4 196
+explain select * from t1 where key1 < 3 or key2 > 195;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using sort_union(i1,i2); Using where
+select * from t1 where key1 < 3 or key2 > 195;
+key1 key2
+0 200
+1 199
+2 198
+3 197
+4 196
+alter table t1 add str1 char (255) not null,
+add zeroval int not null default 0,
+add str2 char (255) not null,
+add str3 char (255) not null;
+update t1 set str1='aaa', str2='bbb', str3=concat(key2, '-', key1 div 2, '_' ,if(key1 mod 2 = 0, 'a', 'A'));
+alter table t1 add primary key (str1, zeroval, str2, str3);
+explain select * from t1 where key1 < 5 or key2 > 197;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using sort_union(i1,i2); Using where
+select * from t1 where key1 < 5 or key2 > 197;
+key1 key2 str1 zeroval str2 str3
+4 196 aaa 0 bbb 196-2_a
+3 197 aaa 0 bbb 197-1_A
+2 198 aaa 0 bbb 198-1_a
+1 199 aaa 0 bbb 199-0_A
+0 200 aaa 0 bbb 200-0_a
+explain select * from t1 where key1 < 3 or key2 > 195;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge i1,i2 i1,i2 4,4 NULL 8 Using sort_union(i1,i2); Using where
+select * from t1 where key1 < 3 or key2 > 195;
+key1 key2 str1 zeroval str2 str3
+4 196 aaa 0 bbb 196-2_a
+3 197 aaa 0 bbb 197-1_A
+2 198 aaa 0 bbb 198-1_a
+1 199 aaa 0 bbb 199-0_A
+0 200 aaa 0 bbb 200-0_a
+drop table t1;
diff --git a/mysql-test/r/index_merge_innodb2.result b/mysql-test/r/index_merge_innodb2.result
new file mode 100644
index 00000000000..91dd989fe90
--- /dev/null
+++ b/mysql-test/r/index_merge_innodb2.result
@@ -0,0 +1,136 @@
+drop table if exists t1;
+create table t1 (
+pk int primary key,
+key1 int,
+key2 int,
+filler char(200),
+filler2 char(200),
+index(key1),
+index(key2)
+) engine=innodb;
+select * from t1 where (key1 >= 2 and key1 <= 10) or (pk >= 4 and pk <=8 );
+pk key1 key2 filler filler2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+9 9 9 filler-data filler-data-2
+10 10 10 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+5 5 5 filler-data filler-data-2
+6 6 6 filler-data filler-data-2
+7 7 7 filler-data filler-data-2
+8 8 8 filler-data filler-data-2
+set @maxv=1000;
+select * from t1 where
+(pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+or key1=18 or key1=60;
+pk key1 key2 filler filler2
+18 18 18 filler-data filler-data-2
+60 60 60 filler-data filler-data-2
+1 1 1 filler-data filler-data-2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+11 11 11 filler-data filler-data-2
+12 12 12 filler-data filler-data-2
+13 13 13 filler-data filler-data-2
+14 14 14 filler-data filler-data-2
+50 50 50 filler-data filler-data-2
+51 51 51 filler-data filler-data-2
+52 52 52 filler-data filler-data-2
+53 53 53 filler-data filler-data-2
+54 54 54 filler-data filler-data-2
+991 991 991 filler-data filler-data-2
+992 992 992 filler-data filler-data-2
+993 993 993 filler-data filler-data-2
+994 994 994 filler-data filler-data-2
+995 995 995 filler-data filler-data-2
+996 996 996 filler-data filler-data-2
+997 997 997 filler-data filler-data-2
+998 998 998 filler-data filler-data-2
+999 999 999 filler-data filler-data-2
+1000 1000 1000 filler-data filler-data-2
+select * from t1 where
+(pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+or key1 < 3 or key1 > @maxv-11;
+pk key1 key2 filler filler2
+990 990 990 filler-data filler-data-2
+1 1 1 filler-data filler-data-2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+11 11 11 filler-data filler-data-2
+12 12 12 filler-data filler-data-2
+13 13 13 filler-data filler-data-2
+14 14 14 filler-data filler-data-2
+50 50 50 filler-data filler-data-2
+51 51 51 filler-data filler-data-2
+52 52 52 filler-data filler-data-2
+53 53 53 filler-data filler-data-2
+54 54 54 filler-data filler-data-2
+991 991 991 filler-data filler-data-2
+992 992 992 filler-data filler-data-2
+993 993 993 filler-data filler-data-2
+994 994 994 filler-data filler-data-2
+995 995 995 filler-data filler-data-2
+996 996 996 filler-data filler-data-2
+997 997 997 filler-data filler-data-2
+998 998 998 filler-data filler-data-2
+999 999 999 filler-data filler-data-2
+1000 1000 1000 filler-data filler-data-2
+select * from t1 where
+(pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+or
+(key1 < 5) or (key1 > 10 and key1 < 15) or (key1 >= 50 and key1 < 55 ) or (key1 > @maxv-10);
+pk key1 key2 filler filler2
+1 1 1 filler-data filler-data-2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+11 11 11 filler-data filler-data-2
+12 12 12 filler-data filler-data-2
+13 13 13 filler-data filler-data-2
+14 14 14 filler-data filler-data-2
+50 50 50 filler-data filler-data-2
+51 51 51 filler-data filler-data-2
+52 52 52 filler-data filler-data-2
+53 53 53 filler-data filler-data-2
+54 54 54 filler-data filler-data-2
+991 991 991 filler-data filler-data-2
+992 992 992 filler-data filler-data-2
+993 993 993 filler-data filler-data-2
+994 994 994 filler-data filler-data-2
+995 995 995 filler-data filler-data-2
+996 996 996 filler-data filler-data-2
+997 997 997 filler-data filler-data-2
+998 998 998 filler-data filler-data-2
+999 999 999 filler-data filler-data-2
+1000 1000 1000 filler-data filler-data-2
+select * from t1 where
+(pk > 10 and pk < 15) or (pk >= 50 and pk < 55 )
+or
+(key1 < 5) or (key1 > @maxv-10);
+pk key1 key2 filler filler2
+1 1 1 filler-data filler-data-2
+2 2 2 filler-data filler-data-2
+3 3 3 filler-data filler-data-2
+4 4 4 filler-data filler-data-2
+991 991 991 filler-data filler-data-2
+992 992 992 filler-data filler-data-2
+993 993 993 filler-data filler-data-2
+994 994 994 filler-data filler-data-2
+995 995 995 filler-data filler-data-2
+996 996 996 filler-data filler-data-2
+997 997 997 filler-data filler-data-2
+998 998 998 filler-data filler-data-2
+999 999 999 filler-data filler-data-2
+1000 1000 1000 filler-data filler-data-2
+11 11 11 filler-data filler-data-2
+12 12 12 filler-data filler-data-2
+13 13 13 filler-data filler-data-2
+14 14 14 filler-data filler-data-2
+50 50 50 filler-data filler-data-2
+51 51 51 filler-data filler-data-2
+52 52 52 filler-data filler-data-2
+53 53 53 filler-data filler-data-2
+54 54 54 filler-data filler-data-2
+drop table t1;
diff --git a/mysql-test/r/index_merge_ror.result b/mysql-test/r/index_merge_ror.result
new file mode 100644
index 00000000000..15ad1026ca0
--- /dev/null
+++ b/mysql-test/r/index_merge_ror.result
@@ -0,0 +1,196 @@
+drop table if exists t0,t1,t2;
+select count(*) from t1;
+count(*)
+64801
+explain select key1,key2 from t1 where key1=100 and key2=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using intersect(key1,key2); Using where; Using index
+select key1,key2 from t1 where key1=100 and key2=100;
+key1 key2
+100 100
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+key1 key2 key3 key4 filler1
+100 100 100 100 key1-key2-key3-key4
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, -1, -1, 'key1-key2');
+insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, 100, 100, 'key4-key3');
+explain select key1,key2,filler1 from t1 where key1=100 and key2=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using intersect(key1,key2); Using where
+select key1,key2,filler1 from t1 where key1=100 and key2=100;
+key1 key2 filler1
+100 100 key1-key2-key3-key4
+100 100 key1-key2
+explain select key1,key2 from t1 where key1=100 and key2=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using intersect(key1,key2); Using where; Using index
+select key1,key2 from t1 where key1=100 and key2=100;
+key1 key2
+100 100
+100 100
+explain select key1,key2,key3,key4 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
+select key1,key2,key3,key4 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+key1 key2 key3 key4
+100 100 100 100
+100 100 -1 -1
+-1 -1 100 100
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+key1 key2 key3 key4 filler1
+100 100 100 100 key1-key2-key3-key4
+100 100 -1 -1 key1-key2
+-1 -1 100 100 key4-key3
+explain select key1,key2,key3 from t1 where key1=100 and key2=100 and key3=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3 key1,key2,key3 5,5,5 NULL 1 Using intersect(key1,key2,key3); Using where; Using index
+select key1,key2,key3 from t1 where key1=100 and key2=100 and key3=100;
+key1 key2 key3
+100 100 100
+insert into t1 (key1,key2,key3,key4,filler1) values (101,101,101,101, 'key1234-101');
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=101;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3 key1,key2,key3 5,5,5 NULL 5 Using union(intersect(key1,key2),key3); Using where
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=101;
+key1 key2 key3 key4 filler1
+100 100 100 100 key1-key2-key3-key4
+100 100 -1 -1 key1-key2
+101 101 101 101 key1234-101
+select key1,key2, filler1 from t1 where key1=100 and key2=100;
+key1 key2 filler1
+100 100 key1-key2-key3-key4
+100 100 key1-key2
+update t1 set filler1='to be deleted' where key1=100 and key2=100;
+update t1 set key1=200,key2=200 where key1=100 and key2=100;
+delete from t1 where key1=200 and key2=200;
+select key1,key2,filler1 from t1 where key2=100 and key2=200;
+key1 key2 filler1
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+key1 key2 key3 key4 filler1
+-1 -1 100 100 key4-key3
+delete from t1 where key3=100 and key4=100;
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key1,key2,key3,key4 5,5,5,5 NULL 8 Using union(intersect(key1,key2),intersect(key3,key4)); Using where
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+key1 key2 key3 key4 filler1
+explain select key1,key2 from t1 where key1=100 and key2=100;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 3 Using intersect(key1,key2); Using where; Using index
+select key1,key2 from t1 where key1=100 and key2=100;
+key1 key2
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key1-key2-key3-key4-1');
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key1-key2-key3-key4-2');
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key1-key2-key3-key4-3');
+explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 16 Using union(key3,intersect(key1,key2),key4); Using where
+select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+key1 key2 key3 key4 filler1
+100 100 200 200 key1-key2-key3-key4-3
+100 100 200 200 key1-key2-key3-key4-2
+100 100 200 200 key1-key2-key3-key4-1
+insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, -1, 200,'key4');
+explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 18 Using union(key3,intersect(key1,key2),key4); Using where
+select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+key1 key2 key3 key4 filler1
+100 100 200 200 key1-key2-key3-key4-3
+100 100 200 200 key1-key2-key3-key4-2
+100 100 200 200 key1-key2-key3-key4-1
+-1 -1 -1 200 key4
+insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, 200, -1,'key3');
+explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2,key3,key4 key3,key1,key2,key4 5,5,5,5 NULL 20 Using union(key3,intersect(key1,key2),key4); Using where
+select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+key1 key2 key3 key4 filler1
+100 100 200 200 key1-key2-key3-key4-3
+100 100 200 200 key1-key2-key3-key4-2
+100 100 200 200 key1-key2-key3-key4-1
+-1 -1 -1 200 key4
+-1 -1 200 -1 key3
+explain select * from t1 where st_a=1 and st_b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b st_a,st_b 4,4 NULL 2508 Using intersect(st_a,st_b); Using where
+explain select st_a,st_b from t1 where st_a=1 and st_b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b st_a,st_b 4,4 NULL 2508 Using intersect(st_a,st_b); Using where; Using index
+explain select st_a from t1 ignore index (st_a) where st_a=1 and st_b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,stb_swt1a_2b,stb_swt1b,st_b st_b 4 const 14720 Using where
+explain select * from t1 where st_a=1 and swt1a=1 and swt2a=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a sta_swt12a 12 const,const,const 958 Using where
+explain select * from t1 where st_b=1 and swt1b=1 and swt2b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref stb_swt1a_2b,stb_swt1b,st_b stb_swt1b 8 const,const 3757 Using where
+explain select * from t1 where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt12a,stb_swt1a_2b 12,12 NULL 42 Using intersect(sta_swt12a,stb_swt1a_2b); Using where
+explain select * from t1 ignore index (sta_swt21a, stb_swt1a_2b)
+where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,st_a,stb_swt1b,st_b sta_swt12a,stb_swt1b 12,8 NULL 42 Using intersect(sta_swt12a,stb_swt1b); Using where
+explain select * from t1 ignore index (sta_swt21a, sta_swt12a, stb_swt1a_2b)
+where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt1a,sta_swt2a,st_a,stb_swt1b,st_b sta_swt1a,sta_swt2a,stb_swt1b 8,8,8 NULL 41 Using intersect(sta_swt1a,sta_swt2a,stb_swt1b); Using where
+explain select * from t1 ignore index (sta_swt21a, sta_swt12a, stb_swt1a_2b, stb_swt1b)
+where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt1a,sta_swt2a,st_a,st_b sta_swt1a,sta_swt2a,st_b 8,8,4 NULL 159 Using intersect(sta_swt1a,sta_swt2a,st_b); Using where
+explain select * from t1
+where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt12a,stb_swt1a_2b 12,12 NULL 42 Using intersect(sta_swt12a,stb_swt1a_2b); Using where
+explain select * from t1
+where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using intersect(sta_swt1a,stb_swt1b); Using where
+explain select st_a from t1
+where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using intersect(sta_swt1a,stb_swt1b); Using where; Using index
+explain select st_a from t1
+where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge sta_swt12a,sta_swt1a,sta_swt2a,sta_swt21a,st_a,stb_swt1a_2b,stb_swt1b,st_b sta_swt1a,stb_swt1b 8,8 NULL 163 Using intersect(sta_swt1a,stb_swt1b); Using where; Using index
+drop table t0,t1;
+create table t2 (
+a char(10),
+b char(10),
+filler1 char(255),
+filler2 char(255),
+key(a(5)),
+key(b(5))
+);
+select count(a) from t2 where a='BBBBBBBB';
+count(a)
+4
+select count(a) from t2 where b='BBBBBBBB';
+count(a)
+4
+explain select count(a) from t2 where a='AAAAAAAA' and b='AAAAAAAA';
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ref a,b a 6 const 4 Using where
+select count(a) from t2 where a='AAAAAAAA' and b='AAAAAAAA';
+count(a)
+4
+select count(a) from t2 ignore index(a,b) where a='AAAAAAAA' and b='AAAAAAAA';
+count(a)
+4
+insert into t2 values ('ab', 'ab', 'uh', 'oh');
+explain select a from t2 where a='ab';
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ref a a 6 const 1 Using where
+drop table t2;
diff --git a/mysql-test/r/index_merge_ror_cpk.result b/mysql-test/r/index_merge_ror_cpk.result
new file mode 100644
index 00000000000..7acfb8dc93b
--- /dev/null
+++ b/mysql-test/r/index_merge_ror_cpk.result
@@ -0,0 +1,120 @@
+drop table if exists t1;
+create table t1
+(
+pk1 int not null,
+pk2 int not null,
+key1 int not null,
+key2 int not null,
+pktail1ok int not null,
+pktail2ok int not null,
+pktail3bad int not null,
+pktail4bad int not null,
+pktail5bad int not null,
+pk2copy int not null,
+badkey int not null,
+filler1 char (200),
+filler2 char (200),
+key (key1),
+key (key2),
+/* keys with tails from CPK members */
+key (pktail1ok, pk1),
+key (pktail2ok, pk1, pk2),
+key (pktail3bad, pk2, pk1),
+key (pktail4bad, pk1, pk2copy),
+key (pktail5bad, pk1, pk2, pk2copy),
+primary key (pk1, pk2)
+) engine=innodb;
+explain select * from t1 where pk1 = 1 and pk2 < 80 and key1=0;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref PRIMARY,key1 PRIMARY 4 const 1 Using where
+select * from t1 where pk1 = 1 and pk2 < 80 and key1=0;
+pk1 pk2 key1 key2 pktail1ok pktail2ok pktail3bad pktail4bad pktail5bad pk2copy badkey filler1 filler2
+1 10 0 0 0 0 0 0 0 10 0 filler-data-10 filler2
+1 11 0 0 0 0 0 0 0 11 0 filler-data-11 filler2
+1 12 0 0 0 0 0 0 0 12 0 filler-data-12 filler2
+1 13 0 0 0 0 0 0 0 13 0 filler-data-13 filler2
+1 14 0 0 0 0 0 0 0 14 0 filler-data-14 filler2
+1 15 0 0 0 0 0 0 0 15 0 filler-data-15 filler2
+1 16 0 0 0 0 0 0 0 16 0 filler-data-16 filler2
+1 17 0 0 0 0 0 0 0 17 0 filler-data-17 filler2
+1 18 0 0 0 0 0 0 0 18 0 filler-data-18 filler2
+1 19 0 0 0 0 0 0 0 19 0 filler-data-19 filler2
+explain select pk1,pk2 from t1 where key1 = 10 and key2=10 and 2*pk1+1 < 2*96+1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2 key1,key2 4,4 NULL 1 Using intersect(key1,key2); Using where; Using index
+select pk1,pk2 from t1 where key1 = 10 and key2=10 and 2*pk1+1 < 2*96+1;
+pk1 pk2
+95 50
+95 51
+95 52
+95 53
+95 54
+95 55
+95 56
+95 57
+95 58
+95 59
+explain select * from t1 where badkey=1 and key1=10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref key1 key1 4 const 101 Using where
+explain select * from t1 where pk1 < 7500 and key1 = 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge PRIMARY,key1 key1,PRIMARY 4,4 NULL 38 Using intersect(key1,PRIMARY); Using where
+explain select * from t1 where pktail1ok=1 and key1=10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,pktail1ok key1,pktail1ok 4,4 NULL 1 Using intersect(key1,pktail1ok); Using where
+explain select * from t1 where pktail2ok=1 and key1=10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,pktail2ok key1,pktail2ok 4,4 NULL 1 Using intersect(key1,pktail2ok); Using where
+select ' The following is actually a deficiency, it uses sort_union currently:' as 'note:';
+note:
+ The following is actually a deficiency, it uses sort_union currently:
+explain select * from t1 where (pktail2ok=1 and pk1< 50000) or key1=10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge PRIMARY,key1,pktail2ok pktail2ok,key1 8,4 NULL 199 Using sort_union(pktail2ok,key1); Using where
+explain select * from t1 where pktail3bad=1 and key1=10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref key1,pktail3bad pktail3bad 4 const 98 Using where
+explain select * from t1 where pktail4bad=1 and key1=10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref key1,pktail4bad pktail4bad 4 const 99 Using where
+explain select * from t1 where pktail5bad=1 and key1=10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 ref key1,pktail5bad pktail5bad 4 const 99 Using where
+explain select pk1,pk2,key1,key2 from t1 where key1 = 10 and key2=10 limit 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2 key1,key2 4,4 NULL 1 Using intersect(key1,key2); Using where; Using index
+select pk1,pk2,key1,key2 from t1 where key1 = 10 and key2=10 limit 10;
+pk1 pk2 key1 key2
+95 50 10 10
+95 51 10 10
+95 52 10 10
+95 53 10 10
+95 54 10 10
+95 55 10 10
+95 56 10 10
+95 57 10 10
+95 58 10 10
+95 59 10 10
+drop table t1;
+create table t1
+(
+RUNID varchar(22),
+SUBMITNR varchar(5),
+ORDERNR char(1) ,
+PROGRAMM varchar(8),
+TESTID varchar(4),
+UCCHECK char(1),
+ETEXT varchar(80),
+ETEXT_TYPE char(1),
+INFO char(1),
+SEVERITY tinyint(3),
+TADIRFLAG char(1),
+PRIMARY KEY (RUNID,SUBMITNR,ORDERNR,PROGRAMM,TESTID,UCCHECK),
+KEY `TVERM~KEY` (PROGRAMM,TESTID,UCCHECK)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+update t1 set `ETEXT` = '', `ETEXT_TYPE`='', `INFO`='', `SEVERITY`='', `TADIRFLAG`=''
+WHERE
+`RUNID`= '' AND `SUBMITNR`= '' AND `ORDERNR`='' AND `PROGRAMM`='' AND
+`TESTID`='' AND `UCCHECK`='';
+drop table t1;
diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result
index 9d830367745..b407c1537bc 100644
--- a/mysql-test/r/innodb.result
+++ b/mysql-test/r/innodb.result
@@ -1376,7 +1376,7 @@ CREATE TABLE t1 (
`id` int(10) unsigned NOT NULL auto_increment,
`id_object` int(10) unsigned default '0',
`id_version` int(10) unsigned NOT NULL default '1',
-label varchar(100) NOT NULL default '',
+`label` varchar(100) NOT NULL default '',
`description` text,
PRIMARY KEY (`id`),
KEY `id_object` (`id_object`),
@@ -1390,8 +1390,8 @@ PRIMARY KEY (`id`),
KEY `id_version` (`id_version`)
) ENGINE=InnoDB;
INSERT INTO t2 VALUES("3524", "1"),("3525", "1"),("1794", "4"),("102", "5"),("1822", "6"),("3382", "9");
-SELECT t2.id, t1.label FROM t2 INNER JOIN
-(SELECT t1.id_object as id_object FROM t1 WHERE t1.label LIKE '%test%') AS lbl
+SELECT t2.id, t1.`label` FROM t2 INNER JOIN
+(SELECT t1.id_object as id_object FROM t1 WHERE t1.`label` LIKE '%test%') AS lbl
ON (t2.id = lbl.id_object) INNER JOIN t1 ON (t2.id = t1.id_object);
id label
3382 Test
diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result
index 71b10699fa9..a666e016348 100644
--- a/mysql-test/r/insert.result
+++ b/mysql-test/r/insert.result
@@ -324,4 +324,16 @@ f_double_u 0
f_float_u 0
f_double_15_1_u 0.0
f_float_3_1_u 0.0
-drop table t1;
+use test;
+drop table if exists t1,t2,t3;
+create table t1(id1 int not null auto_increment primary key, t char(12));
+create table t2(id2 int not null, t char(12));
+create table t3(id3 int not null, t char(12), index(id3));
+select count(*) from t2;
+count(*)
+500
+insert into t2 select t1.* from t1, t2 t, t3 where t1.id1 = t.id2 and t.id2 = t3.id3;
+select count(*) from t2;
+count(*)
+25500
+drop table if exists t1,t2,t3;
diff --git a/mysql-test/r/insert_select.result b/mysql-test/r/insert_select.result
index 7c7ac152aa5..83e47639fe3 100644
--- a/mysql-test/r/insert_select.result
+++ b/mysql-test/r/insert_select.result
@@ -73,9 +73,9 @@ reset master;
insert into t1 select * from t2;
ERROR 23000: Duplicate entry '2' for key 1
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; insert into t1 select * from t2
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 183 use `test`; insert into t1 select * from t2
select * from t1;
a
1
diff --git a/mysql-test/r/insert_update.result b/mysql-test/r/insert_update.result
index 303d7186015..5db36cf6a66 100644
--- a/mysql-test/r/insert_update.result
+++ b/mysql-test/r/insert_update.result
@@ -60,12 +60,12 @@ explain extended SELECT *, VALUES(a) FROM t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 5
Warnings:
-Note 1003 select test.t1.a AS `a`,test.t1.b AS `b`,test.t1.c AS `c`,values(test.t1.a) AS `VALUES(a)` from test.t1
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,values(`test`.`t1`.`a`) AS `VALUES(a)` from `test`.`t1`
explain extended select * from t1 where values(a);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
Warnings:
-Note 1003 select test.t1.a AS `a`,test.t1.b AS `b`,test.t1.c AS `c` from test.t1
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1`
DROP TABLE t1;
create table t1(a int primary key, b int);
insert into t1 values(1,1),(2,2),(3,3),(4,4),(5,5);
diff --git a/mysql-test/r/join_nested.result b/mysql-test/r/join_nested.result
new file mode 100644
index 00000000000..09c3e67ae70
--- /dev/null
+++ b/mysql-test/r/join_nested.result
@@ -0,0 +1,1323 @@
+DROP TABLE IF EXISTS t0,t1,t2,t3,t4,t5,t6,t7,t8,t9;
+CREATE TABLE t0 (a int, b int, c int);
+CREATE TABLE t1 (a int, b int, c int);
+CREATE TABLE t2 (a int, b int, c int);
+CREATE TABLE t3 (a int, b int, c int);
+CREATE TABLE t4 (a int, b int, c int);
+CREATE TABLE t5 (a int, b int, c int);
+CREATE TABLE t6 (a int, b int, c int);
+CREATE TABLE t7 (a int, b int, c int);
+CREATE TABLE t8 (a int, b int, c int);
+CREATE TABLE t9 (a int, b int, c int);
+INSERT INTO t0 VALUES (1,1,0), (1,2,0), (2,2,0);
+INSERT INTO t1 VALUES (1,3,0), (2,2,0), (3,2,0);
+INSERT INTO t2 VALUES (3,3,0), (4,2,0), (5,3,0);
+INSERT INTO t3 VALUES (1,2,0), (2,2,0);
+INSERT INTO t4 VALUES (3,2,0), (4,2,0);
+INSERT INTO t5 VALUES (3,1,0), (2,2,0), (3,3,0);
+INSERT INTO t6 VALUES (3,2,0), (6,2,0), (6,1,0);
+INSERT INTO t7 VALUES (1,1,0), (2,2,0);
+INSERT INTO t8 VALUES (0,2,0), (1,2,0);
+INSERT INTO t9 VALUES (1,1,0), (1,2,0), (3,3,0);
+SELECT t2.a,t2.b
+FROM t2;
+a b
+3 3
+4 2
+5 3
+SELECT t3.a,t3.b
+FROM t3;
+a b
+1 2
+2 2
+SELECT t4.a,t4.b
+FROM t4;
+a b
+3 2
+4 2
+SELECT t3.a,t3.b,t4.a,t4.b
+FROM t3,t4;
+a b a b
+1 2 3 2
+2 2 3 2
+1 2 4 2
+2 2 4 2
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t2.b=t4.b;
+a b a b a b
+3 3 NULL NULL NULL NULL
+4 2 1 2 3 2
+4 2 1 2 4 2
+4 2 2 2 3 2
+4 2 2 2 4 2
+5 3 NULL NULL NULL NULL
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b;
+a b a b a b
+3 3 NULL NULL NULL NULL
+4 2 1 2 3 2
+4 2 1 2 4 2
+5 3 NULL NULL NULL NULL
+EXPLAIN EXTENDED
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t2.b=t4.b
+WHERE t3.a=1 OR t3.c IS NULL;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 2
+Warnings:
+Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where ((`test`.`t3`.`a` = 1) or isnull(`test`.`t3`.`c`))
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t2.b=t4.b
+WHERE t3.a=1 OR t3.c IS NULL;
+a b a b a b
+3 3 NULL NULL NULL NULL
+4 2 1 2 3 2
+4 2 1 2 4 2
+5 3 NULL NULL NULL NULL
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t2.b=t4.b
+WHERE t3.a>1 OR t3.c IS NULL;
+a b a b a b
+3 3 NULL NULL NULL NULL
+4 2 2 2 3 2
+4 2 2 2 4 2
+5 3 NULL NULL NULL NULL
+SELECT t5.a,t5.b
+FROM t5;
+a b
+3 1
+2 2
+3 3
+SELECT t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+FROM t3,t4,t5;
+a b a b a b
+1 2 3 2 3 1
+2 2 3 2 3 1
+1 2 4 2 3 1
+2 2 4 2 3 1
+1 2 3 2 2 2
+2 2 3 2 2 2
+1 2 4 2 2 2
+2 2 4 2 2 2
+1 2 3 2 3 3
+2 2 3 2 3 3
+1 2 4 2 3 3
+2 2 4 2 3 3
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+FROM t2
+LEFT JOIN
+(t3, t4, t5)
+ON t2.b=t4.b;
+a b a b a b a b
+3 3 NULL NULL NULL NULL NULL NULL
+4 2 1 2 3 2 3 1
+4 2 1 2 3 2 2 2
+4 2 1 2 3 2 3 3
+4 2 1 2 4 2 3 1
+4 2 1 2 4 2 2 2
+4 2 1 2 4 2 3 3
+4 2 2 2 3 2 3 1
+4 2 2 2 3 2 2 2
+4 2 2 2 3 2 3 3
+4 2 2 2 4 2 3 1
+4 2 2 2 4 2 2 2
+4 2 2 2 4 2 3 3
+5 3 NULL NULL NULL NULL NULL NULL
+EXPLAIN EXTENDED
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+FROM t2
+LEFT JOIN
+(t3, t4, t5)
+ON t2.b=t4.b
+WHERE t3.a>1 OR t3.c IS NULL;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 2
+1 SIMPLE t5 ALL NULL NULL NULL NULL 3
+Warnings:
+Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where ((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`))
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+FROM t2
+LEFT JOIN
+(t3, t4, t5)
+ON t2.b=t4.b
+WHERE t3.a>1 OR t3.c IS NULL;
+a b a b a b a b
+3 3 NULL NULL NULL NULL NULL NULL
+4 2 2 2 3 2 3 1
+4 2 2 2 3 2 2 2
+4 2 2 2 3 2 3 3
+4 2 2 2 4 2 3 1
+4 2 2 2 4 2 2 2
+4 2 2 2 4 2 3 3
+5 3 NULL NULL NULL NULL NULL NULL
+EXPLAIN EXTENDED
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+FROM t2
+LEFT JOIN
+(t3, t4, t5)
+ON t2.b=t4.b
+WHERE (t3.a>1 OR t3.c IS NULL) AND
+(t5.a<3 OR t5.c IS NULL);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 2
+1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where
+Warnings:
+Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b` from `test`.`t2` left join (`test`.`t3` join `test`.`t4` join `test`.`t5`) on((`test`.`t2`.`b` = `test`.`t4`.`b`)) where (((`test`.`t3`.`a` > 1) or isnull(`test`.`t3`.`c`)) and ((`test`.`t5`.`a` < 3) or isnull(`test`.`t5`.`c`)))
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+FROM t2
+LEFT JOIN
+(t3, t4, t5)
+ON t2.b=t4.b
+WHERE (t3.a>1 OR t3.c IS NULL) AND
+(t5.a<3 OR t5.c IS NULL);
+a b a b a b a b
+3 3 NULL NULL NULL NULL NULL NULL
+4 2 2 2 3 2 2 2
+4 2 2 2 4 2 2 2
+5 3 NULL NULL NULL NULL NULL NULL
+SELECT t6.a,t6.b
+FROM t6;
+a b
+3 2
+6 2
+6 1
+SELECT t7.a,t7.b
+FROM t7;
+a b
+1 1
+2 2
+SELECT t6.a,t6.b,t7.a,t7.b
+FROM t6,t7;
+a b a b
+3 2 1 1
+3 2 2 2
+6 2 1 1
+6 2 2 2
+6 1 1 1
+6 1 2 2
+SELECT t8.a,t8.b
+FROM t8;
+a b
+0 2
+1 2
+EXPLAIN EXTENDED
+SELECT t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t7 ALL NULL NULL NULL NULL 2
+1 SIMPLE t6 ALL NULL NULL NULL NULL 3
+1 SIMPLE t8 ALL NULL NULL NULL NULL 2
+Warnings:
+Note 1003 select `test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10))) where 1
+SELECT t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10;
+a b a b a b
+3 2 1 1 NULL NULL
+3 2 2 2 0 2
+3 2 2 2 1 2
+6 2 1 1 NULL NULL
+6 2 2 2 0 2
+6 2 2 2 1 2
+6 1 1 1 NULL NULL
+6 1 2 2 0 2
+6 1 2 2 1 2
+SELECT t5.a,t5.b
+FROM t5;
+a b
+3 1
+2 2
+3 3
+SELECT t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b;
+a b a b a b a b
+3 1 3 2 1 1 NULL NULL
+3 1 6 2 1 1 NULL NULL
+2 2 3 2 2 2 0 2
+2 2 3 2 2 2 1 2
+2 2 6 2 2 2 0 2
+2 2 6 2 2 2 1 2
+3 3 NULL NULL NULL NULL NULL NULL
+SELECT t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b AND
+(t8.a < 1 OR t8.c IS NULL);
+a b a b a b a b
+3 1 3 2 1 1 NULL NULL
+3 1 6 2 1 1 NULL NULL
+2 2 3 2 2 2 0 2
+2 2 6 2 2 2 0 2
+3 3 NULL NULL NULL NULL NULL NULL
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b;
+a b a b a b
+3 3 NULL NULL NULL NULL
+4 2 1 2 3 2
+4 2 1 2 4 2
+5 3 NULL NULL NULL NULL
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b;
+a b a b a b a b a b a b a b
+3 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+3 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+4 2 1 2 3 2 3 1 3 2 1 1 NULL NULL
+4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL
+4 2 1 2 4 2 3 1 3 2 1 1 NULL NULL
+4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL
+5 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+3 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+3 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+3 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+3 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+4 2 1 2 3 2 2 2 3 2 2 2 0 2
+4 2 1 2 3 2 2 2 3 2 2 2 1 2
+4 2 1 2 3 2 2 2 6 2 2 2 0 2
+4 2 1 2 3 2 2 2 6 2 2 2 1 2
+4 2 1 2 4 2 2 2 3 2 2 2 0 2
+4 2 1 2 4 2 2 2 3 2 2 2 1 2
+4 2 1 2 4 2 2 2 6 2 2 2 0 2
+4 2 1 2 4 2 2 2 6 2 2 2 1 2
+5 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+5 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+5 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+5 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+3 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL
+4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL
+5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+WHERE t2.a > 3 AND
+(t6.a < 6 OR t6.c IS NULL);
+a b a b a b a b a b a b a b
+4 2 1 2 3 2 3 1 3 2 1 1 NULL NULL
+4 2 1 2 4 2 3 1 3 2 1 1 NULL NULL
+5 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+4 2 1 2 3 2 2 2 3 2 2 2 0 2
+4 2 1 2 3 2 2 2 3 2 2 2 1 2
+4 2 1 2 4 2 2 2 3 2 2 2 0 2
+4 2 1 2 4 2 2 2 3 2 2 2 1 2
+5 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+5 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL
+4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL
+5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+SELECT t1.a,t1.b
+FROM t1;
+a b
+1 3
+2 2
+3 2
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2);
+a b a b a b a b a b a b a b a b
+1 3 3 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+1 3 3 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+1 3 3 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+1 3 3 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+1 3 3 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+1 3 3 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+1 3 3 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+1 3 4 2 1 2 3 2 3 1 3 2 1 1 NULL NULL
+1 3 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL
+1 3 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL
+1 3 4 2 1 2 4 2 3 1 3 2 1 1 NULL NULL
+1 3 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL
+1 3 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL
+1 3 5 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+1 3 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+1 3 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+1 3 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+1 3 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+1 3 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+1 3 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+2 2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
+3 2 3 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+3 2 3 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+3 2 3 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+3 2 3 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+3 2 3 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+3 2 3 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+3 2 3 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+3 2 4 2 1 2 3 2 3 1 3 2 1 1 NULL NULL
+3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL
+3 2 4 2 1 2 3 2 2 2 3 2 2 2 0 2
+3 2 4 2 1 2 3 2 2 2 3 2 2 2 1 2
+3 2 4 2 1 2 3 2 2 2 6 2 2 2 0 2
+3 2 4 2 1 2 3 2 2 2 6 2 2 2 1 2
+3 2 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL
+3 2 4 2 1 2 4 2 3 1 3 2 1 1 NULL NULL
+3 2 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL
+3 2 4 2 1 2 4 2 2 2 3 2 2 2 0 2
+3 2 4 2 1 2 4 2 2 2 3 2 2 2 1 2
+3 2 4 2 1 2 4 2 2 2 6 2 2 2 0 2
+3 2 4 2 1 2 4 2 2 2 6 2 2 2 1 2
+3 2 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL
+3 2 5 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+3 2 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+3 2 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+3 2 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+3 2 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+3 2 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+3 2 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2)
+WHERE (t2.a >= 4 OR t2.c IS NULL);
+a b a b a b a b a b a b a b a b
+1 3 4 2 1 2 3 2 3 1 3 2 1 1 NULL NULL
+1 3 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL
+1 3 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL
+1 3 4 2 1 2 4 2 3 1 3 2 1 1 NULL NULL
+1 3 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL
+1 3 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL
+1 3 5 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+1 3 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+1 3 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+1 3 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+1 3 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+1 3 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+1 3 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+2 2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
+3 2 4 2 1 2 3 2 3 1 3 2 1 1 NULL NULL
+3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL
+3 2 4 2 1 2 3 2 2 2 3 2 2 2 0 2
+3 2 4 2 1 2 3 2 2 2 3 2 2 2 1 2
+3 2 4 2 1 2 3 2 2 2 6 2 2 2 0 2
+3 2 4 2 1 2 3 2 2 2 6 2 2 2 1 2
+3 2 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL
+3 2 4 2 1 2 4 2 3 1 3 2 1 1 NULL NULL
+3 2 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL
+3 2 4 2 1 2 4 2 2 2 3 2 2 2 0 2
+3 2 4 2 1 2 4 2 2 2 3 2 2 2 1 2
+3 2 4 2 1 2 4 2 2 2 6 2 2 2 0 2
+3 2 4 2 1 2 4 2 2 2 6 2 2 2 1 2
+3 2 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL
+3 2 5 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+3 2 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+3 2 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+3 2 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+3 2 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+3 2 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+3 2 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+SELECT t0.a,t0.b
+FROM t0;
+a b
+1 1
+1 2
+2 2
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2)
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2
+1 SIMPLE t4 ALL NULL NULL NULL NULL 2
+1 SIMPLE t5 ALL NULL NULL NULL NULL 3
+1 SIMPLE t7 ALL NULL NULL NULL NULL 2
+1 SIMPLE t6 ALL NULL NULL NULL NULL 3
+1 SIMPLE t8 ALL NULL NULL NULL NULL 2
+Warnings:
+Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)))
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2)
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL);
+a b a b a b a b a b a b a b a b a b
+1 2 2 2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
+1 2 3 2 4 2 1 2 3 2 3 1 3 2 1 1 NULL NULL
+1 2 3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL
+1 2 3 2 4 2 1 2 3 2 2 2 3 2 2 2 0 2
+1 2 3 2 4 2 1 2 3 2 2 2 3 2 2 2 1 2
+1 2 3 2 4 2 1 2 3 2 2 2 6 2 2 2 0 2
+1 2 3 2 4 2 1 2 3 2 2 2 6 2 2 2 1 2
+1 2 3 2 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL
+1 2 3 2 4 2 1 2 4 2 3 1 3 2 1 1 NULL NULL
+1 2 3 2 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL
+1 2 3 2 4 2 1 2 4 2 2 2 3 2 2 2 0 2
+1 2 3 2 4 2 1 2 4 2 2 2 3 2 2 2 1 2
+1 2 3 2 4 2 1 2 4 2 2 2 6 2 2 2 0 2
+1 2 3 2 4 2 1 2 4 2 2 2 6 2 2 2 1 2
+1 2 3 2 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL
+1 2 3 2 5 3 NULL NULL NULL NULL 3 1 3 2 1 1 NULL NULL
+1 2 3 2 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL
+1 2 3 2 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 0 2
+1 2 3 2 5 3 NULL NULL NULL NULL 2 2 3 2 2 2 1 2
+1 2 3 2 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2
+1 2 3 2 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 1 2
+1 2 3 2 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2),
+t9
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL) AND
+(t3.a < 5 OR t3.c IS NULL) AND
+(t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+(t5.a >=2 OR t5.c IS NULL) AND
+(t6.a >=4 OR t6.c IS NULL) AND
+(t7.a <= 2 OR t7.c IS NULL) AND
+(t8.a < 1 OR t8.c IS NULL) AND
+(t8.b=t9.b OR t8.c IS NULL) AND
+(t9.a=1);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t7 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t8 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where
+Warnings:
+Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+SELECT t9.a,t9.b
+FROM t9;
+a b
+1 1
+1 2
+3 3
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2),
+t9
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL) AND
+(t3.a < 5 OR t3.c IS NULL) AND
+(t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+(t5.a >=2 OR t5.c IS NULL) AND
+(t6.a >=4 OR t6.c IS NULL) AND
+(t7.a <= 2 OR t7.c IS NULL) AND
+(t8.a < 1 OR t8.c IS NULL) AND
+(t8.b=t9.b OR t8.c IS NULL) AND
+(t9.a=1);
+a b a b a b a b a b a b a b a b a b a b
+1 2 2 2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 1 1
+1 2 3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL 1 1
+1 2 3 2 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL 1 1
+1 2 3 2 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL 1 1
+1 2 3 2 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL 1 1
+1 2 3 2 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL 1 1
+1 2 3 2 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL 1 1
+1 2 2 2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 1 2
+1 2 3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL 1 2
+1 2 3 2 4 2 1 2 3 2 2 2 6 2 2 2 0 2 1 2
+1 2 3 2 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL 1 2
+1 2 3 2 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL 1 2
+1 2 3 2 4 2 1 2 4 2 2 2 6 2 2 2 0 2 1 2
+1 2 3 2 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL 1 2
+1 2 3 2 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL 1 2
+1 2 3 2 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2 1 2
+1 2 3 2 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL 1 2
+SELECT t1.a,t1.b
+FROM t1;
+a b
+1 3
+2 2
+3 2
+SELECT t2.a,t2.b
+FROM t2;
+a b
+3 3
+4 2
+5 3
+SELECT t3.a,t3.b
+FROM t3;
+a b
+1 2
+2 2
+SELECT t2.a,t2.b,t3.a,t3.b
+FROM t2
+LEFT JOIN
+t3
+ON t2.b=t3.b;
+a b a b
+3 3 NULL NULL
+4 2 1 2
+4 2 2 2
+5 3 NULL NULL
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b
+FROM t1, t2
+LEFT JOIN
+t3
+ON t2.b=t3.b
+WHERE t1.a <= 2;
+a b a b a b
+1 3 3 3 NULL NULL
+2 2 3 3 NULL NULL
+1 3 4 2 1 2
+1 3 4 2 2 2
+2 2 4 2 1 2
+2 2 4 2 2 2
+1 3 5 3 NULL NULL
+2 2 5 3 NULL NULL
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b
+FROM t1, t3
+RIGHT JOIN
+t2
+ON t2.b=t3.b
+WHERE t1.a <= 2;
+a b a b a b
+1 3 3 3 NULL NULL
+2 2 3 3 NULL NULL
+1 3 4 2 1 2
+1 3 4 2 2 2
+2 2 4 2 1 2
+2 2 4 2 2 2
+1 3 5 3 NULL NULL
+2 2 5 3 NULL NULL
+SELECT t3.a,t3.b,t4.a,t4.b
+FROM t3,t4;
+a b a b
+1 2 3 2
+2 2 3 2
+1 2 4 2
+2 2 4 2
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b;
+a b a b a b
+3 3 NULL NULL NULL NULL
+4 2 1 2 3 2
+4 2 1 2 4 2
+5 3 NULL NULL NULL NULL
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t1, t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b
+WHERE t1.a <= 2;
+a b a b a b a b
+1 3 3 3 NULL NULL NULL NULL
+2 2 3 3 NULL NULL NULL NULL
+1 3 4 2 1 2 3 2
+1 3 4 2 1 2 4 2
+2 2 4 2 1 2 3 2
+2 2 4 2 1 2 4 2
+1 3 5 3 NULL NULL NULL NULL
+2 2 5 3 NULL NULL NULL NULL
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t1, (t3, t4)
+RIGHT JOIN
+t2
+ON t3.a=1 AND t2.b=t4.b
+WHERE t1.a <= 2;
+a b a b a b a b
+1 3 3 3 NULL NULL NULL NULL
+2 2 3 3 NULL NULL NULL NULL
+1 3 4 2 1 2 3 2
+1 3 4 2 1 2 4 2
+2 2 4 2 1 2 3 2
+2 2 4 2 1 2 4 2
+1 3 5 3 NULL NULL NULL NULL
+2 2 5 3 NULL NULL NULL NULL
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t1, t3, t4
+RIGHT JOIN
+t2
+ON t3.a=1 AND t2.b=t4.b
+WHERE t1.a <= 2;
+a b a b a b a b
+1 3 3 3 1 2 NULL NULL
+1 3 3 3 2 2 NULL NULL
+2 2 3 3 1 2 NULL NULL
+2 2 3 3 2 2 NULL NULL
+1 3 4 2 1 2 3 2
+1 3 4 2 1 2 4 2
+1 3 4 2 2 2 NULL NULL
+2 2 4 2 1 2 3 2
+2 2 4 2 1 2 4 2
+2 2 4 2 2 2 NULL NULL
+1 3 5 3 1 2 NULL NULL
+1 3 5 3 2 2 NULL NULL
+2 2 5 3 1 2 NULL NULL
+2 2 5 3 2 2 NULL NULL
+EXPLAIN EXTENDED
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t1, t3, t4
+RIGHT JOIN
+t2
+ON t3.a=1 AND t2.b=t4.b
+WHERE t1.a <= 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3
+1 SIMPLE t4 ALL NULL NULL NULL NULL 2
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t1` join `test`.`t3` join `test`.`t2` left join `test`.`t4` on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) where (`test`.`t1`.`a` <= 2)
+CREATE INDEX idx_b ON t2(b);
+EXPLAIN EXTENDED
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t3,t4
+LEFT JOIN
+(t1,t2)
+ON t3.a=1 AND t3.b=t2.b AND t2.b=t4.b;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2
+1 SIMPLE t4 ALL NULL NULL NULL NULL 2
+1 SIMPLE t2 ref idx_b idx_b 5 test.t3.b 2
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3
+Warnings:
+Note 1003 select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t3` join `test`.`t4` left join (`test`.`t1` join `test`.`t2`) on(((`test`.`t3`.`a` = 1) and (`test`.`t3`.`b` = `test`.`t2`.`b`) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) where 1
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+FROM t3,t4
+LEFT JOIN
+(t1,t2)
+ON t3.a=1 AND t3.b=t2.b AND t2.b=t4.b;
+a b a b a b
+4 2 1 2 3 2
+4 2 1 2 3 2
+4 2 1 2 3 2
+NULL NULL 2 2 3 2
+4 2 1 2 4 2
+4 2 1 2 4 2
+4 2 1 2 4 2
+NULL NULL 2 2 4 2
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2),
+t9
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL) AND
+(t3.a < 5 OR t3.c IS NULL) AND
+(t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+(t5.a >=2 OR t5.c IS NULL) AND
+(t6.a >=4 OR t6.c IS NULL) AND
+(t7.a <= 2 OR t7.c IS NULL) AND
+(t8.a < 1 OR t8.c IS NULL) AND
+(t8.b=t9.b OR t8.c IS NULL) AND
+(t9.a=1);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t4 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t7 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t8 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where
+Warnings:
+Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+CREATE INDEX idx_b ON t4(b);
+CREATE INDEX idx_b ON t5(b);
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2),
+t9
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL) AND
+(t3.a < 5 OR t3.c IS NULL) AND
+(t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+(t5.a >=2 OR t5.c IS NULL) AND
+(t6.a >=4 OR t6.c IS NULL) AND
+(t7.a <= 2 OR t7.c IS NULL) AND
+(t8.a < 1 OR t8.c IS NULL) AND
+(t8.b=t9.b OR t8.c IS NULL) AND
+(t9.a=1);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t4 ref idx_b idx_b 5 test.t2.b 2 Using where
+1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t7 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t8 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where
+Warnings:
+Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+CREATE INDEX idx_b ON t8(b);
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2),
+t9
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL) AND
+(t3.a < 5 OR t3.c IS NULL) AND
+(t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+(t5.a >=2 OR t5.c IS NULL) AND
+(t6.a >=4 OR t6.c IS NULL) AND
+(t7.a <= 2 OR t7.c IS NULL) AND
+(t8.a < 1 OR t8.c IS NULL) AND
+(t8.b=t9.b OR t8.c IS NULL) AND
+(t9.a=1);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t4 ref idx_b idx_b 5 test.t2.b 2 Using where
+1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t7 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t8 ref idx_b idx_b 5 test.t7.b 2 Using where
+1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where
+Warnings:
+Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+CREATE INDEX idx_b ON t1(b);
+CREATE INDEX idx_a ON t0(a);
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2),
+t9
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL) AND
+(t3.a < 5 OR t3.c IS NULL) AND
+(t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+(t5.a >=2 OR t5.c IS NULL) AND
+(t6.a >=4 OR t6.c IS NULL) AND
+(t7.a <= 2 OR t7.c IS NULL) AND
+(t8.a < 1 OR t8.c IS NULL) AND
+(t8.b=t9.b OR t8.c IS NULL) AND
+(t9.a=1);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t0 ref idx_a idx_a 5 const 1 Using where
+1 SIMPLE t1 ref idx_b idx_b 5 test.t0.b 2 Using where
+1 SIMPLE t2 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t3 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t4 ref idx_b idx_b 5 test.t2.b 2 Using where
+1 SIMPLE t5 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t7 ALL NULL NULL NULL NULL 2 Using where
+1 SIMPLE t6 ALL NULL NULL NULL NULL 3 Using where
+1 SIMPLE t8 ref idx_b idx_b 5 test.t7.b 2 Using where
+1 SIMPLE t9 ALL NULL NULL NULL NULL 3 Using where
+Warnings:
+Note 1003 select `test`.`t0`.`a` AS `a`,`test`.`t0`.`b` AS `b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t3`.`a` AS `a`,`test`.`t3`.`b` AS `b`,`test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t5`.`a` AS `a`,`test`.`t5`.`b` AS `b`,`test`.`t6`.`a` AS `a`,`test`.`t6`.`b` AS `b`,`test`.`t7`.`a` AS `a`,`test`.`t7`.`b` AS `b`,`test`.`t8`.`a` AS `a`,`test`.`t8`.`b` AS `b`,`test`.`t9`.`a` AS `a`,`test`.`t9`.`b` AS `b` from `test`.`t0` join `test`.`t1` left join (`test`.`t2` left join (`test`.`t3` join `test`.`t4`) on(((`test`.`t3`.`a` = 1) and (`test`.`t2`.`b` = `test`.`t4`.`b`))) join `test`.`t5` left join (`test`.`t6` join `test`.`t7` left join `test`.`t8` on(((`test`.`t7`.`b` = `test`.`t8`.`b`) and (`test`.`t6`.`b` < 10)))) on(((`test`.`t6`.`b` >= 2) and (`test`.`t5`.`b` = `test`.`t7`.`b`)))) on((((`test`.`t3`.`b` = 2) or isnull(`test`.`t3`.`c`)) and ((`test`.`t6`.`b` = 2) or isnull(`test`.`t6`.`c`)) and ((`test`.`t1`.`b` = `test`.`t5`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t6`.`c`) or isnull(`test`.`t8`.`c`)) and (`test`.`t1`.`a` <> 2))) join `test`.`t9` where ((`test`.`t0`.`a` = 1) and (`test`.`t0`.`b` = `test`.`t1`.`b`) and ((`test`.`t2`.`a` >= 4) or isnull(`test`.`t2`.`c`)) and ((`test`.`t3`.`a` < 5) or isnull(`test`.`t3`.`c`)) and ((`test`.`t3`.`b` = `test`.`t4`.`b`) or isnull(`test`.`t3`.`c`) or isnull(`test`.`t4`.`c`)) and ((`test`.`t5`.`a` >= 2) or isnull(`test`.`t5`.`c`)) and ((`test`.`t6`.`a` >= 4) or isnull(`test`.`t6`.`c`)) and ((`test`.`t7`.`a` <= 2) or isnull(`test`.`t7`.`c`)) and ((`test`.`t8`.`a` < 1) or isnull(`test`.`t8`.`c`)) and ((`test`.`t8`.`b` = `test`.`t9`.`b`) or isnull(`test`.`t8`.`c`)) and (`test`.`t9`.`a` = 1))
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+FROM t0,t1
+LEFT JOIN
+(
+t2
+LEFT JOIN
+(t3, t4)
+ON t3.a=1 AND t2.b=t4.b,
+t5
+LEFT JOIN
+(
+t6,
+t7
+LEFT JOIN
+t8
+ON t7.b=t8.b AND t6.b < 10
+)
+ON t6.b >= 2 AND t5.b=t7.b
+)
+ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+(t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+(t1.a != 2),
+t9
+WHERE t0.a=1 AND
+t0.b=t1.b AND
+(t2.a >= 4 OR t2.c IS NULL) AND
+(t3.a < 5 OR t3.c IS NULL) AND
+(t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+(t5.a >=2 OR t5.c IS NULL) AND
+(t6.a >=4 OR t6.c IS NULL) AND
+(t7.a <= 2 OR t7.c IS NULL) AND
+(t8.a < 1 OR t8.c IS NULL) AND
+(t8.b=t9.b OR t8.c IS NULL) AND
+(t9.a=1);
+a b a b a b a b a b a b a b a b a b a b
+1 2 2 2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 1 1
+1 2 3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL 1 1
+1 2 3 2 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL 1 1
+1 2 3 2 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL 1 1
+1 2 3 2 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL 1 1
+1 2 3 2 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL 1 1
+1 2 3 2 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL 1 1
+1 2 2 2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 1 2
+1 2 3 2 4 2 1 2 3 2 3 1 6 2 1 1 NULL NULL 1 2
+1 2 3 2 4 2 1 2 3 2 2 2 6 2 2 2 0 2 1 2
+1 2 3 2 4 2 1 2 3 2 3 3 NULL NULL NULL NULL NULL NULL 1 2
+1 2 3 2 4 2 1 2 4 2 3 1 6 2 1 1 NULL NULL 1 2
+1 2 3 2 4 2 1 2 4 2 2 2 6 2 2 2 0 2 1 2
+1 2 3 2 4 2 1 2 4 2 3 3 NULL NULL NULL NULL NULL NULL 1 2
+1 2 3 2 5 3 NULL NULL NULL NULL 3 1 6 2 1 1 NULL NULL 1 2
+1 2 3 2 5 3 NULL NULL NULL NULL 2 2 6 2 2 2 0 2 1 2
+1 2 3 2 5 3 NULL NULL NULL NULL 3 3 NULL NULL NULL NULL NULL NULL 1 2
+SELECT t2.a,t2.b
+FROM t2;
+a b
+3 3
+4 2
+5 3
+SELECT t3.a,t3.b
+FROM t3;
+a b
+1 2
+2 2
+SELECT t2.a,t2.b,t3.a,t3.b
+FROM t2 LEFT JOIN t3 ON t2.b=t3.b
+WHERE t2.a = 4 OR (t2.a > 4 AND t3.a IS NULL);
+a b a b
+4 2 1 2
+4 2 2 2
+5 3 NULL NULL
+SELECT t2.a,t2.b,t3.a,t3.b
+FROM t2 LEFT JOIN (t3) ON t2.b=t3.b
+WHERE t2.a = 4 OR (t2.a > 4 AND t3.a IS NULL);
+a b a b
+4 2 1 2
+4 2 2 2
+5 3 NULL NULL
+ALTER TABLE t3
+CHANGE COLUMN a a1 int,
+CHANGE COLUMN c c1 int;
+SELECT t2.a,t2.b,t3.a1,t3.b
+FROM t2 LEFT JOIN t3 ON t2.b=t3.b
+WHERE t2.a = 4 OR (t2.a > 4 AND t3.a1 IS NULL);
+a b a1 b
+4 2 1 2
+4 2 2 2
+5 3 NULL NULL
+SELECT t2.a,t2.b,t3.a1,t3.b
+FROM t2 NATURAL LEFT JOIN t3
+WHERE t2.a = 4 OR (t2.a > 4 AND t3.a1 IS NULL);
+a b a1 b
+4 2 1 2
+4 2 2 2
+5 3 NULL NULL
+DROP TABLE t0,t1,t2,t3,t4,t5,t6,t7,t8,t9;
+CREATE TABLE t1 (a int);
+CREATE TABLE t2 (a int);
+CREATE TABLE t3 (a int);
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (2);
+INSERT INTO t3 VALUES (2);
+INSERT INTO t1 VALUES (2);
+SELECT * FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.a=t3.a) ON t1.a=t3.a;
+a a a
+1 NULL NULL
+2 2 2
+SELECT * FROM t1 LEFT JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.a=t3.a;
+a a a
+1 NULL NULL
+2 2 2
+DELETE FROM t1 WHERE a=2;
+SELECT * FROM t1 LEFT JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.a=t3.a;
+a a a
+1 NULL NULL
+DELETE FROM t2;
+SELECT * FROM t1 LEFT JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.a=t3.a;
+a a a
+1 NULL NULL
+DROP TABLE t1,t2,t3;
+CREATE TABLE t1(a int, key (a));
+CREATE TABLE t2(b int, key (b));
+CREATE TABLE t3(c int, key (c));
+INSERT INTO t1 VALUES (NULL), (0), (1), (2), (3), (4), (5), (6), (7), (8), (9),
+(10), (11), (12), (13), (14), (15), (16), (17), (18), (19);
+INSERT INTO t2 VALUES (NULL), (0), (1), (2), (3), (4), (5), (6), (7), (8), (9),
+(10), (11), (12), (13), (14), (15), (16), (17), (18), (19);
+INSERT INTO t3 VALUES (0), (1), (2), (3), (4), (5);
+EXPLAIN SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON c < 3 and b = c;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 21 Using index
+1 SIMPLE t3 index c c 5 NULL 6 Using index
+1 SIMPLE t2 ref b b 5 test.t3.c 2 Using index
+EXPLAIN SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON b < 3 and b = c;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 21 Using index
+1 SIMPLE t3 index c c 5 NULL 6 Using index
+1 SIMPLE t2 ref b b 5 test.t3.c 2 Using index
+SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON b < 3 and b = c;
+a b c
+NULL 0 0
+NULL 1 1
+NULL 2 2
+0 0 0
+0 1 1
+0 2 2
+1 0 0
+1 1 1
+1 2 2
+2 0 0
+2 1 1
+2 2 2
+3 0 0
+3 1 1
+3 2 2
+4 0 0
+4 1 1
+4 2 2
+5 0 0
+5 1 1
+5 2 2
+6 0 0
+6 1 1
+6 2 2
+7 0 0
+7 1 1
+7 2 2
+8 0 0
+8 1 1
+8 2 2
+9 0 0
+9 1 1
+9 2 2
+10 0 0
+10 1 1
+10 2 2
+11 0 0
+11 1 1
+11 2 2
+12 0 0
+12 1 1
+12 2 2
+13 0 0
+13 1 1
+13 2 2
+14 0 0
+14 1 1
+14 2 2
+15 0 0
+15 1 1
+15 2 2
+16 0 0
+16 1 1
+16 2 2
+17 0 0
+17 1 1
+17 2 2
+18 0 0
+18 1 1
+18 2 2
+19 0 0
+19 1 1
+19 2 2
+DELETE FROM t3;
+EXPLAIN SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON b < 3 and b = c;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 21 Using index
+1 SIMPLE t3 index c c 5 NULL 0 Using index
+1 SIMPLE t2 ref b b 5 test.t3.c 2 Using index
+SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON b < 3 and b = c;
+a b c
+NULL NULL NULL
+0 NULL NULL
+1 NULL NULL
+2 NULL NULL
+3 NULL NULL
+4 NULL NULL
+5 NULL NULL
+6 NULL NULL
+7 NULL NULL
+8 NULL NULL
+9 NULL NULL
+10 NULL NULL
+11 NULL NULL
+12 NULL NULL
+13 NULL NULL
+14 NULL NULL
+15 NULL NULL
+16 NULL NULL
+17 NULL NULL
+18 NULL NULL
+19 NULL NULL
+DROP TABLE t1,t2,t3;
diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result
index 75bf96cb401..ec377b92371 100644
--- a/mysql-test/r/join_outer.result
+++ b/mysql-test/r/join_outer.result
@@ -356,13 +356,7 @@ select t1.name, t2.name, t2.id, t2.owner, t3.id from t1 left join t2 on (t1.id =
name name id owner id
Antonio Paz El Gato 1 1 1
Antonio Paz Perrito 2 1 1
-Lilliana Angelovska NULL NULL NULL 1
-Thimble Smith NULL NULL NULL 1
-Antonio Paz NULL NULL NULL 2
-Lilliana Angelovska NULL NULL NULL 2
-Thimble Smith NULL NULL NULL 2
-Antonio Paz NULL NULL NULL 3
-Lilliana Angelovska NULL NULL NULL 3
+NULL NULL NULL NULL 2
Thimble Smith Happy 3 3 3
drop table t1,t2;
create table t1 (id int not null, str char(10), index(str));
diff --git a/mysql-test/r/key.result b/mysql-test/r/key.result
index 967ff47e1ea..bb553825caa 100644
--- a/mysql-test/r/key.result
+++ b/mysql-test/r/key.result
@@ -212,14 +212,14 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 Using index
explain select 1 from t1 where id =2 or id=3;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 2 Using where; Using index
+1 SIMPLE t1 index PRIMARY PRIMARY 4 NULL 7 Using where; Using index
explain select name from t1 where id =2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
ALTER TABLE t1 DROP PRIMARY KEY, ADD INDEX (id);
explain select 1 from t1 where id =2;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 ref id id 4 const 1 Using where; Using index
+1 SIMPLE t1 ref id id 4 const 1 Using index
drop table t1;
CREATE TABLE t1 (numeropost mediumint(8) unsigned NOT NULL default '0', numreponse int(10) unsigned NOT NULL auto_increment, PRIMARY KEY (numeropost,numreponse), UNIQUE KEY numreponse (numreponse));
INSERT INTO t1 (numeropost,numreponse) VALUES ('1','1'),('1','2'),('2','3'),('2','4');
diff --git a/mysql-test/r/keywords.result b/mysql-test/r/keywords.result
index c218379110f..88a0ab8abd5 100644
--- a/mysql-test/r/keywords.result
+++ b/mysql-test/r/keywords.result
@@ -1,12 +1,14 @@
drop table if exists t1;
-create table t1 (time time, date date, timestamp timestamp);
-insert into t1 values ("12:22:22","97:02:03","1997-01-02");
+create table t1 (time time, date date, timestamp timestamp,
+quarter int, week int, year int, timestampadd int, timestampdiff int);
+insert into t1 values ("12:22:22","97:02:03","1997-01-02",1,2,3,4,5);
select * from t1;
-time date timestamp
-12:22:22 1997-02-03 1997-01-02 00:00:00
-select t1.time+0,t1.date+0,t1.timestamp+0,concat(date," ",time) from t1;
-t1.time+0 t1.date+0 t1.timestamp+0 concat(date," ",time)
-122222 19970203 19970102000000 1997-02-03 12:22:22
+time date timestamp quarter week year timestampadd timestampdiff
+12:22:22 1997-02-03 1997-01-02 00:00:00 1 2 3 4 5
+select t1.time+0,t1.date+0,t1.timestamp+0,concat(date," ",time),
+t1.quarter+t1.week, t1.year+timestampadd, timestampdiff from t1;
+t1.time+0 t1.date+0 t1.timestamp+0 concat(date," ",time) t1.quarter+t1.week t1.year+timestampadd timestampdiff
+122222 19970203 19970102000000 1997-02-03 12:22:22 3 7 5
drop table t1;
create table events(binlog int);
insert into events values(1);
diff --git a/mysql-test/r/lowercase_table.result b/mysql-test/r/lowercase_table.result
index a30ec0f160c..a8bc2e3d342 100644
--- a/mysql-test/r/lowercase_table.result
+++ b/mysql-test/r/lowercase_table.result
@@ -26,9 +26,9 @@ RENAME TABLE T1 TO T2;
ALTER TABLE T2 ADD new_col int not null;
ALTER TABLE T2 RENAME T3;
show tables like 't_';
-Tables_in_test (t_)
-t3
-t4
+Tables_in_test (t_) table_type
+t3 BASE TABLE
+t4 BASE TABLE
drop table t3,t4;
create table t1 (a int);
select count(*) from T1;
@@ -79,4 +79,4 @@ select C.a, c.a from t1 c, t2 C;
ERROR 42000: Not unique table/alias: 'C'
drop table t1, t2;
show tables;
-Tables_in_test
+Tables_in_test table_type
diff --git a/mysql-test/r/lowercase_table2.result b/mysql-test/r/lowercase_table2.result
index 3be73f6cc6a..228c7d325e5 100644
--- a/mysql-test/r/lowercase_table2.result
+++ b/mysql-test/r/lowercase_table2.result
@@ -4,11 +4,11 @@ DROP DATABASE IF EXISTS `test_$1`;
CREATE TABLE T1 (a int);
INSERT INTO T1 VALUES (1);
SHOW TABLES LIKE "T1";
-Tables_in_test (T1)
-T1
+Tables_in_test (T1) table_type
+T1 BASE TABLE
SHOW TABLES LIKE "t1";
-Tables_in_test (t1)
-T1
+Tables_in_test (t1) table_type
+T1 BASE TABLE
SHOW CREATE TABLE T1;
Table Create Table
T1 CREATE TABLE `T1` (
@@ -16,37 +16,37 @@ T1 CREATE TABLE `T1` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1
RENAME TABLE T1 TO T2;
SHOW TABLES LIKE "T2";
-Tables_in_test (T2)
-T2
+Tables_in_test (T2) table_type
+T2 BASE TABLE
SELECT * FROM t2;
a
1
RENAME TABLE T2 TO t3;
SHOW TABLES LIKE "T3";
-Tables_in_test (T3)
-t3
+Tables_in_test (T3) table_type
+t3 BASE TABLE
RENAME TABLE T3 TO T1;
SHOW TABLES LIKE "T1";
-Tables_in_test (T1)
-T1
+Tables_in_test (T1) table_type
+T1 BASE TABLE
ALTER TABLE T1 add b int;
SHOW TABLES LIKE "T1";
-Tables_in_test (T1)
-T1
+Tables_in_test (T1) table_type
+T1 BASE TABLE
ALTER TABLE T1 RENAME T2;
SHOW TABLES LIKE "T2";
-Tables_in_test (T2)
-T2
+Tables_in_test (T2) table_type
+T2 BASE TABLE
LOCK TABLE T2 WRITE;
ALTER TABLE T2 drop b;
SHOW TABLES LIKE "T2";
-Tables_in_test (T2)
-T2
+Tables_in_test (T2) table_type
+T2 BASE TABLE
UNLOCK TABLES;
RENAME TABLE T2 TO T1;
SHOW TABLES LIKE "T1";
-Tables_in_test (T1)
-T1
+Tables_in_test (T1) table_type
+T1 BASE TABLE
SELECT * from T1;
a
1
@@ -59,11 +59,11 @@ DROP DATABASE `test_$1`;
CREATE TABLE T1 (a int) engine=innodb;
INSERT INTO T1 VALUES (1);
SHOW TABLES LIKE "T1";
-Tables_in_test (T1)
-T1
+Tables_in_test (T1) table_type
+T1 BASE TABLE
SHOW TABLES LIKE "t1";
-Tables_in_test (t1)
-T1
+Tables_in_test (t1) table_type
+T1 BASE TABLE
SHOW CREATE TABLE T1;
Table Create Table
T1 CREATE TABLE `T1` (
@@ -71,37 +71,37 @@ T1 CREATE TABLE `T1` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1
RENAME TABLE T1 TO T2;
SHOW TABLES LIKE "T2";
-Tables_in_test (T2)
-t2
+Tables_in_test (T2) table_type
+t2 BASE TABLE
SELECT * FROM t2;
a
1
RENAME TABLE T2 TO t3;
SHOW TABLES LIKE "T3";
-Tables_in_test (T3)
-t3
+Tables_in_test (T3) table_type
+t3 BASE TABLE
RENAME TABLE T3 TO T1;
SHOW TABLES LIKE "T1";
-Tables_in_test (T1)
-t1
+Tables_in_test (T1) table_type
+t1 BASE TABLE
ALTER TABLE T1 add b int;
SHOW TABLES LIKE "T1";
-Tables_in_test (T1)
-t1
+Tables_in_test (T1) table_type
+t1 BASE TABLE
ALTER TABLE T1 RENAME T2;
SHOW TABLES LIKE "T2";
-Tables_in_test (T2)
-t2
+Tables_in_test (T2) table_type
+t2 BASE TABLE
LOCK TABLE T2 WRITE;
ALTER TABLE T2 drop b;
SHOW TABLES LIKE "T2";
-Tables_in_test (T2)
-t2
+Tables_in_test (T2) table_type
+t2 BASE TABLE
UNLOCK TABLES;
RENAME TABLE T2 TO T1;
SHOW TABLES LIKE "T1";
-Tables_in_test (T1)
-t1
+Tables_in_test (T1) table_type
+t1 BASE TABLE
SELECT * from T1;
a
1
@@ -124,10 +124,10 @@ drop table T1;
create table T1 (A int);
alter table T1 add index (A);
show tables like 'T1%';
-Tables_in_test (T1%)
-T1
+Tables_in_test (T1%) table_type
+T1 BASE TABLE
alter table t1 add index (A);
show tables like 't1%';
-Tables_in_test (t1%)
-t1
+Tables_in_test (t1%) table_type
+t1 BASE TABLE
drop table t1;
diff --git a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result
index 54d99d5609e..29154dc469c 100644
--- a/mysql-test/r/mix_innodb_myisam_binlog.result
+++ b/mysql-test/r/mix_innodb_myisam_binlog.result
@@ -6,12 +6,12 @@ begin;
insert into t1 values(1);
insert into t2 select * from t1;
commit;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; BEGIN
-master-bin.000001 119 Query 1 79 use `test`; insert into t1 values(1)
-master-bin.000001 178 Query 1 79 use `test`; insert into t2 select * from t1
-master-bin.000001 244 Query 1 244 use `test`; COMMIT
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 157 use `test`; BEGIN
+master-bin.000001 157 Query 1 176 use `test`; insert into t1 values(1)
+master-bin.000001 238 Query 1 183 use `test`; insert into t2 select * from t1
+master-bin.000001 326 Query 1 389 use `test`; COMMIT
delete from t1;
delete from t2;
reset master;
@@ -21,12 +21,12 @@ insert into t2 select * from t1;
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; BEGIN
-master-bin.000001 119 Query 1 79 use `test`; insert into t1 values(2)
-master-bin.000001 178 Query 1 79 use `test`; insert into t2 select * from t1
-master-bin.000001 244 Query 1 244 use `test`; ROLLBACK
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 157 use `test`; BEGIN
+master-bin.000001 157 Query 1 176 use `test`; insert into t1 values(2)
+master-bin.000001 238 Query 1 183 use `test`; insert into t2 select * from t1
+master-bin.000001 326 Query 1 391 use `test`; ROLLBACK
delete from t1;
delete from t2;
reset master;
@@ -39,15 +39,15 @@ rollback to savepoint my_savepoint;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
commit;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; BEGIN
-master-bin.000001 119 Query 1 79 use `test`; insert into t1 values(3)
-master-bin.000001 178 Query 1 79 use `test`; savepoint my_savepoint
-master-bin.000001 235 Query 1 79 use `test`; insert into t1 values(4)
-master-bin.000001 294 Query 1 79 use `test`; insert into t2 select * from t1
-master-bin.000001 360 Query 1 79 use `test`; rollback to savepoint my_savepoint
-master-bin.000001 429 Query 1 429 use `test`; COMMIT
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 157 use `test`; BEGIN
+master-bin.000001 157 Query 1 176 use `test`; insert into t1 values(3)
+master-bin.000001 238 Query 1 174 use `test`; savepoint my_savepoint
+master-bin.000001 317 Query 1 176 use `test`; insert into t1 values(4)
+master-bin.000001 398 Query 1 183 use `test`; insert into t2 select * from t1
+master-bin.000001 486 Query 1 186 use `test`; rollback to savepoint my_savepoint
+master-bin.000001 577 Query 1 640 use `test`; COMMIT
delete from t1;
delete from t2;
reset master;
@@ -65,16 +65,16 @@ select a from t1 order by a;
a
5
7
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; BEGIN
-master-bin.000001 119 Query 1 79 use `test`; insert into t1 values(5)
-master-bin.000001 178 Query 1 79 use `test`; savepoint my_savepoint
-master-bin.000001 235 Query 1 79 use `test`; insert into t1 values(6)
-master-bin.000001 294 Query 1 79 use `test`; insert into t2 select * from t1
-master-bin.000001 360 Query 1 79 use `test`; rollback to savepoint my_savepoint
-master-bin.000001 429 Query 1 79 use `test`; insert into t1 values(7)
-master-bin.000001 488 Query 1 488 use `test`; COMMIT
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 157 use `test`; BEGIN
+master-bin.000001 157 Query 1 176 use `test`; insert into t1 values(5)
+master-bin.000001 238 Query 1 174 use `test`; savepoint my_savepoint
+master-bin.000001 317 Query 1 176 use `test`; insert into t1 values(6)
+master-bin.000001 398 Query 1 183 use `test`; insert into t2 select * from t1
+master-bin.000001 486 Query 1 186 use `test`; rollback to savepoint my_savepoint
+master-bin.000001 577 Query 1 176 use `test`; insert into t1 values(7)
+master-bin.000001 658 Query 1 721 use `test`; COMMIT
delete from t1;
delete from t2;
reset master;
@@ -87,40 +87,40 @@ insert into t2 select * from t1;
select get_lock("a",10);
get_lock("a",10)
1
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; BEGIN
-master-bin.000001 119 Query 1 79 use `test`; insert into t1 values(8)
-master-bin.000001 178 Query 1 79 use `test`; insert into t2 select * from t1
-master-bin.000001 244 Query 1 244 use `test`; ROLLBACK
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 157 use `test`; BEGIN
+master-bin.000001 157 Query 1 176 use `test`; insert into t1 values(8)
+master-bin.000001 238 Query 1 183 use `test`; insert into t2 select * from t1
+master-bin.000001 326 Query 1 391 use `test`; ROLLBACK
delete from t1;
delete from t2;
reset master;
insert into t1 values(9);
insert into t2 select * from t1;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; insert into t1 values(9)
-master-bin.000001 138 Query 1 138 use `test`; insert into t2 select * from t1
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 176 use `test`; insert into t1 values(9)
+master-bin.000001 176 Query 1 264 use `test`; insert into t2 select * from t1
delete from t1;
delete from t2;
reset master;
insert into t1 values(10);
begin;
insert into t2 select * from t1;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; insert into t1 values(10)
-master-bin.000001 139 Query 1 139 use `test`; insert into t2 select * from t1
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 177 use `test`; insert into t1 values(10)
+master-bin.000001 177 Query 1 265 use `test`; insert into t2 select * from t1
insert into t1 values(11);
commit;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; insert into t1 values(10)
-master-bin.000001 139 Query 1 139 use `test`; insert into t2 select * from t1
-master-bin.000001 205 Query 1 205 use `test`; BEGIN
-master-bin.000001 245 Query 1 205 use `test`; insert into t1 values(11)
-master-bin.000001 305 Query 1 305 use `test`; COMMIT
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 177 use `test`; insert into t1 values(10)
+master-bin.000001 177 Query 1 265 use `test`; insert into t2 select * from t1
+master-bin.000001 265 Query 1 327 use `test`; BEGIN
+master-bin.000001 327 Query 1 347 use `test`; insert into t1 values(11)
+master-bin.000001 409 Query 1 472 use `test`; COMMIT
alter table t2 engine=INNODB;
delete from t1;
delete from t2;
@@ -129,12 +129,12 @@ begin;
insert into t1 values(12);
insert into t2 select * from t1;
commit;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; BEGIN
-master-bin.000001 119 Query 1 79 use `test`; insert into t1 values(12)
-master-bin.000001 179 Query 1 79 use `test`; insert into t2 select * from t1
-master-bin.000001 245 Query 1 245 use `test`; COMMIT
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 157 use `test`; BEGIN
+master-bin.000001 157 Query 1 177 use `test`; insert into t1 values(12)
+master-bin.000001 239 Query 1 183 use `test`; insert into t2 select * from t1
+master-bin.000001 327 Query 1 390 use `test`; COMMIT
delete from t1;
delete from t2;
reset master;
@@ -142,8 +142,8 @@ begin;
insert into t1 values(13);
insert into t2 select * from t1;
rollback;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
delete from t1;
delete from t2;
reset master;
@@ -154,11 +154,11 @@ insert into t1 values(15);
insert into t2 select * from t1;
rollback to savepoint my_savepoint;
commit;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; BEGIN
-master-bin.000001 119 Query 1 79 use `test`; insert into t1 values(14)
-master-bin.000001 179 Query 1 179 use `test`; COMMIT
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 157 use `test`; BEGIN
+master-bin.000001 157 Query 1 177 use `test`; insert into t1 values(14)
+master-bin.000001 239 Query 1 302 use `test`; COMMIT
delete from t1;
delete from t2;
reset master;
@@ -174,10 +174,10 @@ select a from t1 order by a;
a
16
18
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; BEGIN
-master-bin.000001 119 Query 1 79 use `test`; insert into t1 values(16)
-master-bin.000001 179 Query 1 79 use `test`; insert into t1 values(18)
-master-bin.000001 239 Query 1 239 use `test`; COMMIT
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 157 use `test`; BEGIN
+master-bin.000001 157 Query 1 177 use `test`; insert into t1 values(16)
+master-bin.000001 239 Query 1 177 use `test`; insert into t1 values(18)
+master-bin.000001 321 Query 1 384 use `test`; COMMIT
drop table t1,t2;
diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result
index 39ec9ff4eb9..d809c9112b7 100644
--- a/mysql-test/r/multi_update.result
+++ b/mysql-test/r/multi_update.result
@@ -455,3 +455,29 @@ create table t3 (a int, primary key (a));
delete t1,t3 from t1,t2 where t1.a=t2.a and t2.a=(select t3.a from t3 where t1.a=t3.a);
ERROR 42S02: Unknown table 't3' in MULTI DELETE
drop table t1, t2, t3;
+set @ttype_save=@@storage_engine;
+set @@storage_engine=innodb;
+create table t1 ( c char(8) not null );
+insert into t1 values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9');
+insert into t1 values ('A'),('B'),('C'),('D'),('E'),('F');
+alter table t1 add b char(8) not null;
+alter table t1 add a char(8) not null;
+alter table t1 add primary key (a,b,c);
+update t1 set a=c, b=c;
+create table t2 like t1;
+insert into t2 select * from t1;
+delete t1,t2 from t2,t1 where t1.a<'B' and t2.b=t1.b;
+drop table t1,t2;
+set @@storage_engine=bdb;
+create table t1 ( c char(8) not null );
+insert into t1 values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9');
+insert into t1 values ('A'),('B'),('C'),('D'),('E'),('F');
+alter table t1 add b char(8) not null;
+alter table t1 add a char(8) not null;
+alter table t1 add primary key (a,b,c);
+update t1 set a=c, b=c;
+create table t2 like t1;
+insert into t2 select * from t1;
+delete t1,t2 from t2,t1 where t1.a<'B' and t2.b=t1.b;
+set @@storage_engine=@ttype_save;
+drop table t1,t2;
diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result
index e88ece6b361..c18588d90a7 100644
--- a/mysql-test/r/mysqlbinlog.result
+++ b/mysql-test/r/mysqlbinlog.result
@@ -17,6 +17,8 @@ flush logs;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
use test;
SET TIMESTAMP=1000000000;
+SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1;
+SET @@session.sql_mode=0;
drop table if exists t1,t2;
SET TIMESTAMP=1000000000;
create table t1 (word varchar(20));
@@ -37,6 +39,8 @@ LOAD DATA LOCAL INFILE 'MYSQL_TEST_DIR/var/tmp/words.dat-5-0' INTO TABLE `t1` FI
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
use test;
SET TIMESTAMP=1000000000;
+SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1;
+SET @@session.sql_mode=0;
insert into t1 values ("Alas");
--- --database --
@@ -47,12 +51,16 @@ SET INSERT_ID=1;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
use test;
SET TIMESTAMP=1000000000;
+SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1;
+SET @@session.sql_mode=0;
insert into t1 values ("Alas");
--- Remote --
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
use test;
SET TIMESTAMP=1000000000;
+SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1;
+SET @@session.sql_mode=0;
drop table if exists t1,t2;
SET TIMESTAMP=1000000000;
create table t1 (word varchar(20));
@@ -73,6 +81,8 @@ LOAD DATA LOCAL INFILE 'MYSQL_TEST_DIR/var/tmp/words.dat-5-1' INTO TABLE `t1` FI
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
use test;
SET TIMESTAMP=1000000000;
+SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1;
+SET @@session.sql_mode=0;
insert into t1 values ("Alas");
--- --database --
@@ -83,5 +93,7 @@ SET INSERT_ID=1;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
use test;
SET TIMESTAMP=1000000000;
+SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1;
+SET @@session.sql_mode=0;
insert into t1 values ("Alas");
drop table t1, t2;
diff --git a/mysql-test/r/ndb_autodiscover.result b/mysql-test/r/ndb_autodiscover.result
index f5b908c39e2..313003f0a1f 100644
--- a/mysql-test/r/ndb_autodiscover.result
+++ b/mysql-test/r/ndb_autodiscover.result
@@ -95,7 +95,7 @@ show status like 'handler_discover%';
Variable_name Value
Handler_discover 1
SHOW TABLES FROM test;
-Tables_in_test
+Tables_in_test table_type
create table IF NOT EXISTS t3(
id int not null primary key,
id2 int not null,
diff --git a/mysql-test/r/null_key.result b/mysql-test/r/null_key.result
index 4b7400e5e60..3044b188d06 100644
--- a/mysql-test/r/null_key.result
+++ b/mysql-test/r/null_key.result
@@ -30,13 +30,13 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref a,b a 5 const 3 Using where; Using index
explain select * from t1 where a is null and b=9 or a is null and b=7 limit 3;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a,b a 9 NULL 2 Using where; Using index
+1 SIMPLE t1 ref a,b a 5 const 2 Using where; Using index
explain select * from t1 where a > 1 and a < 3 limit 1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 5 NULL 1 Using where; Using index
+1 SIMPLE t1 index a a 9 NULL 12 Using where; Using index
explain select * from t1 where a > 8 and a < 9;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 5 NULL 1 Using where; Using index
+1 SIMPLE t1 index a a 9 NULL 12 Using where; Using index
select * from t1 where a is null;
a b
NULL 7
diff --git a/mysql-test/r/olap.result b/mysql-test/r/olap.result
index bcbe5a8791c..f398c69a424 100644
--- a/mysql-test/r/olap.result
+++ b/mysql-test/r/olap.result
@@ -85,7 +85,7 @@ explain extended select product, country_id , year, sum(profit) from t1 group by
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 15 Using temporary; Using filesort
Warnings:
-Note 1003 select test.t1.product AS `product`,test.t1.country_id AS `country_id`,test.t1.year AS `year`,sum(test.t1.profit) AS `sum(profit)` from test.t1 group by test.t1.product,test.t1.country_id,test.t1.year with rollup
+Note 1003 select `test`.`t1`.`product` AS `product`,`test`.`t1`.`country_id` AS `country_id`,`test`.`t1`.`year` AS `year`,sum(`test`.`t1`.`profit`) AS `sum(profit)` from `test`.`t1` group by `test`.`t1`.`product`,`test`.`t1`.`country_id`,`test`.`t1`.`year` with rollup
select product, country_id , sum(profit) from t1 group by product desc, country_id with rollup;
product country_id sum(profit)
TV 1 400
diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result
index b3bc4a18a40..a431807a560 100644
--- a/mysql-test/r/order_by.result
+++ b/mysql-test/r/order_by.result
@@ -272,7 +272,7 @@ create table t1 (a int not null, b int, c varchar(10), key (a, b, c));
insert into t1 values (1, NULL, NULL), (1, NULL, 'b'), (1, 1, NULL), (1, 1, 'b'), (1, 1, 'b'), (2, 1, 'a'), (2, 1, 'b'), (2, 2, 'a'), (2, 2, 'b'), (2, 3, 'c'),(1,3,'b');
explain select * from t1 where (a = 1 and b is null and c = 'b') or (a > 2) order by a desc;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 20 NULL 2 Using where; Using index
+1 SIMPLE t1 index a a 20 NULL 11 Using where; Using index
select * from t1 where (a = 1 and b is null and c = 'b') or (a > 2) order by a desc;
a b c
1 NULL b
@@ -328,7 +328,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range a a 9 NULL 5 Using where; Using index
explain select * from t1 where a = 2 and b < 2 order by a desc,b desc;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a a 9 NULL 2 Using where; Using index
+1 SIMPLE t1 ref a a 4 const 2 Using where; Using index
explain select * from t1 where a = 1 order by b desc;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref a a 4 const 5 Using where; Using index
diff --git a/mysql-test/r/ps_1general.result b/mysql-test/r/ps_1general.result
index e9a5f705fa7..dc67fda7efa 100644
--- a/mysql-test/r/ps_1general.result
+++ b/mysql-test/r/ps_1general.result
@@ -252,8 +252,8 @@ mysql
test
prepare stmt4 from ' show tables from test like ''t2%'' ';
execute stmt4;
-Tables_in_test (t2%)
-t2
+Tables_in_test (t2%) table_type
+t2 BASE TABLE
prepare stmt4 from ' show columns from t2 from test like ''a%'' ';
execute stmt4;
Field Type Null Key Default Extra
@@ -382,9 +382,7 @@ ERROR HY000: This command is not supported in the prepared statement protocol ye
prepare stmt1 from ' handler t1 open ';
ERROR HY000: This command is not supported in the prepared statement protocol yet
prepare stmt3 from ' commit ' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
prepare stmt3 from ' rollback ' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
prepare stmt4 from ' SET sql_mode=ansi ';
execute stmt4;
select 'a' || 'b' ;
@@ -419,7 +417,7 @@ def table 253 64 2 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 14 N 1 31 63
@@ -435,7 +433,7 @@ def table 253 64 2 N 1 31 63
def type 253 10 5 N 1 31 63
def possible_keys 253 4096 7 Y 0 31 63
def key 253 64 7 Y 0 31 63
-def key_len 8 3 1 Y 32800 0 8
+def key_len 253 4096 1 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 27 N 1 31 63
@@ -460,11 +458,8 @@ m
1
drop table t3;
prepare stmt3 from ' create index t2_idx on t2(b) ';
-ERROR HY000: This command is not supported in the prepared statement protocol yet
prepare stmt3 from ' drop index t2_idx on t2 ' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
prepare stmt3 from ' alter table t2 drop primary key ';
-ERROR HY000: This command is not supported in the prepared statement protocol yet
drop table if exists new_t2;
prepare stmt3 from ' rename table t2 to new_t2 ';
execute stmt3;
diff --git a/mysql-test/r/ps_2myisam.result b/mysql-test/r/ps_2myisam.result
index b49eedb4067..344f02bb8ab 100644
--- a/mysql-test/r/ps_2myisam.result
+++ b/mysql-test/r/ps_2myisam.result
@@ -577,7 +577,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -648,7 +648,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -935,7 +935,6 @@ execute stmt1 using @arg00;
select a,b from t1 where b=@arg00;
a b
prepare stmt1 from 'truncate table t1' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
test_sequence
------ update tests ------
delete from t1 ;
diff --git a/mysql-test/r/ps_3innodb.result b/mysql-test/r/ps_3innodb.result
index 3a2708376fa..162004c7b0c 100644
--- a/mysql-test/r/ps_3innodb.result
+++ b/mysql-test/r/ps_3innodb.result
@@ -577,7 +577,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -648,7 +648,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -935,7 +935,6 @@ execute stmt1 using @arg00;
select a,b from t1 where b=@arg00;
a b
prepare stmt1 from 'truncate table t1' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
test_sequence
------ update tests ------
delete from t1 ;
diff --git a/mysql-test/r/ps_4heap.result b/mysql-test/r/ps_4heap.result
index 4228d95677d..23874c9b02b 100644
--- a/mysql-test/r/ps_4heap.result
+++ b/mysql-test/r/ps_4heap.result
@@ -578,7 +578,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -649,7 +649,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -936,7 +936,6 @@ execute stmt1 using @arg00;
select a,b from t1 where b=@arg00;
a b
prepare stmt1 from 'truncate table t1' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
test_sequence
------ update tests ------
delete from t1 ;
diff --git a/mysql-test/r/ps_5merge.result b/mysql-test/r/ps_5merge.result
index 03020ccc0f3..d1c3597b22b 100644
--- a/mysql-test/r/ps_5merge.result
+++ b/mysql-test/r/ps_5merge.result
@@ -620,7 +620,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -691,7 +691,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -978,7 +978,6 @@ execute stmt1 using @arg00;
select a,b from t1 where b=@arg00;
a b
prepare stmt1 from 'truncate table t1' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
test_sequence
------ update tests ------
delete from t1 ;
@@ -1813,7 +1812,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -1884,7 +1883,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -2171,7 +2170,6 @@ execute stmt1 using @arg00;
select a,b from t1 where b=@arg00;
a b
prepare stmt1 from 'truncate table t1' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
test_sequence
------ update tests ------
delete from t1 ;
diff --git a/mysql-test/r/ps_6bdb.result b/mysql-test/r/ps_6bdb.result
index b8730cce101..31dfdd7bb87 100644
--- a/mysql-test/r/ps_6bdb.result
+++ b/mysql-test/r/ps_6bdb.result
@@ -577,7 +577,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -648,7 +648,7 @@ def table 253 64 16 N 1 31 63
def type 253 10 3 N 1 31 63
def possible_keys 253 4096 0 Y 0 31 63
def key 253 64 0 Y 0 31 63
-def key_len 8 3 0 Y 32800 0 8
+def key_len 253 4096 0 Y 0 31 63
def ref 253 1024 0 Y 0 31 63
def rows 8 10 1 N 32801 0 8
def Extra 253 255 44 N 1 31 63
@@ -935,7 +935,6 @@ execute stmt1 using @arg00;
select a,b from t1 where b=@arg00;
a b
prepare stmt1 from 'truncate table t1' ;
-ERROR HY000: This command is not supported in the prepared statement protocol yet
test_sequence
------ update tests ------
delete from t1 ;
diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result
index a93ea1aa7fe..84949b3e711 100644
--- a/mysql-test/r/query_cache.result
+++ b/mysql-test/r/query_cache.result
@@ -305,7 +305,7 @@ explain extended select benchmark(1,1) from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 system NULL NULL NULL NULL 0 const row not found
Warnings:
-Note 1003 select sql_no_cache benchmark(1,1) AS `benchmark(1,1)` from test.t1
+Note 1003 select sql_no_cache benchmark(1,1) AS `benchmark(1,1)` from `test`.`t1`
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 0
@@ -851,4 +851,28 @@ select @@character_set_results;
@@character_set_results
NULL
set character_set_results=default;
+create table t1 (a int);
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 0
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 11
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 6
+/**/ select * from t1;
+a
+/**/ select * from t1;
+a
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 1
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 12
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 7
+DROP TABLE t1;
SET GLOBAL query_cache_size=0;
diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result
index 4ca96316800..01ee018a434 100644
--- a/mysql-test/r/range.result
+++ b/mysql-test/r/range.result
@@ -247,7 +247,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref x x 5 const 1 Using where; Using index
explain select count(*) from t1 where x in (1,2);
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range x x 5 NULL 2 Using where; Using index
+1 SIMPLE t1 index x x 5 NULL 9 Using where; Using index
drop table t1;
CREATE TABLE t1 (key1 int(11) NOT NULL default '0', KEY i1 (key1));
INSERT INTO t1 VALUES (0),(0),(1),(1);
@@ -255,7 +255,7 @@ CREATE TABLE t2 (keya int(11) NOT NULL default '0', KEY j1 (keya));
INSERT INTO t2 VALUES (0),(0),(1),(1),(2),(2);
explain select * from t1, t2 where (t1.key1 <t2.keya + 1) and t2.keya=3;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 ref j1 j1 4 const 1 Using where; Using index
+1 SIMPLE t2 ref j1 j1 4 const 1 Using index
1 SIMPLE t1 ALL i1 NULL NULL NULL 4 Range checked for each record (index map: 0x1)
DROP TABLE t1,t2;
CREATE TABLE t1 (
diff --git a/mysql-test/r/rename.result b/mysql-test/r/rename.result
index ec36f015412..c6da8285479 100644
--- a/mysql-test/r/rename.result
+++ b/mysql-test/r/rename.result
@@ -20,10 +20,10 @@ Got one of the listed errors
rename table t3 to t4, t2 to t3, t1 to t2, t4 to t2;
Got one of the listed errors
show tables like "t_";
-Tables_in_test (t_)
-t1
-t2
-t3
+Tables_in_test (t_) table_type
+t1 BASE TABLE
+t2 BASE TABLE
+t3 BASE TABLE
rename table t3 to t1, t2 to t3, t1 to t2, t4 to t1;
Got one of the listed errors
rename table t3 to t4, t5 to t3, t1 to t2, t4 to t1;
@@ -45,12 +45,12 @@ CREATE TABLE t3 (a int);
FLUSH TABLES WITH READ LOCK;
RENAME TABLE t1 TO t2, t3 to t4;
show tables;
-Tables_in_test
-t1
-t3
+Tables_in_test table_type
+t1 BASE TABLE
+t3 BASE TABLE
UNLOCK TABLES;
show tables;
-Tables_in_test
-t2
-t4
+Tables_in_test table_type
+t2 BASE TABLE
+t4 BASE TABLE
drop table t2, t4;
diff --git a/mysql-test/r/rowid_order_bdb.result b/mysql-test/r/rowid_order_bdb.result
new file mode 100644
index 00000000000..bbdc6f6ff77
--- /dev/null
+++ b/mysql-test/r/rowid_order_bdb.result
@@ -0,0 +1,186 @@
+drop table if exists t1, t2, t3,t4;
+create table t1 (
+pk1 int not NULL,
+key1 int(11),
+key2 int(11),
+PRIMARY KEY (pk1),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=bdb;
+insert into t1 values (-5, 1, 1),
+(-100, 1, 1),
+(3, 1, 1),
+(0, 1, 1),
+(10, 1, 1);
+explain select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 5 Using sort_union(key1,key2); Using where
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 key1 key2
+-100 1 1
+-5 1 1
+0 1 1
+3 1 1
+10 1 1
+drop table t1;
+create table t1 (
+pk1 int unsigned not NULL,
+key1 int(11),
+key2 int(11),
+PRIMARY KEY (pk1),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=bdb;
+insert into t1 values (0, 1, 1),
+(0xFFFFFFFF, 1, 1),
+(0xFFFFFFFE, 1, 1),
+(1, 1, 1),
+(2, 1, 1);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 key1 key2
+0 1 1
+1 1 1
+2 1 1
+4294967294 1 1
+4294967295 1 1
+drop table t1;
+create table t1 (
+pk1 char(4) not NULL,
+key1 int(11),
+key2 int(11),
+PRIMARY KEY (pk1),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=bdb collate latin2_general_ci;
+insert into t1 values ('a1', 1, 1),
+('b2', 1, 1),
+('A3', 1, 1),
+('B4', 1, 1);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 key1 key2
+a1 1 1
+A3 1 1
+b2 1 1
+B4 1 1
+drop table t1;
+create table t1 (
+pk1 int not NULL,
+pk2 char(4) not NULL collate latin1_german1_ci,
+pk3 char(4) not NULL collate latin1_bin,
+key1 int(11),
+key2 int(11),
+PRIMARY KEY (pk1,pk2,pk3),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=bdb;
+insert into t1 values
+(1, 'u', 'u', 1, 1),
+(1, 'u', char(0xEC), 1, 1),
+(1, 'u', 'x', 1, 1);
+insert ignore into t1 select pk1, char(0xEC), pk3, key1, key2 from t1;
+insert ignore into t1 select pk1, 'x', pk3, key1, key2 from t1 where pk2='u';
+insert ignore into t1 select 2, pk2, pk3, key1, key2 from t1;
+select * from t1;
+pk1 pk2 pk3 key1 key2
+1 ì u 1 1
+1 ì x 1 1
+1 ì ì 1 1
+1 u u 1 1
+1 u x 1 1
+1 u ì 1 1
+1 x u 1 1
+1 x x 1 1
+1 x ì 1 1
+2 ì u 1 1
+2 ì x 1 1
+2 ì ì 1 1
+2 u u 1 1
+2 u x 1 1
+2 u ì 1 1
+2 x u 1 1
+2 x x 1 1
+2 x ì 1 1
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 pk2 pk3 key1 key2
+1 ì u 1 1
+1 ì x 1 1
+1 ì ì 1 1
+1 u u 1 1
+1 u x 1 1
+1 u ì 1 1
+1 x u 1 1
+1 x x 1 1
+1 x ì 1 1
+2 ì u 1 1
+2 ì x 1 1
+2 ì ì 1 1
+2 u u 1 1
+2 u x 1 1
+2 u ì 1 1
+2 x u 1 1
+2 x x 1 1
+2 x ì 1 1
+alter table t1 drop primary key;
+select * from t1;
+pk1 pk2 pk3 key1 key2
+1 ì u 1 1
+1 ì x 1 1
+1 ì ì 1 1
+1 u u 1 1
+1 u x 1 1
+1 u ì 1 1
+1 x u 1 1
+1 x x 1 1
+1 x ì 1 1
+2 ì u 1 1
+2 ì x 1 1
+2 ì ì 1 1
+2 u u 1 1
+2 u x 1 1
+2 u ì 1 1
+2 x u 1 1
+2 x x 1 1
+2 x ì 1 1
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 pk2 pk3 key1 key2
+1 ì u 1 1
+1 ì x 1 1
+1 ì ì 1 1
+1 u u 1 1
+1 u x 1 1
+1 u ì 1 1
+1 x u 1 1
+1 x x 1 1
+1 x ì 1 1
+2 ì u 1 1
+2 ì x 1 1
+2 ì ì 1 1
+2 u u 1 1
+2 u x 1 1
+2 u ì 1 1
+2 x u 1 1
+2 x x 1 1
+2 x ì 1 1
+drop table t1;
+create table t1 (
+pk1 varchar(8) NOT NULL default '',
+pk2 varchar(4) NOT NULL default '',
+key1 int(11),
+key2 int(11),
+primary key(pk1, pk2),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=bdb;
+insert into t1 values ('','empt',2,2),
+('a','a--a',2,2),
+('bb','b--b',2,2),
+('ccc','c--c',2,2),
+('dddd','d--d',2,2);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 pk2 key1 key2
+ empt 2 2
+a a--a 2 2
+bb b--b 2 2
+ccc c--c 2 2
+dddd d--d 2 2
+drop table t1;
diff --git a/mysql-test/r/rowid_order_innodb.result b/mysql-test/r/rowid_order_innodb.result
new file mode 100644
index 00000000000..f76002e9cb2
--- /dev/null
+++ b/mysql-test/r/rowid_order_innodb.result
@@ -0,0 +1,186 @@
+drop table if exists t1, t2, t3,t4;
+create table t1 (
+pk1 int not NULL,
+key1 int(11),
+key2 int(11),
+PRIMARY KEY (pk1),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=innodb;
+insert into t1 values (-5, 1, 1),
+(-100, 1, 1),
+(3, 1, 1),
+(0, 1, 1),
+(10, 1, 1);
+explain select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index_merge key1,key2 key1,key2 5,5 NULL 4 Using sort_union(key1,key2); Using where
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 key1 key2
+-100 1 1
+-5 1 1
+0 1 1
+3 1 1
+10 1 1
+drop table t1;
+create table t1 (
+pk1 int unsigned not NULL,
+key1 int(11),
+key2 int(11),
+PRIMARY KEY (pk1),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=innodb;
+insert into t1 values (0, 1, 1),
+(0xFFFFFFFF, 1, 1),
+(0xFFFFFFFE, 1, 1),
+(1, 1, 1),
+(2, 1, 1);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 key1 key2
+0 1 1
+1 1 1
+2 1 1
+4294967294 1 1
+4294967295 1 1
+drop table t1;
+create table t1 (
+pk1 char(4) not NULL,
+key1 int(11),
+key2 int(11),
+PRIMARY KEY (pk1),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=innodb collate latin2_general_ci;
+insert into t1 values ('a1', 1, 1),
+('b2', 1, 1),
+('A3', 1, 1),
+('B4', 1, 1);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 key1 key2
+a1 1 1
+A3 1 1
+b2 1 1
+B4 1 1
+drop table t1;
+create table t1 (
+pk1 int not NULL,
+pk2 char(4) not NULL collate latin1_german1_ci,
+pk3 char(4) not NULL collate latin1_bin,
+key1 int(11),
+key2 int(11),
+PRIMARY KEY (pk1,pk2,pk3),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=innodb;
+insert into t1 values
+(1, 'u', 'u', 1, 1),
+(1, 'u', char(0xEC), 1, 1),
+(1, 'u', 'x', 1, 1);
+insert ignore into t1 select pk1, char(0xEC), pk3, key1, key2 from t1;
+insert ignore into t1 select pk1, 'x', pk3, key1, key2 from t1 where pk2='u';
+insert ignore into t1 select 2, pk2, pk3, key1, key2 from t1;
+select * from t1;
+pk1 pk2 pk3 key1 key2
+1 ì u 1 1
+1 ì x 1 1
+1 ì ì 1 1
+1 u u 1 1
+1 u x 1 1
+1 u ì 1 1
+1 x u 1 1
+1 x x 1 1
+1 x ì 1 1
+2 ì u 1 1
+2 ì x 1 1
+2 ì ì 1 1
+2 u u 1 1
+2 u x 1 1
+2 u ì 1 1
+2 x u 1 1
+2 x x 1 1
+2 x ì 1 1
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 pk2 pk3 key1 key2
+1 ì u 1 1
+1 ì x 1 1
+1 ì ì 1 1
+1 u u 1 1
+1 u x 1 1
+1 u ì 1 1
+1 x u 1 1
+1 x x 1 1
+1 x ì 1 1
+2 ì u 1 1
+2 ì x 1 1
+2 ì ì 1 1
+2 u u 1 1
+2 u x 1 1
+2 u ì 1 1
+2 x u 1 1
+2 x x 1 1
+2 x ì 1 1
+alter table t1 drop primary key;
+select * from t1;
+pk1 pk2 pk3 key1 key2
+1 ì u 1 1
+1 ì x 1 1
+1 ì ì 1 1
+1 u u 1 1
+1 u x 1 1
+1 u ì 1 1
+1 x u 1 1
+1 x x 1 1
+1 x ì 1 1
+2 ì u 1 1
+2 ì x 1 1
+2 ì ì 1 1
+2 u u 1 1
+2 u x 1 1
+2 u ì 1 1
+2 x u 1 1
+2 x x 1 1
+2 x ì 1 1
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 pk2 pk3 key1 key2
+1 ì u 1 1
+1 ì x 1 1
+1 ì ì 1 1
+1 u u 1 1
+1 u x 1 1
+1 u ì 1 1
+1 x u 1 1
+1 x x 1 1
+1 x ì 1 1
+2 ì u 1 1
+2 ì x 1 1
+2 ì ì 1 1
+2 u u 1 1
+2 u x 1 1
+2 u ì 1 1
+2 x u 1 1
+2 x x 1 1
+2 x ì 1 1
+drop table t1;
+create table t1 (
+pk1 varchar(8) NOT NULL default '',
+pk2 varchar(4) NOT NULL default '',
+key1 int(11),
+key2 int(11),
+primary key(pk1, pk2),
+KEY key1 (key1),
+KEY key2 (key2)
+) engine=innodb;
+insert into t1 values ('','empt',2,2),
+('a','a--a',2,2),
+('bb','b--b',2,2),
+('ccc','c--c',2,2),
+('dddd','d--d',2,2);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+pk1 pk2 key1 key2
+ empt 2 2
+a a--a 2 2
+bb b--b 2 2
+ccc c--c 2 2
+dddd d--d 2 2
+drop table t1;
diff --git a/mysql-test/r/rpl000009.result b/mysql-test/r/rpl000009.result
index bb82dcb1e6a..4258f43b67e 100644
--- a/mysql-test/r/rpl000009.result
+++ b/mysql-test/r/rpl000009.result
@@ -73,25 +73,25 @@ mysqltest3
test
use mysqltest2;
show tables;
-Tables_in_mysqltest2
-t1
-t3
+Tables_in_mysqltest2 table_type
+t1 BASE TABLE
+t3 BASE TABLE
select * from t1;
n s
1 original foo.t1
use mysqltest3;
show tables;
-Tables_in_mysqltest3
-t1
+Tables_in_mysqltest3 table_type
+t1 BASE TABLE
select * from t1;
n s
1 original foo2.t1
use mysqltest;
show tables;
-Tables_in_mysqltest
-t1
-t2
-t3
+Tables_in_mysqltest table_type
+t1 BASE TABLE
+t2 BASE TABLE
+t3 BASE TABLE
select * from mysqltest.t1;
n s
1 one test
diff --git a/mysql-test/r/rpl000015.result b/mysql-test/r/rpl000015.result
index 8cbbe3ab0e8..38544c0aed7 100644
--- a/mysql-test/r/rpl000015.result
+++ b/mysql-test/r/rpl000015.result
@@ -1,23 +1,23 @@
reset master;
show master status;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 79
+master-bin.000001 95
reset slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
change master to master_host='127.0.0.1';
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 test MASTER_PORT 7 4 slave-relay-bin.000001 4 No No 0 0 0 4 None 0 No #
+# 127.0.0.1 test MASTER_PORT 7 4 # # No No 0 0 0 # None 0 No #
change master to master_host='127.0.0.1',master_user='root',
master_password='',master_port=MASTER_PORT;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 7 4 slave-relay-bin.000001 4 No No 0 0 0 4 None 0 No #
+# 127.0.0.1 root MASTER_PORT 7 4 # # No No 0 0 0 # None 0 No #
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 7 master-bin.000001 79 slave-relay-bin.000001 123 master-bin.000001 Yes Yes 0 0 79 123 None 0 No #
+# 127.0.0.1 root MASTER_PORT 7 master-bin.000001 95 # # master-bin.000001 Yes Yes 0 0 95 # None 0 No #
drop table if exists t1;
create table t1 (n int);
insert into t1 values (10),(45),(90);
diff --git a/mysql-test/r/rpl_change_master.result b/mysql-test/r/rpl_change_master.result
index a6342d47b49..40bcc4100a8 100644
--- a/mysql-test/r/rpl_change_master.result
+++ b/mysql-test/r/rpl_change_master.result
@@ -16,11 +16,11 @@ n
1
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 273 slave-relay-bin.000002 258 master-bin.000001 No No 0 0 214 317 None 0 No #
+# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 355 # # master-bin.000001 No No 0 0 274 # None 0 No #
change master to master_user='root';
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 214 slave-relay-bin.000001 4 master-bin.000001 No No 0 0 214 4 None 0 No #
+# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 274 # # master-bin.000001 No No 0 0 274 # None 0 No #
select release_lock("a");
release_lock("a")
1
diff --git a/mysql-test/r/rpl_charset.result b/mysql-test/r/rpl_charset.result
index a60c9269625..d961173f849 100644
--- a/mysql-test/r/rpl_charset.result
+++ b/mysql-test/r/rpl_charset.result
@@ -103,64 +103,64 @@ a b
1 cp850_general_ci
drop database mysqltest2;
drop database mysqltest3;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; drop database if exists mysqltest2
-master-bin.000001 148 Query 1 148 use `test`; drop database if exists mysqltest3
-master-bin.000001 217 Query 1 217 use `test`; create database mysqltest2 character set latin2
-master-bin.000001 299 Query 1 299 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=30
-master-bin.000001 433 Query 1 433 use `test`; create database mysqltest3
-master-bin.000001 494 Query 1 494 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=64
-master-bin.000001 628 Query 1 628 use `test`; drop database mysqltest3
-master-bin.000001 687 Query 1 687 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=64
-master-bin.000001 821 Query 1 821 use `test`; create database mysqltest3
-master-bin.000001 882 Query 1 882 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 1022 Query 1 1022 use `mysqltest2`; create table t1 (a int auto_increment primary key, b varchar(100))
-master-bin.000001 1129 Query 1 1129 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 1270 Intvar 1 1270 INSERT_ID=1
-master-bin.000001 1298 Query 1 1298 use `mysqltest2`; insert into t1 (b) values(@@character_set_server)
-master-bin.000001 1388 Query 1 1388 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 1529 Intvar 1 1529 INSERT_ID=2
-master-bin.000001 1557 Query 1 1557 use `mysqltest2`; insert into t1 (b) values(@@collation_server)
-master-bin.000001 1643 Query 1 1643 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 1784 Intvar 1 1784 INSERT_ID=3
-master-bin.000001 1812 Query 1 1812 use `mysqltest2`; insert into t1 (b) values(@@character_set_client)
-master-bin.000001 1902 Query 1 1902 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 2043 Intvar 1 2043 INSERT_ID=4
-master-bin.000001 2071 Query 1 2071 use `mysqltest2`; insert into t1 (b) values(@@character_set_connection)
-master-bin.000001 2165 Query 1 2165 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 2306 Intvar 1 2306 INSERT_ID=5
-master-bin.000001 2334 Query 1 2334 use `mysqltest2`; insert into t1 (b) values(@@collation_connection)
-master-bin.000001 2424 Query 1 2424 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 2564 Query 1 2564 use `mysqltest2`; truncate table t1
-master-bin.000001 2622 Query 1 2622 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 2762 Intvar 1 2762 INSERT_ID=1
-master-bin.000001 2790 Query 1 2790 use `mysqltest2`; insert into t1 (b) values(@@collation_connection)
-master-bin.000001 2880 Query 1 2880 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 3020 Intvar 1 3020 INSERT_ID=2
-master-bin.000001 3048 Query 1 3048 use `mysqltest2`; insert into t1 (b) values(LEAST("Müller","Muffler"))
-master-bin.000001 3141 Query 1 3141 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 3282 Intvar 1 3282 INSERT_ID=3
-master-bin.000001 3310 Query 1 3310 use `mysqltest2`; insert into t1 (b) values(@@collation_connection)
-master-bin.000001 3400 Query 1 3400 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 3541 Intvar 1 3541 INSERT_ID=4
-master-bin.000001 3569 Query 1 3569 use `mysqltest2`; insert into t1 (b) values(LEAST("Müller","Muffler"))
-master-bin.000001 3662 Query 1 3662 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 3803 Intvar 1 3803 INSERT_ID=74
-master-bin.000001 3831 Create_file 1 3831 db=mysqltest2;table=t1;file_id=1;block_len=581
-master-bin.000001 4504 Query 1 4504 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 4645 Intvar 1 4645 INSERT_ID=5
-master-bin.000001 4673 Exec_load 1 4673 ;file_id=1
-master-bin.000001 4696 Query 1 4696 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 4837 Query 1 4837 use `mysqltest2`; truncate table t1
-master-bin.000001 4895 Query 1 4895 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 5036 Intvar 1 5036 INSERT_ID=1
-master-bin.000001 5064 User var 1 5064 @`a`=_cp850 0x4DFC6C6C6572 COLLATE cp850_general_ci
-master-bin.000001 5104 Query 1 5104 use `mysqltest2`; insert into t1 (b) values(collation(@a))
-master-bin.000001 5185 Query 1 5185 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 5326 Query 1 5326 use `mysqltest2`; drop database mysqltest2
-master-bin.000001 5391 Query 1 5391 SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
-master-bin.000001 5522 Query 1 5522 drop database mysqltest3
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 181 use `test`; drop database if exists mysqltest2
+master-bin.000001 181 Query 1 267 use `test`; drop database if exists mysqltest3
+master-bin.000001 267 Query 1 366 use `test`; create database mysqltest2 character set latin2
+master-bin.000001 366 Query 1 522 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=30
+master-bin.000001 522 Query 1 600 use `test`; create database mysqltest3
+master-bin.000001 600 Query 1 756 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=64
+master-bin.000001 756 Query 1 832 use `test`; drop database mysqltest3
+master-bin.000001 832 Query 1 988 use `test`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=8,COLLATION_SERVER=64
+master-bin.000001 988 Query 1 1066 use `test`; create database mysqltest3
+master-bin.000001 1066 Query 1 1223 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=8,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 1223 Query 1 1347 use `mysqltest2`; create table t1 (a int auto_increment primary key, b varchar(100))
+master-bin.000001 1347 Query 1 1505 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 1505 Intvar 1 1533 INSERT_ID=1
+master-bin.000001 1533 Query 1 1640 use `mysqltest2`; insert into t1 (b) values(@@character_set_server)
+master-bin.000001 1640 Query 1 1798 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 1798 Intvar 1 1826 INSERT_ID=2
+master-bin.000001 1826 Query 1 1929 use `mysqltest2`; insert into t1 (b) values(@@collation_server)
+master-bin.000001 1929 Query 1 2087 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 2087 Intvar 1 2115 INSERT_ID=3
+master-bin.000001 2115 Query 1 2222 use `mysqltest2`; insert into t1 (b) values(@@character_set_client)
+master-bin.000001 2222 Query 1 2380 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 2380 Intvar 1 2408 INSERT_ID=4
+master-bin.000001 2408 Query 1 2519 use `mysqltest2`; insert into t1 (b) values(@@character_set_connection)
+master-bin.000001 2519 Query 1 2677 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=4,COLLATION_CONNECTION=27,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 2677 Intvar 1 2705 INSERT_ID=5
+master-bin.000001 2705 Query 1 2812 use `mysqltest2`; insert into t1 (b) values(@@collation_connection)
+master-bin.000001 2812 Query 1 2969 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 2969 Query 1 3044 use `mysqltest2`; truncate table t1
+master-bin.000001 3044 Query 1 3201 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 3201 Intvar 1 3229 INSERT_ID=1
+master-bin.000001 3229 Query 1 3336 use `mysqltest2`; insert into t1 (b) values(@@collation_connection)
+master-bin.000001 3336 Query 1 3493 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=5,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 3493 Intvar 1 3521 INSERT_ID=2
+master-bin.000001 3521 Query 1 3631 use `mysqltest2`; insert into t1 (b) values(LEAST("Müller","Muffler"))
+master-bin.000001 3631 Query 1 3789 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 3789 Intvar 1 3817 INSERT_ID=3
+master-bin.000001 3817 Query 1 3924 use `mysqltest2`; insert into t1 (b) values(@@collation_connection)
+master-bin.000001 3924 Query 1 4082 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 4082 Intvar 1 4110 INSERT_ID=4
+master-bin.000001 4110 Query 1 4220 use `mysqltest2`; insert into t1 (b) values(LEAST("Müller","Muffler"))
+master-bin.000001 4220 Query 1 4378 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 4378 Intvar 1 4406 INSERT_ID=74
+master-bin.000001 4406 Create_file 1 5074 db=mysqltest2;table=t1;file_id=1;block_len=581
+master-bin.000001 5074 Query 1 5232 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 5232 Intvar 1 5260 INSERT_ID=5
+master-bin.000001 5260 Exec_load 1 5283 ;file_id=1
+master-bin.000001 5283 Query 1 5441 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 5441 Query 1 5516 use `mysqltest2`; truncate table t1
+master-bin.000001 5516 Query 1 5674 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 5674 Intvar 1 5702 INSERT_ID=1
+master-bin.000001 5702 User var 1 5742 @`a`=_cp850 0x4DFC6C6C6572 COLLATE cp850_general_ci
+master-bin.000001 5742 Query 1 5840 use `mysqltest2`; insert into t1 (b) values(collation(@a))
+master-bin.000001 5840 Query 1 5998 use `mysqltest2`; SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 5998 Query 1 6075 use `mysqltest2`; drop database mysqltest2
+master-bin.000001 6075 Query 1 6228 SET ONE_SHOT CHARACTER_SET_CLIENT=8,COLLATION_CONNECTION=31,COLLATION_DATABASE=9,COLLATION_SERVER=64
+master-bin.000001 6228 Query 1 6300 drop database mysqltest3
set global character_set_server=latin2;
ERROR HY000: Binary logging and replication forbid changing the global server character set or collation
set global character_set_server=latin2;
diff --git a/mysql-test/r/rpl_error_ignored_table.result b/mysql-test/r/rpl_error_ignored_table.result
index 4a562dbfc70..2f87f7ddece 100644
--- a/mysql-test/r/rpl_error_ignored_table.result
+++ b/mysql-test/r/rpl_error_ignored_table.result
@@ -9,9 +9,9 @@ insert into t1 values (1),(1);
ERROR 23000: Duplicate entry '1' for key 1
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 213 slave-relay-bin.000002 257 master-bin.000001 Yes Yes test.t3,test.t1,test.t2 0 0 213 257 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 273 # # master-bin.000001 Yes Yes test.t3,test.t1,test.t2 0 0 273 # None 0 No #
show tables like 't1';
-Tables_in_test (t1)
+Tables_in_test (t1) table_type
drop table t1;
select get_lock('crash_lock%20C', 10);
get_lock('crash_lock%20C', 10)
@@ -26,14 +26,14 @@ select (@id := id) - id from t3;
0
kill @id;
drop table t2,t3;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; create table t1 (a int primary key)
-master-bin.000001 149 Query 1 149 use `test`; insert into t1 values (1),(1)
-master-bin.000001 213 Query 1 213 use `test`; drop table t1
-master-bin.000001 261 Query 1 261 use `test`; create table t2 (a int primary key)
-master-bin.000001 331 Query 1 331 use `test`; insert into t2 values(1)
-master-bin.000001 390 Query 1 390 use `test`; create table t3 (id int)
-master-bin.000001 449 Query 1 449 use `test`; insert into t3 values(connection_id())
-master-bin.000001 522 Query 1 522 use `test`; update t2 set a = a + 1 + get_lock('crash_lock%20C', 10)
-master-bin.000001 613 Query 1 613 use `test`; drop table t2,t3
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 187 use `test`; create table t1 (a int primary key)
+master-bin.000001 187 Query 1 273 use `test`; insert into t1 values (1),(1)
+master-bin.000001 273 Query 1 343 use `test`; drop table t1
+master-bin.000001 343 Query 1 435 use `test`; create table t2 (a int primary key)
+master-bin.000001 435 Query 1 516 use `test`; insert into t2 values(1)
+master-bin.000001 516 Query 1 597 use `test`; create table t3 (id int)
+master-bin.000001 597 Query 1 692 use `test`; insert into t3 values(connection_id())
+master-bin.000001 692 Query 1 805 use `test`; update t2 set a = a + 1 + get_lock('crash_lock%20C', 10)
+master-bin.000001 805 Query 1 878 use `test`; drop table t2,t3
diff --git a/mysql-test/r/rpl_flush_log_loop.result b/mysql-test/r/rpl_flush_log_loop.result
index 25177a6bca3..4b73458386d 100644
--- a/mysql-test/r/rpl_flush_log_loop.result
+++ b/mysql-test/r/rpl_flush_log_loop.result
@@ -14,4 +14,4 @@ start slave;
flush logs;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root SLAVE_PORT 60 slave-bin.000001 161 relay-log.000002 4 slave-bin.000001 Yes Yes 0 0 161 4 None 0 No #
+# 127.0.0.1 root SLAVE_PORT 60 slave-bin.000001 199 # # slave-bin.000001 Yes Yes 0 0 199 # None 0 No #
diff --git a/mysql-test/r/rpl_flush_tables.result b/mysql-test/r/rpl_flush_tables.result
index 70e4774a920..ef785bc9850 100644
--- a/mysql-test/r/rpl_flush_tables.result
+++ b/mysql-test/r/rpl_flush_tables.result
@@ -13,28 +13,28 @@ insert into t4 select * from t3;
rename table t1 to t5, t2 to t1;
flush no_write_to_binlog tables;
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: SERVER_VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; create table t1 (a int)
-master-bin.000001 137 Query 1 137 use `test`; insert into t1 values (10)
-master-bin.000001 198 Query 1 198 use `test`; create table t2 (a int)
-master-bin.000001 256 Query 1 256 use `test`; create table t3 (a int) engine=merge union(t1)
-master-bin.000001 337 Query 1 337 use `test`; create table t4 (a int)
-master-bin.000001 395 Query 1 395 use `test`; insert into t4 select * from t3
-master-bin.000001 461 Query 1 461 use `test`; rename table t1 to t5, t2 to t1
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: SERVER_VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 175 use `test`; create table t1 (a int)
+master-bin.000001 175 Query 1 258 use `test`; insert into t1 values (10)
+master-bin.000001 258 Query 1 338 use `test`; create table t2 (a int)
+master-bin.000001 338 Query 1 441 use `test`; create table t3 (a int) engine=merge union(t1)
+master-bin.000001 441 Query 1 521 use `test`; create table t4 (a int)
+master-bin.000001 521 Query 1 609 use `test`; insert into t4 select * from t3
+master-bin.000001 609 Query 1 697 use `test`; rename table t1 to t5, t2 to t1
select * from t3;
a
flush tables;
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: SERVER_VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; create table t1 (a int)
-master-bin.000001 137 Query 1 137 use `test`; insert into t1 values (10)
-master-bin.000001 198 Query 1 198 use `test`; create table t2 (a int)
-master-bin.000001 256 Query 1 256 use `test`; create table t3 (a int) engine=merge union(t1)
-master-bin.000001 337 Query 1 337 use `test`; create table t4 (a int)
-master-bin.000001 395 Query 1 395 use `test`; insert into t4 select * from t3
-master-bin.000001 461 Query 1 461 use `test`; rename table t1 to t5, t2 to t1
-master-bin.000001 527 Query 1 527 use `test`; flush tables
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: SERVER_VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 175 use `test`; create table t1 (a int)
+master-bin.000001 175 Query 1 258 use `test`; insert into t1 values (10)
+master-bin.000001 258 Query 1 338 use `test`; create table t2 (a int)
+master-bin.000001 338 Query 1 441 use `test`; create table t3 (a int) engine=merge union(t1)
+master-bin.000001 441 Query 1 521 use `test`; create table t4 (a int)
+master-bin.000001 521 Query 1 609 use `test`; insert into t4 select * from t3
+master-bin.000001 609 Query 1 697 use `test`; rename table t1 to t5, t2 to t1
+master-bin.000001 697 Query 1 766 use `test`; flush tables
select * from t3;
a
diff --git a/mysql-test/r/rpl_loaddata.result b/mysql-test/r/rpl_loaddata.result
index 65fc9d1b415..72f7fb8bdbe 100644
--- a/mysql-test/r/rpl_loaddata.result
+++ b/mysql-test/r/rpl_loaddata.result
@@ -22,7 +22,7 @@ day id category name
2003-03-22 2416 a bbbbb
show master status;
File Position Binlog_Do_DB Binlog_Ignore_DB
-slave-bin.000001 964
+slave-bin.000001 1068
drop table t1;
drop table t2;
drop table t3;
@@ -33,7 +33,7 @@ set global sql_slave_skip_counter=1;
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1311 slave-relay-bin.000002 1355 master-bin.000001 Yes Yes 0 0 1311 1355 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1503 # # master-bin.000001 Yes Yes 0 0 1503 # None 0 No #
set sql_log_bin=0;
delete from t1;
set sql_log_bin=1;
@@ -43,7 +43,7 @@ change master to master_user='test';
change master to master_user='root';
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1419 slave-relay-bin.000001 4 master-bin.000001 No No 0 0 1419 4 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1611 # # master-bin.000001 No No 0 0 1611 # None 0 No #
set global sql_slave_skip_counter=1;
start slave;
set sql_log_bin=0;
@@ -54,7 +54,7 @@ stop slave;
reset slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 4 slave-relay-bin.000001 4 No No 0 0 0 4 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 4 # # No No 0 0 0 # None 0 No #
reset master;
create table t2 (day date,id int(9),category enum('a','b','c'),name varchar(60),
unique(day));
@@ -64,5 +64,5 @@ terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by
ERROR 23000: Duplicate entry '2003-03-22' for key 1
show master status;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 491
+master-bin.000001 529
drop table t2;
diff --git a/mysql-test/r/rpl_loaddata_rule_m.result b/mysql-test/r/rpl_loaddata_rule_m.result
index a34453b0a2b..1b7ea5661fb 100644
--- a/mysql-test/r/rpl_loaddata_rule_m.result
+++ b/mysql-test/r/rpl_loaddata_rule_m.result
@@ -10,6 +10,6 @@ create database mysqltest;
create table t1(a int, b int, unique(b));
use mysqltest;
load data infile '../../std_data/rpl_loaddata.dat' into table test.t1;
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
drop database mysqltest;
diff --git a/mysql-test/r/rpl_loaddata_rule_s.result b/mysql-test/r/rpl_loaddata_rule_s.result
index 26893cb1e9e..d7a24410f9c 100644
--- a/mysql-test/r/rpl_loaddata_rule_s.result
+++ b/mysql-test/r/rpl_loaddata_rule_s.result
@@ -10,5 +10,5 @@ load data infile '../../std_data/rpl_loaddata.dat' into table test.t1;
select count(*) from t1;
count(*)
2
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
diff --git a/mysql-test/r/rpl_log.result b/mysql-test/r/rpl_log.result
index 2f8a54369c9..784742fdacb 100644
--- a/mysql-test/r/rpl_log.result
+++ b/mysql-test/r/rpl_log.result
@@ -18,26 +18,26 @@ count(*)
69
drop table t1;
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; create table t1(n int not null auto_increment primary key)
-master-bin.000001 172 Intvar 1 172 INSERT_ID=1
-master-bin.000001 200 Query 1 200 use `test`; insert into t1 values (NULL)
-master-bin.000001 263 Query 1 263 use `test`; drop table t1
-master-bin.000001 311 Query 1 311 use `test`; create table t1 (word char(20) not null)
-master-bin.000001 386 Create_file 1 386 db=test;table=t1;file_id=1;block_len=581
-master-bin.000001 1056 Exec_load 1 1056 ;file_id=1
-master-bin.000001 1079 Query 1 1079 use `test`; drop table t1
-show binlog events from 79 limit 1;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; create table t1(n int not null auto_increment primary key)
-show binlog events from 79 limit 2;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 Query 1 79 use `test`; create table t1(n int not null auto_increment primary key)
-master-bin.000001 172 Intvar 1 172 INSERT_ID=1
-show binlog events from 79 limit 2,1;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 200 Query 1 200 use `test`; insert into t1 values (NULL)
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 210 use `test`; create table t1(n int not null auto_increment primary key)
+master-bin.000001 210 Intvar 1 238 INSERT_ID=1
+master-bin.000001 238 Query 1 323 use `test`; insert into t1 values (NULL)
+master-bin.000001 323 Query 1 393 use `test`; drop table t1
+master-bin.000001 393 Query 1 490 use `test`; create table t1 (word char(20) not null)
+master-bin.000001 490 Create_file 1 1160 db=test;table=t1;file_id=1;block_len=581
+master-bin.000001 1160 Exec_load 1 1183 ;file_id=1
+master-bin.000001 1183 Query 1 1253 use `test`; drop table t1
+show binlog events from 95 limit 1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 210 use `test`; create table t1(n int not null auto_increment primary key)
+show binlog events from 95 limit 2;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 Query 1 210 use `test`; create table t1(n int not null auto_increment primary key)
+master-bin.000001 210 Intvar 1 238 INSERT_ID=1
+show binlog events from 95 limit 2,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 238 Query 1 323 use `test`; insert into t1 values (NULL)
flush logs;
create table t5 (a int);
drop table t5;
@@ -48,24 +48,25 @@ create table t1 (n int);
insert into t1 values (1);
drop table t1;
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; create table t1(n int not null auto_increment primary key)
-master-bin.000001 172 Intvar 1 172 INSERT_ID=1
-master-bin.000001 200 Query 1 200 use `test`; insert into t1 values (NULL)
-master-bin.000001 263 Query 1 263 use `test`; drop table t1
-master-bin.000001 311 Query 1 311 use `test`; create table t1 (word char(20) not null)
-master-bin.000001 386 Create_file 1 386 db=test;table=t1;file_id=1;block_len=581
-master-bin.000001 1056 Exec_load 1 1056 ;file_id=1
-master-bin.000001 1079 Query 1 1079 use `test`; drop table t1
-master-bin.000001 1127 Rotate 1 1127 master-bin.000002;pos=4
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 210 use `test`; create table t1(n int not null auto_increment primary key)
+master-bin.000001 210 Intvar 1 238 INSERT_ID=1
+master-bin.000001 238 Query 1 323 use `test`; insert into t1 values (NULL)
+master-bin.000001 323 Query 1 393 use `test`; drop table t1
+master-bin.000001 393 Query 1 490 use `test`; create table t1 (word char(20) not null)
+master-bin.000001 490 Create_file 1 1160 db=test;table=t1;file_id=1;block_len=581
+master-bin.000001 1160 Exec_load 1 1183 ;file_id=1
+master-bin.000001 1183 Query 1 1253 use `test`; drop table t1
+master-bin.000001 1253 Rotate 1 1297 master-bin.000002;pos=4
show binlog events in 'master-bin.000002';
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000002 4 Query 1 4 use `test`; create table t5 (a int)
-master-bin.000002 62 Query 1 62 use `test`; drop table t5
-master-bin.000002 110 Query 1 110 use `test`; create table t1 (n int)
-master-bin.000002 168 Query 1 168 use `test`; insert into t1 values (1)
-master-bin.000002 228 Query 1 228 use `test`; drop table t1
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000002 4 Format_desc 1 95 Server ver: VERSION, Binlog ver: 4
+master-bin.000002 95 Query 1 175 use `test`; create table t5 (a int)
+master-bin.000002 175 Query 1 245 use `test`; drop table t5
+master-bin.000002 245 Query 1 325 use `test`; create table t1 (n int)
+master-bin.000002 325 Query 1 407 use `test`; insert into t1 values (1)
+master-bin.000002 407 Query 1 477 use `test`; drop table t1
show binary logs;
Log_name
master-bin.000001
@@ -76,26 +77,27 @@ Log_name
slave-bin.000001
slave-bin.000002
show binlog events in 'slave-bin.000001' from 4;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-slave-bin.000001 4 Start 2 4 Server ver: VERSION, Binlog ver: 3
-slave-bin.000001 79 Query 1 79 use `test`; create table t1(n int not null auto_increment primary key)
-slave-bin.000001 172 Intvar 1 172 INSERT_ID=1
-slave-bin.000001 200 Query 1 200 use `test`; insert into t1 values (NULL)
-slave-bin.000001 263 Query 1 263 use `test`; drop table t1
-slave-bin.000001 311 Query 1 311 use `test`; create table t1 (word char(20) not null)
-slave-bin.000001 386 Create_file 1 386 db=test;table=t1;file_id=1;block_len=581
-slave-bin.000001 1065 Exec_load 1 1065 ;file_id=1
-slave-bin.000001 1088 Query 1 1088 use `test`; drop table t1
-slave-bin.000001 1136 Query 1 1136 use `test`; create table t5 (a int)
-slave-bin.000001 1194 Query 1 1194 use `test`; drop table t5
-slave-bin.000001 1242 Rotate 2 1242 slave-bin.000002;pos=4
+Log_name Pos Event_type Server_id End_log_pos Info
+slave-bin.000001 4 Format_desc 2 95 Server ver: VERSION, Binlog ver: 4
+slave-bin.000001 95 Query 1 210 use `test`; create table t1(n int not null auto_increment primary key)
+slave-bin.000001 210 Intvar 1 238 INSERT_ID=1
+slave-bin.000001 238 Query 1 323 use `test`; insert into t1 values (NULL)
+slave-bin.000001 323 Query 1 393 use `test`; drop table t1
+slave-bin.000001 393 Query 1 490 use `test`; create table t1 (word char(20) not null)
+slave-bin.000001 490 Create_file 1 1169 db=test;table=t1;file_id=1;block_len=581
+slave-bin.000001 1169 Exec_load 1 1192 ;file_id=1
+slave-bin.000001 1192 Query 1 1262 use `test`; drop table t1
+slave-bin.000001 1262 Query 1 1342 use `test`; create table t5 (a int)
+slave-bin.000001 1342 Query 1 1412 use `test`; drop table t5
+slave-bin.000001 1412 Rotate 2 1455 slave-bin.000002;pos=4
show binlog events in 'slave-bin.000002' from 4;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-slave-bin.000002 4 Query 1 4 use `test`; create table t1 (n int)
-slave-bin.000002 62 Query 1 62 use `test`; insert into t1 values (1)
-slave-bin.000002 122 Query 1 122 use `test`; drop table t1
+Log_name Pos Event_type Server_id End_log_pos Info
+slave-bin.000002 4 Format_desc 2 95 Server ver: VERSION, Binlog ver: 4
+slave-bin.000002 95 Query 1 175 use `test`; create table t1 (n int)
+slave-bin.000002 175 Query 1 257 use `test`; insert into t1 values (1)
+slave-bin.000002 257 Query 1 327 use `test`; drop table t1
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000002 276 slave-relay-bin.000003 214 master-bin.000002 Yes Yes 0 0 276 214 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000002 477 # # master-bin.000002 Yes Yes 0 0 477 # None 0 No #
show binlog events in 'slave-bin.000005' from 4;
ERROR HY000: Error when executing command SHOW BINLOG EVENTS: Could not find target log
diff --git a/mysql-test/r/rpl_log_pos.result b/mysql-test/r/rpl_log_pos.result
index 10c78272de6..6ea07cf7173 100644
--- a/mysql-test/r/rpl_log_pos.result
+++ b/mysql-test/r/rpl_log_pos.result
@@ -6,10 +6,10 @@ drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
start slave;
show master status;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 79
+master-bin.000001 95
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 79 slave-relay-bin.000002 123 master-bin.000001 Yes Yes 0 0 79 123 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 95 # # master-bin.000001 Yes Yes 0 0 95 # None 0 No #
stop slave;
change master to master_log_pos=73;
start slave;
@@ -17,26 +17,26 @@ stop slave;
change master to master_log_pos=73;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 73 slave-relay-bin.000001 4 master-bin.000001 No No 0 0 73 4 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 73 # # master-bin.000001 No No 0 0 73 # None 0 No #
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 73 slave-relay-bin.000001 48 master-bin.000001 No Yes 0 0 73 48 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 73 # # master-bin.000001 No Yes 0 0 73 # None 0 No #
stop slave;
change master to master_log_pos=173;
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 173 slave-relay-bin.000001 4 master-bin.000001 No Yes 0 0 173 4 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 173 # # master-bin.000001 No Yes 0 0 173 # None 0 No #
show master status;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 79
+master-bin.000001 95
create table if not exists t1 (n int);
drop table if exists t1;
create table t1 (n int);
insert into t1 values (1),(2),(3);
stop slave;
-change master to master_log_pos=79;
+change master to master_log_pos=95;
start slave;
select * from t1;
n
diff --git a/mysql-test/r/rpl_max_relay_size.result b/mysql-test/r/rpl_max_relay_size.result
index 5c3360b0f66..c1589687eee 100644
--- a/mysql-test/r/rpl_max_relay_size.result
+++ b/mysql-test/r/rpl_max_relay_size.result
@@ -16,7 +16,7 @@ select @@global.max_relay_log_size;
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 50477 slave-relay-bin.000014 1221 master-bin.000001 Yes Yes 0 0 50477 1221 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 68137 # # master-bin.000001 Yes Yes 0 0 68137 # None 0 No #
stop slave;
reset slave;
set global max_relay_log_size=(5*4096);
@@ -26,7 +26,7 @@ select @@global.max_relay_log_size;
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 50477 slave-relay-bin.000004 9457 master-bin.000001 Yes Yes 0 0 50477 9457 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 68137 # # master-bin.000001 Yes Yes 0 0 68137 # None 0 No #
stop slave;
reset slave;
set global max_relay_log_size=0;
@@ -36,26 +36,26 @@ select @@global.max_relay_log_size;
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 50477 slave-relay-bin.000008 1283 master-bin.000001 Yes Yes 0 0 50477 1283 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 68137 # # master-bin.000001 Yes Yes 0 0 68137 # None 0 No #
stop slave;
reset slave;
flush logs;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 4 slave-relay-bin.000001 4 No No 0 0 0 4 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 4 # # No No 0 0 0 # None 0 No #
reset slave;
start slave;
flush logs;
create table t1 (a int);
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 50535 slave-relay-bin.000009 62 master-bin.000001 Yes Yes 0 0 50535 62 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 68217 # # master-bin.000001 Yes Yes 0 0 68217 # None 0 No #
flush logs;
drop table t1;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 50583 slave-relay-bin.000010 52 master-bin.000001 Yes Yes 0 0 50583 52 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 68287 # # master-bin.000001 Yes Yes 0 0 68287 # None 0 No #
flush logs;
show master status;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000002 4
+master-bin.000002 95
diff --git a/mysql-test/r/rpl_relayrotate.result b/mysql-test/r/rpl_relayrotate.result
index bf9bbbb9b93..bd6e10409b9 100644
--- a/mysql-test/r/rpl_relayrotate.result
+++ b/mysql-test/r/rpl_relayrotate.result
@@ -13,7 +13,10 @@ start slave;
select master_pos_wait('master-bin.001',3000)>=0;
master_pos_wait('master-bin.001',3000)>=0
1
-select * from t1 where a=8000;
-a
+select max(a) from t1;
+max(a)
8000
+show slave status;
+Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
+# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 687207 # # master-bin.000001 Yes Yes 0 0 687207 # None 0 No #
drop table t1;
diff --git a/mysql-test/r/rpl_replicate_do.result b/mysql-test/r/rpl_replicate_do.result
index ca290d46fda..5cdcb6f30fb 100644
--- a/mysql-test/r/rpl_replicate_do.result
+++ b/mysql-test/r/rpl_replicate_do.result
@@ -28,4 +28,4 @@ ERROR 42S02: Table 'test.t11' doesn't exist
drop table if exists t1,t2,t11;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1340 slave-relay-bin.000002 1384 master-bin.000001 Yes Yes test.t1 0 0 1340 1384 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1554 # # master-bin.000001 Yes Yes test.t1 0 0 1554 # None 0 No #
diff --git a/mysql-test/r/rpl_reset_slave.result b/mysql-test/r/rpl_reset_slave.result
index 42d41533cb0..6161f2112db 100644
--- a/mysql-test/r/rpl_reset_slave.result
+++ b/mysql-test/r/rpl_reset_slave.result
@@ -6,20 +6,20 @@ drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 79 slave-relay-bin.000002 123 master-bin.000001 Yes Yes 0 0 79 123 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 95 # # master-bin.000001 Yes Yes 0 0 95 # None 0 No #
stop slave;
change master to master_user='test';
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 test MASTER_PORT 1 master-bin.000001 79 slave-relay-bin.000001 4 master-bin.000001 No No 0 0 79 4 None 0 No #
+# 127.0.0.1 test MASTER_PORT 1 master-bin.000001 95 # # master-bin.000001 No No 0 0 95 # None 0 No #
reset slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 4 slave-relay-bin.000001 4 No No 0 0 0 4 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 4 # # No No 0 0 0 # None 0 No #
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 79 slave-relay-bin.000002 123 master-bin.000001 Yes Yes 0 0 79 123 None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000001 95 # # master-bin.000001 Yes Yes 0 0 95 # None 0 No #
stop slave;
reset slave;
start slave;
diff --git a/mysql-test/r/rpl_rotate_logs.result b/mysql-test/r/rpl_rotate_logs.result
index 62e5522fad9..85071e13555 100644
--- a/mysql-test/r/rpl_rotate_logs.result
+++ b/mysql-test/r/rpl_rotate_logs.result
@@ -16,7 +16,7 @@ create table t1 (s text);
insert into t1 values('Could not break slave'),('Tried hard');
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 60 master-bin.000001 417 slave-relay-bin.000001 461 master-bin.000001 Yes Yes 0 0 417 461 None 0 No #
+# 127.0.0.1 root MASTER_PORT 60 master-bin.000001 521 # # master-bin.000001 Yes Yes 0 0 521 # None 0 No #
select * from t1;
s
Could not break slave
@@ -57,7 +57,7 @@ master-bin.000003
insert into t2 values (65);
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 60 master-bin.000003 290 slave-relay-bin.000001 1088 master-bin.000003 Yes Yes 0 0 290 1088 None 0 No #
+# 127.0.0.1 root MASTER_PORT 60 master-bin.000003 469 # # master-bin.000003 Yes Yes 0 0 469 # None 0 No #
select * from t2;
m
34
@@ -76,15 +76,16 @@ show binary logs;
Log_name
master-bin.000003
master-bin.000004
+master-bin.000005
show master status;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000004 2886
+master-bin.000005 1387
select * from t4;
a
testing temporary tables part 2
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 60 master-bin.000004 2886 slave-relay-bin.000001 7891 master-bin.000004 Yes Yes 0 0 2886 7891 None 0 No #
+# 127.0.0.1 root MASTER_PORT 60 master-bin.000005 1387 # # master-bin.000005 Yes Yes 0 0 1387 # None 0 No #
lock tables t3 read;
select count(*) from t3 where n >= 4;
count(*)
diff --git a/mysql-test/r/rpl_server_id1.result b/mysql-test/r/rpl_server_id1.result
index 32e14b053d7..ed24c0ef35b 100644
--- a/mysql-test/r/rpl_server_id1.result
+++ b/mysql-test/r/rpl_server_id1.result
@@ -10,7 +10,7 @@ stop slave;
change master to master_port=SLAVE_PORT;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
- 127.0.0.1 root SLAVE_PORT 1 4 slave-relay-bin.000001 4 No No # 0 0 0 4 None 0 No NULL
+ 127.0.0.1 root SLAVE_PORT 1 4 slave-relay-bin.000001 4 No No # 0 0 0 95 None 0 No NULL
start slave;
insert into t1 values (1);
show status like "slave_running";
diff --git a/mysql-test/r/rpl_server_id2.result b/mysql-test/r/rpl_server_id2.result
index 82ab1ff85a7..36a142166fa 100644
--- a/mysql-test/r/rpl_server_id2.result
+++ b/mysql-test/r/rpl_server_id2.result
@@ -10,7 +10,7 @@ stop slave;
change master to master_port=SLAVE_PORT;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
- 127.0.0.1 root SLAVE_PORT 1 4 slave-relay-bin.000001 4 No No # 0 0 0 4 None 0 No NULL
+ 127.0.0.1 root SLAVE_PORT 1 4 slave-relay-bin.000001 4 No No # 0 0 0 95 None 0 No NULL
start slave;
insert into t1 values (1);
select * from t1;
diff --git a/mysql-test/r/rpl_session_var.result b/mysql-test/r/rpl_session_var.result
new file mode 100644
index 00000000000..f1f79ffa597
--- /dev/null
+++ b/mysql-test/r/rpl_session_var.result
@@ -0,0 +1,43 @@
+stop slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+reset master;
+reset slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+start slave;
+drop table if exists t1;
+Warnings:
+Note 1051 Unknown table 't1'
+create table t1(a varchar(100),b int);
+set @@session.sql_mode=pipes_as_concat;
+insert into t1 values('My'||'SQL', 1);
+set @@session.sql_mode=default;
+insert into t1 values('My'||'SQL', 2);
+select * from t1 where b<3 order by a;
+a b
+0 2
+MySQL 1
+select * from t1 where b<3 order by a;
+a b
+0 2
+MySQL 1
+set @@session.sql_mode=ignore_space;
+insert into t1 values(password ('MySQL'), 3);
+set @@session.sql_mode=ansi_quotes;
+create table "t2" ("a" int);
+drop table t1, t2;
+set @@session.sql_mode=default;
+create table t1(a int auto_increment primary key);
+create table t2(b int, a int);
+set @@session.sql_auto_is_null=1;
+insert into t1 values(null);
+insert into t2 select 1,a from t1 where a is null;
+set @@session.sql_auto_is_null=0;
+insert into t1 values(null);
+insert into t2 select 2,a from t1 where a is null;
+select * from t2 order by b;
+b a
+1 1
+select * from t2 order by b;
+b a
+1 1
+drop table t1,t2;
diff --git a/mysql-test/r/rpl_temporary.result b/mysql-test/r/rpl_temporary.result
index b9865d282fa..3e8c11883be 100644
--- a/mysql-test/r/rpl_temporary.result
+++ b/mysql-test/r/rpl_temporary.result
@@ -37,20 +37,20 @@ f
5
7
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; drop table if exists t1,t2
-master-bin.000001 140 Query 1 140 use `test`; create table t1(f int)
-master-bin.000001 197 Query 1 197 use `test`; create table t2(f int)
-master-bin.000001 254 Query 1 254 use `test`; insert into t1 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
-master-bin.000001 351 Query 1 351 use `test`; create temporary table t3(f int)
-master-bin.000001 418 Query 1 418 use `test`; insert into t3 select * from t1 where f<6
-master-bin.000001 494 Query 1 494 use `test`; create temporary table t3(f int)
-master-bin.000001 561 Query 1 561 use `test`; insert into t2 select count(*) from t3
-master-bin.000001 634 Query 1 634 use `test`; insert into t3 select * from t1 where f>=4
-master-bin.000001 711 Query 1 711 use `test`; drop temporary table t3
-master-bin.000001 769 Query 1 769 use `test`; insert into t2 select count(*) from t3
-master-bin.000001 842 Query 1 842 use `test`; drop temporary table t3
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 178 use `test`; drop table if exists t1,t2
+master-bin.000001 178 Query 1 257 use `test`; create table t1(f int)
+master-bin.000001 257 Query 1 336 use `test`; create table t2(f int)
+master-bin.000001 336 Query 1 455 use `test`; insert into t1 values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
+master-bin.000001 455 Query 1 544 use `test`; create temporary table t3(f int)
+master-bin.000001 544 Query 1 642 use `test`; insert into t3 select * from t1 where f<6
+master-bin.000001 642 Query 1 731 use `test`; create temporary table t3(f int)
+master-bin.000001 731 Query 1 826 use `test`; insert into t2 select count(*) from t3
+master-bin.000001 826 Query 1 925 use `test`; insert into t3 select * from t1 where f>=4
+master-bin.000001 925 Query 1 1005 use `test`; drop temporary table t3
+master-bin.000001 1005 Query 1 1100 use `test`; insert into t2 select count(*) from t3
+master-bin.000001 1100 Query 1 1180 use `test`; drop temporary table t3
drop table t1, t2;
use test;
SET TIMESTAMP=1040323920;
diff --git a/mysql-test/r/rpl_timezone.result b/mysql-test/r/rpl_timezone.result
index c7be3324533..6dc8f87393c 100644
--- a/mysql-test/r/rpl_timezone.result
+++ b/mysql-test/r/rpl_timezone.result
@@ -31,14 +31,14 @@ t
2004-01-01 00:00:00
2004-06-11 09:39:02
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; create table t1 (t timestamp)
-master-bin.000001 143 Query 1 143 use `test`; create table t2 (t char(32))
-master-bin.000001 206 Query 1 206 use `test`; SET ONE_SHOT TIME_ZONE='UTC'
-master-bin.000001 269 Query 1 269 use `test`; insert into t1 values ('20040101000000'), ('20040611093902')
-master-bin.000001 364 Query 1 364 use `test`; delete from t1
-master-bin.000001 413 Query 1 413 use `test`; insert into t1 values ('20040101000000'), ('20040611093902')
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 181 use `test`; create table t1 (t timestamp)
+master-bin.000001 181 Query 1 266 use `test`; create table t2 (t char(32))
+master-bin.000001 266 Query 1 351 use `test`; SET ONE_SHOT TIME_ZONE='UTC'
+master-bin.000001 351 Query 1 468 use `test`; insert into t1 values ('20040101000000'), ('20040611093902')
+master-bin.000001 468 Query 1 539 use `test`; delete from t1
+master-bin.000001 539 Query 1 656 use `test`; insert into t1 values ('20040101000000'), ('20040611093902')
set time_zone='MET';
insert into t2 (select t from t1);
select * from t1;
diff --git a/mysql-test/r/rpl_trunc_binlog.result b/mysql-test/r/rpl_trunc_binlog.result
index 085a2937584..5eb5f810a8f 100644
--- a/mysql-test/r/rpl_trunc_binlog.result
+++ b/mysql-test/r/rpl_trunc_binlog.result
@@ -10,4 +10,4 @@ reset slave;
start slave;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_PORT 1 master-bin.000002 4 slave-relay-bin.000002 123 master-bin.000001 Yes No 0 Rolling back unfinished transaction (no COMMIT or ROLLBACK) from relay log. A probable cause is that the master died while writing the transaction to its binary log. 0 79 # None 0 No #
+# 127.0.0.1 root MASTER_PORT 1 master-bin.000002 4 # # master-bin.000001 Yes No 0 Rolling back unfinished transaction (no COMMIT or ROLLBACK) from relay log. A probable cause is that the master died while writing the transaction to its binary log. 0 79 # None 0 No #
diff --git a/mysql-test/r/rpl_until.result b/mysql-test/r/rpl_until.result
index 5772f176919..ba8c0c1f131 100644
--- a/mysql-test/r/rpl_until.result
+++ b/mysql-test/r/rpl_until.result
@@ -13,16 +13,16 @@ insert into t2 values (1),(2);
insert into t2 values (3),(4);
drop table t2;
show binlog events;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3
-master-bin.000001 79 Query 1 79 use `test`; create table t1(n int not null auto_increment primary key)
-master-bin.000001 172 Query 1 172 use `test`; insert into t1 values (1),(2),(3),(4)
-master-bin.000001 244 Query 1 244 use `test`; drop table t1
-master-bin.000001 292 Query 1 292 use `test`; create table t2(n int not null auto_increment primary key)
-master-bin.000001 385 Query 1 385 use `test`; insert into t2 values (1),(2)
-master-bin.000001 449 Query 1 449 use `test`; insert into t2 values (3),(4)
-master-bin.000001 513 Query 1 513 use `test`; drop table t2
-start slave until master_log_file='master-bin.000001', master_log_pos=244;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 4 Format_desc 1 95 Server ver: VERSION, Binlog ver: 4
+master-bin.000001 95 Query 1 210 use `test`; create table t1(n int not null auto_increment primary key)
+master-bin.000001 210 Query 1 304 use `test`; insert into t1 values (1),(2),(3),(4)
+master-bin.000001 304 Query 1 374 use `test`; drop table t1
+master-bin.000001 374 Query 1 489 use `test`; create table t2(n int not null auto_increment primary key)
+master-bin.000001 489 Query 1 575 use `test`; insert into t2 values (1),(2)
+master-bin.000001 575 Query 1 661 use `test`; insert into t2 values (3),(4)
+master-bin.000001 661 Query 1 731 use `test`; drop table t2
+start slave until master_log_file='master-bin.000001', master_log_pos=304;
select * from t1;
n
1
@@ -31,7 +31,7 @@ n
4
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 561 slave-relay-bin.000002 # master-bin.000001 Yes No 0 0 244 # Master master-bin.000001 244 No #
+# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 731 slave-relay-bin.000004 # master-bin.000001 Yes No 0 0 304 # Master master-bin.000001 304 No #
start slave until master_log_file='master-no-such-bin.000001', master_log_pos=291;
select * from t1;
n
@@ -41,21 +41,21 @@ n
4
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 561 slave-relay-bin.000002 # master-bin.000001 Yes No 0 0 244 # Master master-no-such-bin.000001 291 No #
-start slave until relay_log_file='slave-relay-bin.000002', relay_log_pos=537;
+# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 731 slave-relay-bin.000004 # master-bin.000001 Yes No 0 0 304 # Master master-no-such-bin.000001 291 No #
+start slave until relay_log_file='slave-relay-bin.000004', relay_log_pos=710;
select * from t2;
n
1
2
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 561 slave-relay-bin.000002 # master-bin.000001 Yes No 0 0 449 # Relay slave-relay-bin.000002 537 No #
+# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 731 slave-relay-bin.000004 # master-bin.000001 Yes No 0 0 575 # Relay slave-relay-bin.000004 710 No #
start slave;
stop slave;
-start slave until master_log_file='master-bin.000001', master_log_pos=561;
+start slave until master_log_file='master-bin.000001', master_log_pos=710;
show slave status;
Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master
-# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 561 slave-relay-bin.000002 # master-bin.000001 Yes No 0 0 561 # Master master-bin.000001 561 No #
+# 127.0.0.1 root MASTER_MYPORT 1 master-bin.000001 731 slave-relay-bin.000004 # master-bin.000001 Yes No 0 0 731 # Master master-bin.000001 710 No #
start slave until master_log_file='master-bin', master_log_pos=561;
ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL
start slave until master_log_file='master-bin.000001', master_log_pos=561, relay_log_pos=12;
@@ -67,6 +67,6 @@ ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UN
start slave until relay_log_file='slave-relay-bin.000002', master_log_pos=561;
ERROR HY000: Incorrect parameter or combination of parameters for START SLAVE UNTIL
start slave sql_thread;
-start slave until master_log_file='master-bin.000001', master_log_pos=561;
+start slave until master_log_file='master-bin.000001', master_log_pos=710;
Warnings:
Note 1254 Slave is already running
diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result
index 85768270ba3..dd9a11e370f 100644
--- a/mysql-test/r/rpl_user_variables.result
+++ b/mysql-test/r/rpl_user_variables.result
@@ -76,34 +76,34 @@ abcn1n2
NULL
NULL
NULL
-show binlog events from 141;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-slave-bin.000001 141 User var 2 141 @`i1`=12345678901234
-slave-bin.000001 184 User var 2 184 @`i2`=-12345678901234
-slave-bin.000001 227 User var 2 227 @`i3`=0
-slave-bin.000001 270 User var 2 270 @`i4`=-1
-slave-bin.000001 313 Query 1 313 use `test`; insert into t1 values (@i1), (@i2), (@i3), (@i4)
-slave-bin.000001 396 User var 2 396 @`r1`=12.5
-slave-bin.000001 439 User var 2 439 @`r2`=-12.5
-slave-bin.000001 482 Query 1 482 use `test`; insert into t1 values (@r1), (@r2)
-slave-bin.000001 551 User var 2 551 @`s1`=_latin1 0x5468697320697320612074657374 COLLATE latin1_swedish_ci
-slave-bin.000001 600 User var 2 600 @`s2`=_latin1 "" COLLATE latin1_swedish_ci
-slave-bin.000001 635 User var 2 635 @`s3`=_latin1 0x61626327646566 COLLATE latin1_swedish_ci
-slave-bin.000001 677 User var 2 677 @`s4`=_latin1 0x6162635C646566 COLLATE latin1_swedish_ci
-slave-bin.000001 719 User var 2 719 @`s5`=_latin1 0x61626327646566 COLLATE latin1_swedish_ci
-slave-bin.000001 761 Query 1 761 use `test`; insert into t1 values (@s1), (@s2), (@s3), (@s4), (@s5)
-slave-bin.000001 851 User var 2 851 @`n1`=NULL
-slave-bin.000001 877 Query 1 877 use `test`; insert into t1 values (@n1)
-slave-bin.000001 939 User var 2 939 @`n2`=NULL
-slave-bin.000001 965 Query 1 965 use `test`; insert into t1 values (@n2)
-slave-bin.000001 1027 Query 1 1027 use `test`; insert into t1 values (@a:=0), (@a:=@a+1), (@a:=@a+1)
-slave-bin.000001 1115 User var 2 1115 @`a`=2
-slave-bin.000001 1157 Query 1 1157 use `test`; insert into t1 values (@a+(@b:=@a+1))
-slave-bin.000001 1229 User var 2 1229 @`q`=_latin1 0x616263 COLLATE latin1_swedish_ci
-slave-bin.000001 1266 Query 1 1266 use `test`; insert t1 values (@q), (@q:=concat(@q, 'n1')), (@q:=concat(@q, 'n2'))
-slave-bin.000001 1370 User var 2 1370 @`a`=5
-slave-bin.000001 1412 Query 1 1412 use `test`; insert into t1 values (@a),(@a)
-slave-bin.000001 1478 User var 2 1478 @`a`=NULL
-slave-bin.000001 1503 Query 1 1503 use `test`; insert into t1 values (@a),(@a),(@a*5)
+show binlog events from 179;
+Log_name Pos Event_type Server_id End_log_pos Info
+slave-bin.000001 179 User var 2 222 @`i1`=12345678901234
+slave-bin.000001 222 User var 2 265 @`i2`=-12345678901234
+slave-bin.000001 265 User var 2 308 @`i3`=0
+slave-bin.000001 308 User var 2 351 @`i4`=-1
+slave-bin.000001 351 Query 1 456 use `test`; insert into t1 values (@i1), (@i2), (@i3), (@i4)
+slave-bin.000001 456 User var 2 499 @`r1`=12.5
+slave-bin.000001 499 User var 2 542 @`r2`=-12.5
+slave-bin.000001 542 Query 1 633 use `test`; insert into t1 values (@r1), (@r2)
+slave-bin.000001 633 User var 2 682 @`s1`=_latin1 0x5468697320697320612074657374 COLLATE latin1_swedish_ci
+slave-bin.000001 682 User var 2 717 @`s2`=_latin1 "" COLLATE latin1_swedish_ci
+slave-bin.000001 717 User var 2 759 @`s3`=_latin1 0x61626327646566 COLLATE latin1_swedish_ci
+slave-bin.000001 759 User var 2 801 @`s4`=_latin1 0x6162635C646566 COLLATE latin1_swedish_ci
+slave-bin.000001 801 User var 2 843 @`s5`=_latin1 0x61626327646566 COLLATE latin1_swedish_ci
+slave-bin.000001 843 Query 1 955 use `test`; insert into t1 values (@s1), (@s2), (@s3), (@s4), (@s5)
+slave-bin.000001 955 User var 2 981 @`n1`=NULL
+slave-bin.000001 981 Query 1 1065 use `test`; insert into t1 values (@n1)
+slave-bin.000001 1065 User var 2 1091 @`n2`=NULL
+slave-bin.000001 1091 Query 1 1175 use `test`; insert into t1 values (@n2)
+slave-bin.000001 1175 Query 1 1285 use `test`; insert into t1 values (@a:=0), (@a:=@a+1), (@a:=@a+1)
+slave-bin.000001 1285 User var 2 1327 @`a`=2
+slave-bin.000001 1327 Query 1 1421 use `test`; insert into t1 values (@a+(@b:=@a+1))
+slave-bin.000001 1421 User var 2 1458 @`q`=_latin1 0x616263 COLLATE latin1_swedish_ci
+slave-bin.000001 1458 Query 1 1584 use `test`; insert t1 values (@q), (@q:=concat(@q, 'n1')), (@q:=concat(@q, 'n2'))
+slave-bin.000001 1584 User var 2 1626 @`a`=5
+slave-bin.000001 1626 Query 1 1714 use `test`; insert into t1 values (@a),(@a)
+slave-bin.000001 1714 User var 2 1739 @`a`=NULL
+slave-bin.000001 1739 Query 1 1834 use `test`; insert into t1 values (@a),(@a),(@a*5)
drop table t1;
stop slave;
diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result
index 8da1660a109..16b12a436d3 100644
--- a/mysql-test/r/select.result
+++ b/mysql-test/r/select.result
@@ -1393,8 +1393,8 @@ companynr companynr
41 40
explain select distinct t2.companynr,t4.companynr from t2,t4 where t2.companynr=t4.companynr+1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using temporary
-1 SIMPLE t4 index NULL PRIMARY 1 NULL 12 Using where; Using index
+1 SIMPLE t4 index NULL PRIMARY 1 NULL 12 Using index; Using temporary
+1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where
select t2.fld1,t2.companynr,fld3,period from t3,t2 where t2.fld1 = 38208 and t2.fld1=t3.t2nr and period = 1008 or t2.fld1 = 38008 and t2.fld1 =t3.t2nr and period = 1008;
fld1 companynr fld3 period
038008 37 reporters 1008
@@ -1470,7 +1470,7 @@ explain extended select count(*),min(fld4),max(fld4),sum(fld1),avg(fld1),std(fld
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where
Warnings:
-Note 1003 select count(0) AS `count(*)`,min(test.t2.fld4) AS `min(fld4)`,max(test.t2.fld4) AS `max(fld4)`,sum(test.t2.fld1) AS `sum(fld1)`,avg(test.t2.fld1) AS `avg(fld1)`,std(test.t2.fld1) AS `std(fld1)`,variance(test.t2.fld1) AS `variance(fld1)` from test.t2 where ((test.t2.companynr = 34) and (test.t2.fld4 <> _latin1''))
+Note 1003 select count(0) AS `count(*)`,min(`test`.`t2`.`fld4`) AS `min(fld4)`,max(`test`.`t2`.`fld4`) AS `max(fld4)`,sum(`test`.`t2`.`fld1`) AS `sum(fld1)`,avg(`test`.`t2`.`fld1`) AS `avg(fld1)`,std(`test`.`t2`.`fld1`) AS `std(fld1)`,variance(`test`.`t2`.`fld1`) AS `variance(fld1)` from `test`.`t2` where ((`test`.`t2`.`companynr` = 34) and (`test`.`t2`.`fld4` <> _latin1''))
select companynr,count(*),min(fld4),max(fld4),sum(fld1),avg(fld1),std(fld1),variance(fld1) from t2 group by companynr limit 3;
companynr count(*) min(fld4) max(fld4) sum(fld1) avg(fld1) std(fld1) variance(fld1)
00 82 Anthony windmills 10355753 126289.6707 115550.9757 13352027981.7087
@@ -2021,15 +2021,15 @@ select period as "Nuvarande period" from t1 group by "Nuvarande period" limit 1;
Nuvarande period
9410
show tables;
-Tables_in_test
-t1
-t2
-t3
-t4
+Tables_in_test table_type
+t1 BASE TABLE
+t2 BASE TABLE
+t3 BASE TABLE
+t4 BASE TABLE
show tables from test like "s%";
-Tables_in_test (s%)
+Tables_in_test (s%) table_type
show tables from test like "t?";
-Tables_in_test (t?)
+Tables_in_test (t?) table_type
show full columns from t2;
Field Type Collation Null Key Default Extra Privileges Comment
auto int(11) NULL PRI NULL auto_increment select,insert,update,references
@@ -2076,8 +2076,6 @@ gvid the_success the_fail the_size the_time
Warnings:
Warning 1292 Truncated incorrect datetime value: 'wrong-date-value'
Warning 1292 Truncated incorrect datetime value: 'wrong-date-value'
-Warning 1292 Truncated incorrect datetime value: 'wrong-date-value'
-Warning 1292 Truncated incorrect datetime value: 'wrong-date-value'
SELECT a.gvid, (SUM(CASE b.sampletid WHEN 140 THEN b.samplevalue ELSE 0 END)) as the_success,(SUM(CASE b.sampletid WHEN 141 THEN b.samplevalue ELSE 0 END)) as the_fail,(SUM(CASE b.sampletid WHEN 142 THEN b.samplevalue ELSE 0 END)) as the_size,(SUM(CASE b.sampletid WHEN 143 THEN b.samplevalue ELSE 0 END)) as the_time FROM t1 a, t2 b WHERE a.hmid = b.hmid AND a.volid = b.volid AND b.sampletime >= NULL AND b.sampletime < NULL AND b.sampletid IN (140, 141, 142, 143) GROUP BY a.gvid;
gvid the_success the_fail the_size the_time
DROP TABLE t1,t2;
@@ -2157,20 +2155,17 @@ a a a
select * from (t1 as t2 left join t1 as t3 using (a)) inner join t1 on t1.a>1;
a a a
1 1 2
-1 1 3
2 2 2
-2 2 3
3 3 2
+1 1 3
+2 2 3
3 3 3
select * from t1 inner join (t1 as t2 left join t1 as t3 using (a)) on t1.a>1;
a a a
-1 1 NULL
2 1 1
3 1 1
-1 2 NULL
2 2 2
3 2 2
-1 3 NULL
2 3 3
3 3 3
select * from (t1 as t2 left join t1 as t3 using (a)) inner join t1 using ( a );
@@ -2181,13 +2176,7 @@ a a a
select * from t1 inner join (t1 as t2 left join t1 as t3 using (a)) using ( a );
a a a
1 1 1
-2 1 NULL
-3 1 NULL
-1 2 NULL
2 2 2
-3 2 NULL
-1 3 NULL
-2 3 NULL
3 3 3
select * from (t1 as t2 left join t1 as t3 using (a)) left outer join t1 on t1.a>1;
a a a
@@ -2199,14 +2188,12 @@ a a a
3 3 3
select * from t1 left outer join (t1 as t2 left join t1 as t3 using (a)) on t1.a>1;
a a a
-1 1 NULL
+1 NULL NULL
2 1 1
-3 1 1
-1 2 NULL
2 2 2
-3 2 2
-1 3 NULL
2 3 3
+3 1 1
+3 2 2
3 3 3
select * from (t1 as t2 left join t1 as t3 using (a)) left join t1 using ( a );
a a a
@@ -2216,13 +2203,7 @@ a a a
select * from t1 left join (t1 as t2 left join t1 as t3 using (a)) using ( a );
a a a
1 1 1
-2 1 NULL
-3 1 NULL
-1 2 NULL
2 2 2
-3 2 NULL
-1 3 NULL
-2 3 NULL
3 3 3
select * from (t1 as t2 left join t1 as t3 using (a)) natural left join t1;
a a a
@@ -2236,9 +2217,7 @@ a a a
3 3 3
select * from (t1 as t2 left join t1 as t3 using (a)) right join t1 on t1.a>1;
a a a
-1 NULL 1
-2 NULL 1
-3 NULL 1
+NULL NULL 1
1 1 2
2 2 2
3 3 2
@@ -2256,13 +2235,7 @@ a a a
select * from (t1 as t2 left join t1 as t3 using (a)) right outer join t1 using ( a );
a a a
1 1 1
-2 NULL 1
-3 NULL 1
-1 NULL 2
2 2 2
-3 NULL 2
-1 NULL 3
-2 NULL 3
3 3 3
select * from t1 right outer join (t1 as t2 left join t1 as t3 using (a)) using ( a );
a a a
@@ -2272,13 +2245,7 @@ a a a
select * from (t1 as t2 left join t1 as t3 using (a)) natural right join t1;
a a a
1 1 1
-2 NULL 1
-3 NULL 1
-1 NULL 2
2 2 2
-3 NULL 2
-1 NULL 3
-2 NULL 3
3 3 3
select * from t1 natural right join (t1 as t2 left join t1 as t3 using (a));
a a a
@@ -2291,10 +2258,10 @@ a a
2 2
3 3
select * from (t1 as t2 left join t1 as t3 using (a)) natural join t1;
-a a a
-1 1 1
-2 2 2
-3 3 3
+a a
+1 1
+2 2
+3 3
drop table t1;
CREATE TABLE t1 ( aa char(2), id int(11) NOT NULL auto_increment, t2_id int(11) NOT NULL default '0', PRIMARY KEY (id), KEY replace_id (t2_id)) ENGINE=MyISAM;
INSERT INTO t1 VALUES ("1",8264,2506),("2",8299,2517),("3",8301,2518),("4",8302,2519),("5",8303,2520),("6",8304,2521),("7",8305,2522);
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index 8b52e6efedc..ecf91fe6f68 100644
--- a/mysql-test/r/show_check.result
+++ b/mysql-test/r/show_check.result
@@ -107,7 +107,7 @@ show create table t1;
Table Create Table
t1 CREATE TABLE t1 (
test_set set('val1','val2','val3') NOT NULL default '',
- name char(20) default 'O''Brien' COMMENT 'O''Brien as default',
+ `name` char(20) default 'O''Brien' COMMENT 'O''Brien as default',
c int(11) NOT NULL default '0' COMMENT 'int column',
`c-b` int(11) default NULL COMMENT 'name with a minus',
`space 2` int(11) default NULL COMMENT 'name with a space'
@@ -270,7 +270,6 @@ SET @old_sql_mode= @@sql_mode, sql_mode= '';
SET @old_sql_quote_show_create= @@sql_quote_show_create, sql_quote_show_create= OFF;
CREATE TABLE `a/b` (i INT);
ERROR 42000: Incorrect table name 'a/b'
-SET sql_mode= 'ANSI_QUOTES';
SET sql_mode= '';
SET sql_quote_show_create= OFF;
CREATE TABLE t1 (i INT);
diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
new file mode 100644
index 00000000000..6b36b3bb2bf
--- /dev/null
+++ b/mysql-test/r/sp-error.result
@@ -0,0 +1,465 @@
+delete from mysql.proc;
+create procedure syntaxerror(t int)|
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
+create procedure syntaxerror(t int)|
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
+create procedure syntaxerror(t int)|
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
+drop table if exists t3|
+create table t3 ( x int )|
+insert into t3 values (2), (3)|
+create procedure bad_into(out param int)
+select x from t3 into param|
+call bad_into(@x)|
+ERROR 42000: Result consisted of more than one row
+drop procedure bad_into|
+drop table t3|
+create procedure proc1()
+set @x = 42|
+create function func1() returns int
+return 42|
+create procedure foo()
+create procedure bar() set @x=3|
+ERROR 2F003: Can't create a PROCEDURE from within another stored routine
+create procedure foo()
+create function bar() returns double return 2.3|
+ERROR 2F003: Can't create a FUNCTION from within another stored routine
+create procedure proc1()
+set @x = 42|
+ERROR 42000: PROCEDURE proc1 already exists
+create function func1() returns int
+return 42|
+ERROR 42000: FUNCTION func1 already exists
+drop procedure proc1|
+drop function func1|
+alter procedure foo|
+ERROR 42000: PROCEDURE test.foo does not exist
+alter function foo|
+ERROR 42000: FUNCTION test.foo does not exist
+drop procedure foo|
+ERROR 42000: PROCEDURE test.foo does not exist
+drop function foo|
+ERROR 42000: FUNCTION test.foo does not exist
+call foo()|
+ERROR 42000: PROCEDURE test.foo does not exist
+drop procedure if exists foo|
+Warnings:
+Warning 1304 PROCEDURE foo does not exist
+show create procedure foo|
+ERROR 42000: PROCEDURE foo does not exist
+create procedure foo()
+foo: loop
+leave bar;
+end loop|
+ERROR 42000: LEAVE with no matching label: bar
+create procedure foo()
+foo: loop
+iterate bar;
+end loop|
+ERROR 42000: ITERATE with no matching label: bar
+create procedure foo()
+foo: begin
+iterate foo;
+end|
+ERROR 42000: ITERATE with no matching label: foo
+create procedure foo()
+begin
+goto foo;
+end|
+ERROR 42000: GOTO with no matching label: foo
+create procedure foo()
+begin
+begin
+label foo;
+end;
+goto foo;
+end|
+ERROR 42000: GOTO with no matching label: foo
+create procedure foo()
+begin
+goto foo;
+begin
+label foo;
+end;
+end|
+ERROR 42000: GOTO with no matching label: foo
+create procedure foo()
+begin
+begin
+goto foo;
+end;
+begin
+label foo;
+end;
+end|
+ERROR 42000: GOTO with no matching label: foo
+create procedure foo()
+begin
+begin
+label foo;
+end;
+begin
+goto foo;
+end;
+end|
+ERROR 42000: GOTO with no matching label: foo
+create procedure foo()
+foo: loop
+foo: loop
+set @x=2;
+end loop foo;
+end loop foo|
+ERROR 42000: Redefining label foo
+create procedure foo()
+foo: loop
+set @x=2;
+end loop bar|
+ERROR 42000: End-label bar without match
+create procedure foo(out x int)
+begin
+declare y int;
+set x = y;
+end|
+Warnings:
+Warning 1310 Referring to uninitialized variable y
+drop procedure foo|
+create procedure foo()
+return 42|
+ERROR 42000: RETURN is only allowed in a FUNCTION
+create function foo() returns int
+begin
+declare x int;
+select max(c) into x from test.t;
+return x;
+end|
+ERROR 0A000: Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION
+create procedure p(x int)
+insert into test.t1 values (x)|
+create function f(x int) returns int
+return x+42|
+call p()|
+ERROR 42000: Incorrect number of arguments for PROCEDURE p; expected 1, got 0
+call p(1, 2)|
+ERROR 42000: Incorrect number of arguments for PROCEDURE p; expected 1, got 2
+select f()|
+ERROR 42000: Incorrect number of arguments for FUNCTION f; expected 1, got 0
+select f(1, 2)|
+ERROR 42000: Incorrect number of arguments for FUNCTION f; expected 1, got 2
+drop procedure p|
+drop function f|
+create procedure p(val int, out res int)
+begin
+declare x int default 0;
+declare continue handler for foo set x = 1;
+insert into test.t1 values (val);
+if (x) then
+set res = 0;
+else
+set res = 1;
+end if;
+end|
+ERROR 42000: Undefined CONDITION: foo
+create procedure p(val int, out res int)
+begin
+declare x int default 0;
+declare foo condition for 1146;
+declare continue handler for bar set x = 1;
+insert into test.t1 values (val);
+if (x) then
+set res = 0;
+else
+set res = 1;
+end if;
+end|
+ERROR 42000: Undefined CONDITION: bar
+create function f(val int) returns int
+begin
+declare x int;
+set x = val+3;
+end|
+ERROR 42000: No RETURN found in FUNCTION f
+create function f(val int) returns int
+begin
+declare x int;
+set x = val+3;
+if x < 4 then
+return x;
+end if;
+end|
+select f(10)|
+ERROR 2F005: FUNCTION f ended without RETURN
+drop function f|
+create procedure p()
+begin
+declare c cursor for insert into test.t1 values ("foo", 42);
+open c;
+close c;
+end|
+ERROR 42000: Cursor statement must be a SELECT
+create procedure p()
+begin
+declare x int;
+declare c cursor for select * into x from test.t limit 1;
+open c;
+close c;
+end|
+ERROR 42000: Cursor SELECT must not have INTO
+create procedure p()
+begin
+declare c cursor for select * from test.t;
+open cc;
+close c;
+end|
+ERROR 42000: Undefined CURSOR: cc
+drop table if exists t1|
+create table t1 (val int)|
+create procedure p()
+begin
+declare c cursor for select * from test.t1;
+open c;
+open c;
+close c;
+end|
+call p()|
+ERROR 24000: Cursor is already open
+drop procedure p|
+create procedure p()
+begin
+declare c cursor for select * from test.t1;
+open c;
+close c;
+close c;
+end|
+call p()|
+ERROR 24000: Cursor is not open
+drop procedure p|
+alter procedure bar3 sql security invoker|
+ERROR 42000: PROCEDURE test.bar3 does not exist
+alter procedure bar3 name
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
+ERROR 42000: Identifier name 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' is too long
+drop table t1|
+drop table if exists t1|
+create table t1 (val int, x float)|
+insert into t1 values (42, 3.1), (19, 1.2)|
+create procedure p()
+begin
+declare x int;
+declare c cursor for select * from t1;
+open c;
+fetch c into x, y;
+close c;
+end|
+ERROR 42000: Undeclared variable: y
+create procedure p()
+begin
+declare x int;
+declare c cursor for select * from t1;
+open c;
+fetch c into x;
+close c;
+end|
+call p()|
+ERROR HY000: Incorrect number of FETCH variables
+drop procedure p|
+create procedure p()
+begin
+declare x int;
+declare y float;
+declare z int;
+declare c cursor for select * from t1;
+open c;
+fetch c into x, y, z;
+close c;
+end|
+call p()|
+ERROR HY000: Incorrect number of FETCH variables
+drop procedure p|
+create procedure p(in x int, x char(10))
+begin
+end|
+ERROR 42000: Duplicate parameter: x
+create function p(x int, x char(10))
+begin
+end|
+ERROR 42000: Duplicate parameter: x
+create procedure p()
+begin
+declare x float;
+declare x int;
+end|
+ERROR 42000: Duplicate variable: x
+create procedure p()
+begin
+declare c condition for 1064;
+declare c condition for 1065;
+end|
+ERROR 42000: Duplicate condition: c
+create procedure p()
+begin
+declare c cursor for select * from t1;
+declare c cursor for select field from t1;
+end|
+ERROR 42000: Duplicate cursor: c
+create procedure u()
+use sptmp|
+ERROR 42000: USE is not allowed in a stored procedure
+create procedure p()
+begin
+declare c cursor for select * from t1;
+declare x int;
+end|
+ERROR 42000: Variable or condition declaration after cursor or handler declaration
+create procedure p()
+begin
+declare x int;
+declare continue handler for sqlstate '42S99' set x = 1;
+declare foo condition for sqlstate '42S99';
+end|
+ERROR 42000: Variable or condition declaration after cursor or handler declaration
+create procedure p()
+begin
+declare x int;
+declare continue handler for sqlstate '42S99' set x = 1;
+declare c cursor for select * from t1;
+end|
+ERROR 42000: Cursor declaration after handler declaration
+create procedure p()
+begin
+declare continue handler for sqlexception
+begin
+goto L1;
+end;
+select field from t1;
+label L1;
+end|
+ERROR HY000: GOTO is not allowed in a stored procedure handler
+create procedure bug1965()
+begin
+declare c cursor for select val from t1 order by valname;
+open c;
+close c;
+end|
+call bug1965()|
+ERROR 42S22: Unknown column 'valname' in 'order clause'
+drop procedure bug1965|
+select 1 into a|
+ERROR 42000: Undeclared variable: a
+create function bug1654()
+returns int
+return (select sum(t.data) from test.t2 t)|
+ERROR 0A000: Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION
+drop table if exists t3|
+create table t3 (column_1_0 int)|
+create procedure bug1653()
+update t3 set column_1 = 0|
+call bug1653()|
+ERROR 42S22: Unknown column 'column_1' in 'field list'
+drop table t3|
+create table t3 (column_1 int)|
+call bug1653()|
+drop procedure bug1653|
+drop table t3|
+create procedure bug2259()
+begin
+declare v1 int;
+declare c1 cursor for select s1 from t10;
+fetch c1 into v1;
+end|
+call bug2259()|
+ERROR 24000: Cursor is not open
+drop procedure bug2259|
+create procedure bug2272()
+begin
+declare v int;
+update t1 set v = 42;
+end|
+insert into t1 values (666, 51.3)|
+call bug2272()|
+ERROR 42S22: Unknown column 'v' in 'field list'
+delete from t1|
+drop procedure bug2272|
+create procedure bug2329_1()
+begin
+declare v int;
+insert into t1 (v) values (5);
+end|
+create procedure bug2329_2()
+begin
+declare v int;
+replace t1 set v = 5;
+end|
+call bug2329_1()|
+ERROR 42S22: Unknown column 'v' in 'field list'
+call bug2329_2()|
+ERROR 42S22: Unknown column 'v' in 'field list'
+drop procedure bug2329_1|
+drop procedure bug2329_2|
+create function bug3287() returns int
+begin
+declare v int default null;
+case
+when v is not null then return 1;
+end case;
+return 2;
+end|
+select bug3287()|
+ERROR 20000: Case not found for CASE statement
+drop function bug3287|
+create procedure bug3287(x int)
+case x
+when 0 then
+insert into test.t1 values (x, 0.1);
+when 1 then
+insert into test.t1 values (x, 1.1);
+end case|
+call bug3287(2)|
+ERROR 20000: Case not found for CASE statement
+drop procedure bug3287|
+drop table if exists t3|
+create table t3 (s1 int, primary key (s1))|
+insert into t3 values (5),(6)|
+create procedure bug3279(out y int)
+begin
+declare x int default 0;
+begin
+declare exit handler for sqlexception set x = x+1;
+insert into t3 values (5);
+end;
+if x < 2 then
+set x = x+1;
+insert into t3 values (6);
+end if;
+set y = x;
+end|
+set @x = 0|
+call bug3279(@x)|
+ERROR 23000: Duplicate entry '6' for key 1
+select @x|
+@x
+0
+drop procedure bug3279|
+drop table t3|
+create procedure nodb.bug3339() begin end|
+ERROR 42000: Unknown database 'nodb'
+create procedure bug2653_1(a int, out b int)
+set b = aa|
+create procedure bug2653_2(a int, out b int)
+begin
+if aa < 0 then
+set b = - a;
+else
+set b = a;
+end if;
+end|
+call bug2653_1(1, @b)|
+ERROR 42S22: Unknown column 'aa' in 'order clause'
+call bug2653_2(2, @b)|
+ERROR 42S22: Unknown column 'aa' in 'order clause'
+drop procedure bug2653_1|
+drop procedure bug2653_2|
+create procedure bug4344() drop procedure bug4344|
+ERROR HY000: Can't drop a PROCEDURE from within another stored routine
+create procedure bug4344() drop function bug4344|
+ERROR HY000: Can't drop a FUNCTION from within another stored routine
+drop table t1|
diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result
new file mode 100644
index 00000000000..cdcc4595a73
--- /dev/null
+++ b/mysql-test/r/sp-security.result
@@ -0,0 +1,121 @@
+use test;
+grant usage on *.* to user1@localhost;
+flush privileges;
+drop database if exists db1_secret;
+create database db1_secret;
+create procedure db1_secret.dummy() begin end;
+drop procedure db1_secret.dummy;
+use db1_secret;
+create table t1 ( u varchar(64), i int );
+create procedure stamp(i int)
+insert into db1_secret.t1 values (user(), i);
+show procedure status like 'stamp';
+Db Name Type Definer Modified Created Security_type Comment
+db1_secret stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+create function db() returns varchar(64) return database();
+show function status like 'db';
+Db Name Type Definer Modified Created Security_type Comment
+db1_secret db FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+call stamp(1);
+select * from t1;
+u i
+root@localhost 1
+select db();
+db()
+db1_secret
+call db1_secret.stamp(2);
+select db1_secret.db();
+db1_secret.db()
+db1_secret
+select * from db1_secret.t1;
+ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret'
+create procedure db1_secret.dummy() begin end;
+ERROR 42000: Unknown database 'db1_secret'
+drop procedure db1_secret.dummy;
+ERROR 42000: PROCEDURE db1_secret.dummy does not exist
+call db1_secret.stamp(3);
+select db1_secret.db();
+db1_secret.db()
+db1_secret
+select * from db1_secret.t1;
+ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret'
+create procedure db1_secret.dummy() begin end;
+ERROR 42000: Unknown database 'db1_secret'
+drop procedure db1_secret.dummy;
+ERROR 42000: PROCEDURE db1_secret.dummy does not exist
+select * from t1;
+u i
+root@localhost 1
+user1@localhost 2
+anon@localhost 3
+alter procedure stamp sql security invoker;
+show procedure status like 'stamp';
+Db Name Type Definer Modified Created Security_type Comment
+db1_secret stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER
+alter function db sql security invoker;
+show function status like 'db';
+Db Name Type Definer Modified Created Security_type Comment
+db1_secret db FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER
+call stamp(4);
+select * from t1;
+u i
+root@localhost 1
+user1@localhost 2
+anon@localhost 3
+root@localhost 4
+select db();
+db()
+db1_secret
+call db1_secret.stamp(5);
+ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret'
+select db1_secret.db();
+ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret'
+call db1_secret.stamp(6);
+ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret'
+select db1_secret.db();
+ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret'
+drop database if exists db2;
+create database db2;
+use db2;
+create table t2 (s1 int);
+insert into t2 values (0);
+grant usage on db2.* to user1@localhost;
+grant select on db2.* to user1@localhost;
+grant usage on db2.* to user2@localhost;
+grant select,insert,update,delete on db2.* to user2@localhost;
+flush privileges;
+use db2;
+create procedure p () insert into t2 values (1);
+call p();
+ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db2'
+use db2;
+call p();
+ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db2'
+select * from t2;
+s1
+0
+create procedure q () insert into t2 values (2);
+call q();
+select * from t2;
+s1
+0
+2
+use db2;
+call q();
+select * from t2;
+s1
+0
+2
+2
+use test;
+select type,db,name from mysql.proc;
+type db name
+FUNCTION db1_secret db
+PROCEDURE db1_secret stamp
+PROCEDURE db2 p
+PROCEDURE db2 q
+drop database db1_secret;
+drop database db2;
+select type,db,name from mysql.proc;
+type db name
+delete from mysql.user where user='user1' or user='user2';
diff --git a/mysql-test/r/sp-threads.result b/mysql-test/r/sp-threads.result
new file mode 100644
index 00000000000..0bb5c3423e2
--- /dev/null
+++ b/mysql-test/r/sp-threads.result
@@ -0,0 +1,25 @@
+use test;
+drop table if exists t1;
+create table t1 (s1 int, s2 int, s3 int);
+create procedure bug4934()
+begin
+insert into t1 values (1,0,1);
+end//
+use test;
+call bug4934();
+select * from t1;
+s1 s2 s3
+1 0 1
+drop table t1;
+create table t1 (s1 int, s2 int, s3 int);
+drop procedure bug4934;
+create procedure bug4934()
+begin
+end//
+select * from t1;
+s1 s2 s3
+call bug4934();
+select * from t1;
+s1 s2 s3
+drop table t1;
+drop procedure bug4934;
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
new file mode 100644
index 00000000000..afd8c3430b3
--- /dev/null
+++ b/mysql-test/r/sp.result
@@ -0,0 +1,2013 @@
+use test;
+drop table if exists t1;
+drop table if exists t2;
+create table t1 (
+id char(16) not null,
+data int not null
+);
+create table t2 (
+s char(16),
+i int,
+d double
+);
+create procedure foo42()
+insert into test.t1 values ("foo", 42);
+call foo42();
+select * from t1;
+id data
+foo 42
+delete from t1;
+drop procedure foo42;
+create procedure bar(x char(16), y int)
+insert into test.t1 values (x, y);
+call bar("bar", 666);
+select * from t1;
+id data
+bar 666
+delete from t1;
+create procedure empty()
+begin
+end|
+call empty()|
+drop procedure empty|
+create procedure scope(a int, b float)
+begin
+declare b int;
+declare c float;
+begin
+declare c int;
+end;
+end|
+drop procedure scope|
+create procedure two(x1 char(16), x2 char(16), y int)
+begin
+insert into test.t1 values (x1, y);
+insert into test.t1 values (x2, y);
+end|
+call two("one", "two", 3)|
+select * from t1|
+id data
+one 3
+two 3
+delete from t1|
+drop procedure two|
+create procedure locset(x char(16), y int)
+begin
+declare z1, z2 int;
+set z1 = y;
+set z2 = z1+2;
+insert into test.t1 values (x, z2);
+end|
+call locset("locset", 19)|
+select * from t1|
+id data
+locset 21
+delete from t1|
+drop procedure locset|
+create procedure setcontext()
+begin
+declare data int default 2;
+insert into t1 (id, data) values ("foo", 1);
+replace t1 set data = data, id = "bar";
+update t1 set id = "kaka", data = 3 where t1.data = data;
+end|
+call setcontext()|
+select * from t1|
+id data
+foo 1
+kaka 3
+delete from t1|
+drop procedure setcontext|
+drop table if exists t3|
+create table t3 ( d date, i int, f double, s varchar(32) )|
+create procedure nullset()
+begin
+declare ld date;
+declare li int;
+declare lf double;
+declare ls varchar(32);
+set ld = null, li = null, lf = null, ls = null;
+insert into t3 values (ld, li, lf, ls);
+insert into t3 (i, f, s) values ((ld is null), 1, "ld is null"),
+((li is null), 1, "li is null"),
+((li = 0), null, "li = 0"),
+((lf is null), 1, "lf is null"),
+((lf = 0), null, "lf = 0"),
+((ls is null), 1, "ls is null");
+end|
+call nullset()|
+select * from t3|
+d i f s
+NULL NULL NULL NULL
+NULL 1 1 ld is null
+NULL 1 1 li is null
+NULL NULL NULL li = 0
+NULL 1 1 lf is null
+NULL NULL NULL lf = 0
+NULL 1 1 ls is null
+drop table t3|
+drop procedure nullset|
+create procedure mixset(x char(16), y int)
+begin
+declare z int;
+set @z = y, z = 666, max_join_size = 100;
+insert into test.t1 values (x, z);
+end|
+call mixset("mixset", 19)|
+show variables like 'max_join_size'|
+Variable_name Value
+max_join_size 100
+select id,data,@z from t1|
+id data @z
+mixset 666 19
+delete from t1|
+drop procedure mixset|
+create procedure zip(x char(16), y int)
+begin
+declare z int;
+call zap(y, z);
+call bar(x, z);
+end|
+create procedure zap(x int, out y int)
+begin
+declare z int;
+set z = x+1, y = z;
+end|
+call zip("zip", 99)|
+select * from t1|
+id data
+zip 100
+delete from t1|
+drop procedure zip|
+drop procedure bar|
+call zap(7, @zap)|
+select @zap|
+@zap
+8
+drop procedure zap|
+create procedure c1(x int)
+call c2("c", x)|
+create procedure c2(s char(16), x int)
+call c3(x, s)|
+create procedure c3(x int, s char(16))
+call c4("level", x, s)|
+create procedure c4(l char(8), x int, s char(16))
+insert into t1 values (concat(l,s), x)|
+call c1(42)|
+select * from t1|
+id data
+levelc 42
+delete from t1|
+drop procedure c1|
+drop procedure c2|
+drop procedure c3|
+drop procedure c4|
+create procedure iotest(x1 char(16), x2 char(16), y int)
+begin
+call inc2(x2, y);
+insert into test.t1 values (x1, y);
+end|
+create procedure inc2(x char(16), y int)
+begin
+call inc(y);
+insert into test.t1 values (x, y);
+end|
+create procedure inc(inout io int)
+set io = io + 1|
+call iotest("io1", "io2", 1)|
+select * from t1|
+id data
+io2 2
+io1 1
+delete from t1|
+drop procedure iotest|
+drop procedure inc2|
+create procedure incr(inout x int)
+call inc(x)|
+select @zap|
+@zap
+8
+call incr(@zap)|
+select @zap|
+@zap
+9
+drop procedure inc|
+drop procedure incr|
+create procedure cbv1()
+begin
+declare y int default 3;
+call cbv2(y+1, y);
+insert into test.t1 values ("cbv1", y);
+end|
+create procedure cbv2(y1 int, inout y2 int)
+begin
+set y2 = 4711;
+insert into test.t1 values ("cbv2", y1);
+end|
+call cbv1()|
+select * from t1|
+id data
+cbv2 4
+cbv1 4711
+delete from t1|
+drop procedure cbv1|
+drop procedure cbv2|
+insert into t2 values ("a", 1, 1.1), ("b", 2, 1.2), ("c", 3, 1.3)|
+create procedure sub1(id char(16), x int)
+insert into test.t1 values (id, x)|
+create function sub3(i int) returns int
+return i+1|
+call sub1("sub1a", (select 7))|
+call sub1("sub1b", (select max(i) from t2))|
+call sub1("sub1c", (select i,d from t2 limit 1))|
+call sub1("sub1d", (select 1 from (select 1) a))|
+select * from t1|
+id data
+sub1a 7
+sub1b 3
+sub1c 1
+sub1d 1
+select sub3((select max(i) from t2))|
+sub3((select max(i) from t2))
+4
+drop procedure sub1|
+drop function sub3|
+delete from t2|
+create procedure a0(x int)
+while x do
+set x = x-1;
+insert into test.t1 values ("a0", x);
+end while|
+call a0(3)|
+select * from t1|
+id data
+sub1a 7
+sub1b 3
+sub1c 1
+sub1d 1
+a0 2
+a0 1
+a0 0
+delete from t1|
+drop procedure a0|
+create procedure a(x int)
+while x > 0 do
+set x = x-1;
+insert into test.t1 values ("a", x);
+end while|
+call a(3)|
+select * from t1|
+id data
+a 2
+a 1
+a 0
+delete from t1|
+drop procedure a|
+create procedure b(x int)
+repeat
+insert into test.t1 values (repeat("b",3), x);
+set x = x-1;
+until x = 0 end repeat|
+call b(3)|
+select * from t1|
+id data
+bbb 3
+bbb 2
+bbb 1
+delete from t1|
+drop procedure b|
+create procedure b2(x int)
+repeat(select 1 into outfile 'b2');
+insert into test.t1 values (repeat("b2",3), x);
+set x = x-1;
+until x = 0 end repeat|
+drop procedure b2|
+create procedure c(x int)
+hmm: while x > 0 do
+insert into test.t1 values ("c", x);
+set x = x-1;
+iterate hmm;
+insert into test.t1 values ("x", x);
+end while hmm|
+call c(3)|
+select * from t1|
+id data
+c 3
+c 2
+c 1
+delete from t1|
+drop procedure c|
+create procedure d(x int)
+hmm: while x > 0 do
+insert into test.t1 values ("d", x);
+set x = x-1;
+leave hmm;
+insert into test.t1 values ("x", x);
+end while|
+call d(3)|
+select * from t1|
+id data
+d 3
+delete from t1|
+drop procedure d|
+create procedure e(x int)
+foo: loop
+if x = 0 then
+leave foo;
+end if;
+insert into test.t1 values ("e", x);
+set x = x-1;
+end loop foo|
+call e(3)|
+select * from t1|
+id data
+e 3
+e 2
+e 1
+delete from t1|
+drop procedure e|
+create procedure f(x int)
+if x < 0 then
+insert into test.t1 values ("f", 0);
+elseif x = 0 then
+insert into test.t1 values ("f", 1);
+else
+insert into test.t1 values ("f", 2);
+end if|
+call f(-2)|
+call f(0)|
+call f(4)|
+select * from t1|
+id data
+f 0
+f 1
+f 2
+delete from t1|
+drop procedure f|
+create procedure g(x int)
+case
+when x < 0 then
+insert into test.t1 values ("g", 0);
+when x = 0 then
+insert into test.t1 values ("g", 1);
+else
+insert into test.t1 values ("g", 2);
+end case|
+call g(-42)|
+call g(0)|
+call g(1)|
+select * from t1|
+id data
+g 0
+g 1
+g 2
+delete from t1|
+drop procedure g|
+create procedure h(x int)
+case x
+when 0 then
+insert into test.t1 values ("h0", x);
+when 1 then
+insert into test.t1 values ("h1", x);
+else
+insert into test.t1 values ("h?", x);
+end case|
+call h(0)|
+call h(1)|
+call h(17)|
+select * from t1|
+id data
+h0 0
+h1 1
+h? 17
+delete from t1|
+drop procedure h|
+create procedure i(x int)
+foo:
+begin
+if x = 0 then
+leave foo;
+end if;
+insert into test.t1 values ("i", x);
+end foo|
+call i(0)|
+call i(3)|
+select * from t1|
+id data
+i 3
+delete from t1|
+drop procedure i|
+create procedure goto1()
+begin
+declare y int;
+label a;
+select * from t1;
+select count(*) into y from t1;
+if y > 2 then
+goto b;
+end if;
+insert into t1 values ("j", y);
+goto a;
+label b;
+end|
+call goto1()|
+id data
+id data
+j 0
+id data
+j 0
+j 1
+id data
+j 0
+j 1
+j 2
+drop procedure goto1|
+create procedure goto2(a int)
+begin
+declare x int default 0;
+declare continue handler for sqlstate '42S98' set x = 1;
+label a;
+select * from t1;
+b:
+while x < 2 do
+begin
+declare continue handler for sqlstate '42S99' set x = 2;
+if a = 0 then
+set x = x + 1;
+iterate b;
+elseif a = 1 then
+leave b;
+elseif a = 2 then
+set a = 1;
+goto a;
+end if;
+end;
+end while b;
+select * from t1;
+end|
+call goto2(0)|
+id data
+j 0
+j 1
+j 2
+id data
+j 0
+j 1
+j 2
+call goto2(1)|
+id data
+j 0
+j 1
+j 2
+id data
+j 0
+j 1
+j 2
+call goto2(2)|
+id data
+j 0
+j 1
+j 2
+id data
+j 0
+j 1
+j 2
+id data
+j 0
+j 1
+j 2
+drop procedure goto2|
+delete from t1|
+create procedure goto3()
+begin
+label L1;
+begin
+end;
+goto L1;
+end|
+drop procedure goto3|
+create procedure goto4()
+begin
+begin
+label lab1;
+begin
+goto lab1;
+end;
+end;
+end|
+drop procedure goto4|
+create procedure goto5()
+begin
+begin
+begin
+goto lab1;
+end;
+label lab1;
+end;
+end|
+drop procedure goto5|
+create procedure goto6()
+begin
+label L1;
+goto L5;
+begin
+label L2;
+goto L1;
+goto L5;
+begin
+label L3;
+goto L1;
+goto L2;
+goto L3;
+goto L4;
+goto L5;
+end;
+goto L2;
+goto L4;
+label L4;
+end;
+label L5;
+goto L1;
+end|
+drop procedure goto6|
+insert into t1 values ("foo", 3), ("bar", 19)|
+insert into t2 values ("x", 9, 4.1), ("y", -1, 19.2), ("z", 3, 2.2)|
+create procedure sel1()
+begin
+select * from t1;
+end|
+call sel1()|
+id data
+foo 3
+bar 19
+drop procedure sel1|
+create procedure sel2()
+begin
+select * from t1;
+select * from t2;
+end|
+call sel2()|
+id data
+foo 3
+bar 19
+s i d
+x 9 4.1
+y -1 19.2
+z 3 2.2
+drop procedure sel2|
+delete from t1|
+delete from t2|
+create procedure into_test(x char(16), y int)
+begin
+insert into test.t1 values (x, y);
+select id,data into x,y from test.t1 limit 1;
+insert into test.t1 values (concat(x, "2"), y+2);
+end|
+call into_test("into", 100)|
+select * from t1|
+id data
+into 100
+into2 102
+delete from t1|
+drop procedure into_test|
+create procedure into_test2(x char(16), y int)
+begin
+insert into test.t1 values (x, y);
+select id,data into x,@z from test.t1 limit 1;
+insert into test.t1 values (concat(x, "2"), y+2);
+end|
+call into_test2("into", 100)|
+select id,data,@z from t1|
+id data @z
+into 100 100
+into2 102 100
+delete from t1|
+drop procedure into_test2|
+create procedure into_test3()
+begin
+declare x char(16);
+declare y int;
+select * into x,y from test.t1 limit 1;
+insert into test.t2 values (x, y, 0.0);
+end|
+insert into t1 values ("into3", 19)|
+call into_test3()|
+call into_test3()|
+select * from t2|
+s i d
+into3 19 0
+into3 19 0
+delete from t1|
+delete from t2|
+drop procedure into_test3|
+create procedure into_test4()
+begin
+declare x int;
+select data into x from test.t1 limit 1;
+insert into test.t3 values ("into4", x);
+end|
+delete from t1|
+drop table if exists t3|
+create table t3 ( s char(16), d int)|
+call into_test4()|
+Warnings:
+select * from t3|
+s d
+into4 NULL
+insert into t1 values ("i4", 77)|
+call into_test4()|
+select * from t3|
+s d
+into4 NULL
+into4 77
+delete from t1|
+drop table t3|
+drop procedure into_test4|
+create procedure into_outfile(x char(16), y int)
+begin
+insert into test.t1 values (x, y);
+select * into outfile "/tmp/spout" from test.t1;
+insert into test.t1 values (concat(x, "2"), y+2);
+end|
+call into_outfile("ofile", 1)|
+delete from t1|
+drop procedure into_outfile|
+create procedure into_dumpfile(x char(16), y int)
+begin
+insert into test.t1 values (x, y);
+select * into dumpfile "/tmp/spdump" from test.t1 limit 1;
+insert into test.t1 values (concat(x, "2"), y+2);
+end|
+call into_dumpfile("dfile", 1)|
+delete from t1|
+drop procedure into_dumpfile|
+create procedure create_select(x char(16), y int)
+begin
+insert into test.t1 values (x, y);
+create table test.t3 select * from test.t1;
+insert into test.t3 values (concat(x, "2"), y+2);
+end|
+drop table if exists t3|
+call create_select("cs", 90)|
+select * from t1, t3|
+id data id data
+cs 90 cs 90
+cs 90 cs2 92
+drop table if exists t3|
+delete from t1|
+drop procedure create_select|
+create function e() returns double
+return 2.7182818284590452354|
+set @e = e()|
+select e(), @e|
+e() @e
+2.718281828459 2.718281828459
+create function inc(i int) returns int
+return i+1|
+select inc(1), inc(99), inc(-71)|
+inc(1) inc(99) inc(-71)
+2 100 -70
+create function mul(x int, y int) returns int
+return x*y|
+select mul(1,1), mul(3,5), mul(4711, 666)|
+mul(1,1) mul(3,5) mul(4711, 666)
+1 15 3137526
+create function append(s1 char(8), s2 char(8)) returns char(16)
+return concat(s1, s2)|
+select append("foo", "bar")|
+append("foo", "bar")
+foobar
+create function fac(n int unsigned) returns bigint unsigned
+begin
+declare f bigint unsigned default 1;
+while n > 1 do
+set f = f * n;
+set n = n - 1;
+end while;
+return f;
+end|
+select fac(1), fac(2), fac(5), fac(10)|
+fac(1) fac(2) fac(5) fac(10)
+1 2 120 3628800
+create function fun(d double, i int, u int unsigned) returns double
+return mul(inc(i), fac(u)) / e()|
+select fun(2.3, 3, 5)|
+fun(2.3, 3, 5)
+176.58213176229
+insert into t2 values (append("xxx", "yyy"), mul(4,3), e())|
+insert into t2 values (append("a", "b"), mul(2,mul(3,4)), fun(1.7, 4, 6))|
+select * from t2 where s = append("a", "b")|
+s i d
+ab 24 1324.36598821719
+select * from t2 where i = mul(4,3) or i = mul(mul(3,4),2)|
+s i d
+xxxyyy 12 2.71828182845905
+ab 24 1324.36598821719
+select * from t2 where d = e()|
+s i d
+xxxyyy 12 2.71828182845905
+select * from t2|
+s i d
+xxxyyy 12 2.71828182845905
+ab 24 1324.36598821719
+delete from t2|
+drop function e|
+drop function inc|
+drop function mul|
+drop function append|
+drop function fun|
+create procedure hndlr1(val int)
+begin
+declare x int default 0;
+declare foo condition for 1146;
+declare bar condition for sqlstate '42S98'; # Just for testing syntax
+declare zip condition for sqlstate value '42S99'; # Just for testing syntax
+declare continue handler for foo set x = 1;
+insert into test.t666 values ("hndlr1", val); # Non-existing table
+if (x) then
+insert into test.t1 values ("hndlr1", val); # This instead then
+end if;
+end|
+call hndlr1(42)|
+select * from t1|
+id data
+hndlr1 42
+delete from t1|
+drop procedure hndlr1|
+create procedure hndlr2(val int)
+begin
+declare x int default 0;
+begin
+declare exit handler for sqlstate '42S02' set x = 1;
+insert into test.t666 values ("hndlr2", val); # Non-existing table
+end;
+insert into test.t1 values ("hndlr2", x);
+end|
+call hndlr2(42)|
+select * from t1|
+id data
+hndlr2 1
+delete from t1|
+drop procedure hndlr2|
+create procedure hndlr3(val int)
+begin
+declare x int default 0;
+declare continue handler for sqlexception # Any error
+begin
+declare z int;
+set z = 2 * val;
+set x = 1;
+end;
+if val < 10 then
+begin
+declare y int;
+set y = val + 10;
+insert into test.t666 values ("hndlr3", y); # Non-existing table
+if x then
+insert into test.t1 values ("hndlr3", y);
+end if;
+end;
+end if;
+end|
+call hndlr3(3)|
+select * from t1|
+id data
+hndlr3 13
+delete from t1|
+drop procedure hndlr3|
+drop table if exists t3|
+create table t3 ( id char(16), data int )|
+create procedure hndlr4()
+begin
+declare x int default 0;
+declare val int; # No default
+declare continue handler for sqlstate '02000' set x=1;
+select data into val from test.t3 where id='z' limit 1; # No hits
+insert into test.t3 values ('z', val);
+end|
+call hndlr4()|
+select * from t3|
+id data
+z NULL
+drop table t3|
+drop procedure hndlr4|
+create procedure cur1()
+begin
+declare a char(16);
+declare b int;
+declare c double;
+declare done int default 0;
+declare c cursor for select * from test.t2;
+declare continue handler for sqlstate '02000' set done = 1;
+open c;
+repeat
+fetch c into a, b, c;
+if not done then
+insert into test.t1 values (a, b+c);
+end if;
+until done end repeat;
+close c;
+end|
+insert into t2 values ("foo", 42, -1.9), ("bar", 3, 12.1), ("zap", 666, -3.14)|
+call cur1()|
+select * from t1|
+id data
+foo 40
+bar 15
+zap 663
+drop procedure cur1|
+drop table if exists t3|
+create table t3 ( s char(16), i int )|
+create procedure cur2()
+begin
+declare done int default 0;
+declare c1 cursor for select id,data from test.t1;
+declare c2 cursor for select i from test.t2;
+declare continue handler for sqlstate '02000' set done = 1;
+open c1;
+open c2;
+repeat
+begin
+declare a char(16);
+declare b,c int;
+fetch c1 into a, b;
+fetch c2 into c;
+if not done then
+if b < c then
+insert into test.t3 values (a, b);
+else
+insert into test.t3 values (a, c);
+end if;
+end if;
+end;
+until done end repeat;
+close c1;
+close c2;
+end|
+call cur2()|
+select * from t3|
+s i
+foo 40
+bar 3
+zap 663
+delete from t1|
+delete from t2|
+drop table t3|
+drop procedure cur2|
+create procedure chistics()
+language sql
+not deterministic
+sql security definer
+comment 'Characteristics procedure test'
+ insert into t1 values ("chistics", 1)|
+call chistics()|
+select * from t1|
+id data
+chistics 1
+delete from t1|
+alter procedure chistics sql security invoker name chistics2|
+show create procedure chistics2|
+Procedure sql_mode Create Procedure
+chistics2 CREATE PROCEDURE `test`.`chistics2`()
+ SQL SECURITY INVOKER
+ COMMENT 'Characteristics procedure test'
+insert into t1 values ("chistics", 1)
+drop procedure chistics2|
+create function chistics() returns int
+language sql
+deterministic
+sql security invoker
+comment 'Characteristics procedure test'
+ return 42|
+select chistics()|
+chistics()
+42
+alter function chistics name chistics2 comment 'Characteristics function test'|
+show create function chistics2|
+Function sql_mode Create Function
+chistics2 CREATE FUNCTION `test`.`chistics2`() RETURNS int
+ DETERMINISTIC
+ SQL SECURITY INVOKER
+ COMMENT 'Characteristics function test'
+return 42
+drop function chistics2|
+insert into t1 values ("foo", 1), ("bar", 2), ("zip", 3)|
+set @@sql_mode = 'ANSI'|
+create procedure modes(out c1 int, out c2 int)
+begin
+declare done int default 0;
+declare x int;
+declare c cursor for select data from t1;
+declare continue handler for sqlstate '02000' set done = 1;
+select 1 || 2 into c1;
+set c2 = 0;
+open c;
+repeat
+fetch c into x;
+if not done then
+set c2 = c2 + 1;
+end if;
+until done end repeat;
+close c;
+end$
+set @@sql_mode = ''|
+set sql_select_limit = 1|
+call modes(@c1, @c2)|
+set sql_select_limit = default|
+select @c1, @c2|
+@c1 @c2
+12 3
+delete from t1|
+drop procedure modes|
+create database sp_db1|
+drop database sp_db1|
+create database sp_db2|
+use sp_db2|
+create table t3 ( s char(4), t int )|
+insert into t3 values ("abcd", 42), ("dcba", 666)|
+use test|
+drop database sp_db2|
+create database sp_db3|
+use sp_db3|
+create procedure dummy(out x int)
+set x = 42|
+use test|
+drop database sp_db3|
+select type,db,name from mysql.proc where db = 'sp_db3'|
+type db name
+create procedure rc()
+begin
+delete from t1;
+insert into t1 values ("a", 1), ("b", 2), ("c", 3);
+end|
+call rc()|
+select row_count()|
+row_count()
+3
+update t1 set data=42 where id = "b";
+select row_count()|
+row_count()
+1
+delete from t1|
+select row_count()|
+row_count()
+3
+delete from t1|
+select row_count()|
+row_count()
+0
+select * from t1|
+id data
+select row_count()|
+row_count()
+-1
+drop procedure rc|
+create procedure bug822(a_id char(16), a_data int)
+begin
+declare n int;
+select count(*) into n from t1 where id = a_id and data = a_data;
+if n = 0 then
+insert into t1 (id, data) values (a_id, a_data);
+end if;
+end|
+call bug822('foo', 42)|
+call bug822('foo', 42)|
+call bug822('bar', 666)|
+select * from t1|
+id data
+foo 42
+bar 666
+delete from t1|
+drop procedure bug822|
+create procedure bug1495()
+begin
+declare x int;
+select data into x from t1 order by id limit 1;
+if x > 10 then
+insert into t1 values ("less", x-10);
+else
+insert into t1 values ("more", x+10);
+end if;
+end|
+insert into t1 values ('foo', 12)|
+call bug1495()|
+delete from t1 where id='foo'|
+insert into t1 values ('bar', 7)|
+call bug1495()|
+delete from t1 where id='bar'|
+select * from t1|
+id data
+less 2
+more 17
+delete from t1|
+drop procedure bug1495|
+create procedure bug1547(s char(16))
+begin
+declare x int;
+select data into x from t1 where s = id limit 1;
+if x > 10 then
+insert into t1 values ("less", x-10);
+else
+insert into t1 values ("more", x+10);
+end if;
+end|
+insert into t1 values ("foo", 12), ("bar", 7)|
+call bug1547("foo")|
+call bug1547("bar")|
+select * from t1|
+id data
+foo 12
+bar 7
+less 2
+more 17
+delete from t1|
+drop procedure bug1547|
+drop table if exists t70|
+create table t70 (s1 int,s2 int)|
+insert into t70 values (1,2)|
+create procedure bug1656(out p1 int, out p2 int)
+select * into p1, p1 from t70|
+call bug1656(@1, @2)|
+select @1, @2|
+@1 @2
+2 NULL
+drop table t70|
+drop procedure bug1656|
+drop table if exists t3|
+create table t3(a int)|
+create procedure bug1862()
+begin
+insert into t3 values(2);
+flush tables;
+end|
+call bug1862()|
+call bug1862()|
+select * from t3|
+a
+2
+2
+drop table t3|
+drop procedure bug1862|
+create procedure bug1874()
+begin
+declare x int;
+declare y double;
+select max(data) into x from t1;
+insert into t2 values ("max", x, 0);
+select min(data) into x from t1;
+insert into t2 values ("min", x, 0);
+select sum(data) into x from t1;
+insert into t2 values ("sum", x, 0);
+select avg(data) into y from t1;
+insert into t2 values ("avg", 0, y);
+end|
+insert into t1 (data) values (3), (1), (5), (9), (4)|
+call bug1874()|
+select * from t2|
+s i d
+max 9 0
+min 1 0
+sum 22 0
+avg 0 4.4
+delete from t1|
+delete from t2|
+drop procedure bug1874|
+create procedure bug2260()
+begin
+declare v1 int;
+declare c1 cursor for select data from t1;
+declare continue handler for not found set @x2 = 1;
+open c1;
+fetch c1 into v1;
+set @x2 = 2;
+close c1;
+end|
+call bug2260()|
+select @x2|
+@x2
+2
+drop procedure bug2260|
+create procedure bug2267_1()
+begin
+show procedure status;
+end|
+create procedure bug2267_2()
+begin
+show function status;
+end|
+create procedure bug2267_3()
+begin
+show create procedure bug2267_1;
+end|
+create procedure bug2267_4()
+begin
+show create function fac;
+end|
+call bug2267_1()|
+Db Name Type Definer Modified Created Security_type Comment
+test bug2267_1 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+test bug2267_2 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+test bug2267_3 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+test bug2267_4 PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+call bug2267_2()|
+Db Name Type Definer Modified Created Security_type Comment
+test fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+call bug2267_3()|
+Procedure sql_mode Create Procedure
+bug2267_1 CREATE PROCEDURE `test`.`bug2267_1`()
+begin
+show procedure status;
+end
+call bug2267_4()|
+Function sql_mode Create Function
+fac CREATE FUNCTION `test`.`fac`(n int unsigned) RETURNS bigint unsigned
+begin
+declare f bigint unsigned default 1;
+while n > 1 do
+set f = f * n;
+set n = n - 1;
+end while;
+return f;
+end
+drop procedure bug2267_1|
+drop procedure bug2267_2|
+drop procedure bug2267_3|
+drop procedure bug2267_4|
+create procedure bug2227(x int)
+begin
+declare y float default 2.6;
+declare z char(16) default "zzz";
+select 1.3, x, y, 42, z;
+end|
+call bug2227(9)|
+1.3 x y 42 z
+1.3 9 2.6 42 zzz
+drop procedure bug2227|
+create procedure bug2614()
+begin
+drop table if exists t3;
+create table t3 (id int default '0' not null);
+insert into t3 select 12;
+insert into t3 select * from t3;
+end|
+call bug2614()|
+call bug2614()|
+drop table t3|
+drop procedure bug2614|
+create function bug2674 () returns int
+return @@sort_buffer_size|
+set @osbs = @@sort_buffer_size|
+set @@sort_buffer_size = 262000|
+select bug2674()|
+bug2674()
+262000
+drop function bug2674|
+set @@sort_buffer_size = @osbs|
+create procedure bug3259_1 () begin end|
+create procedure BUG3259_2 () begin end|
+create procedure Bug3259_3 () begin end|
+call BUG3259_1()|
+call BUG3259_1()|
+call bug3259_2()|
+call Bug3259_2()|
+call bug3259_3()|
+call bUG3259_3()|
+drop procedure bUg3259_1|
+drop procedure BuG3259_2|
+drop procedure BUG3259_3|
+create function bug2772() returns char(10) character set latin2
+return 'a'|
+select bug2772()|
+bug2772()
+a
+drop function bug2772|
+create procedure bug2776_1(out x int)
+begin
+declare v int;
+set v = default;
+set x = v;
+end|
+create procedure bug2776_2(out x int)
+begin
+declare v int default 42;
+set v = default;
+set x = v;
+end|
+set @x = 1|
+call bug2776_1(@x)|
+select @x|
+@x
+NULL
+call bug2776_2(@x)|
+select @x|
+@x
+42
+drop procedure bug2776_1|
+drop procedure bug2776_2|
+drop table if exists t3|
+create table t3 (s1 smallint)|
+insert into t3 values (123456789012)|
+Warnings:
+Warning 1264 Data truncated; out of range for column 's1' at row 1
+create procedure bug2780()
+begin
+declare exit handler for sqlwarning set @x = 1;
+set @x = 0;
+insert into t3 values (123456789012);
+insert into t3 values (0);
+end|
+call bug2780()|
+select @x|
+@x
+1
+select * from t3|
+s1
+32767
+32767
+drop procedure bug2780|
+drop table t3|
+drop table if exists t3|
+create table t3 (content varchar(10) )|
+insert into t3 values ("test1")|
+insert into t3 values ("test2")|
+drop table if exists t4|
+create table t4 (f1 int, rc int, t3 int)|
+create procedure bug1863(in1 int)
+begin
+declare ind int default 0;
+declare t1 int;
+declare t2 int;
+declare t3 int;
+declare rc int default 0;
+declare continue handler for 1065 set rc = 1;
+drop table if exists temp_t1;
+create temporary table temp_t1 (
+f1 int auto_increment, f2 varchar(20), primary key (f1)
+);
+insert into temp_t1 (f2) select content from t3;
+select f2 into t3 from temp_t1 where f1 = 10;
+if (rc) then
+insert into t4 values (1, rc, t3);
+end if;
+insert into t4 values (2, rc, t3);
+end|
+call bug1863(10)|
+Warnings:
+call bug1863(10)|
+Warnings:
+select * from t4|
+f1 rc t3
+2 0 NULL
+2 0 NULL
+drop procedure bug1863|
+drop table t3, t4|
+drop table if exists t3, t4|
+create table t3 (
+OrderID int not null,
+MarketID int,
+primary key (OrderID)
+)|
+create table t4 (
+MarketID int not null,
+Market varchar(60),
+Status char(1),
+primary key (MarketID)
+)|
+insert t3 (OrderID,MarketID) values (1,1)|
+insert t3 (OrderID,MarketID) values (2,2)|
+insert t4 (MarketID,Market,Status) values (1,"MarketID One","A")|
+insert t4 (MarketID,Market,Status) values (2,"MarketID Two","A")|
+create procedure bug2656_1()
+begin
+select
+m.Market
+from t4 m JOIN t3 o
+ON o.MarketID != 1 and o.MarketID = m.MarketID;
+end |
+create procedure bug2656_2()
+begin
+select
+m.Market
+from
+t4 m, t3 o
+where
+m.MarketID != 1 and m.MarketID = o.MarketID;
+end |
+call bug2656_1()|
+Market
+MarketID Two
+call bug2656_1()|
+Market
+MarketID Two
+call bug2656_2()|
+Market
+MarketID Two
+call bug2656_2()|
+Market
+MarketID Two
+drop procedure bug2656_1|
+drop procedure bug2656_2|
+drop table t3, t4|
+create procedure bug3426(in_time int unsigned, out x int)
+begin
+if in_time is null then
+set @stamped_time=10;
+set x=1;
+else
+set @stamped_time=in_time;
+set x=2;
+end if;
+end|
+call bug3426(1000, @i)|
+select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time|
+@i time
+2 01-01-1970 03:16:40
+call bug3426(NULL, @i)|
+select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time|
+@i time
+1 01-01-1970 03:00:10
+alter procedure bug3426 sql security invoker|
+call bug3426(NULL, @i)|
+select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time|
+@i time
+1 01-01-1970 03:00:10
+call bug3426(1000, @i)|
+select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time|
+@i time
+2 01-01-1970 03:16:40
+drop procedure bug3426|
+drop table if exists t3, t4|
+create table t3 (
+a int primary key,
+ach char(1)
+) engine = innodb|
+create table t4 (
+b int primary key ,
+bch char(1)
+) engine = innodb|
+insert into t3 values (1 , 'aCh1' ) , ('2' , 'aCh2')|
+Warnings:
+Warning 1265 Data truncated for column 'ach' at row 1
+Warning 1265 Data truncated for column 'ach' at row 2
+insert into t4 values (1 , 'bCh1' )|
+Warnings:
+Warning 1265 Data truncated for column 'bch' at row 1
+create procedure bug3448()
+select * from t3 inner join t4 on t3.a = t4.b|
+select * from t3 inner join t4 on t3.a = t4.b|
+a ach b bch
+1 a 1 b
+call bug3448()|
+a ach b bch
+1 a 1 b
+call bug3448()|
+a ach b bch
+1 a 1 b
+drop procedure bug3448|
+drop table t3, t4|
+drop table if exists t3|
+create table t3 (
+id int unsigned auto_increment not null primary key,
+title VARCHAR(200),
+body text,
+fulltext (title,body)
+)|
+insert into t3 (title,body) values
+('MySQL Tutorial','DBMS stands for DataBase ...'),
+('How To Use MySQL Well','After you went through a ...'),
+('Optimizing MySQL','In this tutorial we will show ...'),
+('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
+('MySQL vs. YourSQL','In the following database comparison ...'),
+('MySQL Security','When configured properly, MySQL ...')|
+create procedure bug3734 (param1 varchar(100))
+select * from t3 where match (title,body) against (param1)|
+call bug3734('database')|
+id title body
+5 MySQL vs. YourSQL In the following database comparison ...
+1 MySQL Tutorial DBMS stands for DataBase ...
+call bug3734('Security')|
+id title body
+6 MySQL Security When configured properly, MySQL ...
+drop procedure bug3734|
+drop table t3|
+create procedure bug3863()
+begin
+set @a = 0;
+while @a < 5 do
+set @a = @a + 1;
+end while;
+end|
+call bug3863()|
+select @a|
+@a
+5
+call bug3863()|
+select @a|
+@a
+5
+drop procedure bug3863|
+drop table if exists t3|
+create table t3 (
+id int(10) unsigned not null default 0,
+rid int(10) unsigned not null default 0,
+msg text not null,
+primary key (id),
+unique key rid (rid, id)
+)|
+create procedure bug2460_1(in v int)
+begin
+( select n0.id from t3 as n0 where n0.id = v )
+union
+( select n0.id from t3 as n0, t3 as n1
+where n0.id = n1.rid and n1.id = v )
+union
+( select n0.id from t3 as n0, t3 as n1, t3 as n2
+where n0.id = n1.rid and n1.id = n2.rid and n2.id = v );
+end|
+call bug2460_1(2)|
+id
+call bug2460_1(2)|
+id
+insert into t3 values (1, 1, 'foo'), (2, 1, 'bar'), (3, 1, 'zip zap')|
+call bug2460_1(2)|
+id
+2
+1
+call bug2460_1(2)|
+id
+2
+1
+create procedure bug2460_2()
+begin
+drop table if exists t3;
+create table t3 (s1 int);
+insert into t3 select 1 union select 1;
+end|
+call bug2460_2()|
+call bug2460_2()|
+select * from t3|
+s1
+1
+drop procedure bug2460_1|
+drop procedure bug2460_2|
+drop table t3|
+set @@sql_mode = ''|
+create procedure bug2564_1()
+comment 'Joe''s procedure'
+ insert into `t1` values ("foo", 1)|
+set @@sql_mode = 'ANSI_QUOTES'|
+create procedure bug2564_2()
+insert into "t1" values ('foo', 1)|
+set @@sql_mode = ''$
+create function bug2564_3(x int, y int) returns int
+return x || y$
+set @@sql_mode = 'ANSI'$
+create function bug2564_4(x int, y int) returns int
+return x || y$
+set @@sql_mode = ''|
+show create procedure bug2564_1|
+Procedure sql_mode Create Procedure
+bug2564_1 CREATE PROCEDURE `test`.`bug2564_1`()
+ COMMENT 'Joe''s procedure'
+insert into `t1` values ("foo", 1)
+show create procedure bug2564_2|
+Procedure sql_mode Create Procedure
+bug2564_2 ANSI_QUOTES CREATE PROCEDURE "test"."bug2564_2"()
+insert into "t1" values ('foo', 1)
+show create function bug2564_3|
+Function sql_mode Create Function
+bug2564_3 CREATE FUNCTION `test`.`bug2564_3`(x int, y int) RETURNS int
+return x || y
+show create function bug2564_4|
+Function sql_mode Create Function
+bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI CREATE FUNCTION "test"."bug2564_4"(x int, y int) RETURNS int
+return x || y
+drop procedure bug2564_1|
+drop procedure bug2564_2|
+drop function bug2564_3|
+drop function bug2564_4|
+create function bug3132(s char(20)) returns char(50)
+return concat('Hello, ', s, '!')|
+select bug3132('Bob') union all select bug3132('Judy')|
+bug3132('Bob')
+Hello, Bob!
+Hello, Judy!
+drop function bug3132|
+create procedure bug3843()
+analyze table t1|
+call bug3843()|
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+call bug3843()|
+Table Op Msg_type Msg_text
+test.t1 analyze status Table is already up to date
+select 1+2|
+1+2
+3
+drop procedure bug3843|
+drop table if exists t3|
+create table t3 ( s1 char(10) )|
+insert into t3 values ('a'), ('b')|
+create procedure bug3368(v char(10))
+begin
+select group_concat(v) from t3;
+end|
+call bug3368('x')|
+group_concat(v)
+x,x
+call bug3368('yz')|
+group_concat(v)
+yz,yz
+drop procedure bug3368|
+drop table t3|
+drop table if exists t3|
+create table t3 (f1 int, f2 int);
+insert into t3 values (1,1);
+create procedure bug4579_1 ()
+begin
+declare sf1 int;
+select f1 into sf1 from t3 where f1=1 and f2=1;
+update t3 set f2 = f2 + 1 where f1=1 and f2=1;
+call bug4579_2();
+end|
+create procedure bug4579_2 ()
+begin
+end|
+call bug4579_1()|
+call bug4579_1()|
+Warnings:
+call bug4579_1()|
+Warnings:
+drop procedure bug4579_1|
+drop procedure bug4579_2|
+drop table t3|
+drop table if exists t3|
+create table t3 (f1 int, f2 int, f3 int)|
+insert into t3 values (1,1,1)|
+create procedure bug4726()
+begin
+declare tmp_o_id INT;
+declare tmp_d_id INT default 1;
+while tmp_d_id <= 2 do
+begin
+select f1 into tmp_o_id from t3 where f2=1 and f3=1;
+set tmp_d_id = tmp_d_id + 1;
+end;
+end while;
+end|
+call bug4726()|
+call bug4726()|
+call bug4726()|
+drop procedure bug4726|
+drop table t3|
+drop table if exists t3|
+create table t3 (s1 int)|
+insert into t3 values (3), (4)|
+create procedure bug4318()
+handler t3 read next|
+handler t3 open|
+call bug4318()|
+s1
+3
+call bug4318()|
+s1
+4
+handler t3 close|
+drop procedure bug4318|
+drop table t3|
+create procedure bug4902()
+begin
+show charset like 'foo';
+show collation like 'foo';
+show column types;
+show create table t1;
+show create database test;
+show databases like 'foo';
+show errors;
+show columns from t1;
+show grants for 'root'@'localhost';
+show keys from t1;
+show open tables like 'foo';
+show privileges;
+show status like 'foo';
+show tables like 'foo';
+show variables like 'foo';
+show warnings;
+end|
+call bug4902()|
+Charset Description Default collation Maxlen
+Collation Charset Id Default Compiled Sortlen
+Type Size Min_Value Max_Value Prec Scale Nullable Auto_Increment Unsigned Zerofill Searchable Case_Sensitive Default Comment
+tinyint 1 -128 127 0 0 YES YES NO YES YES NO NULL,0 A very small integer
+tinyint unsigned 1 0 255 0 0 YES YES YES YES YES NO NULL,0 A very small integer
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` char(16) NOT NULL default '',
+ `data` int(11) NOT NULL default '0'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+Database Create Database
+test CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */
+Database (foo)
+Level Code Message
+Field Type Null Key Default Extra
+id char(16)
+data int(11) 0
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
+Database Table In_use Name_locked
+Privilege Context Comment
+Alter Tables To alter the table
+Create Databases,Tables,Indexes To create new databases and tables
+Create temporary tables Databases To use CREATE TEMPORARY TABLE
+Create view Tables To create new views
+Delete Tables To delete existing rows
+Drop Databases,Tables To drop databases, tables, and views
+File File access on server To read and write files on the server
+Grant option Databases,Tables To give to other users those privileges you possess
+Index Tables To create or drop indexes
+Insert Tables To insert data into tables
+Lock tables Databases To use LOCK TABLES (together with SELECT privilege)
+Process Server Admin To view the plain text of currently executing queries
+References Databases,Tables To have references on tables
+Reload Server Admin To reload or refresh tables, logs and privileges
+Replication client Server Admin To ask where the slave or master servers are
+Replication slave Server Admin To read binary log events from the master
+Select Tables To retrieve rows from table
+Show databases Server Admin To see all databases with SHOW DATABASES
+Show view Tables To see views with SHOW CREATE VIEW
+Shutdown Server Admin To shut down the server
+Super Server Admin To use KILL thread, SET GLOBAL, CHANGE MASTER, etc.
+Update Tables To update existing rows
+Usage Server Admin No privileges - allow connect only
+Variable_name Value
+Tables_in_test (foo) table_type
+Variable_name Value
+Level Code Message
+call bug4902()|
+Charset Description Default collation Maxlen
+Collation Charset Id Default Compiled Sortlen
+Type Size Min_Value Max_Value Prec Scale Nullable Auto_Increment Unsigned Zerofill Searchable Case_Sensitive Default Comment
+tinyint 1 -128 127 0 0 YES YES NO YES YES NO NULL,0 A very small integer
+tinyint unsigned 1 0 255 0 0 YES YES YES YES YES NO NULL,0 A very small integer
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` char(16) NOT NULL default '',
+ `data` int(11) NOT NULL default '0'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+Database Create Database
+test CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET latin1 */
+Database (foo)
+Level Code Message
+Field Type Null Key Default Extra
+id char(16)
+data int(11) 0
+Grants for root@localhost
+GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
+Database Table In_use Name_locked
+Privilege Context Comment
+Alter Tables To alter the table
+Create Databases,Tables,Indexes To create new databases and tables
+Create temporary tables Databases To use CREATE TEMPORARY TABLE
+Create view Tables To create new views
+Delete Tables To delete existing rows
+Drop Databases,Tables To drop databases, tables, and views
+File File access on server To read and write files on the server
+Grant option Databases,Tables To give to other users those privileges you possess
+Index Tables To create or drop indexes
+Insert Tables To insert data into tables
+Lock tables Databases To use LOCK TABLES (together with SELECT privilege)
+Process Server Admin To view the plain text of currently executing queries
+References Databases,Tables To have references on tables
+Reload Server Admin To reload or refresh tables, logs and privileges
+Replication client Server Admin To ask where the slave or master servers are
+Replication slave Server Admin To read binary log events from the master
+Select Tables To retrieve rows from table
+Show databases Server Admin To see all databases with SHOW DATABASES
+Show view Tables To see views with SHOW CREATE VIEW
+Shutdown Server Admin To shut down the server
+Super Server Admin To use KILL thread, SET GLOBAL, CHANGE MASTER, etc.
+Update Tables To update existing rows
+Usage Server Admin No privileges - allow connect only
+Variable_name Value
+Tables_in_test (foo) table_type
+Variable_name Value
+Level Code Message
+drop procedure bug4902|
+create procedure bug4902_2()
+begin
+show processlist;
+end|
+call bug4902_2()|
+Id User Host db Command Time State Info
+# root localhost test Query # NULL call bug4902_2()
+call bug4902_2()|
+Id User Host db Command Time State Info
+# root localhost test Query # NULL call bug4902_2()
+drop procedure bug4902_2|
+drop table if exists t3|
+create procedure bug4904()
+begin
+declare continue handler for sqlstate 'HY000' begin end;
+create table t2 as select * from t;
+end|
+call bug4904()|
+drop procedure bug4904|
+create procedure bug336(out y int)
+begin
+declare x int;
+set x = (select sum(t.data) from test.t1 t);
+set y = x;
+end|
+insert into t1 values ("a", 2), ("b", 3)|
+call bug336(@y)|
+select @y|
+@y
+5
+delete from t1|
+drop procedure bug336|
+create procedure bug3157()
+begin
+if exists(select * from t1) then
+set @n= @n + 1;
+end if;
+if (select count(*) from t1) then
+set @n= @n + 1;
+end if;
+end|
+set @n = 0|
+insert into t1 values ("a", 1)|
+call bug3157()|
+select @n|
+@n
+2
+delete from t1|
+drop procedure bug3157|
+create procedure bug5251()
+begin
+end|
+select created into @c1 from mysql.proc
+where db='test' and name='bug5251'|
+alter procedure bug5251 comment 'foobar'|
+select count(*) from mysql.proc
+where db='test' and name='bug5251' and created = @c1|
+count(*)
+1
+drop procedure bug5251|
+create procedure bug5251()
+checksum table t1|
+call bug5251()|
+Table Checksum
+test.t1 0
+call bug5251()|
+Table Checksum
+test.t1 0
+drop procedure bug5251|
+create procedure bug5287(param1 int)
+label1:
+begin
+declare c cursor for select 5;
+loop
+if param1 >= 0 then
+leave label1;
+end if;
+end loop;
+end|
+call bug5287(1)|
+drop procedure bug5287|
+create procedure bug5307()
+begin
+end; set @x = 3|
+call bug5307()|
+select @x|
+@x
+3
+drop procedure bug5307|
+drop table if exists fac|
+create table fac (n int unsigned not null primary key, f bigint unsigned)|
+create procedure ifac(n int unsigned)
+begin
+declare i int unsigned default 1;
+if n > 20 then
+set n = 20; # bigint overflow otherwise
+end if;
+while i <= n do
+begin
+insert into test.fac values (i, fac(i));
+set i = i + 1;
+end;
+end while;
+end|
+call ifac(20)|
+select * from fac|
+n f
+1 1
+2 2
+3 6
+4 24
+5 120
+6 720
+7 5040
+8 40320
+9 362880
+10 3628800
+11 39916800
+12 479001600
+13 6227020800
+14 87178291200
+15 1307674368000
+16 20922789888000
+17 355687428096000
+18 6402373705728000
+19 121645100408832000
+20 2432902008176640000
+drop table fac|
+show function status like '%f%'|
+Db Name Type Definer Modified Created Security_type Comment
+test fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+drop procedure ifac|
+drop function fac|
+show function status like '%f%'|
+Db Name Type Definer Modified Created Security_type Comment
+drop table if exists primes|
+create table primes (
+i int unsigned not null primary key,
+p bigint unsigned not null
+)|
+insert into primes values
+( 0, 3), ( 1, 5), ( 2, 7), ( 3, 11), ( 4, 13),
+( 5, 17), ( 6, 19), ( 7, 23), ( 8, 29), ( 9, 31),
+(10, 37), (11, 41), (12, 43), (13, 47), (14, 53),
+(15, 59), (16, 61), (17, 67), (18, 71), (19, 73),
+(20, 79), (21, 83), (22, 89), (23, 97), (24, 101),
+(25, 103), (26, 107), (27, 109), (28, 113), (29, 127),
+(30, 131), (31, 137), (32, 139), (33, 149), (34, 151),
+(35, 157), (36, 163), (37, 167), (38, 173), (39, 179),
+(40, 181), (41, 191), (42, 193), (43, 197), (44, 199)|
+create procedure opp(n bigint unsigned, out pp bool)
+begin
+declare r double;
+declare b, s bigint unsigned default 0;
+set r = sqrt(n);
+again:
+loop
+if s = 45 then
+set b = b+200, s = 0;
+else
+begin
+declare p bigint unsigned;
+select t.p into p from test.primes t where t.i = s;
+if b+p > r then
+set pp = 1;
+leave again;
+end if;
+if mod(n, b+p) = 0 then
+set pp = 0;
+leave again;
+end if;
+set s = s+1;
+end;
+end if;
+end loop;
+end|
+create procedure ip(m int unsigned)
+begin
+declare p bigint unsigned;
+declare i int unsigned;
+set i=45, p=201;
+while i < m do
+begin
+declare pp bool default 0;
+call opp(p, pp);
+if pp then
+insert into test.primes values (i, p);
+set i = i+1;
+end if;
+set p = p+2;
+end;
+end while;
+end|
+show create procedure opp|
+Procedure sql_mode Create Procedure
+opp CREATE PROCEDURE `test`.`opp`(n bigint unsigned, out pp bool)
+begin
+declare r double;
+declare b, s bigint unsigned default 0;
+set r = sqrt(n);
+again:
+loop
+if s = 45 then
+set b = b+200, s = 0;
+else
+begin
+declare p bigint unsigned;
+select t.p into p from test.primes t where t.i = s;
+if b+p > r then
+set pp = 1;
+leave again;
+end if;
+if mod(n, b+p) = 0 then
+set pp = 0;
+leave again;
+end if;
+set s = s+1;
+end;
+end if;
+end loop;
+end
+show procedure status like '%p%'|
+Db Name Type Definer Modified Created Security_type Comment
+test ip PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+test opp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+call ip(200)|
+select * from primes where i=45 or i=100 or i=199|
+i p
+45 211
+100 557
+199 1229
+drop table primes|
+drop procedure opp|
+drop procedure ip|
+show procedure status like '%p%'|
+Db Name Type Definer Modified Created Security_type Comment
+drop table if exists fib|
+create table fib ( f bigint unsigned not null )|
+insert into fib values (1), (1)|
+create procedure fib(n int unsigned)
+begin
+if n > 0 then
+begin
+declare x, y bigint unsigned;
+declare c cursor for select f from fib order by f desc limit 2;
+open c;
+fetch c into y;
+fetch c into x;
+close c;
+insert into fib values (x+y);
+call fib(n-1);
+end;
+end if;
+end|
+call fib(20)|
+select * from fib order by f asc|
+f
+1
+1
+2
+3
+5
+8
+13
+21
+34
+55
+89
+144
+233
+377
+610
+987
+1597
+2584
+4181
+6765
+10946
+17711
+drop table fib|
+drop procedure fib|
+create procedure bar(x char(16), y int)
+comment "111111111111" sql security invoker
+insert into test.t1 values (x, y)|
+show procedure status like 'bar'|
+Db Name Type Definer Modified Created Security_type Comment
+test bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER 111111111111
+alter procedure bar name bar2 comment "2222222222" sql security definer|
+alter procedure bar2 name bar comment "3333333333"|
+alter procedure bar|
+show create procedure bar|
+Procedure sql_mode Create Procedure
+bar CREATE PROCEDURE `test`.`bar`(x char(16), y int)
+ COMMENT '3333333333'
+insert into test.t1 values (x, y)
+show procedure status like 'bar'|
+Db Name Type Definer Modified Created Security_type Comment
+test bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER 3333333333
+drop procedure bar|
+drop table t1;
+drop table t2;
+create procedure p1 () select (select s1 from t1) from t1;
+create table t1 (s1 int);
+call p1();
+(select s1 from t1)
+insert into t1 values (1);
+call p1();
+(select s1 from t1)
+1
+drop procedure p1;
+drop table t1;
+create function `foo` () returns int return 5;
+select `foo` ();
+`foo` ()
+5
+drop function `foo`;
diff --git a/mysql-test/r/sql_mode.result b/mysql-test/r/sql_mode.result
index 77fe5d06bb0..c4731b93d2b 100644
--- a/mysql-test/r/sql_mode.result
+++ b/mysql-test/r/sql_mode.result
@@ -85,3 +85,252 @@ t1 CREATE TABLE "t1" (
UNIQUE KEY "email" ("email")
)
drop table t1;
+SET @OLD_SQL_MODE=@@SQL_MODE, @@SQL_MODE='';
+show local variables like 'SQL_MODE';
+Variable_name Value
+sql_mode
+CREATE TABLE t1 (p int not null auto_increment, a varchar(20), primary key(p));
+INSERT t1 (a) VALUES
+('\\'),
+('\n'),
+('\b'),
+('\r'),
+('\t'),
+('\x'),
+('\a'),
+('\aa'),
+('\\a'),
+('\\aa'),
+('_'),
+('\_'),
+('\\_'),
+('\\\_'),
+('\\\\_'),
+('%'),
+('\%'),
+('\\%'),
+('\\\%'),
+('\\\\%')
+;
+SELECT p, hex(a) FROM t1;
+p hex(a)
+1 5C
+2 0A
+3 08
+4 0D
+5 09
+6 78
+7 61
+8 6161
+9 5C61
+10 5C6161
+11 5F
+12 5C5F
+13 5C5F
+14 5C5C5F
+15 5C5C5F
+16 25
+17 5C25
+18 5C25
+19 5C5C25
+20 5C5C25
+delete from t1 where a in ('\n','\r','\t', '\b');
+select
+masks.p,
+masks.a as mask,
+examples.a as example
+from
+t1 as masks
+left join t1 as examples on examples.a LIKE masks.a
+order by masks.p, example;
+p mask example
+1 \ \
+6 x x
+7 a a
+8 aa aa
+9 \a a
+10 \aa aa
+11 _ %
+11 _ a
+11 _ x
+11 _ \
+11 _ _
+12 \_ _
+13 \_ _
+14 \\_ \%
+14 \\_ \%
+14 \\_ \a
+14 \\_ \_
+14 \\_ \_
+15 \\_ \%
+15 \\_ \%
+15 \\_ \a
+15 \\_ \_
+15 \\_ \_
+16 % %
+16 % a
+16 % aa
+16 % x
+16 % \
+16 % \%
+16 % \%
+16 % \a
+16 % \aa
+16 % \\%
+16 % \\%
+16 % \\_
+16 % \\_
+16 % \_
+16 % \_
+16 % _
+17 \% %
+18 \% %
+19 \\% \
+19 \\% \%
+19 \\% \%
+19 \\% \a
+19 \\% \aa
+19 \\% \\%
+19 \\% \\%
+19 \\% \\_
+19 \\% \\_
+19 \\% \_
+19 \\% \_
+20 \\% \
+20 \\% \%
+20 \\% \%
+20 \\% \a
+20 \\% \aa
+20 \\% \\%
+20 \\% \\%
+20 \\% \\_
+20 \\% \\_
+20 \\% \_
+20 \\% \_
+DROP TABLE t1;
+SET @@SQL_MODE='NO_BACKSLASH_ESCAPES';
+show local variables like 'SQL_MODE';
+Variable_name Value
+sql_mode NO_BACKSLASH_ESCAPES
+CREATE TABLE t1 (p int not null auto_increment, a varchar(20), primary key(p));
+INSERT t1 (a) VALUES
+('\\'),
+('\n'),
+('\b'),
+('\r'),
+('\t'),
+('\x'),
+('\a'),
+('\aa'),
+('\\a'),
+('\\aa'),
+('_'),
+('\_'),
+('\\_'),
+('\\\_'),
+('\\\\_'),
+('%'),
+('\%'),
+('\\%'),
+('\\\%'),
+('\\\\%')
+;
+SELECT p, hex(a) FROM t1;
+p hex(a)
+1 5C5C
+2 5C6E
+3 5C62
+4 5C72
+5 5C74
+6 5C78
+7 5C61
+8 5C6161
+9 5C5C61
+10 5C5C6161
+11 5F
+12 5C5F
+13 5C5C5F
+14 5C5C5C5F
+15 5C5C5C5C5F
+16 25
+17 5C25
+18 5C5C25
+19 5C5C5C25
+20 5C5C5C5C25
+delete from t1 where a in ('\n','\r','\t', '\b');
+select
+masks.p,
+masks.a as mask,
+examples.a as example
+from
+t1 as masks
+left join t1 as examples on examples.a LIKE masks.a
+order by masks.p, example;
+p mask example
+1 \\ \\
+6 \x \x
+7 \a \a
+8 \aa \aa
+9 \\a \\a
+10 \\aa \\aa
+11 _ %
+11 _ _
+12 \_ \%
+12 \_ \a
+12 \_ \x
+12 \_ \\
+12 \_ \_
+13 \\_ \\%
+13 \\_ \\a
+13 \\_ \\_
+14 \\\_ \\\%
+14 \\\_ \\\_
+15 \\\\_ \\\\%
+15 \\\\_ \\\\_
+16 % %
+16 % \%
+16 % \a
+16 % \aa
+16 % \x
+16 % \\
+16 % \\%
+16 % \\a
+16 % \\aa
+16 % \\\%
+16 % \\\\%
+16 % \\\\_
+16 % \\\_
+16 % \\_
+16 % \_
+16 % _
+17 \% \%
+17 \% \a
+17 \% \aa
+17 \% \x
+17 \% \\
+17 \% \\%
+17 \% \\a
+17 \% \\aa
+17 \% \\\%
+17 \% \\\\%
+17 \% \\\\_
+17 \% \\\_
+17 \% \\_
+17 \% \_
+18 \\% \\
+18 \\% \\%
+18 \\% \\a
+18 \\% \\aa
+18 \\% \\\%
+18 \\% \\\\%
+18 \\% \\\\_
+18 \\% \\\_
+18 \\% \\_
+19 \\\% \\\%
+19 \\\% \\\\%
+19 \\\% \\\\_
+19 \\\% \\\_
+20 \\\\% \\\\%
+20 \\\\% \\\\_
+DROP TABLE t1;
+SET @@SQL_MODE=@OLD_SQL_MODE;
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index ff5c9dfe813..b67989231e0 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -50,7 +50,7 @@ id select_type table type possible_keys key key_len ref rows Extra
Warnings:
Note 1276 Field or reference 'a' of SELECT #3 was resolved in SELECT #1
Note 1276 Field or reference 'b.a' of SELECT #3 was resolved in SELECT #1
-Note 1003 select 1 AS `1` from (select 1 AS `a`) b having ((select b.a AS `a`) = 1)
+Note 1003 select 1 AS `1` from (select 1 AS `a`) `b` having ((select `b`.`a` AS `a`) = 1)
SELECT 1 FROM (SELECT 1 as a) as b HAVING (SELECT a)=1;
1
1
@@ -186,7 +186,7 @@ id select_type table type possible_keys key key_len ref rows Extra
4 SUBQUERY t2 ALL NULL NULL NULL NULL 2
NULL UNION RESULT <union1,3> ALL NULL NULL NULL NULL NULL
Warnings:
-Note 1003 (select test.t2.a AS `a`,test.t2.b AS `b` from test.t2 where (test.t2.b = (select test.t3.a AS `a` from test.t3 order by test.t3.a desc limit 1))) union (select test.t4.a AS `a`,test.t4.b AS `b` from test.t4 where (test.t4.b = (select (max(test.t2.a) * 4) AS `max(t2.a)*4` from test.t2)) order by test.t4.a)
+Note 1003 (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`b` = (select `test`.`t3`.`a` AS `a` from `test`.`t3` order by `test`.`t3`.`a` desc limit 1))) union (select `test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b` from `test`.`t4` where (`test`.`t4`.`b` = (select (max(`test`.`t2`.`a`) * 4) AS `max(t2.a)*4` from `test`.`t2`)) order by `test`.`t4`.`a`)
select (select a from t3 where a<t2.a*4 order by 1 desc limit 1), a from t2;
(select a from t3 where a<t2.a*4 order by 1 desc limit 1) a
3 1
@@ -202,7 +202,7 @@ id select_type table type possible_keys key key_len ref rows Extra
3 DERIVED t2 ALL NULL NULL NULL NULL 2 Using where
2 SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using where; Using filesort
Warnings:
-Note 1003 select (select test.t3.a AS `a` from test.t3 where (test.t3.a < 8) order by test.t3.a desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,tt.a AS `a` from (select test.t2.a AS `a`,test.t2.b AS `b` from test.t2 where (test.t2.a > 1)) tt
+Note 1003 select (select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` < 8) order by `test`.`t3`.`a` desc limit 1) AS `(select t3.a from t3 where a<8 order by 1 desc limit 1)`,`tt`.`a` AS `a` from (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`a` > 1)) `tt`
select * from t1 where t1.a=(select t2.a from t2 where t2.b=(select max(a) from t3) order by 1 desc limit 1);
a
2
@@ -223,7 +223,7 @@ id select_type table type possible_keys key key_len ref rows Extra
3 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using where
Warnings:
Note 1276 Field or reference 't4.a' of SELECT #3 was resolved in SELECT #1
-Note 1003 select test.t4.b AS `b`,(select avg((test.t2.a + (select min(test.t3.a) AS `min(t3.a)` from test.t3 where (test.t3.a >= test.t4.a)))) AS `avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a))` from test.t2) AS `(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2)` from test.t4
+Note 1003 select `test`.`t4`.`b` AS `b`,(select avg((`test`.`t2`.`a` + (select min(`test`.`t3`.`a`) AS `min(t3.a)` from `test`.`t3` where (`test`.`t3`.`a` >= `test`.`t4`.`a`)))) AS `avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a))` from `test`.`t2`) AS `(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2)` from `test`.`t4`
select * from t3 where exists (select * from t2 where t2.b=t3.a);
a
7
@@ -269,7 +269,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 3 Using where
2 SUBQUERY t2 ALL NULL NULL NULL NULL 3
Warnings:
-Note 1003 select test.t3.a AS `a` from test.t3 where (test.t3.a >= (select min(test.t2.b) from test.t2))
+Note 1003 select `test`.`t3`.`a` AS `a` from `test`.`t3` where (`test`.`t3`.`a` >= (select min(`test`.`t2`.`b`) from `test`.`t2`))
select * from t3 where a >= all (select b from t2);
a
7
@@ -313,7 +313,7 @@ NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
Warnings:
Note 1276 Field or reference 't2.a' of SELECT #2 was resolved in SELECT #1
Note 1276 Field or reference 't2.a' of SELECT #3 was resolved in SELECT #1
-Note 1003 select (select test.t1.a AS `a` from test.t1 where (test.t1.a = test.t2.a) union select test.t5.a AS `a` from test.t5 where (test.t5.a = test.t2.a)) AS `(select a from t1 where t1.a=t2.a union select a from t5 where t5.a=t2.a)`,test.t2.a AS `a` from test.t2
+Note 1003 select (select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = `test`.`t2`.`a`) union select `test`.`t5`.`a` AS `a` from `test`.`t5` where (`test`.`t5`.`a` = `test`.`t2`.`a`)) AS `(select a from t1 where t1.a=t2.a union select a from t5 where t5.a=t2.a)`,`test`.`t2`.`a` AS `a` from `test`.`t2`
select (select a from t1 where t1.a=t2.a union all select a from t5 where t5.a=t2.a), a from t2;
ERROR 21000: Subquery returns more than 1 row
create table t6 (patient_uq int, clinic_uq int, index i1 (clinic_uq));
@@ -331,7 +331,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 DEPENDENT SUBQUERY t7 eq_ref PRIMARY PRIMARY 4 test.t6.clinic_uq 1 Using index
Warnings:
Note 1276 Field or reference 'clinic_uq' of SELECT #2 was resolved in SELECT #1
-Note 1003 select test.t6.patient_uq AS `patient_uq`,test.t6.clinic_uq AS `clinic_uq` from test.t6 where exists(select 1 AS `Not_used` from test.t7 where (test.t7.uq = test.t6.clinic_uq))
+Note 1003 select `test`.`t6`.`patient_uq` AS `patient_uq`,`test`.`t6`.`clinic_uq` AS `clinic_uq` from `test`.`t6` where exists(select 1 AS `Not_used` from `test`.`t7` where (`test`.`t7`.`uq` = `test`.`t6`.`clinic_uq`))
select * from t1 where a= (select a from t2,t4 where t2.b=t4.b);
ERROR 23000: Column 'a' in field list is ambiguous
drop table t1,t2,t3;
@@ -366,7 +366,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t8 const PRIMARY PRIMARY 35 const 1
3 SUBQUERY t8 const PRIMARY PRIMARY 35 1 Using index
Warnings:
-Note 1003 select test.t8.pseudo AS `pseudo`,(select test.t8.email AS `email` from test.t8 where (test.t8.pseudo = (select test.t8.pseudo AS `pseudo` from test.t8 where (test.t8.pseudo = _latin1'joce')))) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from test.t8 where (test.t8.pseudo = (select test.t8.pseudo AS `pseudo` from test.t8 where (test.t8.pseudo = _latin1'joce')))
+Note 1003 select `test`.`t8`.`pseudo` AS `pseudo`,(select `test`.`t8`.`email` AS `email` from `test`.`t8` where (`test`.`t8`.`pseudo` = (select `test`.`t8`.`pseudo` AS `pseudo` from `test`.`t8` where (`test`.`t8`.`pseudo` = _latin1'joce')))) AS `(SELECT email FROM t8 WHERE pseudo=(SELECT pseudo FROM t8 WHERE pseudo='joce'))` from `test`.`t8` where (`test`.`t8`.`pseudo` = (select `test`.`t8`.`pseudo` AS `pseudo` from `test`.`t8` where (`test`.`t8`.`pseudo` = _latin1'joce')))
SELECT pseudo FROM t8 WHERE pseudo=(SELECT pseudo,email FROM
t8 WHERE pseudo='joce');
ERROR 21000: Operand should contain 1 column(s)
@@ -392,13 +392,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 Extra
1 SIMPLE t1 index NULL PRIMARY 41 NULL 2 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` = 20020803)
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 Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
2 SUBQUERY t1 index NULL PRIMARY 41 NULL 2 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` = 20020803)) 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
@@ -419,7 +419,7 @@ id select_type table type possible_keys key key_len ref rows Extra
3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
Warnings:
-Note 1003 select 1 AS `1` from test.t1
+Note 1003 select 1 AS `1` from `test`.`t1`
drop table t1;
CREATE TABLE `t1` (
`numeropost` mediumint(8) unsigned NOT NULL auto_increment,
@@ -539,13 +539,13 @@ EXPLAIN EXTENDED SELECT MAX(numreponse) FROM t1 WHERE numeropost='1';
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
Warnings:
-Note 1003 select max(test.t1.numreponse) AS `MAX(numreponse)` from test.t1 where (test.t1.numeropost = _latin1'1')
+Note 1003 select max(`test`.`t1`.`numreponse`) AS `MAX(numreponse)` from `test`.`t1` where (`test`.`t1`.`numeropost` = _latin1'1')
EXPLAIN EXTENDED SELECT numreponse FROM t1 WHERE numeropost='1' AND numreponse=(SELECT MAX(numreponse) FROM t1 WHERE numeropost='1');
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 const PRIMARY,numreponse PRIMARY 7 const,const 1 Using index
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
Warnings:
-Note 1003 select test.t1.numreponse AS `numreponse` from test.t1 where ((test.t1.numeropost = _latin1'1') and (test.t1.numreponse = 3))
+Note 1003 select `test`.`t1`.`numreponse` AS `numreponse` from `test`.`t1` where ((`test`.`t1`.`numeropost` = _latin1'1') and (`test`.`t1`.`numreponse` = 3))
drop table t1;
CREATE TABLE t1 (a int(1));
INSERT INTO t1 VALUES (1);
@@ -652,6 +652,14 @@ x
3
3
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
+select * from t1;
+x
+1
+2
+3
+3
+11
+11
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
ERROR 42S22: Unknown column 'x' in 'field list'
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
@@ -713,7 +721,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ref id id 5 const 1 Using where; Using index
Warnings:
Note 1249 Select 2 was reduced during optimization
-Note 1003 select test.t2.id AS `id` from test.t2 where (test.t2.id = 1)
+Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id` = 1)
SELECT * FROM t2 WHERE id IN (SELECT 1 UNION SELECT 3);
id
1
@@ -726,7 +734,7 @@ id select_type table type possible_keys key key_len ref rows Extra
Warnings:
Note 1249 Select 3 was reduced during optimization
Note 1249 Select 2 was reduced during optimization
-Note 1003 select test.t2.id AS `id` from test.t2 where (test.t2.id = (1 + 1))
+Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where (`test`.`t2`.`id` = (1 + 1))
EXPLAIN EXTENDED SELECT * FROM t2 WHERE id IN (SELECT 1 UNION SELECT 3);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 index NULL id 5 NULL 2 Using where; Using index
@@ -734,7 +742,7 @@ id select_type table type possible_keys key key_len ref rows Extra
3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL No tables used
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
Warnings:
-Note 1003 select test.t2.id AS `id` from test.t2 where <in_optimizer>(test.t2.id,<exists>(select 1 AS `Not_used` having (<cache>(test.t2.id) = <null_helper>(1)) union select 1 AS `Not_used` having (<cache>(test.t2.id) = <null_helper>(3))))
+Note 1003 select `test`.`t2`.`id` AS `id` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`id`,<exists>(select 1 AS `Not_used` having (<cache>(`test`.`t2`.`id`) = <null_helper>(1)) union select 1 AS `Not_used` having (<cache>(`test`.`t2`.`id`) = <null_helper>(3))))
SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 3);
id
SELECT * FROM t2 WHERE id IN (SELECT 5 UNION SELECT 2);
@@ -860,7 +868,7 @@ id select_type table type possible_keys key key_len ref rows Extra
Warnings:
Note 1276 Field or reference 'a' of SELECT #2 was resolved in SELECT #1
Note 1249 Select 2 was reduced during optimization
-Note 1003 select (test.t1.a + 1) AS `(select a+1)` from test.t1
+Note 1003 select (`test`.`t1`.`a` + 1) AS `(select a+1)` from `test`.`t1`
select (select a+1) from t1;
(select a+1)
2.5
@@ -882,7 +890,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL PRIMARY 4 NULL 4 Using index
2 DEPENDENT SUBQUERY t2 index_subquery a a 5 func 2 Using index
Warnings:
-Note 1003 select test.t1.a AS `a`,<in_optimizer>(test.t1.a,<exists>(<index_lookup>(<cache>(test.t1.a) in t2 on a chicking NULL))) AS `t1.a in (select t2.a from t2)` from test.t1
+Note 1003 select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`a`) in t2 on a chicking NULL))) AS `t1.a in (select t2.a from t2)` from `test`.`t1`
CREATE TABLE t3 (a int(11) default '0');
INSERT INTO t3 VALUES (1),(2),(3);
SELECT t1.a, t1.a in (select t2.a from t2,t3 where t3.a=t2.a) FROM t1;
@@ -897,7 +905,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 DEPENDENT SUBQUERY t2 ref_or_null a a 5 func 2 Using where; Using index
2 DEPENDENT SUBQUERY t3 ALL NULL NULL NULL NULL 3 Using where
Warnings:
-Note 1003 select test.t1.a AS `a`,<in_optimizer>(test.t1.a,<exists>(select 1 AS `Not_used` from test.t2 join test.t3 where ((test.t3.a = test.t2.a) and ((<cache>(test.t1.a) = test.t2.a) or isnull(test.t2.a))) having <is_not_null_test>(test.t2.a))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from test.t1
+Note 1003 select `test`.`t1`.`a` AS `a`,<in_optimizer>(`test`.`t1`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t2` join `test`.`t3` where ((`test`.`t3`.`a` = `test`.`t2`.`a`) and ((<cache>(`test`.`t1`.`a`) = `test`.`t2`.`a`) or isnull(`test`.`t2`.`a`))) having <is_not_null_test>(`test`.`t2`.`a`))) AS `t1.a in (select t2.a from t2,t3 where t3.a=t2.a)` from `test`.`t1`
drop table t1,t2,t3;
create table t1 (a float);
select 10.5 IN (SELECT * from t1 LIMIT 1);
@@ -1007,19 +1015,19 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
2 UNCACHEABLE SUBQUERY t1 system NULL NULL NULL NULL 0 const row not found
Warnings:
-Note 1003 select sql_no_cache (select sql_no_cache rand() AS `RAND()` from test.t1) AS `(SELECT RAND() FROM t1)` from test.t1
+Note 1003 select sql_no_cache (select sql_no_cache rand() AS `RAND()` from `test`.`t1`) AS `(SELECT RAND() FROM t1)` from `test`.`t1`
EXPLAIN EXTENDED SELECT (SELECT ENCRYPT('test') FROM t1) FROM t1;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
2 UNCACHEABLE SUBQUERY t1 system NULL NULL NULL NULL 0 const row not found
Warnings:
-Note 1003 select sql_no_cache (select sql_no_cache ecrypt(_latin1'test') AS `ENCRYPT('test')` from test.t1) AS `(SELECT ENCRYPT('test') FROM t1)` from test.t1
+Note 1003 select sql_no_cache (select sql_no_cache ecrypt(_latin1'test') AS `ENCRYPT('test')` from `test`.`t1`) AS `(SELECT ENCRYPT('test') FROM t1)` from `test`.`t1`
EXPLAIN EXTENDED SELECT (SELECT BENCHMARK(1,1) FROM t1) FROM t1;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
2 UNCACHEABLE SUBQUERY t1 system NULL NULL NULL NULL 0 const row not found
Warnings:
-Note 1003 select sql_no_cache (select sql_no_cache benchmark(1,1) AS `BENCHMARK(1,1)` from test.t1) AS `(SELECT BENCHMARK(1,1) FROM t1)` from test.t1
+Note 1003 select sql_no_cache (select sql_no_cache benchmark(1,1) AS `BENCHMARK(1,1)` from `test`.`t1`) AS `(SELECT BENCHMARK(1,1) FROM t1)` from `test`.`t1`
drop table t1;
CREATE TABLE `t1` (
`mot` varchar(30) character set latin1 NOT NULL default '',
@@ -1114,7 +1122,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 UNCACHEABLE SUBQUERY t1 ALL NULL NULL NULL NULL 3
3 UNCACHEABLE SUBQUERY t1 ALL NULL NULL NULL NULL 3
Warnings:
-Note 1003 select sql_no_cache test.t1.a AS `a`,(select sql_no_cache (select sql_no_cache rand() AS `rand()` from test.t1 limit 1) AS `(select rand() from t1 limit 1)` from test.t1 limit 1) AS `(select (select rand() from t1 limit 1) from t1 limit 1)` from test.t1
+Note 1003 select sql_no_cache `test`.`t1`.`a` AS `a`,(select sql_no_cache (select sql_no_cache rand() AS `rand()` from `test`.`t1` limit 1) AS `(select rand() from t1 limit 1)` from `test`.`t1` limit 1) AS `(select (select rand() from t1 limit 1) from t1 limit 1)` from `test`.`t1`
drop table t1;
select t1.Continent, t2.Name, t2.Population from t1 LEFT JOIN t2 ON t1.Code = t2.Country where t2.Population IN (select max(t2.Population) AS Population from t2, t1 where t2.Country = t1.Code group by Continent);
ERROR 42S02: Table 'test.t1' doesn't exist
@@ -1168,7 +1176,7 @@ 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 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
Warnings:
-Note 1003 select <in_optimizer>(0,<exists>(select 1 AS `Not_used` from test.t1 a)) AS `0 IN (SELECT 1 FROM t1 a)`
+Note 1003 select <in_optimizer>(0,<exists>(select 1 AS `Not_used` from `test`.`t1` `a`)) AS `0 IN (SELECT 1 FROM t1 a)`
INSERT INTO t1 (pseudo) VALUES ('test1');
SELECT 0 IN (SELECT 1 FROM t1 a);
0 IN (SELECT 1 FROM t1 a)
@@ -1178,7 +1186,7 @@ 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 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
Warnings:
-Note 1003 select <in_optimizer>(0,<exists>(select 1 AS `Not_used` from test.t1 a)) AS `0 IN (SELECT 1 FROM t1 a)`
+Note 1003 select <in_optimizer>(0,<exists>(select 1 AS `Not_used` from `test`.`t1` `a`)) AS `0 IN (SELECT 1 FROM t1 a)`
drop table t1;
CREATE TABLE `t1` (
`i` int(11) NOT NULL default '0',
@@ -1222,7 +1230,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ref salary salary 5 const 1 Using where
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
Warnings:
-Note 1003 select test.t1.id AS `id` from test.t1 where (test.t1.salary = (select max(test.t1.salary) AS `MAX(salary)` from test.t1))
+Note 1003 select `test`.`t1`.`id` AS `id` from `test`.`t1` where (`test`.`t1`.`salary` = (select max(`test`.`t1`.`salary`) AS `MAX(salary)` from `test`.`t1`))
drop table t1;
CREATE TABLE t1 (
ID int(10) unsigned NOT NULL auto_increment,
@@ -1284,7 +1292,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 index NULL PRIMARY 4 NULL 4 Using where; Using index
2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using index
Warnings:
-Note 1003 select test.t2.a AS `a` from test.t2 where <in_optimizer>(test.t2.a,<exists>(<primary_index_lookup>(<cache>(test.t2.a) in t1 on PRIMARY)))
+Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(<primary_index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on PRIMARY)))
select * from t2 where t2.a in (select a from t1 where t1.b <> 30);
a
2
@@ -1294,7 +1302,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 index NULL PRIMARY 4 NULL 4 Using where; Using index
2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using index; Using where
Warnings:
-Note 1003 select test.t2.a AS `a` from test.t2 where <in_optimizer>(test.t2.a,<exists>(<primary_index_lookup>(<cache>(test.t2.a) in t1 on PRIMARY where (test.t1.b <> 30))))
+Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(<primary_index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on PRIMARY where (`test`.`t1`.`b` <> 30))))
select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a);
a
2
@@ -1305,7 +1313,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 func 1 Using where
2 DEPENDENT SUBQUERY t3 eq_ref PRIMARY PRIMARY 4 test.t1.b 1 Using where; Using index
Warnings:
-Note 1003 select test.t2.a AS `a` from test.t2 where <in_optimizer>(test.t2.a,<exists>(select 1 AS `Not_used` from test.t1 join test.t3 where ((test.t1.b = test.t3.a) and (<cache>(test.t2.a) = test.t1.a))))
+Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t1`.`b` = `test`.`t3`.`a`) and (<cache>(`test`.`t2`.`a`) = `test`.`t1`.`a`))))
drop table t1, t2, t3;
create table t1 (a int, b int, index a (a,b));
create table t2 (a int, index a (a));
@@ -1323,7 +1331,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 index NULL a 5 NULL 4 Using where; Using index
2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 1001 Using index
Warnings:
-Note 1003 select test.t2.a AS `a` from test.t2 where <in_optimizer>(test.t2.a,<exists>(<index_lookup>(<cache>(test.t2.a) in t1 on a)))
+Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(<index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on a)))
select * from t2 where t2.a in (select a from t1 where t1.b <> 30);
a
2
@@ -1333,7 +1341,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 index NULL a 5 NULL 4 Using where; Using index
2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 1001 Using index; Using where
Warnings:
-Note 1003 select test.t2.a AS `a` from test.t2 where <in_optimizer>(test.t2.a,<exists>(<index_lookup>(<cache>(test.t2.a) in t1 on a where (test.t1.b <> 30))))
+Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(<index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on a where (`test`.`t1`.`b` <> 30))))
select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a);
a
2
@@ -1341,10 +1349,10 @@ a
explain extended select * from t2 where t2.a in (select t1.a from t1,t3 where t1.b=t3.a);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 index NULL a 5 NULL 4 Using where; Using index
-2 DEPENDENT SUBQUERY t1 ref a a 5 func 1001 Using where; Using index
-2 DEPENDENT SUBQUERY t3 index a a 5 NULL 3 Using where; Using index
+2 DEPENDENT SUBQUERY t3 index a a 5 NULL 3 Using index
+2 DEPENDENT SUBQUERY t1 ref a a 10 func,test.t3.a 1000 Using where; Using index
Warnings:
-Note 1003 select test.t2.a AS `a` from test.t2 where <in_optimizer>(test.t2.a,<exists>(select 1 AS `Not_used` from test.t1 join test.t3 where ((test.t1.b = test.t3.a) and (<cache>(test.t2.a) = test.t1.a))))
+Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(select 1 AS `Not_used` from `test`.`t1` join `test`.`t3` where ((`test`.`t1`.`b` = `test`.`t3`.`a`) and (<cache>(`test`.`t2`.`a`) = `test`.`t1`.`a`))))
insert into t1 values (3,31);
select * from t2 where t2.a in (select a from t1 where t1.b <> 30);
a
@@ -1360,7 +1368,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 index NULL a 5 NULL 4 Using where; Using index
2 DEPENDENT SUBQUERY t1 index_subquery a a 5 func 1001 Using index; Using where
Warnings:
-Note 1003 select test.t2.a AS `a` from test.t2 where <in_optimizer>(test.t2.a,<exists>(<index_lookup>(<cache>(test.t2.a) in t1 on a where (test.t1.b <> 30))))
+Note 1003 select `test`.`t2`.`a` AS `a` from `test`.`t2` where <in_optimizer>(`test`.`t2`.`a`,<exists>(<index_lookup>(<cache>(`test`.`t2`.`a`) in t1 on a where (`test`.`t1`.`b` <> 30))))
drop table t1, t2, t3;
create table t1 (a int, b int);
create table t2 (a int, b int);
@@ -1417,7 +1425,7 @@ explain extended (select * from t1);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 system NULL NULL NULL NULL 1
Warnings:
-Note 1003 (select test.t1.s1 AS `s1` from test.t1)
+Note 1003 (select `test`.`t1`.`s1` AS `s1` from `test`.`t1`)
(select * from t1);
s1
tttt
@@ -1451,25 +1459,25 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL s1 6 NULL 3 Using index
2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index
Warnings:
-Note 1003 select test.t1.s1 AS `s1`,not(<in_optimizer>(test.t1.s1,<exists>(<index_lookup>(<cache>(test.t1.s1) in t2 on s1 chicking NULL)))) AS `s1 NOT IN (SELECT s1 FROM t2)` from test.t1
+Note 1003 select `test`.`t1`.`s1` AS `s1`,not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 chicking NULL)))) AS `s1 NOT IN (SELECT s1 FROM t2)` from `test`.`t1`
explain extended select s1, s1 = ANY (SELECT s1 FROM t2) from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL s1 6 NULL 3 Using index
2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index
Warnings:
-Note 1003 select test.t1.s1 AS `s1`,<in_optimizer>(test.t1.s1,<exists>(<index_lookup>(<cache>(test.t1.s1) in t2 on s1 chicking NULL))) AS `s1 = ANY (SELECT s1 FROM t2)` from test.t1
+Note 1003 select `test`.`t1`.`s1` AS `s1`,<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 chicking NULL))) AS `s1 = ANY (SELECT s1 FROM t2)` from `test`.`t1`
explain extended select s1, s1 <> ALL (SELECT s1 FROM t2) from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL s1 6 NULL 3 Using index
2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 2 Using index
Warnings:
-Note 1003 select test.t1.s1 AS `s1`,not(<in_optimizer>(test.t1.s1,<exists>(<index_lookup>(<cache>(test.t1.s1) in t2 on s1 chicking NULL)))) AS `s1 <> ALL (SELECT s1 FROM t2)` from test.t1
+Note 1003 select `test`.`t1`.`s1` AS `s1`,not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 chicking NULL)))) AS `s1 <> ALL (SELECT s1 FROM t2)` from `test`.`t1`
explain extended select s1, s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2') from t1;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 index NULL s1 6 NULL 3 Using index
2 DEPENDENT SUBQUERY t2 index_subquery s1 s1 6 func 1 Using index; Using where
Warnings:
-Note 1003 select test.t1.s1 AS `s1`,not(<in_optimizer>(test.t1.s1,<exists>(<index_lookup>(<cache>(test.t1.s1) in t2 on s1 chicking NULL where (test.t2.s1 < _latin1'a2'))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from test.t1
+Note 1003 select `test`.`t1`.`s1` AS `s1`,not(<in_optimizer>(`test`.`t1`.`s1`,<exists>(<index_lookup>(<cache>(`test`.`t1`.`s1`) in t2 on s1 chicking NULL where (`test`.`t2`.`s1` < _latin1'a2'))))) AS `s1 NOT IN (SELECT s1 FROM t2 WHERE s1 < 'a2')` from `test`.`t1`
drop table t1,t2;
create table t2 (a int, b int);
create table t3 (a int);
@@ -1484,7 +1492,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 3 Using where
2 SUBQUERY t2 system NULL NULL NULL NULL 0 const row not found
Warnings:
-Note 1003 select test.t3.a AS `a` from test.t3 where <not>((test.t3.a < (select max(test.t2.b) from test.t2)))
+Note 1003 select `test`.`t3`.`a` AS `a` from `test`.`t3` where <not>((`test`.`t3`.`a` < (select max(`test`.`t2`.`b`) from `test`.`t2`)))
insert into t2 values (2,2), (2,1), (3,3), (3,1);
select * from t3 where a > all (select max(b) from t2 group by a);
a
@@ -1495,7 +1503,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 3 Using where
2 SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using temporary; Using filesort
Warnings:
-Note 1003 select test.t3.a AS `a` from test.t3 where <not>((test.t3.a <= <max>(select max(test.t2.b) AS `max(b)` from test.t2 group by test.t2.a)))
+Note 1003 select `test`.`t3`.`a` AS `a` from `test`.`t3` where <not>((`test`.`t3`.`a` <= <max>(select max(`test`.`t2`.`b`) AS `max(b)` from `test`.`t2` group by `test`.`t2`.`a`)))
drop table t2, t3;
CREATE TABLE `t1` ( `id` mediumint(9) NOT NULL auto_increment, `taskid` bigint(20) NOT NULL default '0', `dbid` int(11) NOT NULL default '0', `create_date` datetime NOT NULL default '0000-00-00 00:00:00', `last_update` datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (`id`)) ENGINE=MyISAM CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `t1` (`id`, `taskid`, `dbid`, `create_date`,`last_update`) VALUES (1, 1, 15, '2003-09-29 10:31:36', '2003-09-29 10:31:36'), (2, 1, 21, now(), now());
@@ -1544,7 +1552,7 @@ id select_type table type possible_keys key key_len ref rows Extra
3 UNION t1 system NULL NULL NULL NULL 1
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL
Warnings:
-Note 1003 select test.t1.s1 AS `s1` from test.t1
+Note 1003 select `test`.`t1`.`s1` AS `s1` from `test`.`t1`
drop table t1;
CREATE TABLE t1 (number char(11) NOT NULL default '') ENGINE=MyISAM CHARSET=latin1;
INSERT INTO t1 VALUES ('69294728265'),('18621828126'),('89356874041'),('95895001874');
@@ -1663,14 +1671,14 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 12 Using where
2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 Using index; Using where
Warnings:
-Note 1003 select test.t1.id AS `id`,test.t1.text AS `text` from test.t1 where not(<in_optimizer>(test.t1.id,<exists>(<primary_index_lookup>(<cache>(test.t1.id) in t1 on PRIMARY where (test.t1.id < 8)))))
+Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where not(<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where (`test`.`t1`.`id` < 8)))))
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 Using where
2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 7 Using where; Using index
Warnings:
Note 1276 Field or reference 'tt.id' of SELECT #2 was resolved in SELECT #1
-Note 1003 select test.tt.id AS `id`,test.tt.text AS `text` from test.t1 tt where not(exists(select test.t1.id AS `id` from test.t1 where ((test.t1.id < 8) and ((test.t1.id = test.tt.id) or isnull(test.t1.id))) having (test.t1.id is not null)))
+Note 1003 select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where not(exists(select `test`.`t1`.`id` AS `id` from `test`.`t1` where ((`test`.`t1`.`id` < 8) and ((`test`.`t1`.`id` = `test`.`tt`.`id`) or isnull(`test`.`t1`.`id`))) having (`test`.`t1`.`id` is not null)))
insert into t1 (id, text) values (1000, 'text1000'), (1001, 'text1001');
create table t2 (id int not null, text varchar(20) not null default '', primary key (id));
insert into t2 (id, text) values (1, 'text1'), (2, 'text2'), (3, 'text3'), (4, 'text4'), (5, 'text5'), (6, 'text6'), (7, 'text7'), (8, 'text8'), (9, 'text9'), (10, 'text10'), (11, 'text1'), (12, 'text2'), (13, 'text3'), (14, 'text4'), (15, 'text5'), (16, 'text6'), (17, 'text7'), (18, 'text8'), (19, 'text9'), (20, 'text10'),(21, 'text1'), (22, 'text2'), (23, 'text3'), (24, 'text4'), (25, 'text5'), (26, 'text6'), (27, 'text7'), (28, 'text8'), (29, 'text9'), (30, 'text10'), (31, 'text1'), (32, 'text2'), (33, 'text3'), (34, 'text4'), (35, 'text5'), (36, 'text6'), (37, 'text7'), (38, 'text8'), (39, 'text9'), (40, 'text10'), (41, 'text1'), (42, 'text2'), (43, 'text3'), (44, 'text4'), (45, 'text5'), (46, 'text6'), (47, 'text7'), (48, 'text8'), (49, 'text9'), (50, 'text10');
@@ -1696,7 +1704,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE b eq_ref PRIMARY PRIMARY 4 test.a.id 2
1 SIMPLE c eq_ref PRIMARY PRIMARY 4 func 1 Using where
Warnings:
-Note 1003 select test.a.id AS `id`,test.a.text AS `text`,test.b.id AS `id`,test.b.text AS `text`,test.c.id AS `id`,test.c.text AS `text` from test.t1 a left join test.t2 b on(((test.a.id = test.b.id) or isnull(test.b.id))) join test.t1 c where (if(isnull(test.b.id),1000,test.b.id) = test.c.id)
+Note 1003 select `test`.`a`.`id` AS `id`,`test`.`a`.`text` AS `text`,`test`.`b`.`id` AS `id`,`test`.`b`.`text` AS `text`,`test`.`c`.`id` AS `id`,`test`.`c`.`text` AS `text` from `test`.`t1` `a` left join `test`.`t2` `b` on(((`test`.`a`.`id` = `test`.`b`.`id`) or isnull(`test`.`b`.`id`))) join `test`.`t1` `c` where (if(isnull(`test`.`b`.`id`),1000,`test`.`b`.`id`) = `test`.`c`.`id`)
drop table t1,t2;
create table t1 (a int);
insert into t1 values (1);
@@ -1824,7 +1832,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where
Warnings:
Note 1276 Field or reference 'up.a' of SELECT #2 was resolved in SELECT #1
-Note 1003 select test.up.a AS `a`,test.up.b AS `b` from test.t1 up where exists(select 1 AS `Not_used` from test.t1 where (test.t1.a = test.up.a))
+Note 1003 select `test`.`up`.`a` AS `a`,`test`.`up`.`b` AS `b` from `test`.`t1` `up` where exists(select 1 AS `Not_used` from `test`.`t1` where (`test`.`t1`.`a` = `test`.`up`.`a`))
drop table t1;
CREATE TABLE t1 (t1_a int);
INSERT INTO t1 VALUES (1);
diff --git a/mysql-test/r/sum_distinct.result b/mysql-test/r/sum_distinct.result
new file mode 100644
index 00000000000..c7f1a660267
--- /dev/null
+++ b/mysql-test/r/sum_distinct.result
@@ -0,0 +1,203 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
+gender CHAR(1),
+name VARCHAR(20)
+);
+SELECT SUM(DISTINCT LENGTH(name)) s1 FROM t1;
+s1
+NULL
+INSERT INTO t1 (gender, name) VALUES (NULL, NULL);
+INSERT INTO t1 (gender, name) VALUES (NULL, NULL);
+INSERT INTO t1 (gender, name) VALUES (NULL, NULL);
+SELECT SUM(DISTINCT LENGTH(name)) s1 FROM t1;
+s1
+NULL
+INSERT INTO t1 (gender, name) VALUES ('F', 'Helen'), ('F', 'Anastasia'),
+('F', 'Katherine'), ('F', 'Margo'), ('F', 'Magdalene'), ('F', 'Mary');
+CREATE TABLE t2 SELECT name FROM t1;
+SELECT (SELECT SUM(DISTINCT LENGTH(name)) FROM t1) FROM t2;
+(SELECT SUM(DISTINCT LENGTH(name)) FROM t1)
+18
+18
+18
+18
+18
+18
+18
+18
+18
+DROP TABLE t2;
+INSERT INTO t1 (gender, name) VALUES ('F', 'Eva'), ('F', 'Sofia'),
+('F', 'Sara'), ('F', 'Golda'), ('F', 'Toba'), ('F', 'Victory'),
+('F', 'Faina'), ('F', 'Miriam'), ('F', 'Beki'), ('F', 'America'),
+('F', 'Susan'), ('F', 'Glory'), ('F', 'Priscilla'), ('F', 'Rosmary'),
+('F', 'Rose'), ('F', 'Margareth'), ('F', 'Elizabeth'), ('F', 'Meredith'),
+('F', 'Julie'), ('F', 'Xenia'), ('F', 'Zena'), ('F', 'Olga'),
+('F', 'Brunhilda'), ('F', 'Nataly'), ('F', 'Lara'), ('F', 'Svetlana'),
+('F', 'Grethem'), ('F', 'Irene');
+SELECT
+SUM(DISTINCT LENGTH(name)) s1,
+SUM(DISTINCT SUBSTRING(NAME, 1, 3)) s2,
+SUM(DISTINCT LENGTH(SUBSTRING(name, 1, 4))) s3
+FROM t1;
+s1 s2 s3
+42 0 7
+SELECT
+SUM(DISTINCT LENGTH(g1.name)) s1,
+SUM(DISTINCT SUBSTRING(g2.name, 1, 3)) s2,
+SUM(DISTINCT LENGTH(SUBSTRING(g3.name, 1, 4))) s3
+FROM t1 g1, t1 g2, t1 g3;
+s1 s2 s3
+42 0 7
+SELECT
+SUM(DISTINCT LENGTH(g1.name)) s1,
+SUM(DISTINCT SUBSTRING(g2.name, 1, 3)) s2,
+SUM(DISTINCT LENGTH(SUBSTRING(g3.name, 1, 4))) s3
+FROM t1 g1, t1 g2, t1 g3 GROUP BY LENGTH(SUBSTRING(g3.name, 5, 10));
+s1 s2 s3
+42 0 NULL
+42 0 7
+42 0 4
+42 0 4
+42 0 4
+42 0 4
+42 0 4
+SELECT SQL_BUFFER_RESULT
+SUM(DISTINCT LENGTH(name)) s1,
+SUM(DISTINCT SUBSTRING(NAME, 1, 3)) s2,
+SUM(DISTINCT LENGTH(SUBSTRING(name, 1, 4))) s3
+FROM t1;
+s1 s2 s3
+42 0 7
+SELECT SQL_BUFFER_RESULT
+SUM(DISTINCT LENGTH(g1.name)) s1,
+SUM(DISTINCT SUBSTRING(g2.name, 1, 3)) s2,
+SUM(DISTINCT LENGTH(SUBSTRING(g3.name, 1, 4))) s3
+FROM t1 g1, t1 g2, t1 g3 GROUP BY LENGTH(SUBSTRING(g3.name, 5, 10));
+s1 s2 s3
+42 0 NULL
+42 0 7
+42 0 4
+42 0 4
+42 0 4
+42 0 4
+42 0 4
+SET @l=1;
+UPDATE t1 SET name=CONCAT(name, @l:=@l+1);
+SELECT SUM(DISTINCT RIGHT(name, 1)) FROM t1;
+SUM(DISTINCT RIGHT(name, 1))
+45
+SELECT SUM(DISTINCT id) FROM t1;
+SUM(DISTINCT id)
+703
+SELECT SUM(DISTINCT id % 11) FROM t1;
+SUM(DISTINCT id % 11)
+55
+DROP TABLE t1;
+CREATE TABLE t1 (id INTEGER);
+CREATE TABLE t2 (id INTEGER);
+INSERT INTO t1 (id) VALUES (1), (1), (1),(1);
+INSERT INTO t2 (id) SELECT id FROM t1;
+INSERT INTO t1 (id) SELECT id FROM t2;
+/* 8 */
+INSERT INTO t1 (id) SELECT id FROM t2;
+/* 12 */
+INSERT INTO t1 (id) SELECT id FROM t2;
+/* 16 */
+INSERT INTO t1 (id) SELECT id FROM t2;
+/* 20 */
+INSERT INTO t1 (id) SELECT id FROM t2;
+/* 24 */
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+1 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+2 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+4 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+8 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+16 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+32 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+64 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+128 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+256 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+512 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+1024 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+2048 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+4096 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+8192 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 SELECT id FROM t1 ORDER BY id*rand();
+SELECT SUM(DISTINCT id) sm FROM t1;
+sm
+134225920
+SELECT SUM(DISTINCT id) sm FROM t2;
+sm
+134225920
+SELECT SUM(DISTINCT id) sm FROM t1 group by id % 13;
+sm
+10327590
+10328851
+10330112
+10331373
+10332634
+10317510
+10318770
+10320030
+10321290
+10322550
+10323810
+10325070
+10326330
+SET max_heap_table_size=16384;
+SHOW variables LIKE 'max_heap_table_size';
+Variable_name Value
+max_heap_table_size 16384
+SELECT SUM(DISTINCT id) sm FROM t1;
+sm
+134225920
+SELECT SUM(DISTINCT id) sm FROM t2;
+sm
+134225920
+SELECT SUM(DISTINCT id) sm FROM t1 GROUP BY id % 13;
+sm
+10327590
+10328851
+10330112
+10331373
+10332634
+10317510
+10318770
+10320030
+10321290
+10322550
+10323810
+10325070
+10326330
+DROP TABLE t1;
+DROP TABLE t2;
diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result
index ebb24159373..1b5fc4f8b27 100644
--- a/mysql-test/r/system_mysql_db.result
+++ b/mysql-test/r/system_mysql_db.result
@@ -1,20 +1,21 @@
show tables;
-Tables_in_db
-columns_priv
-db
-func
-help_category
-help_keyword
-help_relation
-help_topic
-host
-tables_priv
-time_zone
-time_zone_leap_second
-time_zone_name
-time_zone_transition
-time_zone_transition_type
-user
+Tables_in_db table_type
+columns_priv BASE TABLE
+db BASE TABLE
+func BASE TABLE
+help_category BASE TABLE
+help_keyword BASE TABLE
+help_relation BASE TABLE
+help_topic BASE TABLE
+host BASE TABLE
+proc BASE TABLE
+tables_priv BASE TABLE
+time_zone BASE TABLE
+time_zone_leap_second BASE TABLE
+time_zone_name BASE TABLE
+time_zone_transition BASE TABLE
+time_zone_transition_type BASE TABLE
+user BASE TABLE
show create table db;
Table Create Table
db CREATE TABLE `db` (
@@ -33,6 +34,8 @@ db CREATE TABLE `db` (
`Alter_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Create_tmp_table_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Lock_tables_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
PRIMARY KEY (`Host`,`Db`,`User`),
KEY `User` (`User`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges'
@@ -53,6 +56,8 @@ host CREATE TABLE `host` (
`Alter_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Create_tmp_table_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Lock_tables_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
PRIMARY KEY (`Host`,`Db`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Host privileges; Merged with database privileges'
show create table user;
@@ -82,6 +87,8 @@ user CREATE TABLE `user` (
`Execute_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Repl_slave_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`Repl_client_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
+ `Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N',
`ssl_type` enum('','ANY','X509','SPECIFIED') collate utf8_bin NOT NULL default '',
`ssl_cipher` blob NOT NULL,
`x509_issuer` blob NOT NULL,
@@ -127,4 +134,4 @@ columns_priv CREATE TABLE `columns_priv` (
PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`,`Column_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Column privileges'
show tables;
-Tables_in_test
+Tables_in_test table_type
diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result
index 2f42bedf67a..99e0a69834e 100644
--- a/mysql-test/r/union.result
+++ b/mysql-test/r/union.result
@@ -88,7 +88,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 UNION t2 ALL NULL NULL NULL NULL 4 Using filesort
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL Using filesort
Warnings:
-Note 1003 (select test.t1.a AS `a`,test.t1.b AS `b` from test.t1 limit 2) union all (select test.t2.a AS `a`,test.t2.b AS `b` from test.t2 order by test.t2.a limit 1) order by b desc
+Note 1003 (select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` limit 2) union all (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` order by `test`.`t2`.`a` limit 1) order by `b` desc
(select sql_calc_found_rows a,b from t1 limit 2) union all (select a,b from t2 order by a) limit 2;
a b
1 a
@@ -480,7 +480,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 UNION t2 const PRIMARY PRIMARY 4 const 1
NULL UNION RESULT <union1,2> ALL NULL NULL NULL NULL NULL
Warnings:
-Note 1003 (select test.t1.a AS `a`,test.t1.b AS `b` from test.t1 where (test.t1.a = 1)) union (select test.t2.a AS `a`,test.t2.b AS `b` from test.t2 where (test.t2.a = 1))
+Note 1003 (select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where (`test`.`t1`.`a` = 1)) union (select `test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` where (`test`.`t2`.`a` = 1))
(select * from t1 where a=5) union (select * from t2 where a=1);
a b
1 10
diff --git a/mysql-test/r/user_var.result b/mysql-test/r/user_var.result
index 2750478c1c5..9bbfefeb34a 100644
--- a/mysql-test/r/user_var.result
+++ b/mysql-test/r/user_var.result
@@ -172,19 +172,21 @@ insert into t1 values (@var1);
create table t2 (c char(30)) charset=ucs2;
set @v=convert('abc' using ucs2);
insert into t2 values (@v);
-show binlog events from 79;
-Log_name Pos Event_type Server_id Orig_log_pos Info
-master-bin.000001 79 User var 1 79 @`a b`=_latin1 0x68656C6C6F COLLATE latin1_swedish_ci
-master-bin.000001 120 Query 1 120 use `test`; INSERT INTO t1 VALUES(@`a b`)
-master-bin.000001 184 User var 1 184 @`var1`=_latin1 0x273B616161 COLLATE latin1_swedish_ci
-master-bin.000001 226 Query 1 226 use `test`; insert into t1 values (@var1)
-master-bin.000001 290 Query 1 290 use `test`; create table t2 (c char(30)) charset=ucs2
-master-bin.000001 366 User var 1 366 @`v`=_ucs2 0x006100620063 COLLATE ucs2_general_ci
-master-bin.000001 406 Query 1 406 use `test`; insert into t2 values (@v)
+show binlog events from 95;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 95 User var 1 136 @`a b`=_latin1 0x68656C6C6F COLLATE latin1_swedish_ci
+master-bin.000001 136 Query 1 222 use `test`; INSERT INTO t1 VALUES(@`a b`)
+master-bin.000001 222 User var 1 264 @`var1`=_latin1 0x273B616161 COLLATE latin1_swedish_ci
+master-bin.000001 264 Query 1 350 use `test`; insert into t1 values (@var1)
+master-bin.000001 350 Query 1 448 use `test`; create table t2 (c char(30)) charset=ucs2
+master-bin.000001 448 User var 1 488 @`v`=_ucs2 0x006100620063 COLLATE ucs2_general_ci
+master-bin.000001 488 Query 1 571 use `test`; insert into t2 values (@v)
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
SET @`a b`:=_latin1 0x68656C6C6F COLLATE latin1_swedish_ci;
use test;
SET TIMESTAMP=10000;
+SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=1, @@session.unique_checks=1;
+SET @@session.sql_mode=0;
INSERT INTO t1 VALUES(@`a b`);
SET @`var1`:=_latin1 0x273B616161 COLLATE latin1_swedish_ci;
SET TIMESTAMP=10000;
diff --git a/mysql-test/r/varbinary.result b/mysql-test/r/varbinary.result
index ab5779859ae..e55e6b35915 100644
--- a/mysql-test/r/varbinary.result
+++ b/mysql-test/r/varbinary.result
@@ -15,7 +15,7 @@ explain extended select * from t1 where UNIQ=0x38afba1d73e6a18a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 const UNIQ UNIQ 8 const 1
Warnings:
-Note 1003 select test.t1.ID AS `ID`,test.t1.UNIQ AS `UNIQ` from test.t1 where (test.t1.UNIQ = 4084688022709641610)
+Note 1003 select `test`.`t1`.`ID` AS `ID`,`test`.`t1`.`UNIQ` AS `UNIQ` from `test`.`t1` where (`test`.`t1`.`UNIQ` = 4084688022709641610)
drop table t1;
select x'hello';
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'x'hello'' at line 1
diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result
index 5d3f32cdd55..a84cf733f21 100644
--- a/mysql-test/r/variables.result
+++ b/mysql-test/r/variables.result
@@ -1,8 +1,30 @@
drop table if exists t1,t2;
-set @`test`=1,@TEST=3,@select=2,@t5=1.23456;
-select @test,@`select`,@TEST,@not_used;
-@test @`select` @TEST @not_used
-1 2 3 NULL
+set @`test`=1;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+1 1 1 1 1
+set @TEST=2;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+2 2 2 2 2
+set @"tEST"=3;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+3 3 3 3 3
+set @`TeST`=4;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+4 4 4 4 4
+select @`teST`:=5;
+@`teST`:=5
+5
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+5 5 5 5 5
+set @select=2,@t5=1.23456;
+select @`select`,@not_used;
+@`select` @not_used
+2 NULL
set @test_int=10,@test_double=1e-10,@test_string="abcdeghi",@test_string2="abcdefghij",@select=NULL;
select @test_int,@test_double,@test_string,@test_string2,@select;
@test_int @test_double @test_string @test_string2 @select
@@ -341,6 +363,8 @@ set sql_buffer_result=1;
set sql_log_bin=1;
set sql_log_off=1;
set sql_log_update=1;
+Warnings:
+Note 1314 The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored
set sql_low_priority_updates=1;
set sql_max_join_size=200;
select @@sql_max_join_size,@@max_join_size;
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
new file mode 100644
index 00000000000..18da4882894
--- /dev/null
+++ b/mysql-test/r/view.result
@@ -0,0 +1,1168 @@
+drop table if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6;
+drop view if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6;
+drop database if exists mysqltest;
+use test;
+create view v1 (c,d) as select a,b from t1;
+ERROR 42S02: Table 'test.t1' doesn't exist
+create temporary table t1 (a int, b int);
+create view v1 (c) as select b+1 from t1;
+ERROR HY000: View's SELECT contains a temporary table 't1'
+drop table t1;
+create table t1 (a int, b int);
+insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10);
+create view v1 (c,d) as select a,b+@@global.max_user_connections from t1;
+ERROR HY000: View's SELECT contains a variable or parameter
+create view v1 (c) as select b+1 from t1;
+select c from v1;
+c
+3
+4
+5
+6
+11
+create temporary table t1 (a int, b int);
+select * from t1;
+a b
+select c from v1;
+c
+3
+4
+5
+6
+11
+show create table v1;
+Table Create Table
+v1 CREATE VIEW `test`.`v1` AS select (`test`.`t1`.`b` + 1) AS `c` from `test`.`t1`
+show create view v1;
+Table Create Table
+v1 CREATE VIEW `test`.`v1` AS select (`test`.`t1`.`b` + 1) AS `c` from `test`.`t1`
+show create view t1;
+ERROR HY000: 'test.t1' is not VIEW
+drop table t1;
+select a from v1;
+ERROR 42S22: Unknown column 'a' in 'field list'
+select v1.a from v1;
+ERROR 42S22: Unknown column 'v1.a' in 'field list'
+select b from v1;
+ERROR 42S22: Unknown column 'b' in 'field list'
+select v1.b from v1;
+ERROR 42S22: Unknown column 'v1.b' in 'field list'
+explain extended select c from v1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 5
+Warnings:
+Note 1003 select (`test`.`t1`.`b` + 1) AS `c` from `test`.`v1`
+create algorithm=temptable view v2 (c) as select b+1 from t1;
+show create table v2;
+Table Create Table
+v2 CREATE ALGORITHM=TMPTABLE VIEW `test`.`v2` AS select (`test`.`t1`.`b` + 1) AS `c` from `test`.`t1`
+select c from v2;
+c
+3
+4
+5
+6
+11
+explain extended select c from v2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5
+2 DERIVED t1 ALL NULL NULL NULL NULL 5
+Warnings:
+Note 1003 select `v2`.`c` AS `c` from `test`.`v2`
+create view v3 (c) as select a+1 from v1;
+ERROR 42S22: Unknown column 'a' in 'field list'
+create view v3 (c) as select b+1 from v1;
+ERROR 42S22: Unknown column 'b' in 'field list'
+create view v3 (c) as select c+1 from v1;
+select c from v3;
+c
+4
+5
+6
+7
+12
+explain extended select c from v3;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 5
+Warnings:
+Note 1003 select ((`test`.`t1`.`b` + 1) + 1) AS `c` from `test`.`v3`
+create algorithm=temptable view v4 (c) as select c+1 from v2;
+select c from v4;
+c
+4
+5
+6
+7
+12
+explain extended select c from v4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5
+2 DERIVED <derived3> ALL NULL NULL NULL NULL 5
+3 DERIVED t1 ALL NULL NULL NULL NULL 5
+Warnings:
+Note 1003 select `v4`.`c` AS `c` from `test`.`v4`
+create view v5 (c) as select c+1 from v2;
+select c from v5;
+c
+4
+5
+6
+7
+12
+explain extended select c from v5;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived3> ALL NULL NULL NULL NULL 5
+3 DERIVED t1 ALL NULL NULL NULL NULL 5
+Warnings:
+Note 1003 select (`v2`.`c` + 1) AS `c` from `test`.`v5`
+create algorithm=temptable view v6 (c) as select c+1 from v1;
+select c from v6;
+c
+4
+5
+6
+7
+12
+explain extended select c from v6;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5
+2 DERIVED t1 ALL NULL NULL NULL NULL 5
+Warnings:
+Note 1003 select `v6`.`c` AS `c` from `test`.`v6`
+show tables;
+Tables_in_test table_type
+t1 BASE TABLE
+v1 VIEW
+v2 VIEW
+v3 VIEW
+v4 VIEW
+v5 VIEW
+v6 VIEW
+show table status;
+Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
+t1 MyISAM 9 Fixed 5 9 45 38654705663 1024 0 NULL # # NULL latin1_swedish_ci NULL
+v1 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view
+v2 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view
+v3 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view
+v4 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view
+v5 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view
+v6 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL # # NULL NULL NULL NULL view
+drop view v1,v2,v3,v4,v5,v6;
+create view v1 (c,d,e,f) as select a,b,
+a in (select a+2 from t1), a = all (select a from t1) from t1;
+create view v2 as select c, d from v1;
+select * from v1;
+c d e f
+1 2 0 0
+1 3 0 0
+2 4 0 0
+2 5 0 0
+3 10 1 0
+select * from v2;
+c d
+1 2
+1 3
+2 4
+2 5
+3 10
+create view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1;
+ERROR 42S01: Table 'v1' already exists
+create or replace view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1;
+drop view v2;
+alter view v2 as select c, d from v1;
+ERROR 42S02: Table 'test.v2' doesn't exist
+create or replace view v2 as select c, d from v1;
+alter view v1 (c,d) as select a,max(b) from t1 group by a;
+select * from v1;
+c d
+1 3
+2 5
+3 10
+select * from v2;
+c d
+1 3
+2 5
+3 10
+grant create view on test.* to test@localhost;
+show grants for test@localhost;
+Grants for test@localhost
+GRANT USAGE ON *.* TO 'test'@'localhost'
+GRANT CREATE VIEW ON `test`.* TO 'test'@'localhost'
+revoke create view on test.* from test@localhost;
+show grants for test@localhost;
+Grants for test@localhost
+GRANT USAGE ON *.* TO 'test'@'localhost'
+drop view v100;
+ERROR 42S02: Unknown table 'test.v100'
+drop view t1;
+ERROR HY000: 'test.t1' is not VIEW
+drop table v1;
+ERROR 42S02: Unknown table 'v1'
+drop view v1,v2;
+drop table t1;
+create table t1 (a int);
+insert into t1 values (1), (2), (3);
+create view v1 (a) as select a+1 from t1;
+create view v2 (a) as select a-1 from t1;
+select * from t1 natural left join v1;
+a a
+1 NULL
+2 2
+3 3
+select * from v2 natural left join t1;
+a a
+0 NULL
+1 1
+2 2
+select * from v2 natural left join v1;
+a a
+0 NULL
+1 NULL
+2 2
+drop view v1, v2;
+drop table t1;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int);
+create table mysqltest.t2 (a int, b int);
+grant select on mysqltest.t1 to mysqltest_1@localhost;
+grant create view,select on test.* to mysqltest_1@localhost;
+create view v1 as select * from mysqltest.t1;
+create view mysqltest.v2 as select * from mysqltest.t1;
+ERROR 42000: create view command denied to user 'mysqltest_1'@'localhost' for table 'v2'
+create view v2 as select * from mysqltest.t2;
+ERROR 42000: ANY command denied to user 'mysqltest_1'@'localhost' for table 't2'
+revoke all privileges on mysqltest.t1 from mysqltest_1@localhost;
+revoke all privileges on test.* from mysqltest_1@localhost;
+drop database mysqltest;
+drop view test.v1;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int);
+create view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1;
+grant select (c) on mysqltest.v1 to mysqltest_1@localhost;
+select c from mysqltest.v1;
+c
+select d from mysqltest.v1;
+ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for column 'd' in table 'v1'
+revoke all privileges on mysqltest.v1 from mysqltest_1@localhost;
+delete from mysql.user where user='mysqltest_1';
+drop database mysqltest;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int);
+create algorithm=temptable view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1;
+grant select (c) on mysqltest.v1 to mysqltest_1@localhost;
+select c from mysqltest.v1;
+c
+select d from mysqltest.v1;
+ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for column 'd' in table 'v1'
+revoke all privileges on mysqltest.v1 from mysqltest_1@localhost;
+delete from mysql.user where user='mysqltest_1';
+drop database mysqltest;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int);
+create table mysqltest.t2 (a int, b int);
+create view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1;
+create algorithm=temptable view mysqltest.v2 (c,d) as select a+1,b+1 from mysqltest.t1;
+create view mysqltest.v3 (c,d) as select a+1,b+1 from mysqltest.t2;
+create algorithm=temptable view mysqltest.v4 (c,d) as select a+1,b+1 from mysqltest.t2;
+grant select on mysqltest.v1 to mysqltest_1@localhost;
+grant select on mysqltest.v2 to mysqltest_1@localhost;
+grant select on mysqltest.v3 to mysqltest_1@localhost;
+grant select on mysqltest.v4 to mysqltest_1@localhost;
+select c from mysqltest.v1;
+c
+select c from mysqltest.v2;
+c
+select c from mysqltest.v3;
+c
+select c from mysqltest.v4;
+c
+show columns from mysqltest.v1;
+Field Type Null Key Default Extra
+c bigint(20) YES NULL
+d bigint(20) YES NULL
+show columns from mysqltest.v2;
+Field Type Null Key Default Extra
+c bigint(20) YES NULL
+d bigint(20) YES NULL
+explain select c from mysqltest.v1;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+show create table mysqltest.v1;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+explain select c from mysqltest.v2;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+show create table mysqltest.v2;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+explain select c from mysqltest.v3;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+show create table mysqltest.v3;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+explain select c from mysqltest.v4;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+show create table mysqltest.v4;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+grant select on mysqltest.t1 to mysqltest_1@localhost;
+explain select c from mysqltest.v1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
+show create table mysqltest.v1;
+Table Create Table
+v1 CREATE VIEW `mysqltest`.`v1` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1`
+explain select c from mysqltest.v2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> system NULL NULL NULL NULL 0 const row not found
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
+show create table mysqltest.v2;
+Table Create Table
+v2 CREATE ALGORITHM=TMPTABLE VIEW `mysqltest`.`v2` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1`
+explain select c from mysqltest.v3;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+show create table mysqltest.v3;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+explain select c from mysqltest.v4;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+show create table mysqltest.v4;
+ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table
+grant show view on mysqltest.* to mysqltest_1@localhost;
+explain select c from mysqltest.v1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 system NULL NULL NULL NULL 0 const row not found
+show create table mysqltest.v1;
+Table Create Table
+v1 CREATE VIEW `mysqltest`.`v1` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1`
+explain select c from mysqltest.v2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> system NULL NULL NULL NULL 0 const row not found
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
+show create table mysqltest.v2;
+Table Create Table
+v2 CREATE ALGORITHM=TMPTABLE VIEW `mysqltest`.`v2` AS select (`mysqltest`.`t1`.`a` + 1) AS `c`,(`mysqltest`.`t1`.`b` + 1) AS `d` from `mysqltest`.`t1`
+explain select c from mysqltest.v3;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t2 system NULL NULL NULL NULL 0 const row not found
+show create table mysqltest.v3;
+Table Create Table
+v3 CREATE VIEW `mysqltest`.`v3` AS select (`mysqltest`.`t2`.`a` + 1) AS `c`,(`mysqltest`.`t2`.`b` + 1) AS `d` from `mysqltest`.`t2`
+explain select c from mysqltest.v4;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> system NULL NULL NULL NULL 0 const row not found
+2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
+show create table mysqltest.v4;
+Table Create Table
+v4 CREATE ALGORITHM=TMPTABLE VIEW `mysqltest`.`v4` AS select (`mysqltest`.`t2`.`a` + 1) AS `c`,(`mysqltest`.`t2`.`b` + 1) AS `d` from `mysqltest`.`t2`
+revoke all privileges on mysqltest.* from mysqltest_1@localhost;
+delete from mysql.user where user='mysqltest_1';
+drop database mysqltest;
+set GLOBAL query_cache_size=1355776;
+flush status;
+create table t1 (a int, b int);
+create view v1 (c,d) as select sql_no_cache a,b from t1;
+create view v2 (c,d) as select a+rand(),b from t1;
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 0
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 0
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 0
+select * from v1;
+c d
+select * from v2;
+c d
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 0
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 0
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 0
+select * from v1;
+c d
+select * from v2;
+c d
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 0
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 0
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 0
+drop view v1,v2;
+set query_cache_type=demand;
+flush status;
+create view v1 (c,d) as select sql_cache a,b from t1;
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 0
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 0
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 0
+select * from v1;
+c d
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 1
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 0
+select * from t1;
+a b
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 1
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 0
+select * from v1;
+c d
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 1
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 1
+select * from t1;
+a b
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 1
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 1
+drop view v1;
+set query_cache_type=default;
+drop table t1;
+set GLOBAL query_cache_size=default;
+create table t1 (a int);
+insert into t1 values (1), (2), (3), (1), (2), (3);
+create view v1 as select distinct a from t1;
+select * from v1;
+a
+1
+2
+3
+explain select * from v1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 3
+2 DERIVED t1 ALL NULL NULL NULL NULL 6 Using temporary
+select * from t1;
+a
+1
+2
+3
+1
+2
+3
+drop view v1;
+drop table t1;
+create table t1 (a int);
+create view v1 as select distinct a from t1 WITH CHECK OPTION;
+create view v2 as select distinct a from t1 WITH CASCADED CHECK OPTION;
+create view v3 as select distinct a from t1 WITH LOCAL CHECK OPTION;
+drop view v3 RESTRICT;
+drop view v2 CASCADE;
+drop view v1;
+drop table t1;
+create table t1 (a int, b int);
+insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10);
+create view v1 (c) as select b+1 from t1;
+select test.c from v1 test;
+c
+3
+4
+5
+6
+11
+create algorithm=temptable view v2 (c) as select b+1 from t1;
+select test.c from v2 test;
+c
+3
+4
+5
+6
+11
+select test1.* from v1 test1, v2 test2 where test1.c=test2.c;
+c
+3
+4
+5
+6
+11
+select test2.* from v1 test1, v2 test2 where test1.c=test2.c;
+c
+3
+4
+5
+6
+11
+drop table t1;
+drop view v1,v2;
+create table t1 (a int);
+insert into t1 values (1), (2), (3), (4);
+create view v1 as select a+1 from t1 order by 1 desc limit 2;
+select * from v1;
+a+1
+5
+4
+explain select * from v1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2
+2 DERIVED t1 ALL NULL NULL NULL NULL 4 Using filesort
+drop view v1;
+drop table t1;
+create table t1 (a int);
+insert into t1 values (1), (2), (3), (4);
+create view v1 as select a+1 from t1;
+create table t2 select * from v1;
+show columns from t2;
+Field Type Null Key Default Extra
+a+1 bigint(17) YES NULL
+select * from t2;
+a+1
+2
+3
+4
+5
+drop view v1;
+drop table t1,t2;
+create table t1 (a int, b int, primary key(a));
+insert into t1 values (10,2), (20,3), (30,4), (40,5), (50,10);
+create view v1 (a,c) as select a, b+1 from t1;
+create algorithm=temptable view v2 (a,c) as select a, b+1 from t1;
+update v1 set c=a+c;
+ERROR HY000: Column 'c' is not updatable
+update v2 set a=a+c;
+ERROR HY000: The target table v2 of the UPDATE is not updatable
+update v1 set a=a+c;
+select * from v1;
+a c
+13 3
+24 4
+35 5
+46 6
+61 11
+select * from t1;
+a b
+13 2
+24 3
+35 4
+46 5
+61 10
+drop table t1;
+drop view v1,v2;
+create table t1 (a int, b int, primary key(a));
+insert into t1 values (10,2), (20,3), (30,4), (40,5), (50,10);
+create table t2 (x int);
+insert into t2 values (10), (20);
+create view v1 (a,c) as select a, b+1 from t1;
+create algorithm=temptable view v2 (a,c) as select a, b+1 from t1;
+update t2,v1 set v1.c=v1.a+v1.c where t2.x=v1.a;
+ERROR HY000: Column 'c' is not updatable
+update t2,v2 set v2.a=v2.v2.a+c where t2.x=v2.a;
+ERROR HY000: The target table v2 of the UPDATE is not updatable
+update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.a;
+select * from v1;
+a c
+13 3
+24 4
+30 5
+40 6
+50 11
+select * from t1;
+a b
+13 2
+24 3
+30 4
+40 5
+50 10
+drop table t1,t2;
+drop view v1,v2;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int, primary key(a));
+insert into mysqltest.t1 values (10,2), (20,3), (30,4), (40,5), (50,10);
+create table mysqltest.t2 (x int);
+insert into mysqltest.t2 values (3), (4), (5), (6);
+create view mysqltest.v1 (a,c) as select a, b+1 from mysqltest.t1;
+create view mysqltest.v2 (a,c) as select a, b from mysqltest.t1;
+create view mysqltest.v3 (a,c) as select a, b+1 from mysqltest.t1;
+grant update (a) on mysqltest.v2 to mysqltest_1@localhost;
+grant update on mysqltest.v1 to mysqltest_1@localhost;
+grant select on mysqltest.* to mysqltest_1@localhost;
+use mysqltest;
+update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.c;
+select * from t1;
+a b
+13 2
+24 3
+35 4
+46 5
+50 10
+update v1 set a=a+c;
+select * from t1;
+a b
+16 2
+28 3
+40 4
+52 5
+61 10
+update t2,v2 set v2.a=v2.a+v2.c where t2.x=v2.c;
+select * from t1;
+a b
+16 2
+31 3
+44 4
+57 5
+61 10
+update v2 set a=a+c;
+select * from t1;
+a b
+18 2
+34 3
+48 4
+62 5
+71 10
+update t2,v2 set v2.c=v2.a+v2.c where t2.x=v2.c;
+ERROR 42000: UPDATE command denied to user 'mysqltest_1'@'localhost' for column 'c' in table 'v2'
+update v2 set c=a+c;
+ERROR 42000: UPDATE command denied to user 'mysqltest_1'@'localhost' for column 'c' in table 'v2'
+update t2,v3 set v3.a=v3.a+v3.c where t2.x=v3.c;
+ERROR 42000: UPDATE command denied to user 'mysqltest_1'@'localhost' for column 'a' in table 'v3'
+update v3 set a=a+c;
+ERROR 42000: update command denied to user 'mysqltest_1'@'localhost' for table 'v3'
+use test;
+REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
+drop database mysqltest;
+create table t1 (a int, b int, primary key(b));
+insert into t1 values (1,20), (2,30), (3,40), (4,50), (5,100);
+create view v1 (c) as select b from t1 where a<3;
+select * from v1;
+c
+20
+30
+explain extended select * from v1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 5 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`b` AS `c` from `test`.`v1` where (`test`.`t1`.`a` < 3)
+update v1 set c=c+1;
+select * from t1;
+a b
+1 21
+2 31
+3 40
+4 50
+5 100
+create view v2 (c) as select b from t1 where a>=3;
+select * from v1, v2;
+c c
+21 40
+31 40
+21 50
+31 50
+21 100
+31 100
+drop view v1, v2;
+drop table t1;
+create table t1 (a int, b int, primary key(a));
+insert into t1 values (1,2), (2,3), (3,4), (4,5), (5,10);
+create view v1 (a,c) as select a, b+1 from t1;
+create algorithm=temptable view v2 (a,c) as select a, b+1 from t1;
+delete from v2 where c < 4;
+ERROR HY000: The target table v2 of the DELETE is not updatable
+delete from v1 where c < 4;
+select * from v1;
+a c
+2 4
+3 5
+4 6
+5 11
+select * from t1;
+a b
+2 3
+3 4
+4 5
+5 10
+drop table t1;
+drop view v1,v2;
+create table t1 (a int, b int, primary key(a));
+insert into t1 values (1,2), (2,3), (3,4), (4,5), (5,10);
+create table t2 (x int);
+insert into t2 values (1), (2), (3), (4);
+create view v1 (a,c) as select a, b+1 from t1;
+create algorithm=temptable view v2 (a,c) as select a, b+1 from t1;
+delete v2 from t2,v2 where t2.x=v2.a;
+ERROR HY000: The target table v2 of the DELETE is not updatable
+delete v1 from t2,v1 where t2.x=v1.a;
+select * from v1;
+a c
+5 11
+select * from t1;
+a b
+5 10
+drop table t1,t2;
+drop view v1,v2;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int, primary key(a));
+insert into mysqltest.t1 values (1,2), (2,3), (3,4), (4,5), (5,10);
+create table mysqltest.t2 (x int);
+insert into mysqltest.t2 values (3), (4), (5), (6);
+create view mysqltest.v1 (a,c) as select a, b+1 from mysqltest.t1;
+create view mysqltest.v2 (a,c) as select a, b+1 from mysqltest.t1;
+grant delete on mysqltest.v1 to mysqltest_1@localhost;
+grant select on mysqltest.* to mysqltest_1@localhost;
+use mysqltest;
+delete from v1 where c < 4;
+select * from t1;
+a b
+2 3
+3 4
+4 5
+5 10
+delete v1 from t2,v1 where t2.x=v1.c;
+select * from t1;
+a b
+5 10
+delete v2 from t2,v2 where t2.x=v2.c;
+ERROR 42000: delete command denied to user 'mysqltest_1'@'localhost' for table 'v2'
+delete from v2 where c < 4;
+ERROR 42000: delete command denied to user 'mysqltest_1'@'localhost' for table 'v2'
+use test;
+REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
+drop database mysqltest;
+create table t1 (a int, b int, c int, primary key(a,b));
+insert into t1 values (10,2,-1), (20,3,-2), (30,4,-3), (40,5,-4), (50,10,-5);
+create view v1 (x,y) as select a, b from t1;
+create view v2 (x,y) as select a, c from t1;
+set sql_updatable_view_key=YES;
+update v1 set x=x+1;
+update v2 set x=x+1;
+ERROR HY000: The target table v2 of the UPDATE is not updatable
+set sql_updatable_view_key=LIMIT1;
+update v1 set x=x+1;
+update v2 set x=x+1;
+Warnings:
+Note 1354 View being updated does not have complete key of underlying table in it
+update v1 set x=x+1 limit 1;
+update v2 set x=x+1 limit 1;
+ERROR HY000: The target table v2 of the UPDATE is not updatable
+set sql_updatable_view_key=NO;
+update v1 set x=x+1 limit 1;
+update v2 set x=x+1 limit 1;
+Warnings:
+Note 1354 View being updated does not have complete key of underlying table in it
+set sql_updatable_view_key=DEFAULT;
+select * from t1;
+a b c
+16 2 -1
+23 3 -2
+33 4 -3
+43 5 -4
+53 10 -5
+drop table t1;
+drop view v1,v2;
+create table t1 (a int, b int, c int, primary key(a,b));
+insert into t1 values (10,2,-1), (20,3,-2);
+create view v1 (x,y,z) as select c, b, a from t1;
+create view v2 (x,y) as select b, a from t1;
+create view v3 (x,y,z) as select b, a, b from t1;
+create view v4 (x,y,z) as select c+1, b, a from t1;
+create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1;
+insert into v3 values (-60,4,30);
+ERROR HY000: The target table v3 of the INSERT is not updatable
+insert into v4 values (-60,4,30);
+ERROR HY000: The target table v4 of the INSERT is not updatable
+insert into v5 values (-60,4,30);
+ERROR HY000: The target table v5 of the INSERT is not updatable
+insert into v1 values (-60,4,30);
+insert into v1 (z,y,x) values (50,6,-100);
+insert into v2 values (5,40);
+select * from t1;
+a b c
+10 2 -1
+20 3 -2
+30 4 -60
+50 6 -100
+40 5 NULL
+drop table t1;
+drop view v1,v2,v3,v4,v5;
+create table t1 (a int, b int, c int, primary key(a,b));
+insert into t1 values (10,2,-1), (20,3,-2);
+create table t2 (a int, b int, c int, primary key(a,b));
+insert into t2 values (30,4,-60);
+create view v1 (x,y,z) as select c, b, a from t1;
+create view v2 (x,y) as select b, a from t1;
+create view v3 (x,y,z) as select b, a, b from t1;
+create view v4 (x,y,z) as select c+1, b, a from t1;
+create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1;
+insert into v3 select c, b, a from t2;
+ERROR HY000: The target table v3 of the INSERT is not updatable
+insert into v4 select c, b, a from t2;
+ERROR HY000: The target table v4 of the INSERT is not updatable
+insert into v5 select c, b, a from t2;
+ERROR HY000: The target table v5 of the INSERT is not updatable
+insert into v1 select c, b, a from t2;
+insert into v1 (z,y,x) select a+20,b+2,-100 from t2;
+insert into v2 select b+1, a+10 from t2;
+select * from t1;
+a b c
+10 2 -1
+20 3 -2
+30 4 -60
+50 6 -100
+40 5 NULL
+drop table t1, t2;
+drop view v1,v2,v3,v4,v5;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int, primary key(a));
+insert into mysqltest.t1 values (1,2), (2,3);
+create table mysqltest.t2 (x int, y int);
+insert into mysqltest.t2 values (3,4);
+create view mysqltest.v1 (a,c) as select a, b from mysqltest.t1;
+create view mysqltest.v2 (a,c) as select a, b from mysqltest.t1;
+grant insert on mysqltest.v1 to mysqltest_1@localhost;
+grant select on mysqltest.* to mysqltest_1@localhost;
+use mysqltest;
+insert into v1 values (5,6);
+select * from t1;
+a b
+1 2
+2 3
+5 6
+insert into v1 select x,y from t2;
+select * from t1;
+a b
+1 2
+2 3
+5 6
+3 4
+insert into v2 values (5,6);
+ERROR 42000: insert command denied to user 'mysqltest_1'@'localhost' for table 'v2'
+insert into v2 select x,y from t2;
+ERROR 42000: insert command denied to user 'mysqltest_1'@'localhost' for table 'v2'
+use test;
+REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
+drop database mysqltest;
+create table t1 (a int, primary key(a));
+insert into t1 values (1), (2), (3);
+create view v1 (x) as select a from t1 where a > 1;
+select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x);
+a x
+1 NULL
+2 2
+3 3
+drop table t1;
+drop view v1;
+create table t1 (a int, primary key(a));
+insert into t1 values (1), (2), (3), (200);
+create view v1 (x) as select a from t1 where a > 1;
+create view v2 (y) as select x from v1 where x < 100;
+select * from v2;
+x
+2
+3
+drop table t1;
+drop view v1,v2;
+create table t1 (a int, primary key(a));
+insert into t1 values (1), (2), (3), (200);
+create ALGORITHM=TEMPTABLE view v1 (x) as select a from t1;
+create view v2 (y) as select x from v1;
+update v2 set y=10 where y=2;
+ERROR HY000: The target table v2 of the UPDATE is not updatable
+drop table t1;
+drop view v1,v2;
+create table t1 (a int not null auto_increment, b int not null, primary key(a), unique(b));
+create view v1 (x) as select b from t1;
+insert into v1 values (1);
+select last_insert_id();
+last_insert_id()
+0
+insert into t1 (b) values (2);
+select last_insert_id();
+last_insert_id()
+2
+select * from t1;
+a b
+1 1
+2 2
+drop view v1;
+drop table t1;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int);
+create table mysqltest.t2 (a int, b int);
+grant update on mysqltest.t1 to mysqltest_1@localhost;
+grant update(b) on mysqltest.t2 to mysqltest_1@localhost;
+grant create view,update on test.* to mysqltest_1@localhost;
+create view v1 as select * from mysqltest.t1;
+create view v2 as select b from mysqltest.t2;
+create view mysqltest.v1 as select * from mysqltest.t1;
+ERROR 42000: create view command denied to user 'mysqltest_1'@'localhost' for table 'v1'
+create view v3 as select a from mysqltest.t2;
+ERROR 42000: ANY command denied to user 'mysqltest_1'@'localhost' for column 'a' in table 't2'
+create table mysqltest.v3 (b int);
+grant create view on mysqltest.v3 to mysqltest_1@localhost;
+drop table mysqltest.v3;
+create view mysqltest.v3 as select b from mysqltest.t2;
+ERROR 42000: create view command denied to user 'mysqltest_1'@'localhost' for column 'b' in table 'v3'
+create table mysqltest.v3 (b int);
+grant create view, update on mysqltest.v3 to mysqltest_1@localhost;
+drop table mysqltest.v3;
+create view mysqltest.v3 as select b from mysqltest.t2;
+grant select(b) on mysqltest.v3 to mysqltest_1@localhost;
+drop view mysqltest.v3;
+create view mysqltest.v3 as select b from mysqltest.t2;
+ERROR 42000: create view command denied to user 'mysqltest_1'@'localhost' for table 'v3'
+create view v4 as select b+1 from mysqltest.t2;
+ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for column 'b' in table 't2'
+grant create view,update,select on test.* to mysqltest_1@localhost;
+create view v4 as select b+1 from mysqltest.t2;
+ERROR 42000: SELECT command denied to user 'mysqltest_1'@'localhost' for column 'b' in table 't2'
+grant update,select(b) on mysqltest.t2 to mysqltest_1@localhost;
+create view v4 as select b+1 from mysqltest.t2;
+REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
+drop database mysqltest;
+drop view v1,v2;
+set sql_mode='ansi';
+create table t1 ("a*b" int);
+create view v1 as select "a*b" from t1;
+show create view v1;
+Table Create Table
+v1 CREATE VIEW "test"."v1" AS select `test`.`t1`.`a*b` AS `a*b` from `test`.`t1`
+drop view v1;
+drop table t1;
+set sql_mode=default;
+create table t1 (t_column int);
+create view v1 as select 'a';
+select * from v1, t1;
+a t_column
+drop view v1;
+drop table t1;
+create table `t1a``b` (col1 char(2));
+create view v1 as select * from `t1a``b`;
+select * from v1;
+col1
+describe v1;
+Field Type Null Key Default Extra
+col1 char(2) YES NULL
+drop view v1;
+drop table `t1a``b`;
+create table t1 (col1 char(5),col2 char(5));
+create view v1 as select * from t1;
+drop table t1;
+create table t1 (col1 char(5),newcol2 char(5));
+insert into v1 values('a','aa');
+ERROR HY000: View 'test.v1' references invalid table(s) or column(s)
+drop table t1;
+select * from v1;
+ERROR HY000: View 'test.v1' references invalid table(s) or column(s)
+drop view v1;
+create view v1 (a,a) as select 'a','a';
+ERROR 42S21: Duplicate column name 'a'
+create procedure p1 () begin declare v int; create view v1 as select v; end;//
+Warnings:
+Warning 1310 Referring to uninitialized variable v
+call p1();
+ERROR HY000: View's SELECT contains a variable or parameter
+drop procedure p1;
+create table t1 (col1 int,col2 char(22));
+insert into t1 values(5,'Hello, world of views');
+create view v1 as select * from t1;
+create view v2 as select * from v1;
+update v2 set col2='Hello, view world';
+select * from t1;
+col1 col2
+5 Hello, view world
+drop view v2, v1;
+drop table t1;
+create table t1 (a int, b int);
+create view v1 as select a, sum(b) from t1 group by a;
+select b from v1 use index (some_index) where b=1;
+ERROR 42000: Key column 'some_index' doesn't exist in table
+drop view v1;
+drop table t1;
+create table t1 (col1 char(5),col2 char(5));
+create view v1 (col1,col2) as select col1,col2 from t1;
+insert into v1 values('s1','p1'),('s1','p2'),('s1','p3'),('s1','p4'),('s2','p1'),('s3','p2'),('s4','p4');
+select distinct first.col2 from t1 first where first.col2 in (select second.col2 from t1 second where second.col1<>first.col1);
+col2
+p1
+p2
+p4
+select distinct first.col2 from v1 first where first.col2 in (select second.col2 from t1 second where second.col1<>first.col1);
+col2
+p1
+p2
+p4
+drop view v1;
+drop table t1;
+create table t1 (a int);
+create view v1 as select a from t1;
+insert into t1 values (1);
+SET @v0 = '2';
+PREPARE stmt FROM 'UPDATE v1 SET a = ?';
+EXECUTE stmt USING @v0;
+DEALLOCATE PREPARE stmt;
+SET @v0 = '3';
+PREPARE stmt FROM 'insert into v1 values (?)';
+EXECUTE stmt USING @v0;
+DEALLOCATE PREPARE stmt;
+SET @v0 = '4';
+PREPARE stmt FROM 'insert into v1 (a) values (?)';
+EXECUTE stmt USING @v0;
+DEALLOCATE PREPARE stmt;
+select * from t1;
+a
+2
+3
+4
+drop view v1;
+drop table t1;
+CREATE VIEW v02 AS SELECT * FROM DUAL;
+ERROR HY000: No tables used
+SHOW TABLES;
+Tables_in_test table_type
+v4 VIEW
+CREATE VIEW v1 AS SELECT EXISTS (SELECT 1 UNION SELECT 2);
+select * from v1;
+EXISTS (SELECT 1 UNION SELECT 2)
+1
+drop view v1;
+create table t1 (col1 int,col2 char(22));
+create view v1 as select * from t1;
+create index i1 on v1 (col1);
+ERROR HY000: 'test.v1' is not BASE TABLE
+drop view v1;
+drop table t1;
+CREATE VIEW v1 (f1,f2,f3,f4) AS SELECT connection_id(), pi(), current_user(), version();
+SHOW CREATE VIEW v1;
+Table Create Table
+v1 CREATE VIEW `test`.`v1` AS select sql_no_cache connection_id() AS `f1`,pi() AS `f2`,current_user() AS `f3`,version() AS `f4`
+drop view v1;
+create table t1 (s1 int);
+create table t2 (s2 int);
+insert into t1 values (1), (2);
+insert into t2 values (2), (3);
+create view v1 as select * from t1,t2 union all select * from t1,t2;
+select * from v1;
+s1 s2
+1 2
+2 2
+1 3
+2 3
+1 2
+2 2
+1 3
+2 3
+drop view v1;
+drop tables t1, t2;
+create table t1 (col1 int);
+insert into t1 values (1);
+create view v1 as select count(*) from t1;
+insert into t1 values (null);
+select * from v1;
+count(*)
+2
+drop view v1;
+drop table t1;
+create table t1 (a int);
+create table t2 (a int);
+create view v1 as select a from t1;
+create view v2 as select a from t2 where a in (select a from v1);
+show create view v2;
+Table Create Table
+v2 CREATE VIEW `test`.`v2` AS select `test`.`t2`.`a` AS `a` from `test`.`t2` where `a` in (select `v1`.`a` AS `a` from `test`.`v1`)
+drop view v2, v1;
+drop table t1, t2;
+CREATE VIEW `v 1` AS select 5 AS `5`;
+show create view `v 1`;
+Table Create Table
+v 1 CREATE VIEW `test`.`v 1` AS select 5 AS `5`
+drop view `v 1`;
+create database mysqltest;
+create table mysqltest.t1 (a int, b int);
+create view mysqltest.v1 as select a from mysqltest.t1;
+alter view mysqltest.v1 as select b from mysqltest.t1;
+alter view mysqltest.v1 as select a from mysqltest.t1;
+drop database mysqltest;
+CREATE TABLE t1 (c1 int not null auto_increment primary key, c2 varchar(20), fulltext(c2));
+insert into t1 (c2) VALUES ('real Beer'),('Water'),('Kossu'),('Coca-Cola'),('Vodka'),('Wine'),('almost real Beer');
+select * from t1 WHERE match (c2) against ('Beer');
+c1 c2
+1 real Beer
+7 almost real Beer
+CREATE VIEW v1 AS SELECT * from t1 WHERE match (c2) against ('Beer');
+select * from v1;
+c1 c2
+1 real Beer
+7 almost real Beer
+drop view v1;
+drop table t1;
+create table t1 (a int);
+insert into t1 values (1),(1),(2),(2),(3),(3);
+create view v1 as select a from t1;
+select distinct a from v1;
+a
+1
+2
+3
+select distinct a from v1 limit 2;
+a
+1
+2
+select distinct a from t1 limit 2;
+a
+1
+2
+prepare stmt1 from "select distinct a from v1 limit 2";
+execute stmt1;
+a
+1
+2
+execute stmt1;
+a
+1
+2
+deallocate prepare stmt1;
+drop view v1;
+drop table t1;
+create table t1 (tg_column bigint);
+create view v1 as select count(tg_column) as vg_column from t1;
+select avg(vg_column) from v1;
+avg(vg_column)
+0.0000
+drop view v1;
+drop table t1;
+create table t1 (col1 bigint not null, primary key (col1));
+create table t2 (col1 bigint not null, key (col1));
+create view v1 as select * from t1;
+create view v2 as select * from t2;
+insert into v1 values (1);
+insert into v2 values (1);
+create view v3 (a,b) as select v1.col1 as a, v2.col1 as b from v1, v2 where v1.col1 = v2.col1;
+select * from v3;
+a b
+1 1
+show create view v3;
+Table Create Table
+v3 CREATE VIEW `test`.`v3` AS select `v1`.`col1` AS `a`,`v2`.`col1` AS `b` from `test`.`v1` join `test`.`v2` where (`v1`.`col1` = `v2`.`col1`)
+drop view v3, v2, v1;
+drop table t2, t1;
diff --git a/mysql-test/r/view_skip_grants.result b/mysql-test/r/view_skip_grants.result
new file mode 100644
index 00000000000..48cb9f2aa25
--- /dev/null
+++ b/mysql-test/r/view_skip_grants.result
@@ -0,0 +1,6 @@
+drop table if exists t1,v1;
+drop view if exists t1,v1;
+use test;
+create table t1 (field1 INT);
+CREATE VIEW v1 AS SELECT field1 FROM t1;
+drop view v1;
diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test
index fbfd3ccdef1..64e3fe8929b 100644
--- a/mysql-test/t/derived.test
+++ b/mysql-test/t/derived.test
@@ -101,7 +101,7 @@ SELECT a.x FROM (SELECT 1 AS x) AS a HAVING a.x = 1;
#
# Connect without a database
create table t1 select 1 as a;
-connect (con1,localhost,mysqltest_1,,*NO-ONE*,$MASTER_MYPORT,master.sock);
+connect (con1,localhost,mysqltest_1,,*NO-ONE*,$MASTER_MYPORT,$MASTER_MYSOCK);
connection con1;
--error 1046
select 2 as a from (select * from t1) b;
diff --git a/mysql-test/t/distinct.test b/mysql-test/t/distinct.test
index 3c1f18b7524..a3862786cc3 100644
--- a/mysql-test/t/distinct.test
+++ b/mysql-test/t/distinct.test
@@ -326,9 +326,9 @@ drop table t1,t2;
CREATE TABLE t1 (
html varchar(5) default NULL,
rin int(11) default '0',
- out int(11) default '0'
+ rout int(11) default '0'
) ENGINE=MyISAM;
INSERT INTO t1 VALUES ('1',1,0);
-SELECT DISTINCT html,SUM(out)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin;
+SELECT DISTINCT html,SUM(rout)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin;
drop table t1;
diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test
index 74f4c1bad44..1416d5f0f9a 100644
--- a/mysql-test/t/func_group.test
+++ b/mysql-test/t/func_group.test
@@ -220,6 +220,7 @@ insert into t2 values('DEN','Denver','CO','BDL');
insert into t2 values('SDC','San Diego','CA','TWU');
insert into t2 values('NOL','New Orleans','LA','GTM');
insert into t2 values('LAK','Los Angeles','CA','TWU');
+insert into t2 values('AAA','AAA','AA','AME');
# Show the table contents
select * from t1;
diff --git a/mysql-test/t/func_sapdb.test b/mysql-test/t/func_sapdb.test
index 24028437fde..93e57daad82 100644
--- a/mysql-test/t/func_sapdb.test
+++ b/mysql-test/t/func_sapdb.test
@@ -94,7 +94,8 @@ insert into test values
('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01');
SELECT ADDTIME(t1,t2) As ttt, ADDTIME(t2, t3) As qqq from test;
-SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test;
+SELECT TIMEDIFF(t1, t4) As ttt, TIMEDIFF(t2, t3) As qqq,
+ TIMEDIFF(t3, t2) As eee, TIMEDIFF(t2, t4) As rrr from test;
drop table t1, test;
diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test
index 67192c55ef9..0ca3d86818e 100644
--- a/mysql-test/t/func_time.test
+++ b/mysql-test/t/func_time.test
@@ -255,6 +255,30 @@ select date_add(date,INTERVAL "1 1" YEAR_MONTH) from t1;
select date_add(date,INTERVAL "1:1:1" HOUR_SECOND) from t1;
select date_add(date,INTERVAL "1 1:1" DAY_MINUTE) from t1;
select date_add(date,INTERVAL "1 1:1:1" DAY_SECOND) from t1;
+select date_add(date,INTERVAL "1" WEEK) from t1;
+select date_add(date,INTERVAL "1" QUARTER) from t1;
+select timestampadd(MINUTE, 1, date) from t1;
+select timestampadd(WEEK, 1, date) from t1;
+select timestampadd(SQL_TSI_SECOND, 1, date) from t1;
+select timestampadd(SQL_TSI_FRAC_SECOND, 1, date) from t1;
+
+select timestampdiff(MONTH, '2001-02-01', '2001-05-01') as a;
+select timestampdiff(YEAR, '2002-05-01', '2001-01-01') as a;
+select timestampdiff(QUARTER, '2002-05-01', '2001-01-01') as a;
+select timestampdiff(MONTH, '2000-03-28', '2000-02-29') as a;
+select timestampdiff(MONTH, '1991-03-28', '2000-02-29') as a;
+select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a;
+select timestampdiff(SQL_TSI_HOUR, '2001-02-01', '2001-05-01') as a;
+select timestampdiff(SQL_TSI_DAY, '2001-02-01', '2001-05-01') as a;
+select timestampdiff(SQL_TSI_MINUTE, '2001-02-01 12:59:59', '2001-05-01 12:58:59') as a;
+select timestampdiff(SQL_TSI_SECOND, '2001-02-01 12:59:59', '2001-05-01 12:58:58') as a;
+select timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a;
+
+select timestampdiff(SQL_TSI_DAY, '1986-02-01', '1986-03-01') as a1,
+ timestampdiff(SQL_TSI_DAY, '1900-02-01', '1900-03-01') as a2,
+ timestampdiff(SQL_TSI_DAY, '1996-02-01', '1996-03-01') as a3,
+ timestampdiff(SQL_TSI_DAY, '2000-02-01', '2000-03-01') as a4;
+
select date_add(time,INTERVAL 1 SECOND) from t1;
drop table t1;
@@ -296,3 +320,5 @@ INSERT INTO t1 VALUES (NOW());
SELECT count(*) FROM t1 WHERE d>FROM_DAYS(TO_DAYS(@TMP)) AND d<=FROM_DAYS(TO_DAYS(@TMP)+1);
DROP TABLE t1;
+explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1,
+ timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2;
diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test
index e1319690dc5..a892b761964 100644
--- a/mysql-test/t/grant.test
+++ b/mysql-test/t/grant.test
@@ -189,3 +189,8 @@ show grants for test11@localhost;
delete from mysql.user where user='test11';
delete from mysql.db where user='test11';
+
+#
+# just SHOW PRIVILEGES test
+#
+SHOW PRIVILEGES;
diff --git a/mysql-test/t/grant_cache.test b/mysql-test/t/grant_cache.test
index e5bde977bb7..1ec4a52fdd1 100644
--- a/mysql-test/t/grant_cache.test
+++ b/mysql-test/t/grant_cache.test
@@ -10,7 +10,7 @@ drop database if exists mysqltest;
reset query cache;
flush status;
-connect (root,localhost,root,,test,$MASTER_MYPORT,master.sock);
+connect (root,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connection root;
show grants for current_user;
show grants;
@@ -25,7 +25,7 @@ insert into mysqltest.t2 values (3,3,3);
create table test.t1 (a char (10));
insert into test.t1 values ("test.t1");
select * from t1;
-connect (root2,localhost,root,,mysqltest,$MASTER_MYPORT,master.sock);
+connect (root2,localhost,root,,mysqltest,$MASTER_MYPORT,$MASTER_MYSOCK);
connection root2;
# put queries in cache
select * from t1;
@@ -43,7 +43,7 @@ grant SELECT on test.t1 to mysqltest_2@localhost;
grant SELECT(a) on mysqltest.t1 to mysqltest_3@localhost;
# The following queries should be fetched from cache
-connect (user1,localhost,mysqltest_1,,mysqltest,$MASTER_MYPORT,master.sock);
+connect (user1,localhost,mysqltest_1,,mysqltest,$MASTER_MYPORT,$MASTER_MYSOCK);
connection user1;
show grants for current_user();
show status like "Qcache_queries_in_cache";
@@ -68,12 +68,12 @@ show status like "Qcache_hits";
show status like "Qcache_not_cached";
# Don't use '' as user because it will pick Unix login
-connect (unkuser,localhost,unkuser,,,$MASTER_MYPORT,master.sock);
+connect (unkuser,localhost,unkuser,,,$MASTER_MYPORT,$MASTER_MYSOCK);
connection unkuser;
show grants for current_user();
# The following queries should be fetched from cache
-connect (user2,localhost,mysqltest_2,,mysqltest,$MASTER_MYPORT,master.sock);
+connect (user2,localhost,mysqltest_2,,mysqltest,$MASTER_MYPORT,$MASTER_MYSOCK);
connection user2;
select "user2";
select * from t1;
@@ -88,7 +88,7 @@ show status like "Qcache_hits";
show status like "Qcache_not_cached";
# The following queries should not be fetched from cache
-connect (user3,localhost,mysqltest_3,,mysqltest,$MASTER_MYPORT,master.sock);
+connect (user3,localhost,mysqltest_3,,mysqltest,$MASTER_MYPORT,$MASTER_MYSOCK);
connection user3;
select "user3";
--replace_result 127.0.0.1 localhost
@@ -109,7 +109,7 @@ show status like "Qcache_hits";
show status like "Qcache_not_cached";
# Connect without a database
-connect (user4,localhost,mysqltest_1,,*NO-ONE*,$MASTER_MYPORT,master.sock);
+connect (user4,localhost,mysqltest_1,,*NO-ONE*,$MASTER_MYPORT,$MASTER_MYSOCK);
connection user4;
select "user4";
show grants;
diff --git a/mysql-test/t/greedy_optimizer.test b/mysql-test/t/greedy_optimizer.test
new file mode 100644
index 00000000000..e547d85b7f3
--- /dev/null
+++ b/mysql-test/t/greedy_optimizer.test
@@ -0,0 +1,313 @@
+#
+# A simple test of the greedy query optimization algorithm and the switches that
+# control the optimizationprocess.
+#
+
+#
+# Schema
+#
+--disable_warnings
+drop table if exists t1,t2,t3,t4,t5,t6,t7;
+--enable_warnings
+
+create table t1 (
+ c11 integer,c12 integer,c13 integer,c14 integer,c15 integer,c16 integer,
+ primary key (c11)
+);
+create table t2 (
+ c21 integer,c22 integer,c23 integer,c24 integer,c25 integer,c26 integer
+);
+create table t3 (
+ c31 integer,c32 integer,c33 integer,c34 integer,c35 integer,c36 integer,
+ primary key (c31)
+);
+create table t4 (
+ c41 integer,c42 integer,c43 integer,c44 integer,c45 integer,c46 integer
+);
+create table t5 (
+ c51 integer,c52 integer,c53 integer,c54 integer,c55 integer,c56 integer,
+ primary key (c51)
+);
+create table t6 (
+ c61 integer,c62 integer,c63 integer,c64 integer,c65 integer,c66 integer
+);
+create table t7 (
+ c71 integer,c72 integer,c73 integer,c74 integer,c75 integer,c76 integer,
+ primary key (c71)
+);
+
+#
+# Data
+# cardinality(Ti) = cardinality(T(i-1)) + 3
+#
+insert into t1 values (1,2,3,4,5,6);
+insert into t1 values (2,2,3,4,5,6);
+insert into t1 values (3,2,3,4,5,6);
+
+insert into t2 values (1,2,3,4,5,6);
+insert into t2 values (2,2,3,4,5,6);
+insert into t2 values (3,2,3,4,5,6);
+insert into t2 values (4,2,3,4,5,6);
+insert into t2 values (5,2,3,4,5,6);
+insert into t2 values (6,2,3,4,5,6);
+
+insert into t3 values (1,2,3,4,5,6);
+insert into t3 values (2,2,3,4,5,6);
+insert into t3 values (3,2,3,4,5,6);
+insert into t3 values (4,2,3,4,5,6);
+insert into t3 values (5,2,3,4,5,6);
+insert into t3 values (6,2,3,4,5,6);
+insert into t3 values (7,2,3,4,5,6);
+insert into t3 values (8,2,3,4,5,6);
+insert into t3 values (9,2,3,4,5,6);
+
+insert into t4 values (1,2,3,4,5,6);
+insert into t4 values (2,2,3,4,5,6);
+insert into t4 values (3,2,3,4,5,6);
+insert into t4 values (4,2,3,4,5,6);
+insert into t4 values (5,2,3,4,5,6);
+insert into t4 values (6,2,3,4,5,6);
+insert into t4 values (7,2,3,4,5,6);
+insert into t4 values (8,2,3,4,5,6);
+insert into t4 values (9,2,3,4,5,6);
+insert into t4 values (10,2,3,4,5,6);
+insert into t4 values (11,2,3,4,5,6);
+insert into t4 values (12,2,3,4,5,6);
+
+insert into t5 values (1,2,3,4,5,6);
+insert into t5 values (2,2,3,4,5,6);
+insert into t5 values (3,2,3,4,5,6);
+insert into t5 values (4,2,3,4,5,6);
+insert into t5 values (5,2,3,4,5,6);
+insert into t5 values (6,2,3,4,5,6);
+insert into t5 values (7,2,3,4,5,6);
+insert into t5 values (8,2,3,4,5,6);
+insert into t5 values (9,2,3,4,5,6);
+insert into t5 values (10,2,3,4,5,6);
+insert into t5 values (11,2,3,4,5,6);
+insert into t5 values (12,2,3,4,5,6);
+insert into t5 values (13,2,3,4,5,6);
+insert into t5 values (14,2,3,4,5,6);
+insert into t5 values (15,2,3,4,5,6);
+
+insert into t6 values (1,2,3,4,5,6);
+insert into t6 values (2,2,3,4,5,6);
+insert into t6 values (3,2,3,4,5,6);
+insert into t6 values (4,2,3,4,5,6);
+insert into t6 values (5,2,3,4,5,6);
+insert into t6 values (6,2,3,4,5,6);
+insert into t6 values (7,2,3,4,5,6);
+insert into t6 values (8,2,3,4,5,6);
+insert into t6 values (9,2,3,4,5,6);
+insert into t6 values (10,2,3,4,5,6);
+insert into t6 values (11,2,3,4,5,6);
+insert into t6 values (12,2,3,4,5,6);
+insert into t6 values (13,2,3,4,5,6);
+insert into t6 values (14,2,3,4,5,6);
+insert into t6 values (15,2,3,4,5,6);
+insert into t6 values (16,2,3,4,5,6);
+insert into t6 values (17,2,3,4,5,6);
+insert into t6 values (18,2,3,4,5,6);
+
+insert into t7 values (1,2,3,4,5,6);
+insert into t7 values (2,2,3,4,5,6);
+insert into t7 values (3,2,3,4,5,6);
+insert into t7 values (4,2,3,4,5,6);
+insert into t7 values (5,2,3,4,5,6);
+insert into t7 values (6,2,3,4,5,6);
+insert into t7 values (7,2,3,4,5,6);
+insert into t7 values (8,2,3,4,5,6);
+insert into t7 values (9,2,3,4,5,6);
+insert into t7 values (10,2,3,4,5,6);
+insert into t7 values (11,2,3,4,5,6);
+insert into t7 values (12,2,3,4,5,6);
+insert into t7 values (13,2,3,4,5,6);
+insert into t7 values (14,2,3,4,5,6);
+insert into t7 values (15,2,3,4,5,6);
+insert into t7 values (16,2,3,4,5,6);
+insert into t7 values (17,2,3,4,5,6);
+insert into t7 values (18,2,3,4,5,6);
+insert into t7 values (19,2,3,4,5,6);
+insert into t7 values (20,2,3,4,5,6);
+insert into t7 values (21,2,3,4,5,6);
+
+#
+# The actual test begins here
+#
+
+# Check the default values for the optimizer paramters
+
+select @@optimizer_search_depth;
+select @@optimizer_prune_level;
+
+-- This value swithes back to the old implementation of 'find_best()'
+-- set optimizer_search_depth=63; - old (independent of the optimizer_prune_level)
+--
+-- These are the values for the parameters that control the greedy optimizer
+-- (total 6 combinations - 3 for optimizer_search_depth, 2 for optimizer_prune_level):
+--
+-- set optimizer_search_depth=0; - automatic
+-- set optimizer_search_depth=1; - min
+-- set optimizer_search_depth=62; - max (default)
+--
+-- set optimizer_prune_level=0 - exhaustive;
+-- set optimizer_prune_level=1 - heuristic; -- default
+
+
+#
+# Compile several queries with all combinations of the query
+# optimizer parameters. Each test query has two variants, where
+# in the second variant the tables in the FROM clause are in
+# inverse order to the tables in the first variant.
+# Due to pre-sorting of tables before compilation, there should
+# be no difference in the plans for each two such query variants.
+#
+
+# First, for reference compile the test queries with the 'old' optimization
+# procedure 'find_best'. Notice that 'find_best' does not depend on the
+# choice of heuristic.
+
+set optimizer_search_depth=63;
+select @@optimizer_search_depth;
+
+-- 6-table join, chain
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, star
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, clique
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+
+
+# Test the new optimization procedures
+
+set optimizer_prune_level=0;
+select @@optimizer_prune_level;
+
+set optimizer_search_depth=0;
+select @@optimizer_search_depth;
+
+-- 6-table join, chain
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, star
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, clique
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+
+set optimizer_search_depth=1;
+select @@optimizer_search_depth;
+
+-- 6-table join, chain
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, star
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, clique
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+
+set optimizer_search_depth=62;
+select @@optimizer_search_depth;
+
+-- 6-table join, chain
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, star
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, clique
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+
+
+set optimizer_prune_level=1;
+select @@optimizer_prune_level;
+
+set optimizer_search_depth=0;
+select @@optimizer_search_depth;
+
+-- 6-table join, chain
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, star
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, clique
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+
+set optimizer_search_depth=1;
+select @@optimizer_search_depth;
+
+-- 6-table join, chain
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, star
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, clique
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+
+set optimizer_search_depth=62;
+select @@optimizer_search_depth;
+
+-- 6-table join, chain
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c12 = t2.c21 and t2.c22 = t3.c31 and t3.c32 = t4.c41 and t4.c42 = t5.c51 and t5.c52 = t6.c61 and t6.c62 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, star
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71;
+show status like 'Last_query_cost';
+-- 6-table join, clique
+explain select t1.c11 from t1, t2, t3, t4, t5, t6, t7 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+explain select t1.c11 from t7, t6, t5, t4, t3, t2, t1 where t1.c11 = t2.c21 and t1.c12 = t3.c31 and t1.c13 = t4.c41 and t1.c14 = t5.c51 and t1.c15 = t6.c61 and t1.c16 = t7.c71 and t2.c22 = t3.c32 and t2.c23 = t4.c42 and t2.c24 = t5.c52 and t2.c25 = t6.c62 and t2.c26 = t7.c72 and t3.c33 = t4.c43 and t3.c34 = t5.c53 and t3.c35 = t6.c63 and t3.c36 = t7.c73 and t4.c42 = t5.c54 and t4.c43 = t6.c64 and t4.c44 = t7.c74 and t5.c52 = t6.c65 and t5.c53 = t7.c75 and t6.c62 = t7.c76;
+show status like 'Last_query_cost';
+
+drop table t1,t2,t3,t4,t5,t6,t7;
diff --git a/mysql-test/t/index_merge.test b/mysql-test/t/index_merge.test
new file mode 100644
index 00000000000..a4f7b71e5a3
--- /dev/null
+++ b/mysql-test/t/index_merge.test
@@ -0,0 +1,281 @@
+#
+# Index merge tests
+#
+
+--disable_warnings
+drop table if exists t0, t1, t2, t3,t4;
+--enable_warnings
+
+# Create and fill a table with simple keys
+create table t0
+(
+ key1 int not null,
+ INDEX i1(key1)
+);
+
+--disable_query_log
+insert into t0 values (1),(2),(3),(4),(5),(6),(7),(8);
+
+let $1=7;
+set @d=8;
+while ($1)
+{
+ eval insert into t0 select key1+@d from t0;
+ eval set @d=@d*2;
+ dec $1;
+}
+--enable_query_log
+
+alter table t0 add key2 int not null, add index i2(key2);
+alter table t0 add key3 int not null, add index i3(key3);
+alter table t0 add key4 int not null, add index i4(key4);
+alter table t0 add key5 int not null, add index i5(key5);
+alter table t0 add key6 int not null, add index i6(key6);
+alter table t0 add key7 int not null, add index i7(key7);
+alter table t0 add key8 int not null, add index i8(key8);
+
+update t0 set key2=key1,key3=key1,key4=key1,key5=key1,key6=key1,key7=key1,key8=1024-key1;
+analyze table t0;
+
+# 1. One index
+explain select * from t0 where key1 < 3 or key1 > 1020;
+
+# 2. Simple cases
+explain
+select * from t0 where key1 < 3 or key2 > 1020;
+select * from t0 where key1 < 3 or key2 > 1020;
+
+explain select * from t0 where key1 < 3 or key2 <4;
+
+explain
+select * from t0 where (key1 > 30 and key1<35) or (key2 >32 and key2 < 40);
+select * from t0 where (key1 > 30 and key1<35) or (key2 >32 and key2 < 40);
+
+# 3. Check that index_merge doesn't break "ignore/force/use index"
+explain select * from t0 ignore index (i2) where key1 < 3 or key2 <4;
+explain select * from t0 where (key1 < 3 or key2 <4) and key3 = 50;
+explain select * from t0 use index (i1,i2) where (key1 < 3 or key2 <4) and key3 = 50;
+
+explain select * from t0 where (key1 > 1 or key2 > 2);
+explain select * from t0 force index (i1,i2) where (key1 > 1 or key2 > 2);
+
+
+# 4. Check if conjuncts are grouped by keyuse
+explain
+ select * from t0 where key1<3 or key2<3 or (key1>5 and key1<8) or
+ (key1>10 and key1<12) or (key2>100 and key2<110);
+
+# 5. Check index_merge with conjuncts that are always true/false
+# verify fallback to "range" if there is only one non-confluent condition
+explain select * from t0 where key2 = 45 or key1 <=> null;
+
+explain select * from t0 where key2 = 45 or key1 is not null;
+explain select * from t0 where key2 = 45 or key1 is null;
+
+# the last conj. is always false and will be discarded
+explain select * from t0 where key2=10 or key3=3 or key4 <=> null;
+
+# the last conj. is always true and will cause 'all' scan
+explain select * from t0 where key2=10 or key3=3 or key4 is null;
+
+# some more complicated cases
+explain select key1 from t0 where (key1 <=> null) or (key2 < 5) or
+ (key3=10) or (key4 <=> null);
+explain select key1 from t0 where (key1 <=> null) or (key1 < 5) or
+ (key3=10) or (key4 <=> null);
+
+# 6.Several ways to do index_merge, (ignored) index_merge vs. range
+explain select * from t0 where
+ (key1 < 3 or key2 < 3) and (key3 < 4 or key4 < 4) and (key5 < 5 or key6 < 5);
+
+explain
+select * from t0 where (key1 < 3 or key2 < 6) and (key1 < 7 or key3 < 4);
+
+select * from t0 where (key1 < 3 or key2 < 6) and (key1 < 7 or key3 < 4);
+
+
+explain select * from t0 where
+ (key1 < 3 or key2 < 3) and (key3 < 4 or key4 < 4) and (key5 < 2 or key6 < 2);
+
+# now index_merge is not used at all when "range" is possible
+explain select * from t0 where
+ (key1 < 3 or key2 < 3) and (key3 < 100);
+
+# this even can cause "all" scan:
+explain select * from t0 where
+ (key1 < 3 or key2 < 3) and (key3 < 1000);
+
+
+# 7. Complex cases
+# tree_or(List<SEL_IMERGE>, range SEL_TREE).
+explain select * from t0 where
+ ((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
+ or
+ key2 > 5;
+
+explain select * from t0 where
+ ((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
+ or
+ key1 < 7;
+
+select * from t0 where
+ ((key1 < 4 or key2 < 4) and (key2 <5 or key3 < 4))
+ or
+ key1 < 7;
+
+# tree_or(List<SEL_IMERGE>, List<SEL_IMERGE>).
+explain select * from t0 where
+ ((key1 < 4 or key2 < 4) and (key3 <5 or key5 < 4))
+ or
+ ((key5 < 5 or key6 < 6) and (key7 <7 or key8 < 4));
+
+explain select * from t0 where
+ ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+ or
+ ((key7 <7 or key8 < 4) and (key5 < 5 or key6 < 6));
+
+explain select * from t0 where
+ ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+ or
+ ((key3 <7 or key5 < 2) and (key5 < 5 or key6 < 6));
+
+explain select * from t0 where
+ ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+ or
+ (((key3 <7 and key7 < 6) or key5 < 2) and (key5 < 5 or key6 < 6));
+
+explain select * from t0 where
+ ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+ or
+ ((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
+
+explain select * from t0 force index(i1, i2, i3, i4, i5, i6 ) where
+ ((key3 <5 or key5 < 4) and (key1 < 4 or key2 < 4))
+ or
+ ((key3 >=5 or key5 < 2) and (key5 < 5 or key6 < 6));
+
+# 8. Verify that "order by" after index merge uses filesort
+select * from t0 where key1 < 5 or key8 < 4 order by key1;
+
+explain
+select * from t0 where key1 < 5 or key8 < 4 order by key1;
+
+# 9. Check that index_merge cost is compared to 'index' where possible
+create table t2 like t0;
+insert into t2 select * from t0;
+
+alter table t2 add index i1_3(key1, key3);
+alter table t2 add index i2_3(key2, key3);
+alter table t2 drop index i1;
+alter table t2 drop index i2;
+alter table t2 add index i321(key3, key2, key1);
+
+# index_merge vs 'index', index_merge is better.
+explain select key3 from t2 where key1 = 100 or key2 = 100;
+
+# index_merge vs 'index', 'index' is better.
+explain select key3 from t2 where key1 <100 or key2 < 100;
+
+# index_merge vs 'all', index_merge is better.
+explain select key7 from t2 where key1 <100 or key2 < 100;
+
+# 10. Multipart keys.
+create table t4 (
+ key1a int not null,
+ key1b int not null,
+ key2 int not null,
+ key2_1 int not null,
+ key2_2 int not null,
+ key3 int not null,
+
+ index i1a (key1a, key1b),
+ index i1b (key1b, key1a),
+
+ index i2_1(key2, key2_1),
+ index i2_2(key2, key2_1)
+);
+
+insert into t4 select key1,key1,key1 div 10, key1 % 10, key1 % 10, key1 from t0;
+
+# the following will be handled by index_merge:
+select * from t4 where key1a = 3 or key1b = 4;
+explain select * from t4 where key1a = 3 or key1b = 4;
+
+# and the following will not
+explain select * from t4 where key2 = 1 and (key2_1 = 1 or key3 = 5);
+
+explain select * from t4 where key2 = 1 and (key2_1 = 1 or key2_2 = 5);
+
+explain select * from t4 where key2_1 = 1 or key2_2 = 5;
+
+
+# 11. Multitable selects
+create table t1 like t0;
+insert into t1 select * from t0;
+
+# index_merge on first table in join
+explain select * from t0 left join t1 on (t0.key1=t1.key1)
+ where t0.key1=3 or t0.key2=4;
+
+select * from t0 left join t1 on (t0.key1=t1.key1)
+ where t0.key1=3 or t0.key2=4;
+
+explain
+select * from t0,t1 where (t0.key1=t1.key1) and ( t0.key1=3 or t0.key2=4);
+
+# index_merge vs. ref
+explain
+select * from t0,t1 where (t0.key1=t1.key1) and
+ (t0.key1=3 or t0.key2=4) and t1.key1<200;
+
+# index_merge vs. ref
+explain
+select * from t0,t1 where (t0.key1=t1.key1) and
+ (t0.key1=3 or t0.key2<4) and t1.key1=2;
+
+# index_merge on second table in join
+explain select * from t0,t1 where t0.key1 = 5 and
+ (t1.key1 = t0.key1 or t1.key8 = t0.key1);
+
+# Fix for bug#1974
+explain select * from t0,t1 where t0.key1 < 3 and
+ (t1.key1 = t0.key1 or t1.key8 = t0.key1);
+
+# index_merge inside union
+explain select * from t1 where key1=3 or key2=4
+ union select * from t1 where key1<4 or key3=5;
+
+# index merge in subselect
+explain select * from (select * from t1 where key1 = 3 or key2 =3) as Z where key8 >5;
+
+# 12. check for long index_merges.
+create table t3 like t0;
+insert into t3 select * from t0;
+alter table t3 add key9 int not null, add index i9(key9);
+alter table t3 add keyA int not null, add index iA(keyA);
+alter table t3 add keyB int not null, add index iB(keyB);
+alter table t3 add keyC int not null, add index iC(keyC);
+update t3 set key9=key1,keyA=key1,keyB=key1,keyC=key1;
+
+explain select * from t3 where
+ key1=1 or key2=2 or key3=3 or key4=4 or
+ key5=5 or key6=6 or key7=7 or key8=8 or
+ key9=9 or keyA=10 or keyB=11 or keyC=12;
+
+select * from t3 where
+ key1=1 or key2=2 or key3=3 or key4=4 or
+ key5=5 or key6=6 or key7=7 or key8=8 or
+ key9=9 or keyA=10 or keyB=11 or keyC=12;
+
+# Test for Bug#3183
+explain select * from t0 where key1 < 3 or key2 < 4;
+select * from t0 where key1 < 3 or key2 < 4;
+
+update t0 set key8=123 where key1 < 3 or key2 < 4;
+select * from t0 where key1 < 3 or key2 < 4;
+
+delete from t0 where key1 < 3 or key2 < 4;
+select * from t0 where key1 < 3 or key2 < 4;
+select count(*) from t0;
+
+drop table t0, t1, t2, t3, t4;
diff --git a/mysql-test/t/index_merge_bdb.test b/mysql-test/t/index_merge_bdb.test
new file mode 100644
index 00000000000..c49e6ab3175
--- /dev/null
+++ b/mysql-test/t/index_merge_bdb.test
@@ -0,0 +1,52 @@
+#
+# 2-sweeps read Index_merge test
+#
+-- source include/have_bdb.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (
+ pk int primary key,
+ key1 int,
+ key2 int,
+ filler char(200),
+ filler2 char(200),
+ index(key1),
+ index(key2)
+) engine=bdb;
+
+
+--disable_query_log
+let $1=1000;
+while ($1)
+{
+ eval insert into t1 values($1, $1, $1, 'filler-data','filler-data-2');
+ dec $1;
+}
+--enable_query_log
+
+select * from t1 where (key1 >= 2 and key1 <= 10) or (pk >= 4 and pk <=8 );
+
+set @maxv=1000;
+
+select * from t1 where
+ (pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+ or key1=18 or key1=60;
+
+select * from t1 where
+ (pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+ or key1 < 3 or key1 > @maxv-11;
+
+select * from t1 where
+ (pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+ or
+ (key1 < 5) or (key1 > 10 and key1 < 15) or (key1 >= 50 and key1 < 55 ) or (key1 > @maxv-10);
+
+select * from t1 where
+ (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 )
+ or
+ (key1 < 5) or (key1 > @maxv-10);
+
+drop table t1;
diff --git a/mysql-test/t/index_merge_innodb.test b/mysql-test/t/index_merge_innodb.test
new file mode 100644
index 00000000000..2da40c5719a
--- /dev/null
+++ b/mysql-test/t/index_merge_innodb.test
@@ -0,0 +1,54 @@
+#
+# Index merge tests
+#
+-- source include/have_innodb.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1
+(
+ key1 int not null,
+ key2 int not null,
+
+ INDEX i1(key1),
+ INDEX i2(key2)
+) engine=innodb;
+
+--disable_query_log
+let $1=200;
+while ($1)
+{
+ eval insert into t1 values (200-$1, $1);
+ dec $1;
+}
+--enable_query_log
+
+# No primary key
+explain select * from t1 where key1 < 5 or key2 > 197;
+
+select * from t1 where key1 < 5 or key2 > 197;
+
+explain select * from t1 where key1 < 3 or key2 > 195;
+select * from t1 where key1 < 3 or key2 > 195;
+
+# Primary key as case-sensitive string with \0s.
+# also make primary key be longer then max. index length of MyISAM.
+alter table t1 add str1 char (255) not null,
+ add zeroval int not null default 0,
+ add str2 char (255) not null,
+ add str3 char (255) not null;
+
+update t1 set str1='aaa', str2='bbb', str3=concat(key2, '-', key1 div 2, '_' ,if(key1 mod 2 = 0, 'a', 'A'));
+
+alter table t1 add primary key (str1, zeroval, str2, str3);
+
+explain select * from t1 where key1 < 5 or key2 > 197;
+
+select * from t1 where key1 < 5 or key2 > 197;
+
+explain select * from t1 where key1 < 3 or key2 > 195;
+select * from t1 where key1 < 3 or key2 > 195;
+
+drop table t1;
diff --git a/mysql-test/t/index_merge_innodb2.test b/mysql-test/t/index_merge_innodb2.test
new file mode 100644
index 00000000000..ec4ea672bc1
--- /dev/null
+++ b/mysql-test/t/index_merge_innodb2.test
@@ -0,0 +1,52 @@
+#
+# 2-sweeps read Index_merge test
+#
+-- source include/have_innodb.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (
+ pk int primary key,
+ key1 int,
+ key2 int,
+ filler char(200),
+ filler2 char(200),
+ index(key1),
+ index(key2)
+) engine=innodb;
+
+
+--disable_query_log
+let $1=1000;
+while ($1)
+{
+ eval insert into t1 values($1, $1, $1, 'filler-data','filler-data-2');
+ dec $1;
+}
+--enable_query_log
+
+select * from t1 where (key1 >= 2 and key1 <= 10) or (pk >= 4 and pk <=8 );
+
+set @maxv=1000;
+
+select * from t1 where
+ (pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+ or key1=18 or key1=60;
+
+select * from t1 where
+ (pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+ or key1 < 3 or key1 > @maxv-11;
+
+select * from t1 where
+ (pk < 5) or (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 ) or (pk > @maxv-10)
+ or
+ (key1 < 5) or (key1 > 10 and key1 < 15) or (key1 >= 50 and key1 < 55 ) or (key1 > @maxv-10);
+
+select * from t1 where
+ (pk > 10 and pk < 15) or (pk >= 50 and pk < 55 )
+ or
+ (key1 < 5) or (key1 > @maxv-10);
+
+drop table t1;
diff --git a/mysql-test/t/index_merge_ror.test b/mysql-test/t/index_merge_ror.test
new file mode 100644
index 00000000000..223bd241d96
--- /dev/null
+++ b/mysql-test/t/index_merge_ror.test
@@ -0,0 +1,249 @@
+#
+# ROR-index_merge tests.
+#
+--disable_warnings
+drop table if exists t0,t1,t2;
+--enable_warnings
+--disable_query_log
+create table t1
+(
+ /* Field names reflect value(rowid) distribution, st=STairs, swt= SaWTooth */
+ st_a int not null,
+ swt1a int not null,
+ swt2a int not null,
+
+ st_b int not null,
+ swt1b int not null,
+ swt2b int not null,
+
+ /* fields/keys for row retrieval tests */
+ key1 int,
+ key2 int,
+ key3 int,
+ key4 int,
+
+ /* make rows much bigger then keys */
+ filler1 char (200),
+ filler2 char (200),
+ filler3 char (200),
+ filler4 char (200),
+ filler5 char (200),
+ filler6 char (200),
+
+ /* order of keys is important */
+ key sta_swt12a(st_a,swt1a,swt2a),
+ key sta_swt1a(st_a,swt1a),
+ key sta_swt2a(st_a,swt2a),
+ key sta_swt21a(st_a,swt2a,swt1a),
+
+ key st_a(st_a),
+ key stb_swt1a_2b(st_b,swt1b,swt2a),
+ key stb_swt1b(st_b,swt1b),
+ key st_b(st_b),
+
+ key(key1),
+ key(key2),
+ key(key3),
+ key(key4)
+) ;
+
+# Fill table
+create table t0 as select * from t1;
+let $cnt=1000;
+while ($cnt)
+{
+ eval insert into t0 values (1, 2, 3, 1, 2, 3, 0, 0, 0, 0, 'data1', 'data2', 'data3', 'data4', 'data5', 'data6');
+ dec $cnt;
+}
+
+alter table t1 disable keys;
+let $1=4;
+while ($1)
+{
+ let $2=4;
+ while ($2)
+ {
+ let $3=4;
+ while ($3)
+ {
+ eval insert into t1 select $1, $2, $3, $1 ,$2, $3, key1, key2, key3, key4, filler1, filler2, filler3, filler4, filler5, filler6 from t0;
+ dec $3;
+ }
+ dec $2;
+ }
+ dec $1;
+}
+
+# Row retrieval tests
+# -1 is used for values 'out of any range we are using'
+# insert enough rows for index intersection to be used for (key1,key2)
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 100, 100,'key1-key2-key3-key4');
+let $cnt=400;
+while ($cnt)
+{
+ eval insert into t1 (key1, key2, key3, key4, filler1) values (100, -1, 100, -1,'key1-key3');
+ dec $cnt;
+}
+let $cnt=400;
+while ($cnt)
+{
+ eval insert into t1 (key1, key2, key3, key4, filler1) values (-1, 100, -1, 100,'key2-key4');
+ dec $cnt;
+}
+alter table t1 enable keys;
+--enable_query_log
+select count(*) from t1;
+
+# One row results tests for cases where a single row matches all conditions
+explain select key1,key2 from t1 where key1=100 and key2=100;
+select key1,key2 from t1 where key1=100 and key2=100;
+
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+
+# Several-rows results
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, -1, -1, 'key1-key2');
+insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, 100, 100, 'key4-key3');
+
+# ROR-intersection, not covering
+explain select key1,key2,filler1 from t1 where key1=100 and key2=100;
+select key1,key2,filler1 from t1 where key1=100 and key2=100;
+
+# ROR-intersection, covering
+explain select key1,key2 from t1 where key1=100 and key2=100;
+select key1,key2 from t1 where key1=100 and key2=100;
+
+# ROR-union of ROR-intersections
+explain select key1,key2,key3,key4 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+select key1,key2,key3,key4 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+
+# 3-way ROR-intersection
+explain select key1,key2,key3 from t1 where key1=100 and key2=100 and key3=100;
+select key1,key2,key3 from t1 where key1=100 and key2=100 and key3=100;
+
+# ROR-union(ROR-intersection, ROR-range)
+insert into t1 (key1,key2,key3,key4,filler1) values (101,101,101,101, 'key1234-101');
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=101;
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=101;
+
+# Run some ROR updates/deletes
+select key1,key2, filler1 from t1 where key1=100 and key2=100;
+update t1 set filler1='to be deleted' where key1=100 and key2=100;
+update t1 set key1=200,key2=200 where key1=100 and key2=100;
+delete from t1 where key1=200 and key2=200;
+select key1,key2,filler1 from t1 where key2=100 and key2=200;
+
+# ROR-union(ROR-intersection) with one of ROR-intersection giving empty
+# results
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+
+delete from t1 where key3=100 and key4=100;
+
+# ROR-union with all ROR-intersections giving empty results
+explain select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+select key1,key2,key3,key4,filler1 from t1 where key1=100 and key2=100 or key3=100 and key4=100;
+
+# ROR-intersection with empty result
+explain select key1,key2 from t1 where key1=100 and key2=100;
+select key1,key2 from t1 where key1=100 and key2=100;
+
+# ROR-union tests with various cases.
+# All scans returning duplicate rows:
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key1-key2-key3-key4-1');
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key1-key2-key3-key4-2');
+insert into t1 (key1, key2, key3, key4, filler1) values (100, 100, 200, 200,'key1-key2-key3-key4-3');
+
+explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+
+insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, -1, 200,'key4');
+
+explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+
+insert into t1 (key1, key2, key3, key4, filler1) values (-1, -1, 200, -1,'key3');
+
+explain select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+select key1,key2,key3,key4,filler1 from t1 where key3=200 or (key1=100 and key2=100) or key4=200;
+
+##
+## Optimizer tests
+##
+
+# Check that the shortest key is used for ROR-intersection, covering and non-covering.
+explain select * from t1 where st_a=1 and st_b=1;
+explain select st_a,st_b from t1 where st_a=1 and st_b=1;
+
+# Check if "ingore index" syntax works
+explain select st_a from t1 ignore index (st_a) where st_a=1 and st_b=1;
+
+# Do many tests
+# Check that keys that don't improve selectivity are skipped.
+#
+
+explain select * from t1 where st_a=1 and swt1a=1 and swt2a=1;
+
+explain select * from t1 where st_b=1 and swt1b=1 and swt2b=1;
+
+explain select * from t1 where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
+
+explain select * from t1 ignore index (sta_swt21a, stb_swt1a_2b)
+ where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
+
+explain select * from t1 ignore index (sta_swt21a, sta_swt12a, stb_swt1a_2b)
+ where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
+
+explain select * from t1 ignore index (sta_swt21a, sta_swt12a, stb_swt1a_2b, stb_swt1b)
+ where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1 and swt2b=1;
+
+explain select * from t1
+ where st_a=1 and swt1a=1 and swt2a=1 and st_b=1 and swt1b=1;
+
+explain select * from t1
+ where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
+
+explain select st_a from t1
+ where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
+
+explain select st_a from t1
+ where st_a=1 and swt1a=1 and st_b=1 and swt1b=1 and swt1b=1;
+
+drop table t0,t1;
+
+# 'Partially' covered fields test
+
+create table t2 (
+ a char(10),
+ b char(10),
+ filler1 char(255),
+ filler2 char(255),
+ key(a(5)),
+ key(b(5))
+);
+
+--disable_query_log
+let $1=8;
+while ($1)
+{
+ eval insert into t2 values (repeat(char($1+64), 8),repeat(char($1+64), 8),'filler1', 'filler2');
+ dec $1;
+}
+insert into t2 select * from t2;
+insert into t2 select * from t2;
+--enable_query_log
+
+# The table row buffer is reused. Fill it with rows that don't match.
+select count(a) from t2 where a='BBBBBBBB';
+select count(a) from t2 where b='BBBBBBBB';
+
+# BUG#1:
+explain select count(a) from t2 where a='AAAAAAAA' and b='AAAAAAAA';
+select count(a) from t2 where a='AAAAAAAA' and b='AAAAAAAA';
+select count(a) from t2 ignore index(a,b) where a='AAAAAAAA' and b='AAAAAAAA';
+
+insert into t2 values ('ab', 'ab', 'uh', 'oh');
+explain select a from t2 where a='ab';
+drop table t2;
diff --git a/mysql-test/t/index_merge_ror_cpk.test b/mysql-test/t/index_merge_ror_cpk.test
new file mode 100644
index 00000000000..bf8eb5b77c7
--- /dev/null
+++ b/mysql-test/t/index_merge_ror_cpk.test
@@ -0,0 +1,108 @@
+#
+# Clustered PK ROR-index_merge tests
+#
+-- source include/have_innodb.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1
+(
+ pk1 int not null,
+ pk2 int not null,
+
+ key1 int not null,
+ key2 int not null,
+
+ pktail1ok int not null,
+ pktail2ok int not null,
+ pktail3bad int not null,
+ pktail4bad int not null,
+ pktail5bad int not null,
+
+ pk2copy int not null,
+ badkey int not null,
+
+ filler1 char (200),
+ filler2 char (200),
+ key (key1),
+ key (key2),
+
+ /* keys with tails from CPK members */
+ key (pktail1ok, pk1),
+ key (pktail2ok, pk1, pk2),
+ key (pktail3bad, pk2, pk1),
+ key (pktail4bad, pk1, pk2copy),
+ key (pktail5bad, pk1, pk2, pk2copy),
+
+ primary key (pk1, pk2)
+) engine=innodb;
+
+--disable_query_log
+set autocommit=0;
+let $1=10000;
+while ($1)
+{
+ eval insert into t1 values ($1 div 10,$1 mod 100, $1/100,$1/100, $1/100,$1/100,$1/100,$1/100,$1/100, $1 mod 100, $1/1000,'filler-data-$1','filler2');
+ dec $1;
+}
+set autocommit=1;
+--enable_query_log
+
+# Verify that range scan on CPK is ROR
+# (use index_intersection because it is impossible to check that for index union)
+explain select * from t1 where pk1 = 1 and pk2 < 80 and key1=0;
+# CPK scan + 1 ROR range scan is a special case
+select * from t1 where pk1 = 1 and pk2 < 80 and key1=0;
+
+# Verify that CPK fields are considered to be covered by index scans
+explain select pk1,pk2 from t1 where key1 = 10 and key2=10 and 2*pk1+1 < 2*96+1;
+select pk1,pk2 from t1 where key1 = 10 and key2=10 and 2*pk1+1 < 2*96+1;
+
+# Verify that CPK is always used for index intersection scans
+# (this is because it is used as a filter, not for retrieval)
+explain select * from t1 where badkey=1 and key1=10;
+explain select * from t1 where pk1 < 7500 and key1 = 10;
+
+# Verify that keys with 'tails' of PK members are ok.
+explain select * from t1 where pktail1ok=1 and key1=10;
+explain select * from t1 where pktail2ok=1 and key1=10;
+
+select ' The following is actually a deficiency, it uses sort_union currently:' as 'note:';
+explain select * from t1 where (pktail2ok=1 and pk1< 50000) or key1=10;
+
+explain select * from t1 where pktail3bad=1 and key1=10;
+explain select * from t1 where pktail4bad=1 and key1=10;
+explain select * from t1 where pktail5bad=1 and key1=10;
+
+# Test for problem with innodb key values prefetch buffer:
+explain select pk1,pk2,key1,key2 from t1 where key1 = 10 and key2=10 limit 10;
+select pk1,pk2,key1,key2 from t1 where key1 = 10 and key2=10 limit 10;
+
+drop table t1;
+# Testcase for BUG#4984
+create table t1
+(
+ RUNID varchar(22),
+ SUBMITNR varchar(5),
+ ORDERNR char(1) ,
+ PROGRAMM varchar(8),
+ TESTID varchar(4),
+ UCCHECK char(1),
+ ETEXT varchar(80),
+ ETEXT_TYPE char(1),
+ INFO char(1),
+ SEVERITY tinyint(3),
+ TADIRFLAG char(1),
+ PRIMARY KEY (RUNID,SUBMITNR,ORDERNR,PROGRAMM,TESTID,UCCHECK),
+ KEY `TVERM~KEY` (PROGRAMM,TESTID,UCCHECK)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+update t1 set `ETEXT` = '', `ETEXT_TYPE`='', `INFO`='', `SEVERITY`='', `TADIRFLAG`=''
+WHERE
+ `RUNID`= '' AND `SUBMITNR`= '' AND `ORDERNR`='' AND `PROGRAMM`='' AND
+ `TESTID`='' AND `UCCHECK`='';
+
+drop table t1;
+
diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test
index 50ab3bdb8bd..e2e0afce6e6 100644
--- a/mysql-test/t/innodb.test
+++ b/mysql-test/t/innodb.test
@@ -998,7 +998,7 @@ CREATE TABLE t1 (
`id` int(10) unsigned NOT NULL auto_increment,
`id_object` int(10) unsigned default '0',
`id_version` int(10) unsigned NOT NULL default '1',
- label varchar(100) NOT NULL default '',
+ `label` varchar(100) NOT NULL default '',
`description` text,
PRIMARY KEY (`id`),
KEY `id_object` (`id_object`),
@@ -1016,8 +1016,8 @@ CREATE TABLE t2 (
INSERT INTO t2 VALUES("3524", "1"),("3525", "1"),("1794", "4"),("102", "5"),("1822", "6"),("3382", "9");
-SELECT t2.id, t1.label FROM t2 INNER JOIN
-(SELECT t1.id_object as id_object FROM t1 WHERE t1.label LIKE '%test%') AS lbl
+SELECT t2.id, t1.`label` FROM t2 INNER JOIN
+(SELECT t1.id_object as id_object FROM t1 WHERE t1.`label` LIKE '%test%') AS lbl
ON (t2.id = lbl.id_object) INNER JOIN t1 ON (t2.id = t1.id_object);
drop table t1,t2;
diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test
index fd728c453aa..dc47b3ba990 100644
--- a/mysql-test/t/insert.test
+++ b/mysql-test/t/insert.test
@@ -139,4 +139,34 @@ set @value= -1;
insert into t1 values(null,@value,@value,@value,@value,@value,@value,@value,@value,@value);
--query_vertical select * from t1 where number =last_insert_id()
-drop table t1;
+use test;
+--disable_warnings
+drop table if exists t1,t2,t3;
+--enable_warnings
+create table t1(id1 int not null auto_increment primary key, t char(12));
+create table t2(id2 int not null, t char(12));
+create table t3(id3 int not null, t char(12), index(id3));
+disable_query_log;
+let $1 = 100;
+while ($1)
+ {
+ let $2 = 5;
+ eval insert into t1(t) values ('$1');
+ while ($2)
+ {
+ eval insert into t2(id2,t) values ($1,'$2');
+ let $3 = 10;
+ while ($3)
+ {
+ eval insert into t3(id3,t) values ($1,'$2');
+ dec $3;
+ }
+ dec $2;
+ }
+ dec $1;
+ }
+enable_query_log;
+select count(*) from t2;
+insert into t2 select t1.* from t1, t2 t, t3 where t1.id1 = t.id2 and t.id2 = t3.id3;
+select count(*) from t2;
+drop table if exists t1,t2,t3;
diff --git a/mysql-test/t/join_nested.test b/mysql-test/t/join_nested.test
new file mode 100644
index 00000000000..9591f1fa7ed
--- /dev/null
+++ b/mysql-test/t/join_nested.test
@@ -0,0 +1,754 @@
+
+--disable_warnings
+DROP TABLE IF EXISTS t0,t1,t2,t3,t4,t5,t6,t7,t8,t9;
+--enable_warnings
+
+CREATE TABLE t0 (a int, b int, c int);
+CREATE TABLE t1 (a int, b int, c int);
+CREATE TABLE t2 (a int, b int, c int);
+CREATE TABLE t3 (a int, b int, c int);
+CREATE TABLE t4 (a int, b int, c int);
+CREATE TABLE t5 (a int, b int, c int);
+CREATE TABLE t6 (a int, b int, c int);
+CREATE TABLE t7 (a int, b int, c int);
+CREATE TABLE t8 (a int, b int, c int);
+CREATE TABLE t9 (a int, b int, c int);
+
+INSERT INTO t0 VALUES (1,1,0), (1,2,0), (2,2,0);
+INSERT INTO t1 VALUES (1,3,0), (2,2,0), (3,2,0);
+INSERT INTO t2 VALUES (3,3,0), (4,2,0), (5,3,0);
+INSERT INTO t3 VALUES (1,2,0), (2,2,0);
+INSERT INTO t4 VALUES (3,2,0), (4,2,0);
+INSERT INTO t5 VALUES (3,1,0), (2,2,0), (3,3,0);
+INSERT INTO t6 VALUES (3,2,0), (6,2,0), (6,1,0);
+INSERT INTO t7 VALUES (1,1,0), (2,2,0);
+INSERT INTO t8 VALUES (0,2,0), (1,2,0);
+INSERT INTO t9 VALUES (1,1,0), (1,2,0), (3,3,0);
+
+
+SELECT t2.a,t2.b
+ FROM t2;
+
+SELECT t3.a,t3.b
+ FROM t3;
+
+SELECT t4.a,t4.b
+ FROM t4;
+
+SELECT t3.a,t3.b,t4.a,t4.b
+ FROM t3,t4;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t2.b=t4.b;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b;
+
+EXPLAIN EXTENDED
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t2.b=t4.b
+ WHERE t3.a=1 OR t3.c IS NULL;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t2.b=t4.b
+ WHERE t3.a=1 OR t3.c IS NULL;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t2.b=t4.b
+ WHERE t3.a>1 OR t3.c IS NULL;
+
+SELECT t5.a,t5.b
+ FROM t5;
+
+SELECT t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+ FROM t3,t4,t5;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4, t5)
+ ON t2.b=t4.b;
+
+EXPLAIN EXTENDED
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4, t5)
+ ON t2.b=t4.b
+ WHERE t3.a>1 OR t3.c IS NULL;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4, t5)
+ ON t2.b=t4.b
+ WHERE t3.a>1 OR t3.c IS NULL;
+
+EXPLAIN EXTENDED
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4, t5)
+ ON t2.b=t4.b
+ WHERE (t3.a>1 OR t3.c IS NULL) AND
+ (t5.a<3 OR t5.c IS NULL);
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,t5.a,t5.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4, t5)
+ ON t2.b=t4.b
+ WHERE (t3.a>1 OR t3.c IS NULL) AND
+ (t5.a<3 OR t5.c IS NULL);
+
+SELECT t6.a,t6.b
+ FROM t6;
+
+SELECT t7.a,t7.b
+ FROM t7;
+
+SELECT t6.a,t6.b,t7.a,t7.b
+ FROM t6,t7;
+
+SELECT t8.a,t8.b
+ FROM t8;
+
+EXPLAIN EXTENDED
+SELECT t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10;
+
+SELECT t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10;
+
+SELECT t5.a,t5.b
+ FROM t5;
+
+SELECT t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b;
+
+SELECT t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b AND
+ (t8.a < 1 OR t8.c IS NULL);
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ WHERE t2.a > 3 AND
+ (t6.a < 6 OR t6.c IS NULL);
+
+SELECT t1.a,t1.b
+ FROM t1;
+
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2);
+
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2)
+ WHERE (t2.a >= 4 OR t2.c IS NULL);
+
+SELECT t0.a,t0.b
+ FROM t0;
+
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2)
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL);
+
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2)
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL);
+
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2),
+ t9
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL) AND
+ (t3.a < 5 OR t3.c IS NULL) AND
+ (t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+ (t5.a >=2 OR t5.c IS NULL) AND
+ (t6.a >=4 OR t6.c IS NULL) AND
+ (t7.a <= 2 OR t7.c IS NULL) AND
+ (t8.a < 1 OR t8.c IS NULL) AND
+ (t8.b=t9.b OR t8.c IS NULL) AND
+ (t9.a=1);
+
+SELECT t9.a,t9.b
+ FROM t9;
+
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2),
+ t9
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL) AND
+ (t3.a < 5 OR t3.c IS NULL) AND
+ (t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+ (t5.a >=2 OR t5.c IS NULL) AND
+ (t6.a >=4 OR t6.c IS NULL) AND
+ (t7.a <= 2 OR t7.c IS NULL) AND
+ (t8.a < 1 OR t8.c IS NULL) AND
+ (t8.b=t9.b OR t8.c IS NULL) AND
+ (t9.a=1);
+
+SELECT t1.a,t1.b
+ FROM t1;
+
+SELECT t2.a,t2.b
+ FROM t2;
+
+SELECT t3.a,t3.b
+ FROM t3;
+
+SELECT t2.a,t2.b,t3.a,t3.b
+ FROM t2
+ LEFT JOIN
+ t3
+ ON t2.b=t3.b;
+
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b
+ FROM t1, t2
+ LEFT JOIN
+ t3
+ ON t2.b=t3.b
+ WHERE t1.a <= 2;
+
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b
+ FROM t1, t3
+ RIGHT JOIN
+ t2
+ ON t2.b=t3.b
+ WHERE t1.a <= 2;
+
+SELECT t3.a,t3.b,t4.a,t4.b
+ FROM t3,t4;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b;
+
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t1, t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b
+ WHERE t1.a <= 2;
+
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t1, (t3, t4)
+ RIGHT JOIN
+ t2
+ ON t3.a=1 AND t2.b=t4.b
+ WHERE t1.a <= 2;
+
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t1, t3, t4
+ RIGHT JOIN
+ t2
+ ON t3.a=1 AND t2.b=t4.b
+ WHERE t1.a <= 2;
+
+EXPLAIN EXTENDED
+SELECT t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t1, t3, t4
+ RIGHT JOIN
+ t2
+ ON t3.a=1 AND t2.b=t4.b
+ WHERE t1.a <= 2;
+
+CREATE INDEX idx_b ON t2(b);
+
+EXPLAIN EXTENDED
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t3,t4
+ LEFT JOIN
+ (t1,t2)
+ ON t3.a=1 AND t3.b=t2.b AND t2.b=t4.b;
+
+SELECT t2.a,t2.b,t3.a,t3.b,t4.a,t4.b
+ FROM t3,t4
+ LEFT JOIN
+ (t1,t2)
+ ON t3.a=1 AND t3.b=t2.b AND t2.b=t4.b;
+
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2),
+ t9
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL) AND
+ (t3.a < 5 OR t3.c IS NULL) AND
+ (t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+ (t5.a >=2 OR t5.c IS NULL) AND
+ (t6.a >=4 OR t6.c IS NULL) AND
+ (t7.a <= 2 OR t7.c IS NULL) AND
+ (t8.a < 1 OR t8.c IS NULL) AND
+ (t8.b=t9.b OR t8.c IS NULL) AND
+ (t9.a=1);
+
+CREATE INDEX idx_b ON t4(b);
+CREATE INDEX idx_b ON t5(b);
+
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2),
+ t9
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL) AND
+ (t3.a < 5 OR t3.c IS NULL) AND
+ (t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+ (t5.a >=2 OR t5.c IS NULL) AND
+ (t6.a >=4 OR t6.c IS NULL) AND
+ (t7.a <= 2 OR t7.c IS NULL) AND
+ (t8.a < 1 OR t8.c IS NULL) AND
+ (t8.b=t9.b OR t8.c IS NULL) AND
+ (t9.a=1);
+
+CREATE INDEX idx_b ON t8(b);
+
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2),
+ t9
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL) AND
+ (t3.a < 5 OR t3.c IS NULL) AND
+ (t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+ (t5.a >=2 OR t5.c IS NULL) AND
+ (t6.a >=4 OR t6.c IS NULL) AND
+ (t7.a <= 2 OR t7.c IS NULL) AND
+ (t8.a < 1 OR t8.c IS NULL) AND
+ (t8.b=t9.b OR t8.c IS NULL) AND
+ (t9.a=1);
+
+CREATE INDEX idx_b ON t1(b);
+CREATE INDEX idx_a ON t0(a);
+
+EXPLAIN EXTENDED
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2),
+ t9
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL) AND
+ (t3.a < 5 OR t3.c IS NULL) AND
+ (t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+ (t5.a >=2 OR t5.c IS NULL) AND
+ (t6.a >=4 OR t6.c IS NULL) AND
+ (t7.a <= 2 OR t7.c IS NULL) AND
+ (t8.a < 1 OR t8.c IS NULL) AND
+ (t8.b=t9.b OR t8.c IS NULL) AND
+ (t9.a=1);
+
+SELECT t0.a,t0.b,t1.a,t1.b,t2.a,t2.b,t3.a,t3.b,t4.a,t4.b,
+ t5.a,t5.b,t6.a,t6.b,t7.a,t7.b,t8.a,t8.b,t9.a,t9.b
+ FROM t0,t1
+ LEFT JOIN
+ (
+ t2
+ LEFT JOIN
+ (t3, t4)
+ ON t3.a=1 AND t2.b=t4.b,
+ t5
+ LEFT JOIN
+ (
+ t6,
+ t7
+ LEFT JOIN
+ t8
+ ON t7.b=t8.b AND t6.b < 10
+ )
+ ON t6.b >= 2 AND t5.b=t7.b
+ )
+ ON (t3.b=2 OR t3.c IS NULL) AND (t6.b=2 OR t6.c IS NULL) AND
+ (t1.b=t5.b OR t3.c IS NULL OR t6.c IS NULL or t8.c IS NULL) AND
+ (t1.a != 2),
+ t9
+ WHERE t0.a=1 AND
+ t0.b=t1.b AND
+ (t2.a >= 4 OR t2.c IS NULL) AND
+ (t3.a < 5 OR t3.c IS NULL) AND
+ (t3.b=t4.b OR t3.c IS NULL OR t4.c IS NULL) AND
+ (t5.a >=2 OR t5.c IS NULL) AND
+ (t6.a >=4 OR t6.c IS NULL) AND
+ (t7.a <= 2 OR t7.c IS NULL) AND
+ (t8.a < 1 OR t8.c IS NULL) AND
+ (t8.b=t9.b OR t8.c IS NULL) AND
+ (t9.a=1);
+
+SELECT t2.a,t2.b
+ FROM t2;
+
+SELECT t3.a,t3.b
+ FROM t3;
+
+SELECT t2.a,t2.b,t3.a,t3.b
+ FROM t2 LEFT JOIN t3 ON t2.b=t3.b
+ WHERE t2.a = 4 OR (t2.a > 4 AND t3.a IS NULL);
+
+SELECT t2.a,t2.b,t3.a,t3.b
+ FROM t2 LEFT JOIN (t3) ON t2.b=t3.b
+ WHERE t2.a = 4 OR (t2.a > 4 AND t3.a IS NULL);
+
+ALTER TABLE t3
+ CHANGE COLUMN a a1 int,
+ CHANGE COLUMN c c1 int;
+
+SELECT t2.a,t2.b,t3.a1,t3.b
+ FROM t2 LEFT JOIN t3 ON t2.b=t3.b
+ WHERE t2.a = 4 OR (t2.a > 4 AND t3.a1 IS NULL);
+
+SELECT t2.a,t2.b,t3.a1,t3.b
+ FROM t2 NATURAL LEFT JOIN t3
+ WHERE t2.a = 4 OR (t2.a > 4 AND t3.a1 IS NULL);
+
+DROP TABLE t0,t1,t2,t3,t4,t5,t6,t7,t8,t9;
+
+CREATE TABLE t1 (a int);
+CREATE TABLE t2 (a int);
+CREATE TABLE t3 (a int);
+
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (2);
+INSERT INTO t3 VALUES (2);
+INSERT INTO t1 VALUES (2);
+
+#check proper syntax for nested outer joins
+
+SELECT * FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.a=t3.a) ON t1.a=t3.a;
+
+#must be equivalent to:
+
+SELECT * FROM t1 LEFT JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.a=t3.a;
+
+#check that everything is al right when all tables contain not more than 1 row
+#(bug #4922)
+
+DELETE FROM t1 WHERE a=2;
+SELECT * FROM t1 LEFT JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.a=t3.a;
+DELETE FROM t2;
+SELECT * FROM t1 LEFT JOIN t2 LEFT JOIN t3 ON t2.a=t3.a ON t1.a=t3.a;
+
+DROP TABLE t1,t2,t3;
+
+#on expression for a nested outer join does not depend on the outer table
+#bug #4976
+
+CREATE TABLE t1(a int, key (a));
+CREATE TABLE t2(b int, key (b));
+CREATE TABLE t3(c int, key (c));
+
+INSERT INTO t1 VALUES (NULL), (0), (1), (2), (3), (4), (5), (6), (7), (8), (9),
+(10), (11), (12), (13), (14), (15), (16), (17), (18), (19);
+
+INSERT INTO t2 VALUES (NULL), (0), (1), (2), (3), (4), (5), (6), (7), (8), (9),
+(10), (11), (12), (13), (14), (15), (16), (17), (18), (19);
+
+INSERT INTO t3 VALUES (0), (1), (2), (3), (4), (5);
+
+EXPLAIN SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON c < 3 and b = c;
+EXPLAIN SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON b < 3 and b = c;
+SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON b < 3 and b = c;
+
+DELETE FROM t3;
+EXPLAIN SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON b < 3 and b = c;
+SELECT a, b, c FROM t1 LEFT JOIN (t2, t3) ON b < 3 and b = c;
+
+DROP TABLE t1,t2,t3;
diff --git a/mysql-test/t/keywords.test b/mysql-test/t/keywords.test
index e7ec63afe54..3392bfa1b3b 100644
--- a/mysql-test/t/keywords.test
+++ b/mysql-test/t/keywords.test
@@ -6,10 +6,12 @@
drop table if exists t1;
--enable_warnings
-create table t1 (time time, date date, timestamp timestamp);
-insert into t1 values ("12:22:22","97:02:03","1997-01-02");
+create table t1 (time time, date date, timestamp timestamp,
+quarter int, week int, year int, timestampadd int, timestampdiff int);
+insert into t1 values ("12:22:22","97:02:03","1997-01-02",1,2,3,4,5);
select * from t1;
-select t1.time+0,t1.date+0,t1.timestamp+0,concat(date," ",time) from t1;
+select t1.time+0,t1.date+0,t1.timestamp+0,concat(date," ",time),
+ t1.quarter+t1.week, t1.year+timestampadd, timestampdiff from t1;
drop table t1;
create table events(binlog int);
insert into events values(1);
diff --git a/mysql-test/t/mix_innodb_myisam_binlog.test b/mysql-test/t/mix_innodb_myisam_binlog.test
index 9c576b080ca..ba519acd8c2 100644
--- a/mysql-test/t/mix_innodb_myisam_binlog.test
+++ b/mysql-test/t/mix_innodb_myisam_binlog.test
@@ -25,7 +25,7 @@ insert into t1 values(1);
insert into t2 select * from t1;
commit;
-show binlog events from 79;
+show binlog events from 95;
delete from t1;
delete from t2;
@@ -37,7 +37,7 @@ insert into t2 select * from t1;
# should say some changes to non-transact1onal tables couldn't be rolled back
rollback;
-show binlog events from 79;
+show binlog events from 95;
delete from t1;
delete from t2;
@@ -51,7 +51,7 @@ insert into t2 select * from t1;
rollback to savepoint my_savepoint;
commit;
-show binlog events from 79;
+show binlog events from 95;
delete from t1;
delete from t2;
@@ -67,7 +67,7 @@ insert into t1 values(7);
commit;
select a from t1 order by a; # check that savepoints work :)
-show binlog events from 79;
+show binlog events from 95;
# and when ROLLBACK is not explicit?
delete from t1;
@@ -87,7 +87,7 @@ connection con2;
# so SHOW BINLOG EVENTS may come before con1 does the loggin. To be sure that
# logging has been done, we use a user lock.
select get_lock("a",10);
-show binlog events from 79;
+show binlog events from 95;
# and when not in a transact1on?
delete from t1;
@@ -97,10 +97,10 @@ reset master;
insert into t1 values(9);
insert into t2 select * from t1;
-show binlog events from 79;
+show binlog events from 95;
# Check that when the query updat1ng the MyISAM table is the first in the
-# transact1on, we log it immediately.
+# transaction, we log it immediately.
delete from t1;
delete from t2;
reset master;
@@ -108,11 +108,11 @@ reset master;
insert into t1 values(10); # first make t1 non-empty
begin;
insert into t2 select * from t1;
-show binlog events from 79;
+show binlog events from 95;
insert into t1 values(11);
commit;
-show binlog events from 79;
+show binlog events from 95;
# Check that things work like before this BEGIN/ROLLBACK code was added,
@@ -129,7 +129,7 @@ insert into t1 values(12);
insert into t2 select * from t1;
commit;
-show binlog events from 79;
+show binlog events from 95;
delete from t1;
delete from t2;
@@ -140,7 +140,7 @@ insert into t1 values(13);
insert into t2 select * from t1;
rollback;
-show binlog events from 79;
+show binlog events from 95;
delete from t1;
delete from t2;
@@ -154,7 +154,7 @@ insert into t2 select * from t1;
rollback to savepoint my_savepoint;
commit;
-show binlog events from 79;
+show binlog events from 95;
delete from t1;
delete from t2;
@@ -170,6 +170,6 @@ insert into t1 values(18);
commit;
select a from t1 order by a; # check that savepoints work :)
-show binlog events from 79;
+show binlog events from 95;
drop table t1,t2;
diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test
index c2814606aa2..d28619f0313 100644
--- a/mysql-test/t/multi_update.test
+++ b/mysql-test/t/multi_update.test
@@ -388,7 +388,7 @@ drop table t1, t2;
# prevelege chexk for multiupdate with other tables
#
-connect (root,localhost,root,,test,$MASTER_MYPORT,master.sock);
+connect (root,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connection root;
--disable_warnings
create database mysqltest;
@@ -398,7 +398,7 @@ create table mysqltest.t2 (a int, b int, primary key (a));
create table mysqltest.t3 (a int, b int, primary key (a));
grant select on mysqltest.* to mysqltest_1@localhost;
grant update on mysqltest.t1 to mysqltest_1@localhost;
-connect (user1,localhost,mysqltest_1,,mysqltest,$MASTER_MYPORT,master.sock);
+connect (user1,localhost,mysqltest_1,,mysqltest,$MASTER_MYPORT,$MASTER_MYSOCK);
connection user1;
update t1, t2 set t1.b=1 where t1.a=t2.a;
update t1, t2 set t1.b=(select t3.b from t3 where t1.a=t3.a) where t1.a=t2.a;
@@ -417,3 +417,49 @@ create table t3 (a int, primary key (a));
-- error 1109
delete t1,t3 from t1,t2 where t1.a=t2.a and t2.a=(select t3.a from t3 where t1.a=t3.a);
drop table t1, t2, t3;
+
+#
+# Test for bug #1980.
+#
+set @ttype_save=@@storage_engine;
+
+--disable_warnings
+set @@storage_engine=innodb;
+create table t1 ( c char(8) not null );
+--enable_warnings
+
+insert into t1 values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9');
+insert into t1 values ('A'),('B'),('C'),('D'),('E'),('F');
+
+alter table t1 add b char(8) not null;
+alter table t1 add a char(8) not null;
+alter table t1 add primary key (a,b,c);
+update t1 set a=c, b=c;
+
+create table t2 like t1;
+insert into t2 select * from t1;
+
+delete t1,t2 from t2,t1 where t1.a<'B' and t2.b=t1.b;
+
+drop table t1,t2;
+
+--disable_warnings
+set @@storage_engine=bdb;
+create table t1 ( c char(8) not null );
+--enable_warnings
+
+insert into t1 values ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9');
+insert into t1 values ('A'),('B'),('C'),('D'),('E'),('F');
+
+alter table t1 add b char(8) not null;
+alter table t1 add a char(8) not null;
+alter table t1 add primary key (a,b,c);
+update t1 set a=c, b=c;
+
+create table t2 like t1;
+insert into t2 select * from t1;
+
+delete t1,t2 from t2,t1 where t1.a<'B' and t2.b=t1.b;
+
+set @@storage_engine=@ttype_save;
+drop table t1,t2;
diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test
index ea66a44d44e..a8bbe60d58e 100644
--- a/mysql-test/t/mysqlbinlog.test
+++ b/mysql-test/t/mysqlbinlog.test
@@ -61,7 +61,7 @@ select "--- --database --" as "";
select "--- --position --" as "";
--enable_query_log
--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
---exec $MYSQL_BINLOG --short-form --local-load=$MYSQL_TEST_DIR/var/tmp/ --position=27 $MYSQL_TEST_DIR/var/log/master-bin.000002
+--exec $MYSQL_BINLOG --short-form --local-load=$MYSQL_TEST_DIR/var/tmp/ --position=118 $MYSQL_TEST_DIR/var/log/master-bin.000002
# These are tests for remote binlog.
# They should return the same as previous test.
@@ -93,7 +93,7 @@ select "--- --database --" as "";
select "--- --position --" as "";
--enable_query_log
--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
---exec $MYSQL_BINLOG --short-form --local-load=$MYSQL_TEST_DIR/var/tmp/ --read-from-remote-server --position=27 --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000002
+--exec $MYSQL_BINLOG --short-form --local-load=$MYSQL_TEST_DIR/var/tmp/ --read-from-remote-server --position=118 --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000002
# clean up
drop table t1, t2;
diff --git a/mysql-test/t/ps_1general.test b/mysql-test/t/ps_1general.test
index 084253a106a..cc8cb4c4ba0 100644
--- a/mysql-test/t/ps_1general.test
+++ b/mysql-test/t/ps_1general.test
@@ -405,9 +405,7 @@ prepare stmt1 from ' handler t1 open ';
## commit/rollback
---error 1295
prepare stmt3 from ' commit ' ;
---error 1295
prepare stmt3 from ' rollback ' ;
@@ -474,11 +472,8 @@ execute stmt3 using @arg00;
select m from t3;
drop table t3;
---error 1295
prepare stmt3 from ' create index t2_idx on t2(b) ';
---error 1295
prepare stmt3 from ' drop index t2_idx on t2 ' ;
---error 1295
prepare stmt3 from ' alter table t2 drop primary key ';
--disable_warnings
drop table if exists new_t2;
diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test
index b3111b614c6..e5dc69d21aa 100644
--- a/mysql-test/t/query_cache.test
+++ b/mysql-test/t/query_cache.test
@@ -604,9 +604,7 @@ set character_set_results=cp1251;
SELECT a,'Â','â'='Â' FROM t1;
show status like "Qcache_hits";
show status like "Qcache_queries_in_cache";
-#
-# Keep things tidy
-#
+
DROP TABLE t1;
#
@@ -626,5 +624,20 @@ DROP TABLE t1;
set character_set_results=null;
select @@character_set_results;
set character_set_results=default;
+# comments before command
+#
+create table t1 (a int);
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+/**/ select * from t1;
+/**/ select * from t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+#
+# Keep things tidy
+#
+DROP TABLE t1;
SET GLOBAL query_cache_size=0;
diff --git a/mysql-test/t/rowid_order_bdb.test b/mysql-test/t/rowid_order_bdb.test
new file mode 100644
index 00000000000..ef133054c35
--- /dev/null
+++ b/mysql-test/t/rowid_order_bdb.test
@@ -0,0 +1,108 @@
+#
+# Test for rowid ordering (and comparison) functions.
+# do index_merge select for tables with PK of various types.
+#
+--disable_warnings
+drop table if exists t1, t2, t3,t4;
+--enable_warnings
+
+-- source include/have_bdb.inc
+
+# Signed number as rowid
+create table t1 (
+ pk1 int not NULL,
+ key1 int(11),
+ key2 int(11),
+ PRIMARY KEY (pk1),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=bdb;
+insert into t1 values (-5, 1, 1),
+ (-100, 1, 1),
+ (3, 1, 1),
+ (0, 1, 1),
+ (10, 1, 1);
+explain select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+drop table t1;
+
+# Unsigned numbers as rowids
+create table t1 (
+ pk1 int unsigned not NULL,
+ key1 int(11),
+ key2 int(11),
+ PRIMARY KEY (pk1),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=bdb;
+insert into t1 values (0, 1, 1),
+ (0xFFFFFFFF, 1, 1),
+ (0xFFFFFFFE, 1, 1),
+ (1, 1, 1),
+ (2, 1, 1);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+drop table t1;
+
+# Case-insensitive char(N)
+create table t1 (
+ pk1 char(4) not NULL,
+ key1 int(11),
+ key2 int(11),
+ PRIMARY KEY (pk1),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=bdb collate latin2_general_ci;
+insert into t1 values ('a1', 1, 1),
+ ('b2', 1, 1),
+ ('A3', 1, 1),
+ ('B4', 1, 1);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+drop table t1;
+
+# Multi-part PK
+create table t1 (
+ pk1 int not NULL,
+ pk2 char(4) not NULL collate latin1_german1_ci,
+ pk3 char(4) not NULL collate latin1_bin,
+ key1 int(11),
+ key2 int(11),
+ PRIMARY KEY (pk1,pk2,pk3),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=bdb;
+insert into t1 values
+ (1, 'u', 'u', 1, 1),
+ (1, 'u', char(0xEC), 1, 1),
+ (1, 'u', 'x', 1, 1);
+insert ignore into t1 select pk1, char(0xEC), pk3, key1, key2 from t1;
+insert ignore into t1 select pk1, 'x', pk3, key1, key2 from t1 where pk2='u';
+insert ignore into t1 select 2, pk2, pk3, key1, key2 from t1;
+select * from t1;
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+
+# Hidden PK
+alter table t1 drop primary key;
+select * from t1;
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+drop table t1;
+
+# Variable-length PK
+# this is also test for Bug#2688
+create table t1 (
+ pk1 varchar(8) NOT NULL default '',
+ pk2 varchar(4) NOT NULL default '',
+ key1 int(11),
+ key2 int(11),
+ primary key(pk1, pk2),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=bdb;
+insert into t1 values ('','empt',2,2),
+ ('a','a--a',2,2),
+ ('bb','b--b',2,2),
+ ('ccc','c--c',2,2),
+ ('dddd','d--d',2,2);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+
+drop table t1;
+
diff --git a/mysql-test/t/rowid_order_innodb.test b/mysql-test/t/rowid_order_innodb.test
new file mode 100644
index 00000000000..fb4959d78e6
--- /dev/null
+++ b/mysql-test/t/rowid_order_innodb.test
@@ -0,0 +1,108 @@
+#
+# Test for rowid ordering (and comparison) functions.
+# do index_merge select for tables with PK of various types.
+#
+--disable_warnings
+drop table if exists t1, t2, t3,t4;
+--enable_warnings
+
+-- source include/have_innodb.inc
+
+# Signed number as rowid
+create table t1 (
+ pk1 int not NULL,
+ key1 int(11),
+ key2 int(11),
+ PRIMARY KEY (pk1),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=innodb;
+insert into t1 values (-5, 1, 1),
+ (-100, 1, 1),
+ (3, 1, 1),
+ (0, 1, 1),
+ (10, 1, 1);
+explain select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+drop table t1;
+
+# Unsigned numbers as rowids
+create table t1 (
+ pk1 int unsigned not NULL,
+ key1 int(11),
+ key2 int(11),
+ PRIMARY KEY (pk1),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=innodb;
+insert into t1 values (0, 1, 1),
+ (0xFFFFFFFF, 1, 1),
+ (0xFFFFFFFE, 1, 1),
+ (1, 1, 1),
+ (2, 1, 1);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+drop table t1;
+
+# Case-insensitive char(N)
+create table t1 (
+ pk1 char(4) not NULL,
+ key1 int(11),
+ key2 int(11),
+ PRIMARY KEY (pk1),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=innodb collate latin2_general_ci;
+insert into t1 values ('a1', 1, 1),
+ ('b2', 1, 1),
+ ('A3', 1, 1),
+ ('B4', 1, 1);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+drop table t1;
+
+# Multi-part PK
+create table t1 (
+ pk1 int not NULL,
+ pk2 char(4) not NULL collate latin1_german1_ci,
+ pk3 char(4) not NULL collate latin1_bin,
+ key1 int(11),
+ key2 int(11),
+ PRIMARY KEY (pk1,pk2,pk3),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=innodb;
+insert into t1 values
+ (1, 'u', 'u', 1, 1),
+ (1, 'u', char(0xEC), 1, 1),
+ (1, 'u', 'x', 1, 1);
+insert ignore into t1 select pk1, char(0xEC), pk3, key1, key2 from t1;
+insert ignore into t1 select pk1, 'x', pk3, key1, key2 from t1 where pk2='u';
+insert ignore into t1 select 2, pk2, pk3, key1, key2 from t1;
+select * from t1;
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+
+# Hidden PK
+alter table t1 drop primary key;
+select * from t1;
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+drop table t1;
+
+# Variable-length PK
+# this is also test for Bug#2688
+create table t1 (
+ pk1 varchar(8) NOT NULL default '',
+ pk2 varchar(4) NOT NULL default '',
+ key1 int(11),
+ key2 int(11),
+ primary key(pk1, pk2),
+ KEY key1 (key1),
+ KEY key2 (key2)
+) engine=innodb;
+insert into t1 values ('','empt',2,2),
+ ('a','a--a',2,2),
+ ('bb','b--b',2,2),
+ ('ccc','c--c',2,2),
+ ('dddd','d--d',2,2);
+select * from t1 force index(key1, key2) where key1 < 3 or key2 < 3;
+
+drop table t1;
+
diff --git a/mysql-test/t/rpl000010-slave.opt b/mysql-test/t/rpl000010-slave.opt
index 429a7f63f7b..0dbfb311e33 100644
--- a/mysql-test/t/rpl000010-slave.opt
+++ b/mysql-test/t/rpl000010-slave.opt
@@ -1 +1 @@
---disconnect-slave-event-count=1
+--disconnect-slave-event-count=2
diff --git a/mysql-test/t/rpl000015.test b/mysql-test/t/rpl000015.test
index b7fff94f7f3..da73c5f4db2 100644
--- a/mysql-test/t/rpl000015.test
+++ b/mysql-test/t/rpl000015.test
@@ -1,4 +1,4 @@
-connect (master,localhost,root,,test,$MASTER_MYPORT,master.sock);
+connect (master,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connect (slave,localhost,root,,test,$SLAVE_MYPORT,slave.sock);
connection master;
reset master;
@@ -7,24 +7,24 @@ save_master_pos;
connection slave;
reset slave;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
change master to master_host='127.0.0.1';
# The following needs to be cleaned up when change master is fixed
--replace_result $MASTER_MYPORT MASTER_PORT $MYSQL_TCP_PORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
--replace_result $MASTER_MYPORT MASTER_PORT
eval change master to master_host='127.0.0.1',master_user='root',
master_password='',master_port=$MASTER_MYPORT;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
start slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
connection master;
--disable_warnings
diff --git a/mysql-test/t/rpl000017.test b/mysql-test/t/rpl000017.test
index cf808a2cbc0..3b39a6b49a6 100644
--- a/mysql-test/t/rpl000017.test
+++ b/mysql-test/t/rpl000017.test
@@ -1,4 +1,4 @@
-connect (master,localhost,root,,test,$MASTER_MYPORT,master.sock);
+connect (master,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connect (slave,localhost,root,,test,$SLAVE_MYPORT,slave.sock);
connection master;
reset master;
diff --git a/mysql-test/t/rpl000018.test b/mysql-test/t/rpl000018.test
index 884ec9727d2..fd2be2399a5 100644
--- a/mysql-test/t/rpl000018.test
+++ b/mysql-test/t/rpl000018.test
@@ -4,7 +4,7 @@
#
require_manager;
-connect (master,localhost,root,,test,0,master.sock);
+connect (master,localhost,root,,test,0,$MASTER_MYPORT);
connect (slave,localhost,root,,test,0,slave.sock);
connection master;
reset master;
diff --git a/mysql-test/t/rpl_change_master.test b/mysql-test/t/rpl_change_master.test
index e6452b5b619..ddac966b073 100644
--- a/mysql-test/t/rpl_change_master.test
+++ b/mysql-test/t/rpl_change_master.test
@@ -12,11 +12,11 @@ connection slave;
stop slave;
select * from t1;
--replace_result $MASTER_MYPORT MASTER_MYPORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
change master to master_user='root';
--replace_result $MASTER_MYPORT MASTER_MYPORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# Will restart from after the values(2), which is bug
select release_lock("a");
diff --git a/mysql-test/t/rpl_charset.test b/mysql-test/t/rpl_charset.test
index 83e7d95e28c..ba2ebe03a12 100644
--- a/mysql-test/t/rpl_charset.test
+++ b/mysql-test/t/rpl_charset.test
@@ -106,7 +106,7 @@ select * from mysqltest2.t1 order by a;
connection master;
drop database mysqltest2;
drop database mysqltest3;
-show binlog events from 79;
+show binlog events from 95;
sync_slave_with_master;
# Check that we can't change global.collation_server
diff --git a/mysql-test/t/rpl_empty_master_crash.test b/mysql-test/t/rpl_empty_master_crash.test
index bee9ef72dc4..98a630c69ca 100644
--- a/mysql-test/t/rpl_empty_master_crash.test
+++ b/mysql-test/t/rpl_empty_master_crash.test
@@ -1,6 +1,6 @@
source include/master-slave.inc;
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
#
diff --git a/mysql-test/t/rpl_error_ignored_table.test b/mysql-test/t/rpl_error_ignored_table.test
index 0062a67ff1a..487869e5fef 100644
--- a/mysql-test/t/rpl_error_ignored_table.test
+++ b/mysql-test/t/rpl_error_ignored_table.test
@@ -15,7 +15,7 @@ sync_with_master;
# The port number is different when doing the release build with
# Do-compile, hence we have to replace the port number here accordingly
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# check that the table has been ignored, because otherwise the test is nonsense
show tables like 't1';
@@ -48,7 +48,7 @@ connection master;
--error 0,1053;
reap;
connection master1;
-show binlog events from 79;
+show binlog events from 95;
save_master_pos;
connection slave;
# SQL slave thread should not have stopped (because table of the killed
diff --git a/mysql-test/t/rpl_flush_log_loop.test b/mysql-test/t/rpl_flush_log_loop.test
index 74920722868..ccaae8ad765 100644
--- a/mysql-test/t/rpl_flush_log_loop.test
+++ b/mysql-test/t/rpl_flush_log_loop.test
@@ -18,5 +18,5 @@ sleep 5;
flush logs;
sleep 5;
--replace_result $SLAVE_MYPORT SLAVE_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
diff --git a/mysql-test/t/rpl_heap.test b/mysql-test/t/rpl_heap.test
index 0bc71eaf30c..3452f3990bf 100644
--- a/mysql-test/t/rpl_heap.test
+++ b/mysql-test/t/rpl_heap.test
@@ -7,7 +7,7 @@ require_manager;
# issue a query after the server restart.
# Maybe this is something awkward in mysqltest or in the manager?
# So we use sockets.
-connect (master,localhost,root,,test,0,master.sock);
+connect (master,localhost,root,,test,0,$MASTER_MYPORT);
connect (slave,localhost,root,,test,0,slave.sock);
connection master;
diff --git a/mysql-test/t/rpl_loaddata.test b/mysql-test/t/rpl_loaddata.test
index 10213644836..487ad4a7aa6 100644
--- a/mysql-test/t/rpl_loaddata.test
+++ b/mysql-test/t/rpl_loaddata.test
@@ -37,7 +37,7 @@ select * from t3;
# restarted for this test, the file_id is uncertain (would cause test
# failures). So instead, we test if the binlog looks long enough to
# contain LOAD DATA. That is, I (Guilhem) have done SHOW BINLOG EVENTS on my
-# machine, saw that the binlog is of size 964 when things go fine.
+# machine, saw that the binlog is of size 1068 (in 5.0.0) when things go fine.
# If LOAD DATA was not logged, the binlog would be shorter.
show master status;
@@ -72,7 +72,7 @@ set global sql_slave_skip_counter=1;
start slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# Trigger error again to test CHANGE MASTER
@@ -92,7 +92,7 @@ stop slave;
change master to master_user='test';
change master to master_user='root';
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# Trigger error again to test RESET SLAVE
@@ -114,7 +114,7 @@ wait_for_slave_to_stop;
stop slave;
reset slave;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# Finally, see if logging is done ok on master for a failing LOAD DATA INFILE
diff --git a/mysql-test/t/rpl_loaddata_rule_m.test b/mysql-test/t/rpl_loaddata_rule_m.test
index 678dae13889..3f19a09c6f7 100644
--- a/mysql-test/t/rpl_loaddata_rule_m.test
+++ b/mysql-test/t/rpl_loaddata_rule_m.test
@@ -19,5 +19,5 @@ create database mysqltest;
create table t1(a int, b int, unique(b));
use mysqltest;
load data infile '../../std_data/rpl_loaddata.dat' into table test.t1;
-show binlog events from 79; # should be nothing
+show binlog events from 95; # should be nothing
drop database mysqltest;
diff --git a/mysql-test/t/rpl_loaddata_rule_s.test b/mysql-test/t/rpl_loaddata_rule_s.test
index 1ea4f6825f5..8163cd20f00 100644
--- a/mysql-test/t/rpl_loaddata_rule_s.test
+++ b/mysql-test/t/rpl_loaddata_rule_s.test
@@ -17,4 +17,4 @@ save_master_pos;
connection slave;
sync_with_master;
select count(*) from t1; # check that LOAD was replicated
-show binlog events from 79; # should be nothing
+show binlog events from 95; # should be nothing
diff --git a/mysql-test/t/rpl_log.test b/mysql-test/t/rpl_log.test
index 8fdccdd068d..178199d6160 100644
--- a/mysql-test/t/rpl_log.test
+++ b/mysql-test/t/rpl_log.test
@@ -38,9 +38,9 @@ select count(*) from t1;
drop table t1;
--replace_result $VERSION VERSION
show binlog events;
-show binlog events from 79 limit 1;
-show binlog events from 79 limit 2;
-show binlog events from 79 limit 2,1;
+show binlog events from 95 limit 1;
+show binlog events from 95 limit 2;
+show binlog events from 95 limit 2,1;
flush logs;
# We need an extra update before doing save_master_pos.
@@ -82,6 +82,7 @@ insert into t1 values (1);
drop table t1;
--replace_result $VERSION VERSION
show binlog events;
+--replace_result $VERSION VERSION
show binlog events in 'master-bin.000002';
show binary logs;
save_master_pos;
@@ -94,7 +95,7 @@ show binlog events in 'slave-bin.000001' from 4;
--replace_result $MASTER_MYPORT MASTER_PORT $VERSION VERSION
show binlog events in 'slave-bin.000002' from 4;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# Need to recode the following
diff --git a/mysql-test/t/rpl_log_pos.test b/mysql-test/t/rpl_log_pos.test
index a40736577c8..634bf9449fd 100644
--- a/mysql-test/t/rpl_log_pos.test
+++ b/mysql-test/t/rpl_log_pos.test
@@ -5,7 +5,7 @@ source include/master-slave.inc;
show master status;
sync_slave_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
stop slave;
change master to master_log_pos=73;
@@ -15,19 +15,19 @@ stop slave;
change master to master_log_pos=73;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
start slave;
sleep 5;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
stop slave;
change master to master_log_pos=173;
start slave;
sleep 2;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
connection master;
show master status;
@@ -38,7 +38,7 @@ insert into t1 values (1),(2),(3);
save_master_pos;
connection slave;
stop slave;
-change master to master_log_pos=79;
+change master to master_log_pos=95;
start slave;
sync_with_master;
select * from t1;
diff --git a/mysql-test/t/rpl_max_relay_size.test b/mysql-test/t/rpl_max_relay_size.test
index cbcc115a942..963a76fb959 100644
--- a/mysql-test/t/rpl_max_relay_size.test
+++ b/mysql-test/t/rpl_max_relay_size.test
@@ -29,7 +29,7 @@ select @@global.max_relay_log_size;
start slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
stop slave;
reset slave;
@@ -38,7 +38,7 @@ select @@global.max_relay_log_size;
start slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
stop slave;
reset slave;
@@ -47,7 +47,7 @@ select @@global.max_relay_log_size;
start slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# Tests below are mainly to ensure that we have not coded with wrong assumptions
@@ -58,7 +58,7 @@ reset slave;
# (to make sure it does not crash).
flush logs;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
reset slave;
@@ -74,7 +74,7 @@ save_master_pos;
connection slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# one more rotation, to be sure Relay_Log_Space is correctly updated
flush logs;
@@ -84,7 +84,7 @@ save_master_pos;
connection slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
connection master;
diff --git a/mysql-test/t/rpl_openssl.test b/mysql-test/t/rpl_openssl.test
index 8a36904f4d4..779ec4e84bf 100644
--- a/mysql-test/t/rpl_openssl.test
+++ b/mysql-test/t/rpl_openssl.test
@@ -45,7 +45,7 @@ select * from t1;
#checking show slave status
--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR $MASTER_MYPORT MASTER_MYPORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
#checking if replication works without ssl also performing clean up
@@ -58,5 +58,5 @@ save_master_pos;
connection slave;
sync_with_master;
--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR $MASTER_MYPORT MASTER_MYPORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
diff --git a/mysql-test/t/rpl_redirect.test b/mysql-test/t/rpl_redirect.test
index 3b5ad6ba88d..d6f37e7f7f6 100644
--- a/mysql-test/t/rpl_redirect.test
+++ b/mysql-test/t/rpl_redirect.test
@@ -12,7 +12,7 @@ sync_with_master;
#discover slaves
connection master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
SHOW SLAVE STATUS;
--replace_result $SLAVE_MYPORT SLAVE_PORT
SHOW SLAVE HOSTS;
diff --git a/mysql-test/t/rpl_relayrotate-slave.opt b/mysql-test/t/rpl_relayrotate-slave.opt
index 8b671423363..3a4abbf091e 100644
--- a/mysql-test/t/rpl_relayrotate-slave.opt
+++ b/mysql-test/t/rpl_relayrotate-slave.opt
@@ -1,4 +1,3 @@
--O max_binlog_size=16384
+-O max_relay_log_size=16384
--innodb
--log-warnings
-
diff --git a/mysql-test/t/rpl_relayrotate.test b/mysql-test/t/rpl_relayrotate.test
index 1bc6b574663..2fde590356a 100644
--- a/mysql-test/t/rpl_relayrotate.test
+++ b/mysql-test/t/rpl_relayrotate.test
@@ -55,8 +55,10 @@ start slave;
# reading, MASTER_POS_WAIT() will do it for sure
# (the only statement with position>=3000 is COMMIT).
select master_pos_wait('master-bin.001',3000)>=0;
-select * from t1 where a=8000;
-
+select max(a) from t1;
+--replace_column 1 # 8 # 9 # 23 # 33 #
+--replace_result $MASTER_MYPORT MASTER_MYPORT
+show slave status;
connection master;
# The following DROP is a very important cleaning task:
diff --git a/mysql-test/t/rpl_replicate_do.test b/mysql-test/t/rpl_replicate_do.test
index 7066f6e53d8..108dd54ce0a 100644
--- a/mysql-test/t/rpl_replicate_do.test
+++ b/mysql-test/t/rpl_replicate_do.test
@@ -33,6 +33,6 @@ connection slave;
sync_with_master;
# show slave status, just to see of it prints replicate-do-table
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
diff --git a/mysql-test/t/rpl_reset_slave.test b/mysql-test/t/rpl_reset_slave.test
index d58e9c711d1..1b27e059f92 100644
--- a/mysql-test/t/rpl_reset_slave.test
+++ b/mysql-test/t/rpl_reset_slave.test
@@ -11,24 +11,24 @@ save_master_pos;
connection slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
stop slave;
change master to master_user='test';
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
reset slave;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
start slave;
sync_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# test of crash with temp tables & RESET SLAVE
diff --git a/mysql-test/t/rpl_rotate_logs.test b/mysql-test/t/rpl_rotate_logs.test
index da4d5f0bce1..c3c0ff5be10 100644
--- a/mysql-test/t/rpl_rotate_logs.test
+++ b/mysql-test/t/rpl_rotate_logs.test
@@ -9,7 +9,7 @@
# changes
# - Test creating a duplicate key error and recover from it
-connect (master,localhost,root,,test,$MASTER_MYPORT,master.sock);
+connect (master,localhost,root,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
--disable_warnings
drop table if exists t1, t2, t3, t4;
--enable_warnings
@@ -55,7 +55,7 @@ create table t1 (s text);
insert into t1 values('Could not break slave'),('Tried hard');
sync_slave_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
select * from t1;
connection master;
@@ -108,7 +108,7 @@ show binary logs;
insert into t2 values (65);
sync_slave_with_master;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
select * from t2;
@@ -140,7 +140,7 @@ sync_with_master;
select * from t4;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
# because of concurrent insert, the table may not be up to date
# if we do not lock
diff --git a/mysql-test/t/rpl_session_var.test b/mysql-test/t/rpl_session_var.test
new file mode 100644
index 00000000000..2379df721b7
--- /dev/null
+++ b/mysql-test/t/rpl_session_var.test
@@ -0,0 +1,42 @@
+# Replication of session variables.
+# FOREIGN_KEY_CHECKS is tested in rpl_insert_id.test
+
+source include/master-slave.inc;
+drop table if exists t1;
+create table t1(a varchar(100),b int);
+set @@session.sql_mode=pipes_as_concat;
+insert into t1 values('My'||'SQL', 1);
+set @@session.sql_mode=default;
+insert into t1 values('My'||'SQL', 2);
+select * from t1 where b<3 order by a;
+save_master_pos;
+connection slave;
+sync_with_master;
+select * from t1 where b<3 order by a;
+connection master;
+# if the slave does the next sync_with_master fine, then it means it accepts the
+# two lines of ANSI syntax below, which is what we want to check.
+set @@session.sql_mode=ignore_space;
+insert into t1 values(password ('MySQL'), 3);
+set @@session.sql_mode=ansi_quotes;
+create table "t2" ("a" int);
+drop table t1, t2;
+set @@session.sql_mode=default;
+create table t1(a int auto_increment primary key);
+create table t2(b int, a int);
+set @@session.sql_auto_is_null=1;
+insert into t1 values(null);
+insert into t2 select 1,a from t1 where a is null;
+set @@session.sql_auto_is_null=0;
+insert into t1 values(null);
+insert into t2 select 2,a from t1 where a is null;
+select * from t2 order by b;
+save_master_pos;
+connection slave;
+sync_with_master;
+select * from t2 order by b;
+connection master;
+drop table t1,t2;
+save_master_pos;
+connection slave;
+sync_with_master;
diff --git a/mysql-test/t/rpl_trunc_binlog.test b/mysql-test/t/rpl_trunc_binlog.test
index 2ade41ee96d..b2e7e52f5e4 100644
--- a/mysql-test/t/rpl_trunc_binlog.test
+++ b/mysql-test/t/rpl_trunc_binlog.test
@@ -21,5 +21,5 @@ start slave;
# can't sync_with_master so we must sleep
sleep 3;
--replace_result $MASTER_MYPORT MASTER_PORT
---replace_column 1 # 23 # 33 #
+--replace_column 1 # 8 # 9 # 23 # 33 #
show slave status;
diff --git a/mysql-test/t/rpl_until.test b/mysql-test/t/rpl_until.test
index 5eaec0727b6..f7ca51ecabc 100644
--- a/mysql-test/t/rpl_until.test
+++ b/mysql-test/t/rpl_until.test
@@ -24,7 +24,7 @@ show binlog events;
# try to replicate all queries until drop of t1
connection slave;
-start slave until master_log_file='master-bin.000001', master_log_pos=244;
+start slave until master_log_file='master-bin.000001', master_log_pos=304;
sleep 2;
# here table should be still not deleted
select * from t1;
@@ -42,7 +42,7 @@ sleep 2;
show slave status;
# try replicate all until second insert to t2;
-start slave until relay_log_file='slave-relay-bin.000002', relay_log_pos=537;
+start slave until relay_log_file='slave-relay-bin.000004', relay_log_pos=710;
sleep 2;
select * from t2;
--replace_result $MASTER_MYPORT MASTER_MYPORT
@@ -57,8 +57,8 @@ connection slave;
sync_with_master;
stop slave;
-# this should stop immideately
-start slave until master_log_file='master-bin.000001', master_log_pos=561;
+# this should stop immediately as we are already there
+start slave until master_log_file='master-bin.000001', master_log_pos=710;
# 2 is not enough when running with valgrind
real_sleep 4
# here the sql slave thread should be stopped
@@ -79,4 +79,4 @@ start slave until relay_log_file='slave-relay-bin.000002';
start slave until relay_log_file='slave-relay-bin.000002', master_log_pos=561;
start slave sql_thread;
-start slave until master_log_file='master-bin.000001', master_log_pos=561;
+start slave until master_log_file='master-bin.000001', master_log_pos=710;
diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test
index 35fbec72ac8..7aab1c23c1a 100644
--- a/mysql-test/t/rpl_user_variables.test
+++ b/mysql-test/t/rpl_user_variables.test
@@ -43,7 +43,7 @@ save_master_pos;
connection slave;
sync_with_master;
select * from t1;
-show binlog events from 141;
+show binlog events from 179;
connection master;
drop table t1;
save_master_pos;
diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test
index 759ed7d22b3..9ed1ac2d63e 100644
--- a/mysql-test/t/show_check.test
+++ b/mysql-test/t/show_check.test
@@ -167,16 +167,24 @@ CREATE TABLE `a/b` (i INT);
#CREATE TABLE ```ab````cd``` (i INT);
#SHOW CREATE TABLE ```ab````cd```;
#DROP TABLE ```ab````cd```;
-
+#
#CREATE TABLE ```a` (i INT);
#SHOW CREATE TABLE ```a`;
#DROP TABLE ```a`;
-
-SET sql_mode= 'ANSI_QUOTES';
-
+#
+#SET sql_mode= 'ANSI_QUOTES';
+#
#CREATE TABLE """a" (i INT);
#SHOW CREATE TABLE """a";
#DROP TABLE """a";
+#
+#Bug #4374 SHOW TABLE STATUS FROM ignores collation_connection
+#set names latin1;
+#create database `ä`;
+#create table `ä`.`ä` (a int) engine=heap;
+#--replace_column 7 # 8 # 9 #
+#show table status from `ä` LIKE 'ä';
+#drop database `ä`;
#########################################################
# end of part that must be uncommented when WL#1324 is done
#########################################################
@@ -307,7 +315,6 @@ delete from mysql.db
where user='mysqltest_1' || user='mysqltest_2' || user='mysqltest_3';
flush privileges;
-#Bug #4374 SHOW TABLE STATUS FROM ignores collation_connection
# This test fails on MAC OSX, so it is temporary disabled.
# This needs WL#1324 to be done.
#set names latin1;
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
new file mode 100644
index 00000000000..f8abab0e7e3
--- /dev/null
+++ b/mysql-test/t/sp-error.test
@@ -0,0 +1,633 @@
+#
+# Stored PROCEDURE error tests
+#
+
+# Make sure we don't have any procedures left.
+delete from mysql.proc;
+
+delimiter |;
+
+# This should give three syntax errors (sometimes crashed; bug #643)
+# (Unfortunately, this is not a 100% test, on some platforms this
+# passed despite the bug.)
+--error 1064
+create procedure syntaxerror(t int)|
+--error 1064
+create procedure syntaxerror(t int)|
+--error 1064
+create procedure syntaxerror(t int)|
+
+# Check that we get the right error, i.e. UDF declaration parses correctly,
+# but foo.so doesn't exist.
+# QQ This generates an error message containing a misleading errno which
+# might vary between systems (it usually doesn't have anything to do with
+# the actual failing dlopen()).
+#--error 1126
+#create function foo returns real soname "foo.so"|
+
+
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 ( x int )|
+insert into t3 values (2), (3)|
+
+create procedure bad_into(out param int)
+ select x from t3 into param|
+
+--error 1172
+call bad_into(@x)|
+
+drop procedure bad_into|
+drop table t3|
+
+
+create procedure proc1()
+ set @x = 42|
+
+create function func1() returns int
+ return 42|
+
+# Can't create recursively
+--error 1302
+create procedure foo()
+ create procedure bar() set @x=3|
+--error 1302
+create procedure foo()
+ create function bar() returns double return 2.3|
+
+# Already exists
+--error 1303
+create procedure proc1()
+ set @x = 42|
+--error 1303
+create function func1() returns int
+ return 42|
+
+drop procedure proc1|
+drop function func1|
+
+# Does not exist
+--error 1304
+alter procedure foo|
+--error 1304
+alter function foo|
+--error 1304
+drop procedure foo|
+--error 1304
+drop function foo|
+--error 1304
+call foo()|
+drop procedure if exists foo|
+--error 1304
+show create procedure foo|
+
+# LEAVE/ITERATE/GOTO with no match
+--error 1307
+create procedure foo()
+foo: loop
+ leave bar;
+end loop|
+--error 1307
+create procedure foo()
+foo: loop
+ iterate bar;
+end loop|
+--error 1307
+create procedure foo()
+foo: begin
+ iterate foo;
+end|
+--error 1307
+create procedure foo()
+begin
+ goto foo;
+end|
+--error 1307
+create procedure foo()
+begin
+ begin
+ label foo;
+ end;
+ goto foo;
+end|
+--error 1307
+create procedure foo()
+begin
+ goto foo;
+ begin
+ label foo;
+ end;
+end|
+--error 1307
+create procedure foo()
+begin
+ begin
+ goto foo;
+ end;
+ begin
+ label foo;
+ end;
+end|
+--error 1307
+create procedure foo()
+begin
+ begin
+ label foo;
+ end;
+ begin
+ goto foo;
+ end;
+end|
+
+# Redefining label
+--error 1308
+create procedure foo()
+foo: loop
+ foo: loop
+ set @x=2;
+ end loop foo;
+end loop foo|
+
+# End label mismatch
+--error 1309
+create procedure foo()
+foo: loop
+ set @x=2;
+end loop bar|
+
+# Referring to undef variable
+create procedure foo(out x int)
+begin
+ declare y int;
+ set x = y;
+end|
+drop procedure foo|
+
+# RETURN in FUNCTION only
+--error 1312
+create procedure foo()
+ return 42|
+
+# Doesn't allow queries in FUNCTIONs (for now :-( )
+--error 1313
+create function foo() returns int
+begin
+ declare x int;
+ select max(c) into x from test.t;
+ return x;
+end|
+
+# Wrong number of arguments
+create procedure p(x int)
+ insert into test.t1 values (x)|
+create function f(x int) returns int
+ return x+42|
+
+--error 1317
+call p()|
+--error 1317
+call p(1, 2)|
+--error 1317
+select f()|
+--error 1317
+select f(1, 2)|
+
+drop procedure p|
+drop function f|
+
+--error 1318
+create procedure p(val int, out res int)
+begin
+ declare x int default 0;
+ declare continue handler for foo set x = 1;
+
+ insert into test.t1 values (val);
+ if (x) then
+ set res = 0;
+ else
+ set res = 1;
+ end if;
+end|
+
+--error 1318
+create procedure p(val int, out res int)
+begin
+ declare x int default 0;
+ declare foo condition for 1146;
+ declare continue handler for bar set x = 1;
+
+ insert into test.t1 values (val);
+ if (x) then
+ set res = 0;
+ else
+ set res = 1;
+ end if;
+end|
+
+--error 1319
+create function f(val int) returns int
+begin
+ declare x int;
+
+ set x = val+3;
+end|
+
+create function f(val int) returns int
+begin
+ declare x int;
+
+ set x = val+3;
+ if x < 4 then
+ return x;
+ end if;
+end|
+
+--error 1320
+select f(10)|
+
+drop function f|
+
+--error 1321
+create procedure p()
+begin
+ declare c cursor for insert into test.t1 values ("foo", 42);
+
+ open c;
+ close c;
+end|
+
+--error 1322
+create procedure p()
+begin
+ declare x int;
+ declare c cursor for select * into x from test.t limit 1;
+
+ open c;
+ close c;
+end|
+
+--error 1323
+create procedure p()
+begin
+ declare c cursor for select * from test.t;
+
+ open cc;
+ close c;
+end|
+
+--disable_warnings
+drop table if exists t1|
+--enable_warnings
+create table t1 (val int)|
+
+create procedure p()
+begin
+ declare c cursor for select * from test.t1;
+
+ open c;
+ open c;
+ close c;
+end|
+--error 1324
+call p()|
+drop procedure p|
+
+create procedure p()
+begin
+ declare c cursor for select * from test.t1;
+
+ open c;
+ close c;
+ close c;
+end|
+--error 1325
+call p()|
+drop procedure p|
+
+--error 1304
+alter procedure bar3 sql security invoker|
+--error 1059
+alter procedure bar3 name
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
+
+drop table t1|
+
+--disable_warnings
+drop table if exists t1|
+--enable_warnings
+create table t1 (val int, x float)|
+insert into t1 values (42, 3.1), (19, 1.2)|
+
+--error 1326
+create procedure p()
+begin
+ declare x int;
+ declare c cursor for select * from t1;
+
+ open c;
+ fetch c into x, y;
+ close c;
+end|
+
+create procedure p()
+begin
+ declare x int;
+ declare c cursor for select * from t1;
+
+ open c;
+ fetch c into x;
+ close c;
+end|
+--error 1327
+call p()|
+drop procedure p|
+
+create procedure p()
+begin
+ declare x int;
+ declare y float;
+ declare z int;
+ declare c cursor for select * from t1;
+
+ open c;
+ fetch c into x, y, z;
+ close c;
+end|
+--error 1327
+call p()|
+drop procedure p|
+
+--error 1329
+create procedure p(in x int, x char(10))
+begin
+end|
+--error 1329
+create function p(x int, x char(10))
+begin
+end|
+
+--error 1330
+create procedure p()
+begin
+ declare x float;
+ declare x int;
+end|
+
+--error 1331
+create procedure p()
+begin
+ declare c condition for 1064;
+ declare c condition for 1065;
+end|
+
+--error 1332
+create procedure p()
+begin
+ declare c cursor for select * from t1;
+ declare c cursor for select field from t1;
+end|
+
+# USE is not allowed
+--error 1335
+create procedure u()
+ use sptmp|
+
+# Enforced standard order of declarations
+--error 1336
+create procedure p()
+begin
+ declare c cursor for select * from t1;
+ declare x int;
+end|
+--error 1336
+create procedure p()
+begin
+ declare x int;
+ declare continue handler for sqlstate '42S99' set x = 1;
+ declare foo condition for sqlstate '42S99';
+end|
+
+--error 1337
+create procedure p()
+begin
+ declare x int;
+ declare continue handler for sqlstate '42S99' set x = 1;
+ declare c cursor for select * from t1;
+end|
+
+--error 1357
+create procedure p()
+begin
+ declare continue handler for sqlexception
+ begin
+ goto L1;
+ end;
+
+ select field from t1;
+ label L1;
+end|
+
+#
+# BUG#1965
+#
+create procedure bug1965()
+begin
+ declare c cursor for select val from t1 order by valname;
+ open c;
+ close c;
+end|
+
+--error 1054
+call bug1965()|
+drop procedure bug1965|
+
+#
+# BUG#1966
+#
+--error 1326
+select 1 into a|
+
+#
+# BUG#1654
+#
+--error 1313
+create function bug1654()
+ returns int
+return (select sum(t.data) from test.t2 t)|
+
+#
+# BUG#1653
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 (column_1_0 int)|
+
+create procedure bug1653()
+ update t3 set column_1 = 0|
+
+--error 1054
+call bug1653()|
+drop table t3|
+create table t3 (column_1 int)|
+call bug1653()|
+
+drop procedure bug1653|
+drop table t3|
+
+#
+# BUG#2259
+#
+# Note: When this bug existed, it did not necessarily cause a crash
+# in all builds, but valgrind did give warnings.
+create procedure bug2259()
+begin
+ declare v1 int;
+ declare c1 cursor for select s1 from t10;
+
+ fetch c1 into v1;
+end|
+
+--error 1325
+call bug2259()|
+drop procedure bug2259|
+
+#
+# BUG#2272
+#
+create procedure bug2272()
+begin
+ declare v int;
+
+ update t1 set v = 42;
+end|
+
+insert into t1 values (666, 51.3)|
+--error 1054
+call bug2272()|
+delete from t1|
+drop procedure bug2272|
+
+#
+# BUG#2329
+#
+create procedure bug2329_1()
+begin
+ declare v int;
+
+ insert into t1 (v) values (5);
+end|
+
+create procedure bug2329_2()
+begin
+ declare v int;
+
+ replace t1 set v = 5;
+end|
+
+--error 1054
+call bug2329_1()|
+--error 1054
+call bug2329_2()|
+drop procedure bug2329_1|
+drop procedure bug2329_2|
+
+#
+# BUG#3287
+#
+create function bug3287() returns int
+begin
+ declare v int default null;
+
+ case
+ when v is not null then return 1;
+ end case;
+ return 2;
+end|
+--error 1338
+select bug3287()|
+drop function bug3287|
+
+create procedure bug3287(x int)
+case x
+when 0 then
+ insert into test.t1 values (x, 0.1);
+when 1 then
+ insert into test.t1 values (x, 1.1);
+end case|
+--error 1338
+call bug3287(2)|
+drop procedure bug3287|
+
+#
+# BUG#3297
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 (s1 int, primary key (s1))|
+insert into t3 values (5),(6)|
+
+create procedure bug3279(out y int)
+begin
+ declare x int default 0;
+ begin
+ declare exit handler for sqlexception set x = x+1;
+ insert into t3 values (5);
+ end;
+ if x < 2 then
+ set x = x+1;
+ insert into t3 values (6);
+ end if;
+ set y = x;
+end|
+
+set @x = 0|
+--error 1062
+call bug3279(@x)|
+select @x|
+drop procedure bug3279|
+drop table t3|
+
+#
+# BUG#3339
+#
+--error 1049
+create procedure nodb.bug3339() begin end|
+
+#
+# BUG#2653
+#
+create procedure bug2653_1(a int, out b int)
+ set b = aa|
+
+create procedure bug2653_2(a int, out b int)
+begin
+ if aa < 0 then
+ set b = - a;
+ else
+ set b = a;
+ end if;
+end|
+
+--error 1054
+call bug2653_1(1, @b)|
+--error 1054
+call bug2653_2(2, @b)|
+
+drop procedure bug2653_1|
+drop procedure bug2653_2|
+
+#
+# BUG#4344
+#
+--error 1356
+create procedure bug4344() drop procedure bug4344|
+--error 1356
+create procedure bug4344() drop function bug4344|
+
+
+drop table t1|
+
+delimiter ;|
diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test
new file mode 100644
index 00000000000..d1c9c6c639d
--- /dev/null
+++ b/mysql-test/t/sp-security.test
@@ -0,0 +1,192 @@
+#
+# Testing SQL SECURITY of stored procedures
+#
+
+connect (con1root,localhost,root,,);
+
+connection con1root;
+use test;
+
+# Create user user1 with no particular access rights
+grant usage on *.* to user1@localhost;
+flush privileges;
+
+--disable_warnings
+drop database if exists db1_secret;
+--enable_warnings
+# Create our secret database
+create database db1_secret;
+
+# Can create a procedure in other db
+create procedure db1_secret.dummy() begin end;
+drop procedure db1_secret.dummy;
+
+use db1_secret;
+
+create table t1 ( u varchar(64), i int );
+
+# A test procedure and function
+create procedure stamp(i int)
+ insert into db1_secret.t1 values (user(), i);
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show procedure status like 'stamp';
+
+create function db() returns varchar(64) return database();
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show function status like 'db';
+
+# root can, of course
+call stamp(1);
+select * from t1;
+select db();
+
+connect (con2user1,localhost,user1,,);
+connect (con3anon,localhost,anon,,);
+
+
+#
+# User1 can
+#
+connection con2user1;
+
+# This should work...
+call db1_secret.stamp(2);
+select db1_secret.db();
+
+# ...but not this
+--error 1044
+select * from db1_secret.t1;
+
+# ...and not this
+--error 1049
+create procedure db1_secret.dummy() begin end;
+--error 1304
+drop procedure db1_secret.dummy;
+
+
+#
+# Anonymous can
+#
+connection con3anon;
+
+# This should work...
+call db1_secret.stamp(3);
+select db1_secret.db();
+
+# ...but not this
+--error 1044
+select * from db1_secret.t1;
+
+# ...and not this
+--error 1049
+create procedure db1_secret.dummy() begin end;
+--error 1304
+drop procedure db1_secret.dummy;
+
+
+#
+# Check it out
+#
+connection con1root;
+select * from t1;
+
+#
+# Change to invoker's rights
+#
+alter procedure stamp sql security invoker;
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show procedure status like 'stamp';
+
+alter function db sql security invoker;
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show function status like 'db';
+
+# root still can
+call stamp(4);
+select * from t1;
+select db();
+
+#
+# User1 cannot
+#
+connection con2user1;
+
+# This should not work
+--error 1044
+call db1_secret.stamp(5);
+--error 1044
+select db1_secret.db();
+
+#
+# Anonymous cannot
+#
+connection con3anon;
+
+# This should not work
+--error 1044
+call db1_secret.stamp(6);
+--error 1044
+select db1_secret.db();
+
+#
+# BUG#2777
+#
+
+connection con1root;
+--disable_warnings
+drop database if exists db2;
+--enable_warnings
+create database db2;
+
+use db2;
+
+create table t2 (s1 int);
+insert into t2 values (0);
+
+grant usage on db2.* to user1@localhost;
+grant select on db2.* to user1@localhost;
+grant usage on db2.* to user2@localhost;
+grant select,insert,update,delete on db2.* to user2@localhost;
+flush privileges;
+
+connection con2user1;
+use db2;
+
+create procedure p () insert into t2 values (1);
+
+# Check that this doesn't work.
+--error 1044
+call p();
+
+connect (con4user2,localhost,user2,,);
+
+connection con4user2;
+use db2;
+
+# This should not work, since p is executed with definer's (user1's) rights.
+--error 1044
+call p();
+select * from t2;
+
+create procedure q () insert into t2 values (2);
+
+call q();
+select * from t2;
+
+connection con2user1;
+use db2;
+
+# This should work
+call q();
+select * from t2;
+
+# Clean up
+connection con1root;
+use test;
+select type,db,name from mysql.proc;
+drop database db1_secret;
+drop database db2;
+# Make sure the routines are gone
+select type,db,name from mysql.proc;
+# Get rid of the users
+delete from mysql.user where user='user1' or user='user2';
diff --git a/mysql-test/t/sp-threads.test b/mysql-test/t/sp-threads.test
new file mode 100644
index 00000000000..27888158f03
--- /dev/null
+++ b/mysql-test/t/sp-threads.test
@@ -0,0 +1,54 @@
+#
+# Testing stored procedures with multiple connections
+#
+
+connect (con1root,localhost,root,,);
+connect (con2root,localhost,root,,);
+
+connection con1root;
+use test;
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (s1 int, s2 int, s3 int);
+
+delimiter //;
+create procedure bug4934()
+begin
+ insert into t1 values (1,0,1);
+end//
+delimiter ;//
+
+
+connection con2root;
+use test;
+
+call bug4934();
+select * from t1;
+
+
+connection con1root;
+
+drop table t1;
+create table t1 (s1 int, s2 int, s3 int);
+
+drop procedure bug4934;
+delimiter //;
+create procedure bug4934()
+begin
+end//
+delimiter ;//
+
+
+connection con2root;
+
+select * from t1;
+call bug4934();
+select * from t1;
+
+connection con1root;
+
+drop table t1;
+drop procedure bug4934;
+
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
new file mode 100644
index 00000000000..92d6110cf7a
--- /dev/null
+++ b/mysql-test/t/sp.test
@@ -0,0 +1,2161 @@
+#
+# Basic stored PROCEDURE tests
+#
+# Please keep this file free of --error cases and other
+# things that will not run in a single debugged mysqld
+# process (e.g. master-slave things).
+
+use test;
+
+--disable_warnings
+drop table if exists t1;
+drop table if exists t2;
+--enable_warnings
+
+create table t1 (
+ id char(16) not null,
+ data int not null
+);
+create table t2 (
+ s char(16),
+ i int,
+ d double
+);
+
+
+# Single statement, no params.
+create procedure foo42()
+ insert into test.t1 values ("foo", 42);
+
+call foo42();
+select * from t1;
+delete from t1;
+drop procedure foo42;
+
+
+# Single statement, two IN params.
+create procedure bar(x char(16), y int)
+ insert into test.t1 values (x, y);
+
+call bar("bar", 666);
+select * from t1;
+delete from t1;
+# Don't drop procedure yet...
+
+
+# Now for multiple statements...
+delimiter |;
+
+# Empty statement
+create procedure empty()
+begin
+end|
+
+call empty()|
+drop procedure empty|
+
+# Scope test. This is legal (warnings might be possible in the future,
+# but for the time being, we just accept it).
+create procedure scope(a int, b float)
+begin
+ declare b int;
+ declare c float;
+
+ begin
+ declare c int;
+ end;
+end|
+
+drop procedure scope|
+
+# Two statements.
+create procedure two(x1 char(16), x2 char(16), y int)
+begin
+ insert into test.t1 values (x1, y);
+ insert into test.t1 values (x2, y);
+end|
+
+call two("one", "two", 3)|
+select * from t1|
+delete from t1|
+drop procedure two|
+
+
+# Simple test of local variables and SET.
+create procedure locset(x char(16), y int)
+begin
+ declare z1, z2 int;
+ set z1 = y;
+ set z2 = z1+2;
+ insert into test.t1 values (x, z2);
+end|
+
+call locset("locset", 19)|
+select * from t1|
+delete from t1|
+drop procedure locset|
+
+
+# In some contexts local variables are not recognized
+# (and in some, you have to qualify the identifier).
+create procedure setcontext()
+begin
+ declare data int default 2;
+
+ insert into t1 (id, data) values ("foo", 1);
+ replace t1 set data = data, id = "bar";
+ update t1 set id = "kaka", data = 3 where t1.data = data;
+end|
+
+call setcontext()|
+select * from t1|
+delete from t1|
+drop procedure setcontext|
+
+
+# Set things to null
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 ( d date, i int, f double, s varchar(32) )|
+
+create procedure nullset()
+begin
+ declare ld date;
+ declare li int;
+ declare lf double;
+ declare ls varchar(32);
+
+ set ld = null, li = null, lf = null, ls = null;
+ insert into t3 values (ld, li, lf, ls);
+
+ insert into t3 (i, f, s) values ((ld is null), 1, "ld is null"),
+ ((li is null), 1, "li is null"),
+ ((li = 0), null, "li = 0"),
+ ((lf is null), 1, "lf is null"),
+ ((lf = 0), null, "lf = 0"),
+ ((ls is null), 1, "ls is null");
+end|
+
+call nullset()|
+select * from t3|
+drop table t3|
+drop procedure nullset|
+
+
+# The peculiar (non-standard) mixture of variables types in SET.
+create procedure mixset(x char(16), y int)
+begin
+ declare z int;
+
+ set @z = y, z = 666, max_join_size = 100;
+ insert into test.t1 values (x, z);
+end|
+
+call mixset("mixset", 19)|
+show variables like 'max_join_size'|
+select id,data,@z from t1|
+delete from t1|
+drop procedure mixset|
+
+
+# Multiple CALL statements, one with OUT parameter.
+create procedure zip(x char(16), y int)
+begin
+ declare z int;
+ call zap(y, z);
+ call bar(x, z);
+end|
+
+# SET local variables and OUT parameter.
+create procedure zap(x int, out y int)
+begin
+ declare z int;
+ set z = x+1, y = z;
+end|
+
+call zip("zip", 99)|
+select * from t1|
+delete from t1|
+drop procedure zip|
+drop procedure bar|
+
+# Top-level OUT parameter
+call zap(7, @zap)|
+select @zap|
+
+drop procedure zap|
+
+
+# "Deep" calls...
+create procedure c1(x int)
+ call c2("c", x)|
+create procedure c2(s char(16), x int)
+ call c3(x, s)|
+create procedure c3(x int, s char(16))
+ call c4("level", x, s)|
+create procedure c4(l char(8), x int, s char(16))
+ insert into t1 values (concat(l,s), x)|
+
+call c1(42)|
+select * from t1|
+delete from t1|
+drop procedure c1|
+drop procedure c2|
+drop procedure c3|
+drop procedure c4|
+
+# INOUT test
+create procedure iotest(x1 char(16), x2 char(16), y int)
+begin
+ call inc2(x2, y);
+ insert into test.t1 values (x1, y);
+end|
+
+create procedure inc2(x char(16), y int)
+begin
+ call inc(y);
+ insert into test.t1 values (x, y);
+end|
+
+create procedure inc(inout io int)
+ set io = io + 1|
+
+call iotest("io1", "io2", 1)|
+select * from t1|
+delete from t1|
+drop procedure iotest|
+drop procedure inc2|
+
+# Propagating top-level @-vars
+create procedure incr(inout x int)
+ call inc(x)|
+
+# Before
+select @zap|
+call incr(@zap)|
+# After
+select @zap|
+
+drop procedure inc|
+drop procedure incr|
+
+# Call-by-value test
+# The expected result is:
+# ("cbv2", 4)
+# ("cbv1", 4711)
+create procedure cbv1()
+begin
+ declare y int default 3;
+
+ call cbv2(y+1, y);
+ insert into test.t1 values ("cbv1", y);
+end|
+
+create procedure cbv2(y1 int, inout y2 int)
+begin
+ set y2 = 4711;
+ insert into test.t1 values ("cbv2", y1);
+end|
+
+call cbv1()|
+select * from t1|
+delete from t1|
+drop procedure cbv1|
+drop procedure cbv2|
+
+
+# Subselect arguments
+
+insert into t2 values ("a", 1, 1.1), ("b", 2, 1.2), ("c", 3, 1.3)|
+
+create procedure sub1(id char(16), x int)
+ insert into test.t1 values (id, x)|
+
+# QQ This doesn't work yet
+#create procedure sub2(id char(16))
+#begin
+# declare x int;
+# set x = (select sum(t.x) from test.t2 t);
+# insert into test.t1 values (id, x);
+#end|
+
+create function sub3(i int) returns int
+ return i+1|
+
+call sub1("sub1a", (select 7))|
+call sub1("sub1b", (select max(i) from t2))|
+call sub1("sub1c", (select i,d from t2 limit 1))|
+call sub1("sub1d", (select 1 from (select 1) a))|
+#call sub2("sub2");
+select * from t1|
+select sub3((select max(i) from t2))|
+drop procedure sub1|
+#drop procedure sub2|
+drop function sub3|
+delete from t2|
+
+# Basic tests of the flow control constructs
+
+# Just test on 'x'...
+create procedure a0(x int)
+while x do
+ set x = x-1;
+ insert into test.t1 values ("a0", x);
+end while|
+
+call a0(3)|
+select * from t1|
+delete from t1|
+drop procedure a0|
+
+
+# The same, but with a more traditional test.
+create procedure a(x int)
+while x > 0 do
+ set x = x-1;
+ insert into test.t1 values ("a", x);
+end while|
+
+call a(3)|
+select * from t1|
+delete from t1|
+drop procedure a|
+
+
+# REPEAT
+create procedure b(x int)
+repeat
+ insert into test.t1 values (repeat("b",3), x);
+ set x = x-1;
+until x = 0 end repeat|
+
+call b(3)|
+select * from t1|
+delete from t1|
+drop procedure b|
+
+
+# Check that repeat isn't parsed the wrong way
+create procedure b2(x int)
+repeat(select 1 into outfile 'b2');
+ insert into test.t1 values (repeat("b2",3), x);
+ set x = x-1;
+until x = 0 end repeat|
+
+# We don't actually want to call it.
+drop procedure b2|
+
+
+# Labelled WHILE with ITERATE (pointless really)
+create procedure c(x int)
+hmm: while x > 0 do
+ insert into test.t1 values ("c", x);
+ set x = x-1;
+ iterate hmm;
+ insert into test.t1 values ("x", x);
+end while hmm|
+
+call c(3)|
+select * from t1|
+delete from t1|
+drop procedure c|
+
+
+# Labelled WHILE with LEAVE
+create procedure d(x int)
+hmm: while x > 0 do
+ insert into test.t1 values ("d", x);
+ set x = x-1;
+ leave hmm;
+ insert into test.t1 values ("x", x);
+end while|
+
+call d(3)|
+select * from t1|
+delete from t1|
+drop procedure d|
+
+
+# LOOP, with simple IF statement
+create procedure e(x int)
+foo: loop
+ if x = 0 then
+ leave foo;
+ end if;
+ insert into test.t1 values ("e", x);
+ set x = x-1;
+end loop foo|
+
+call e(3)|
+select * from t1|
+delete from t1|
+drop procedure e|
+
+
+# A full IF statement
+create procedure f(x int)
+if x < 0 then
+ insert into test.t1 values ("f", 0);
+elseif x = 0 then
+ insert into test.t1 values ("f", 1);
+else
+ insert into test.t1 values ("f", 2);
+end if|
+
+call f(-2)|
+call f(0)|
+call f(4)|
+select * from t1|
+delete from t1|
+drop procedure f|
+
+
+# This form of CASE is really just syntactic sugar for IF-ELSEIF-...
+create procedure g(x int)
+case
+when x < 0 then
+ insert into test.t1 values ("g", 0);
+when x = 0 then
+ insert into test.t1 values ("g", 1);
+else
+ insert into test.t1 values ("g", 2);
+end case|
+
+call g(-42)|
+call g(0)|
+call g(1)|
+select * from t1|
+delete from t1|
+drop procedure g|
+
+
+# The "simple CASE"
+create procedure h(x int)
+case x
+when 0 then
+ insert into test.t1 values ("h0", x);
+when 1 then
+ insert into test.t1 values ("h1", x);
+else
+ insert into test.t1 values ("h?", x);
+end case|
+
+call h(0)|
+call h(1)|
+call h(17)|
+select * from t1|
+delete from t1|
+drop procedure h|
+
+
+# It's actually possible to LEAVE a BEGIN-END block
+create procedure i(x int)
+foo:
+begin
+ if x = 0 then
+ leave foo;
+ end if;
+ insert into test.t1 values ("i", x);
+end foo|
+
+call i(0)|
+call i(3)|
+select * from t1|
+delete from t1|
+drop procedure i|
+
+
+# The non-standard GOTO, for compatibility
+#
+# QQQ The "label" syntax is temporary, it will (hopefully)
+# change to the more common "L:" syntax soon.
+#
+create procedure goto1()
+begin
+ declare y int;
+
+label a;
+ select * from t1;
+ select count(*) into y from t1;
+ if y > 2 then
+ goto b;
+ end if;
+ insert into t1 values ("j", y);
+ goto a;
+label b;
+end|
+
+call goto1()|
+drop procedure goto1|
+
+# With dummy handlers, just to test restore of contexts with jumps
+create procedure goto2(a int)
+begin
+ declare x int default 0;
+ declare continue handler for sqlstate '42S98' set x = 1;
+
+label a;
+ select * from t1;
+b:
+ while x < 2 do
+ begin
+ declare continue handler for sqlstate '42S99' set x = 2;
+
+ if a = 0 then
+ set x = x + 1;
+ iterate b;
+ elseif a = 1 then
+ leave b;
+ elseif a = 2 then
+ set a = 1;
+ goto a;
+ end if;
+ end;
+ end while b;
+
+ select * from t1;
+end|
+
+call goto2(0)|
+call goto2(1)|
+call goto2(2)|
+
+drop procedure goto2|
+delete from t1|
+
+# Check label visibility for some more cases. We don't call these.
+create procedure goto3()
+begin
+ label L1;
+ begin
+ end;
+ goto L1;
+end|
+drop procedure goto3|
+
+create procedure goto4()
+begin
+ begin
+ label lab1;
+ begin
+ goto lab1;
+ end;
+ end;
+end|
+drop procedure goto4|
+
+create procedure goto5()
+begin
+ begin
+ begin
+ goto lab1;
+ end;
+ label lab1;
+ end;
+end|
+drop procedure goto5|
+
+create procedure goto6()
+begin
+ label L1;
+ goto L5;
+ begin
+ label L2;
+ goto L1;
+ goto L5;
+ begin
+ label L3;
+ goto L1;
+ goto L2;
+ goto L3;
+ goto L4;
+ goto L5;
+ end;
+ goto L2;
+ goto L4;
+ label L4;
+ end;
+ label L5;
+ goto L1;
+end|
+drop procedure goto6|
+
+# SELECT with one of more result set sent back to the clinet
+insert into t1 values ("foo", 3), ("bar", 19)|
+insert into t2 values ("x", 9, 4.1), ("y", -1, 19.2), ("z", 3, 2.2)|
+
+create procedure sel1()
+begin
+ select * from t1;
+end|
+
+call sel1()|
+drop procedure sel1|
+
+create procedure sel2()
+begin
+ select * from t1;
+ select * from t2;
+end|
+
+call sel2()|
+drop procedure sel2|
+delete from t1|
+delete from t2|
+
+# SELECT INTO local variables
+create procedure into_test(x char(16), y int)
+begin
+ insert into test.t1 values (x, y);
+ select id,data into x,y from test.t1 limit 1;
+ insert into test.t1 values (concat(x, "2"), y+2);
+end|
+
+call into_test("into", 100)|
+select * from t1|
+delete from t1|
+drop procedure into_test|
+
+
+# SELECT INTO with a mix of local and global variables
+create procedure into_test2(x char(16), y int)
+begin
+ insert into test.t1 values (x, y);
+ select id,data into x,@z from test.t1 limit 1;
+ insert into test.t1 values (concat(x, "2"), y+2);
+end|
+
+call into_test2("into", 100)|
+select id,data,@z from t1|
+delete from t1|
+drop procedure into_test2|
+
+
+# SELECT * INTO ... (bug test)
+create procedure into_test3()
+begin
+ declare x char(16);
+ declare y int;
+
+ select * into x,y from test.t1 limit 1;
+ insert into test.t2 values (x, y, 0.0);
+end|
+
+insert into t1 values ("into3", 19)|
+# Two call needed for bug test
+call into_test3()|
+call into_test3()|
+select * from t2|
+delete from t1|
+delete from t2|
+drop procedure into_test3|
+
+
+# SELECT INTO with no data is a warning ("no data", which we will
+# not see normally). When not caught, execution proceeds.
+create procedure into_test4()
+begin
+ declare x int;
+
+ select data into x from test.t1 limit 1;
+ insert into test.t3 values ("into4", x);
+end|
+
+delete from t1|
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 ( s char(16), d int)|
+call into_test4()|
+select * from t3|
+insert into t1 values ("i4", 77)|
+call into_test4()|
+select * from t3|
+delete from t1|
+drop table t3|
+drop procedure into_test4|
+
+
+# These two (and the two procedures above) caused an assert() to fail in
+# sql_base.cc:lock_tables() at some point.
+
+create procedure into_outfile(x char(16), y int)
+begin
+ insert into test.t1 values (x, y);
+ select * into outfile "/tmp/spout" from test.t1;
+ insert into test.t1 values (concat(x, "2"), y+2);
+end|
+
+system rm -f /tmp/spout|
+call into_outfile("ofile", 1)|
+system rm -f /tmp/spout|
+delete from t1|
+drop procedure into_outfile|
+
+create procedure into_dumpfile(x char(16), y int)
+begin
+ insert into test.t1 values (x, y);
+ select * into dumpfile "/tmp/spdump" from test.t1 limit 1;
+ insert into test.t1 values (concat(x, "2"), y+2);
+end|
+
+system rm -f /tmp/spdump|
+call into_dumpfile("dfile", 1)|
+system rm -f /tmp/spdump|
+delete from t1|
+drop procedure into_dumpfile|
+
+
+create procedure create_select(x char(16), y int)
+begin
+ insert into test.t1 values (x, y);
+ create table test.t3 select * from test.t1;
+ insert into test.t3 values (concat(x, "2"), y+2);
+end|
+
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+call create_select("cs", 90)|
+select * from t1, t3|
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+delete from t1|
+drop procedure create_select|
+
+
+# A minimal, constant FUNCTION.
+create function e() returns double
+ return 2.7182818284590452354|
+
+set @e = e()|
+select e(), @e|
+
+# A minimal function with one argument
+create function inc(i int) returns int
+ return i+1|
+
+select inc(1), inc(99), inc(-71)|
+
+# A minimal function with two arguments
+create function mul(x int, y int) returns int
+ return x*y|
+
+select mul(1,1), mul(3,5), mul(4711, 666)|
+
+# A minimal string function
+create function append(s1 char(8), s2 char(8)) returns char(16)
+ return concat(s1, s2)|
+
+select append("foo", "bar")|
+
+# A function with flow control
+create function fac(n int unsigned) returns bigint unsigned
+begin
+ declare f bigint unsigned default 1;
+
+ while n > 1 do
+ set f = f * n;
+ set n = n - 1;
+ end while;
+ return f;
+end|
+
+select fac(1), fac(2), fac(5), fac(10)|
+
+# Nested calls
+create function fun(d double, i int, u int unsigned) returns double
+ return mul(inc(i), fac(u)) / e()|
+
+select fun(2.3, 3, 5)|
+
+
+# Various function calls in differen statements
+
+insert into t2 values (append("xxx", "yyy"), mul(4,3), e())|
+insert into t2 values (append("a", "b"), mul(2,mul(3,4)), fun(1.7, 4, 6))|
+
+# These don't work yet.
+select * from t2 where s = append("a", "b")|
+select * from t2 where i = mul(4,3) or i = mul(mul(3,4),2)|
+select * from t2 where d = e()|
+select * from t2|
+delete from t2|
+
+drop function e|
+drop function inc|
+drop function mul|
+drop function append|
+drop function fun|
+
+
+#
+# CONDITIONs and HANDLERs
+#
+
+create procedure hndlr1(val int)
+begin
+ declare x int default 0;
+ declare foo condition for 1146;
+ declare bar condition for sqlstate '42S98'; # Just for testing syntax
+ declare zip condition for sqlstate value '42S99'; # Just for testing syntax
+ declare continue handler for foo set x = 1;
+
+ insert into test.t666 values ("hndlr1", val); # Non-existing table
+ if (x) then
+ insert into test.t1 values ("hndlr1", val); # This instead then
+ end if;
+end|
+
+call hndlr1(42)|
+select * from t1|
+delete from t1|
+drop procedure hndlr1|
+
+create procedure hndlr2(val int)
+begin
+ declare x int default 0;
+
+ begin
+ declare exit handler for sqlstate '42S02' set x = 1;
+
+ insert into test.t666 values ("hndlr2", val); # Non-existing table
+ end;
+
+ insert into test.t1 values ("hndlr2", x);
+end|
+
+call hndlr2(42)|
+select * from t1|
+delete from t1|
+drop procedure hndlr2|
+
+
+create procedure hndlr3(val int)
+begin
+ declare x int default 0;
+ declare continue handler for sqlexception # Any error
+ begin
+ declare z int;
+
+ set z = 2 * val;
+ set x = 1;
+ end;
+
+ if val < 10 then
+ begin
+ declare y int;
+
+ set y = val + 10;
+ insert into test.t666 values ("hndlr3", y); # Non-existing table
+ if x then
+ insert into test.t1 values ("hndlr3", y);
+ end if;
+ end;
+ end if;
+end|
+
+call hndlr3(3)|
+select * from t1|
+delete from t1|
+drop procedure hndlr3|
+
+
+# Variables might be uninitialized when using handlers
+# (Otherwise the compiler can detect if a variable is not set, but
+# not in this case.)
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 ( id char(16), data int )|
+
+create procedure hndlr4()
+begin
+ declare x int default 0;
+ declare val int; # No default
+ declare continue handler for sqlstate '02000' set x=1;
+
+ select data into val from test.t3 where id='z' limit 1; # No hits
+
+ insert into test.t3 values ('z', val);
+end|
+
+call hndlr4()|
+select * from t3|
+drop table t3|
+drop procedure hndlr4|
+
+
+#
+# Cursors
+#
+create procedure cur1()
+begin
+ declare a char(16);
+ declare b int;
+ declare c double;
+ declare done int default 0;
+ declare c cursor for select * from test.t2;
+ declare continue handler for sqlstate '02000' set done = 1;
+
+ open c;
+ repeat
+ fetch c into a, b, c;
+ if not done then
+ insert into test.t1 values (a, b+c);
+ end if;
+ until done end repeat;
+ close c;
+end|
+
+insert into t2 values ("foo", 42, -1.9), ("bar", 3, 12.1), ("zap", 666, -3.14)|
+call cur1()|
+select * from t1|
+drop procedure cur1|
+
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 ( s char(16), i int )|
+
+create procedure cur2()
+begin
+ declare done int default 0;
+ declare c1 cursor for select id,data from test.t1;
+ declare c2 cursor for select i from test.t2;
+ declare continue handler for sqlstate '02000' set done = 1;
+
+ open c1;
+ open c2;
+ repeat
+ begin
+ declare a char(16);
+ declare b,c int;
+
+ fetch c1 into a, b;
+ fetch c2 into c;
+ if not done then
+ if b < c then
+ insert into test.t3 values (a, b);
+ else
+ insert into test.t3 values (a, c);
+ end if;
+ end if;
+ end;
+ until done end repeat;
+ close c1;
+ close c2;
+end|
+
+call cur2()|
+select * from t3|
+delete from t1|
+delete from t2|
+drop table t3|
+drop procedure cur2|
+
+
+# The few characteristics we parse
+create procedure chistics()
+ language sql
+ not deterministic
+ sql security definer
+ comment 'Characteristics procedure test'
+ insert into t1 values ("chistics", 1)|
+
+# Call it, just to make sure.
+call chistics()|
+select * from t1|
+delete from t1|
+alter procedure chistics sql security invoker name chistics2|
+show create procedure chistics2|
+drop procedure chistics2|
+
+create function chistics() returns int
+ language sql
+ deterministic
+ sql security invoker
+ comment 'Characteristics procedure test'
+ return 42|
+
+# Call it, just to make sure.
+select chistics()|
+alter function chistics name chistics2 comment 'Characteristics function test'|
+show create function chistics2|
+drop function chistics2|
+
+
+# Check mode settings
+insert into t1 values ("foo", 1), ("bar", 2), ("zip", 3)|
+
+set @@sql_mode = 'ANSI'|
+delimiter $|
+create procedure modes(out c1 int, out c2 int)
+begin
+ declare done int default 0;
+ declare x int;
+ declare c cursor for select data from t1;
+ declare continue handler for sqlstate '02000' set done = 1;
+
+ select 1 || 2 into c1;
+ set c2 = 0;
+ open c;
+ repeat
+ fetch c into x;
+ if not done then
+ set c2 = c2 + 1;
+ end if;
+ until done end repeat;
+ close c;
+end$
+delimiter |$
+set @@sql_mode = ''|
+
+set sql_select_limit = 1|
+call modes(@c1, @c2)|
+set sql_select_limit = default|
+
+select @c1, @c2|
+delete from t1|
+drop procedure modes|
+
+
+# Check that dropping a database without routines works.
+# (Dropping with routines is tested in sp-security.test)
+# First an empty db.
+create database sp_db1|
+drop database sp_db1|
+
+# Again, with a table.
+create database sp_db2|
+use sp_db2|
+# Just put something in here...
+create table t3 ( s char(4), t int )|
+insert into t3 values ("abcd", 42), ("dcba", 666)|
+use test|
+drop database sp_db2|
+
+# And yet again, with just a procedure.
+create database sp_db3|
+use sp_db3|
+create procedure dummy(out x int)
+ set x = 42|
+use test|
+drop database sp_db3|
+# Check that it's gone
+select type,db,name from mysql.proc where db = 'sp_db3'|
+
+
+# ROW_COUNT() function after a CALL
+# We test the other cases here too, although it's not strictly SP specific
+create procedure rc()
+begin
+ delete from t1;
+ insert into t1 values ("a", 1), ("b", 2), ("c", 3);
+end|
+
+call rc()|
+select row_count()|
+update t1 set data=42 where id = "b";
+select row_count()|
+delete from t1|
+select row_count()|
+delete from t1|
+select row_count()|
+select * from t1|
+select row_count()|
+drop procedure rc|
+
+
+#
+# Test cases for old bugs
+#
+
+#
+# BUG#822
+#
+create procedure bug822(a_id char(16), a_data int)
+begin
+ declare n int;
+ select count(*) into n from t1 where id = a_id and data = a_data;
+ if n = 0 then
+ insert into t1 (id, data) values (a_id, a_data);
+ end if;
+end|
+
+call bug822('foo', 42)|
+call bug822('foo', 42)|
+call bug822('bar', 666)|
+select * from t1|
+delete from t1|
+drop procedure bug822|
+
+#
+# BUG#1495
+#
+create procedure bug1495()
+begin
+ declare x int;
+
+ select data into x from t1 order by id limit 1;
+ if x > 10 then
+ insert into t1 values ("less", x-10);
+ else
+ insert into t1 values ("more", x+10);
+ end if;
+end|
+
+insert into t1 values ('foo', 12)|
+call bug1495()|
+delete from t1 where id='foo'|
+insert into t1 values ('bar', 7)|
+call bug1495()|
+delete from t1 where id='bar'|
+select * from t1|
+delete from t1|
+drop procedure bug1495|
+
+#
+# BUG#1547
+#
+create procedure bug1547(s char(16))
+begin
+ declare x int;
+
+ select data into x from t1 where s = id limit 1;
+ if x > 10 then
+ insert into t1 values ("less", x-10);
+ else
+ insert into t1 values ("more", x+10);
+ end if;
+end|
+
+insert into t1 values ("foo", 12), ("bar", 7)|
+call bug1547("foo")|
+call bug1547("bar")|
+select * from t1|
+delete from t1|
+drop procedure bug1547|
+
+#
+# BUG#1656
+#
+--disable_warnings
+drop table if exists t70|
+--enable_warnings
+create table t70 (s1 int,s2 int)|
+insert into t70 values (1,2)|
+
+create procedure bug1656(out p1 int, out p2 int)
+ select * into p1, p1 from t70|
+
+call bug1656(@1, @2)|
+select @1, @2|
+drop table t70|
+drop procedure bug1656|
+
+#
+# BUG#1862
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3(a int)|
+
+create procedure bug1862()
+begin
+ insert into t3 values(2);
+ flush tables;
+end|
+
+call bug1862()|
+# the second call caused a segmentation
+call bug1862()|
+select * from t3|
+drop table t3|
+drop procedure bug1862|
+
+#
+# BUG#1874
+#
+create procedure bug1874()
+begin
+ declare x int;
+ declare y double;
+ select max(data) into x from t1;
+ insert into t2 values ("max", x, 0);
+ select min(data) into x from t1;
+ insert into t2 values ("min", x, 0);
+ select sum(data) into x from t1;
+ insert into t2 values ("sum", x, 0);
+ select avg(data) into y from t1;
+ insert into t2 values ("avg", 0, y);
+end|
+
+insert into t1 (data) values (3), (1), (5), (9), (4)|
+call bug1874()|
+select * from t2|
+delete from t1|
+delete from t2|
+drop procedure bug1874|
+
+#
+# BUG#2260
+#
+create procedure bug2260()
+begin
+ declare v1 int;
+ declare c1 cursor for select data from t1;
+ declare continue handler for not found set @x2 = 1;
+
+ open c1;
+ fetch c1 into v1;
+ set @x2 = 2;
+ close c1;
+end|
+
+call bug2260()|
+select @x2|
+drop procedure bug2260|
+
+#
+# BUG#2267
+#
+create procedure bug2267_1()
+begin
+ show procedure status;
+end|
+
+create procedure bug2267_2()
+begin
+ show function status;
+end|
+
+create procedure bug2267_3()
+begin
+ show create procedure bug2267_1;
+end|
+
+create procedure bug2267_4()
+begin
+ show create function fac;
+end|
+
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+call bug2267_1()|
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+call bug2267_2()|
+call bug2267_3()|
+call bug2267_4()|
+
+drop procedure bug2267_1|
+drop procedure bug2267_2|
+drop procedure bug2267_3|
+drop procedure bug2267_4|
+
+#
+# BUG#2227
+#
+create procedure bug2227(x int)
+begin
+ declare y float default 2.6;
+ declare z char(16) default "zzz";
+
+ select 1.3, x, y, 42, z;
+end|
+
+call bug2227(9)|
+drop procedure bug2227|
+
+#
+# BUG#2614
+#
+create procedure bug2614()
+begin
+ drop table if exists t3;
+ create table t3 (id int default '0' not null);
+ insert into t3 select 12;
+ insert into t3 select * from t3;
+end|
+
+--disable_warnings
+call bug2614()|
+--enable_warnings
+call bug2614()|
+drop table t3|
+drop procedure bug2614|
+
+#
+# BUG#2674
+#
+create function bug2674 () returns int
+ return @@sort_buffer_size|
+
+set @osbs = @@sort_buffer_size|
+set @@sort_buffer_size = 262000|
+select bug2674()|
+drop function bug2674|
+set @@sort_buffer_size = @osbs|
+
+#
+# BUG#3259
+#
+create procedure bug3259_1 () begin end|
+create procedure BUG3259_2 () begin end|
+create procedure Bug3259_3 () begin end|
+
+call BUG3259_1()|
+call BUG3259_1()|
+call bug3259_2()|
+call Bug3259_2()|
+call bug3259_3()|
+call bUG3259_3()|
+
+drop procedure bUg3259_1|
+drop procedure BuG3259_2|
+drop procedure BUG3259_3|
+
+#
+# BUG##2772
+#
+create function bug2772() returns char(10) character set latin2
+ return 'a'|
+
+select bug2772()|
+drop function bug2772|
+
+#
+# BUG#2776
+#
+create procedure bug2776_1(out x int)
+begin
+ declare v int;
+
+ set v = default;
+ set x = v;
+end|
+
+create procedure bug2776_2(out x int)
+begin
+ declare v int default 42;
+
+ set v = default;
+ set x = v;
+end|
+
+set @x = 1|
+call bug2776_1(@x)|
+select @x|
+call bug2776_2(@x)|
+select @x|
+drop procedure bug2776_1|
+drop procedure bug2776_2|
+
+#
+# BUG#2780
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 (s1 smallint)|
+
+insert into t3 values (123456789012)|
+
+create procedure bug2780()
+begin
+ declare exit handler for sqlwarning set @x = 1;
+
+ set @x = 0;
+ insert into t3 values (123456789012);
+ insert into t3 values (0);
+end|
+
+call bug2780()|
+select @x|
+select * from t3|
+
+drop procedure bug2780|
+drop table t3|
+
+#
+# BUG#1863
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 (content varchar(10) )|
+insert into t3 values ("test1")|
+insert into t3 values ("test2")|
+
+--disable_warnings
+drop table if exists t4|
+--enable_warnings
+create table t4 (f1 int, rc int, t3 int)|
+
+create procedure bug1863(in1 int)
+begin
+
+ declare ind int default 0;
+ declare t1 int;
+ declare t2 int;
+ declare t3 int;
+
+ declare rc int default 0;
+ declare continue handler for 1065 set rc = 1;
+
+ drop table if exists temp_t1;
+ create temporary table temp_t1 (
+ f1 int auto_increment, f2 varchar(20), primary key (f1)
+ );
+
+ insert into temp_t1 (f2) select content from t3;
+
+ select f2 into t3 from temp_t1 where f1 = 10;
+
+ if (rc) then
+ insert into t4 values (1, rc, t3);
+ end if;
+
+ insert into t4 values (2, rc, t3);
+
+end|
+
+call bug1863(10)|
+call bug1863(10)|
+select * from t4|
+
+drop procedure bug1863|
+drop table t3, t4|
+
+#
+# BUG#2656
+#
+--disable_warnings
+drop table if exists t3, t4|
+--enable_warnings
+
+create table t3 (
+ OrderID int not null,
+ MarketID int,
+ primary key (OrderID)
+)|
+
+create table t4 (
+ MarketID int not null,
+ Market varchar(60),
+ Status char(1),
+ primary key (MarketID)
+)|
+
+insert t3 (OrderID,MarketID) values (1,1)|
+insert t3 (OrderID,MarketID) values (2,2)|
+insert t4 (MarketID,Market,Status) values (1,"MarketID One","A")|
+insert t4 (MarketID,Market,Status) values (2,"MarketID Two","A")|
+
+create procedure bug2656_1()
+begin
+ select
+ m.Market
+ from t4 m JOIN t3 o
+ ON o.MarketID != 1 and o.MarketID = m.MarketID;
+end |
+
+create procedure bug2656_2()
+begin
+ select
+ m.Market
+ from
+ t4 m, t3 o
+ where
+ m.MarketID != 1 and m.MarketID = o.MarketID;
+
+end |
+
+call bug2656_1()|
+call bug2656_1()|
+call bug2656_2()|
+call bug2656_2()|
+drop procedure bug2656_1|
+drop procedure bug2656_2|
+drop table t3, t4|
+
+
+#
+# BUG#3426
+#
+create procedure bug3426(in_time int unsigned, out x int)
+begin
+ if in_time is null then
+ set @stamped_time=10;
+ set x=1;
+ else
+ set @stamped_time=in_time;
+ set x=2;
+ end if;
+end|
+
+call bug3426(1000, @i)|
+select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time|
+call bug3426(NULL, @i)|
+select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time|
+# Clear SP cache
+alter procedure bug3426 sql security invoker|
+call bug3426(NULL, @i)|
+select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time|
+call bug3426(1000, @i)|
+select @i, from_unixtime(@stamped_time, '%d-%m-%Y %h:%i:%s') as time|
+
+drop procedure bug3426|
+
+#
+# BUG#3448
+#
+--disable_warnings
+drop table if exists t3, t4|
+
+create table t3 (
+ a int primary key,
+ ach char(1)
+) engine = innodb|
+
+create table t4 (
+ b int primary key ,
+ bch char(1)
+) engine = innodb|
+--enable_warnings
+
+insert into t3 values (1 , 'aCh1' ) , ('2' , 'aCh2')|
+insert into t4 values (1 , 'bCh1' )|
+
+create procedure bug3448()
+ select * from t3 inner join t4 on t3.a = t4.b|
+
+select * from t3 inner join t4 on t3.a = t4.b|
+call bug3448()|
+call bug3448()|
+
+drop procedure bug3448|
+drop table t3, t4|
+
+
+#
+# BUG#3734
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 (
+ id int unsigned auto_increment not null primary key,
+ title VARCHAR(200),
+ body text,
+ fulltext (title,body)
+)|
+
+insert into t3 (title,body) values
+ ('MySQL Tutorial','DBMS stands for DataBase ...'),
+ ('How To Use MySQL Well','After you went through a ...'),
+ ('Optimizing MySQL','In this tutorial we will show ...'),
+ ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
+ ('MySQL vs. YourSQL','In the following database comparison ...'),
+ ('MySQL Security','When configured properly, MySQL ...')|
+
+create procedure bug3734 (param1 varchar(100))
+ select * from t3 where match (title,body) against (param1)|
+
+call bug3734('database')|
+call bug3734('Security')|
+
+drop procedure bug3734|
+drop table t3|
+
+#
+# BUG#3863
+#
+create procedure bug3863()
+begin
+ set @a = 0;
+ while @a < 5 do
+ set @a = @a + 1;
+ end while;
+end|
+
+call bug3863()|
+select @a|
+call bug3863()|
+select @a|
+
+drop procedure bug3863|
+
+#
+# BUG#2460
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 (
+ id int(10) unsigned not null default 0,
+ rid int(10) unsigned not null default 0,
+ msg text not null,
+ primary key (id),
+ unique key rid (rid, id)
+)|
+
+create procedure bug2460_1(in v int)
+begin
+ ( select n0.id from t3 as n0 where n0.id = v )
+ union
+ ( select n0.id from t3 as n0, t3 as n1
+ where n0.id = n1.rid and n1.id = v )
+ union
+ ( select n0.id from t3 as n0, t3 as n1, t3 as n2
+ where n0.id = n1.rid and n1.id = n2.rid and n2.id = v );
+end|
+
+call bug2460_1(2)|
+call bug2460_1(2)|
+insert into t3 values (1, 1, 'foo'), (2, 1, 'bar'), (3, 1, 'zip zap')|
+call bug2460_1(2)|
+call bug2460_1(2)|
+
+create procedure bug2460_2()
+begin
+ drop table if exists t3;
+ create table t3 (s1 int);
+ insert into t3 select 1 union select 1;
+end|
+
+call bug2460_2()|
+call bug2460_2()|
+select * from t3|
+
+drop procedure bug2460_1|
+drop procedure bug2460_2|
+drop table t3|
+
+
+#
+# BUG#2564
+#
+set @@sql_mode = ''|
+create procedure bug2564_1()
+ comment 'Joe''s procedure'
+ insert into `t1` values ("foo", 1)|
+
+set @@sql_mode = 'ANSI_QUOTES'|
+create procedure bug2564_2()
+ insert into "t1" values ('foo', 1)|
+
+delimiter $|
+set @@sql_mode = ''$
+create function bug2564_3(x int, y int) returns int
+ return x || y$
+
+set @@sql_mode = 'ANSI'$
+create function bug2564_4(x int, y int) returns int
+ return x || y$
+delimiter |$
+
+set @@sql_mode = ''|
+show create procedure bug2564_1|
+show create procedure bug2564_2|
+show create function bug2564_3|
+show create function bug2564_4|
+
+drop procedure bug2564_1|
+drop procedure bug2564_2|
+drop function bug2564_3|
+drop function bug2564_4|
+
+#
+# BUG#3132
+#
+create function bug3132(s char(20)) returns char(50)
+ return concat('Hello, ', s, '!')|
+
+select bug3132('Bob') union all select bug3132('Judy')|
+drop function bug3132|
+
+#
+# BUG#3843
+#
+create procedure bug3843()
+ analyze table t1|
+
+# Testing for packets out of order
+call bug3843()|
+call bug3843()|
+select 1+2|
+
+drop procedure bug3843|
+
+#
+# BUG#3368
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 ( s1 char(10) )|
+insert into t3 values ('a'), ('b')|
+
+create procedure bug3368(v char(10))
+begin
+ select group_concat(v) from t3;
+end|
+
+call bug3368('x')|
+call bug3368('yz')|
+drop procedure bug3368|
+drop table t3|
+
+#
+# BUG#4579
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 (f1 int, f2 int);
+insert into t3 values (1,1);
+
+create procedure bug4579_1 ()
+begin
+ declare sf1 int;
+
+ select f1 into sf1 from t3 where f1=1 and f2=1;
+ update t3 set f2 = f2 + 1 where f1=1 and f2=1;
+ call bug4579_2();
+end|
+
+create procedure bug4579_2 ()
+begin
+end|
+
+call bug4579_1()|
+call bug4579_1()|
+call bug4579_1()|
+
+drop procedure bug4579_1|
+drop procedure bug4579_2|
+drop table t3|
+
+
+#
+# BUG#4726
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+
+create table t3 (f1 int, f2 int, f3 int)|
+insert into t3 values (1,1,1)|
+
+create procedure bug4726()
+begin
+ declare tmp_o_id INT;
+ declare tmp_d_id INT default 1;
+
+ while tmp_d_id <= 2 do
+ begin
+ select f1 into tmp_o_id from t3 where f2=1 and f3=1;
+ set tmp_d_id = tmp_d_id + 1;
+ end;
+ end while;
+end|
+
+call bug4726()|
+call bug4726()|
+call bug4726()|
+
+drop procedure bug4726|
+drop table t3|
+
+#
+# BUG#4318
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+
+create table t3 (s1 int)|
+insert into t3 values (3), (4)|
+
+create procedure bug4318()
+ handler t3 read next|
+
+handler t3 open|
+# Expect no results, as tables are closed, but there shouldn't be any errors
+call bug4318()|
+call bug4318()|
+handler t3 close|
+
+drop procedure bug4318|
+drop table t3|
+
+#
+# BUG#4902: Stored procedure with SHOW WARNINGS leads to packet error
+#
+# Added tests for most other show commands we could find too.
+# (Skipping those already tested, and the ones depending on optional handlers.)
+#
+# Note: This will return a large number of results of different formats,
+# which makes it impossible to filter with --replace_column.
+# It's possible that some of these are not deterministic across
+# platforms. If so, just remove the offending command.
+#
+create procedure bug4902()
+begin
+ show charset like 'foo';
+ show collation like 'foo';
+ show column types;
+ show create table t1;
+ show create database test;
+ show databases like 'foo';
+ show errors;
+ show columns from t1;
+ show grants for 'root'@'localhost';
+ show keys from t1;
+ show open tables like 'foo';
+ show privileges;
+ show status like 'foo';
+ show tables like 'foo';
+ show variables like 'foo';
+ show warnings;
+end|
+#show binlog events;
+#show storage engines;
+#show master status;
+#show slave hosts;
+#show slave status;
+
+call bug4902()|
+call bug4902()|
+
+drop procedure bug4902|
+
+# We need separate SP for SHOW PROCESSLIST since we want use replace_column
+create procedure bug4902_2()
+begin
+ show processlist;
+end|
+--replace_column 1 # 6 #
+call bug4902_2()|
+--replace_column 1 # 6 #
+call bug4902_2()|
+drop procedure bug4902_2|
+
+#
+# BUG#4904
+#
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+
+create procedure bug4904()
+begin
+ declare continue handler for sqlstate 'HY000' begin end;
+
+ create table t2 as select * from t;
+end|
+
+call bug4904()|
+
+drop procedure bug4904|
+
+#
+# BUG#336
+#
+create procedure bug336(out y int)
+begin
+ declare x int;
+ set x = (select sum(t.data) from test.t1 t);
+ set y = x;
+end|
+
+insert into t1 values ("a", 2), ("b", 3)|
+call bug336(@y)|
+select @y|
+delete from t1|
+drop procedure bug336|
+
+#
+# BUG#3157
+#
+create procedure bug3157()
+begin
+ if exists(select * from t1) then
+ set @n= @n + 1;
+ end if;
+ if (select count(*) from t1) then
+ set @n= @n + 1;
+ end if;
+end|
+
+set @n = 0|
+insert into t1 values ("a", 1)|
+call bug3157()|
+select @n|
+delete from t1|
+drop procedure bug3157|
+
+#
+# BUG#5251: mysql changes creation time of a procedure/function when altering
+#
+create procedure bug5251()
+begin
+end|
+
+select created into @c1 from mysql.proc
+ where db='test' and name='bug5251'|
+--sleep 2
+alter procedure bug5251 comment 'foobar'|
+select count(*) from mysql.proc
+ where db='test' and name='bug5251' and created = @c1|
+
+drop procedure bug5251|
+
+#
+# BUG#5279: Stored procedure packets out of order if CHECKSUM TABLE
+#
+create procedure bug5251()
+ checksum table t1|
+
+call bug5251()|
+call bug5251()|
+drop procedure bug5251|
+
+#
+# BUG#5287: Stored procedure crash if leave outside loop
+#
+create procedure bug5287(param1 int)
+label1:
+ begin
+ declare c cursor for select 5;
+
+ loop
+ if param1 >= 0 then
+ leave label1;
+ end if;
+ end loop;
+end|
+call bug5287(1)|
+drop procedure bug5287|
+
+
+#
+# BUG#5307: Stored procedure allows statement after BEGIN ... END
+#
+create procedure bug5307()
+begin
+end; set @x = 3|
+
+call bug5307()|
+select @x|
+drop procedure bug5307|
+
+
+#
+# Some "real" examples
+#
+
+# fac
+
+--disable_warnings
+drop table if exists fac|
+--enable_warnings
+create table fac (n int unsigned not null primary key, f bigint unsigned)|
+
+create procedure ifac(n int unsigned)
+begin
+ declare i int unsigned default 1;
+
+ if n > 20 then
+ set n = 20; # bigint overflow otherwise
+ end if;
+ while i <= n do
+ begin
+ insert into test.fac values (i, fac(i));
+ set i = i + 1;
+ end;
+ end while;
+end|
+
+call ifac(20)|
+select * from fac|
+drop table fac|
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show function status like '%f%'|
+drop procedure ifac|
+drop function fac|
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show function status like '%f%'|
+
+
+# primes
+
+--disable_warnings
+drop table if exists primes|
+--enable_warnings
+
+create table primes (
+ i int unsigned not null primary key,
+ p bigint unsigned not null
+)|
+
+insert into primes values
+ ( 0, 3), ( 1, 5), ( 2, 7), ( 3, 11), ( 4, 13),
+ ( 5, 17), ( 6, 19), ( 7, 23), ( 8, 29), ( 9, 31),
+ (10, 37), (11, 41), (12, 43), (13, 47), (14, 53),
+ (15, 59), (16, 61), (17, 67), (18, 71), (19, 73),
+ (20, 79), (21, 83), (22, 89), (23, 97), (24, 101),
+ (25, 103), (26, 107), (27, 109), (28, 113), (29, 127),
+ (30, 131), (31, 137), (32, 139), (33, 149), (34, 151),
+ (35, 157), (36, 163), (37, 167), (38, 173), (39, 179),
+ (40, 181), (41, 191), (42, 193), (43, 197), (44, 199)|
+
+create procedure opp(n bigint unsigned, out pp bool)
+begin
+ declare r double;
+ declare b, s bigint unsigned default 0;
+
+ set r = sqrt(n);
+
+ again:
+ loop
+ if s = 45 then
+ set b = b+200, s = 0;
+ else
+ begin
+ declare p bigint unsigned;
+
+ select t.p into p from test.primes t where t.i = s;
+ if b+p > r then
+ set pp = 1;
+ leave again;
+ end if;
+ if mod(n, b+p) = 0 then
+ set pp = 0;
+ leave again;
+ end if;
+ set s = s+1;
+ end;
+ end if;
+ end loop;
+end|
+
+create procedure ip(m int unsigned)
+begin
+ declare p bigint unsigned;
+ declare i int unsigned;
+
+ set i=45, p=201;
+
+ while i < m do
+ begin
+ declare pp bool default 0;
+
+ call opp(p, pp);
+ if pp then
+ insert into test.primes values (i, p);
+ set i = i+1;
+ end if;
+ set p = p+2;
+ end;
+ end while;
+end|
+show create procedure opp|
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show procedure status like '%p%'|
+
+# This isn't the fastest way in the world to compute prime numbers, so
+# don't be too ambitious. ;-)
+call ip(200)|
+# We don't want to select the entire table here, just pick a few
+# examples.
+# The expected result is:
+# i p
+# --- ----
+# 45 211
+# 100 557
+# 199 1229
+select * from primes where i=45 or i=100 or i=199|
+drop table primes|
+drop procedure opp|
+drop procedure ip|
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show procedure status like '%p%'|
+
+
+# Fibonacci, for recursion test. (Yet Another Numerical series :)
+
+--disable_warnings
+drop table if exists fib|
+--enable_warnings
+create table fib ( f bigint unsigned not null )|
+
+insert into fib values (1), (1)|
+
+# We deliberately do it the awkward way, fetching the last two
+# values from the table, in order to exercise various statements
+# and table accesses at each turn.
+create procedure fib(n int unsigned)
+begin
+ if n > 0 then
+ begin
+ declare x, y bigint unsigned;
+ declare c cursor for select f from fib order by f desc limit 2;
+
+ open c;
+ fetch c into y;
+ fetch c into x;
+ close c;
+ insert into fib values (x+y);
+ call fib(n-1);
+ end;
+ end if;
+end|
+
+call fib(20)|
+
+select * from fib order by f asc|
+drop table fib|
+drop procedure fib|
+
+
+#
+# Comment & suid
+#
+
+create procedure bar(x char(16), y int)
+ comment "111111111111" sql security invoker
+ insert into test.t1 values (x, y)|
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show procedure status like 'bar'|
+alter procedure bar name bar2 comment "2222222222" sql security definer|
+alter procedure bar2 name bar comment "3333333333"|
+alter procedure bar|
+show create procedure bar|
+--replace_column 5 '0000-00-00 00:00:00' 6 '0000-00-00 00:00:00'
+show procedure status like 'bar'|
+drop procedure bar|
+delimiter ;|
+drop table t1;
+drop table t2;
+
+#
+# rexecution
+#
+create procedure p1 () select (select s1 from t1) from t1;
+create table t1 (s1 int);
+call p1();
+insert into t1 values (1);
+call p1();
+drop procedure p1;
+drop table t1;
+
+#
+# backticks
+#
+create function `foo` () returns int return 5;
+select `foo` ();
+drop function `foo`;
diff --git a/mysql-test/t/sql_mode.test b/mysql-test/t/sql_mode.test
index 95e83b4b9e2..c7b6510db9d 100644
--- a/mysql-test/t/sql_mode.test
+++ b/mysql-test/t/sql_mode.test
@@ -28,3 +28,98 @@ set sql_mode="postgresql,oracle,mssql,db2,maxdb";
select @@sql_mode;
show create table t1;
drop table t1;
+
+#
+# test for
+# WL 1941 "NO_C_ESCAPES sql_mode"
+#
+# an sql_mode to disable \n, \r, \b, etc escapes in string literals. actually, to
+# disable special meaning of backslash completely. It's not in the SQL standard
+# and it causes some R/3 tests to fail.
+#
+
+SET @OLD_SQL_MODE=@@SQL_MODE, @@SQL_MODE='';
+show local variables like 'SQL_MODE';
+
+CREATE TABLE t1 (p int not null auto_increment, a varchar(20), primary key(p));
+INSERT t1 (a) VALUES
+('\\'),
+('\n'),
+('\b'),
+('\r'),
+('\t'),
+('\x'),
+('\a'),
+('\aa'),
+('\\a'),
+('\\aa'),
+('_'),
+('\_'),
+('\\_'),
+('\\\_'),
+('\\\\_'),
+('%'),
+('\%'),
+('\\%'),
+('\\\%'),
+('\\\\%')
+;
+
+SELECT p, hex(a) FROM t1;
+
+delete from t1 where a in ('\n','\r','\t', '\b');
+
+select
+ masks.p,
+ masks.a as mask,
+ examples.a as example
+from
+ t1 as masks
+ left join t1 as examples on examples.a LIKE masks.a
+order by masks.p, example;
+
+DROP TABLE t1;
+
+SET @@SQL_MODE='NO_BACKSLASH_ESCAPES';
+show local variables like 'SQL_MODE';
+
+CREATE TABLE t1 (p int not null auto_increment, a varchar(20), primary key(p));
+INSERT t1 (a) VALUES
+('\\'),
+('\n'),
+('\b'),
+('\r'),
+('\t'),
+('\x'),
+('\a'),
+('\aa'),
+('\\a'),
+('\\aa'),
+('_'),
+('\_'),
+('\\_'),
+('\\\_'),
+('\\\\_'),
+('%'),
+('\%'),
+('\\%'),
+('\\\%'),
+('\\\\%')
+;
+
+SELECT p, hex(a) FROM t1;
+
+delete from t1 where a in ('\n','\r','\t', '\b');
+
+select
+ masks.p,
+ masks.a as mask,
+ examples.a as example
+from
+ t1 as masks
+ left join t1 as examples on examples.a LIKE masks.a
+order by masks.p, example;
+
+DROP TABLE t1;
+
+SET @@SQL_MODE=@OLD_SQL_MODE;
diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test
index 7fcd0565ae7..3d10b88da5c 100644
--- a/mysql-test/t/subselect.test
+++ b/mysql-test/t/subselect.test
@@ -352,7 +352,9 @@ INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
select * from t1;
INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2;
select * from t1;
+# After this, only data based on old t1 records should have been added.
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
+select * from t1;
-- error 1054
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
@@ -507,6 +509,9 @@ select ROW(1, 1, 'a') IN (select b,a,c from t1 where c='b' or c='a');
select ROW(1, 1, 'a') IN (select b,a,c from t1 limit 2);
drop table t1;
+#
+# DO & SET
+#
create table t1 (a int);
insert into t1 values (1);
do @a:=(SELECT a from t1);
diff --git a/mysql-test/t/sum_distinct.test b/mysql-test/t/sum_distinct.test
new file mode 100644
index 00000000000..efbb21a7b85
--- /dev/null
+++ b/mysql-test/t/sum_distinct.test
@@ -0,0 +1,188 @@
+#
+# Various tests for SUM(DISTINCT ...)
+#
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (
+ id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ gender CHAR(1),
+ name VARCHAR(20)
+);
+
+# According to ANSI SQL, SUM(DISTINCT ...) should return NULL for empty
+# record set
+
+SELECT SUM(DISTINCT LENGTH(name)) s1 FROM t1;
+
+# According to ANSI SQL, SUM(DISTINCT ...) should return NULL for records sets
+# entirely consisting of NULLs
+
+INSERT INTO t1 (gender, name) VALUES (NULL, NULL);
+INSERT INTO t1 (gender, name) VALUES (NULL, NULL);
+INSERT INTO t1 (gender, name) VALUES (NULL, NULL);
+
+SELECT SUM(DISTINCT LENGTH(name)) s1 FROM t1;
+
+
+# Filling table with t1
+
+INSERT INTO t1 (gender, name) VALUES ('F', 'Helen'), ('F', 'Anastasia'),
+('F', 'Katherine'), ('F', 'Margo'), ('F', 'Magdalene'), ('F', 'Mary');
+
+CREATE TABLE t2 SELECT name FROM t1;
+
+SELECT (SELECT SUM(DISTINCT LENGTH(name)) FROM t1) FROM t2;
+
+DROP TABLE t2;
+
+INSERT INTO t1 (gender, name) VALUES ('F', 'Eva'), ('F', 'Sofia'),
+('F', 'Sara'), ('F', 'Golda'), ('F', 'Toba'), ('F', 'Victory'),
+('F', 'Faina'), ('F', 'Miriam'), ('F', 'Beki'), ('F', 'America'),
+('F', 'Susan'), ('F', 'Glory'), ('F', 'Priscilla'), ('F', 'Rosmary'),
+('F', 'Rose'), ('F', 'Margareth'), ('F', 'Elizabeth'), ('F', 'Meredith'),
+('F', 'Julie'), ('F', 'Xenia'), ('F', 'Zena'), ('F', 'Olga'),
+('F', 'Brunhilda'), ('F', 'Nataly'), ('F', 'Lara'), ('F', 'Svetlana'),
+('F', 'Grethem'), ('F', 'Irene');
+
+SELECT
+ SUM(DISTINCT LENGTH(name)) s1,
+ SUM(DISTINCT SUBSTRING(NAME, 1, 3)) s2,
+ SUM(DISTINCT LENGTH(SUBSTRING(name, 1, 4))) s3
+FROM t1;
+
+SELECT
+ SUM(DISTINCT LENGTH(g1.name)) s1,
+ SUM(DISTINCT SUBSTRING(g2.name, 1, 3)) s2,
+ SUM(DISTINCT LENGTH(SUBSTRING(g3.name, 1, 4))) s3
+FROM t1 g1, t1 g2, t1 g3;
+
+SELECT
+ SUM(DISTINCT LENGTH(g1.name)) s1,
+ SUM(DISTINCT SUBSTRING(g2.name, 1, 3)) s2,
+ SUM(DISTINCT LENGTH(SUBSTRING(g3.name, 1, 4))) s3
+FROM t1 g1, t1 g2, t1 g3 GROUP BY LENGTH(SUBSTRING(g3.name, 5, 10));
+
+# here we explicitly request summing through temporary table (so
+# Item_sum_sum_distinct::copy_or_same() is called)
+
+SELECT SQL_BUFFER_RESULT
+ SUM(DISTINCT LENGTH(name)) s1,
+ SUM(DISTINCT SUBSTRING(NAME, 1, 3)) s2,
+ SUM(DISTINCT LENGTH(SUBSTRING(name, 1, 4))) s3
+FROM t1;
+
+SELECT SQL_BUFFER_RESULT
+ SUM(DISTINCT LENGTH(g1.name)) s1,
+ SUM(DISTINCT SUBSTRING(g2.name, 1, 3)) s2,
+ SUM(DISTINCT LENGTH(SUBSTRING(g3.name, 1, 4))) s3
+FROM t1 g1, t1 g2, t1 g3 GROUP BY LENGTH(SUBSTRING(g3.name, 5, 10));
+
+# this test demonstrates that strings are automatically converted to numbers
+# before summing
+
+SET @l=1;
+UPDATE t1 SET name=CONCAT(name, @l:=@l+1);
+
+SELECT SUM(DISTINCT RIGHT(name, 1)) FROM t1;
+
+# this is a test case for ordinary t1
+
+SELECT SUM(DISTINCT id) FROM t1;
+SELECT SUM(DISTINCT id % 11) FROM t1;
+
+DROP TABLE t1;
+
+#
+# Test the case when distinct values doesn't fit in memory and
+# filesort is used (see uniques.cc:merge_walk)
+#
+
+CREATE TABLE t1 (id INTEGER);
+CREATE TABLE t2 (id INTEGER);
+
+INSERT INTO t1 (id) VALUES (1), (1), (1),(1);
+INSERT INTO t2 (id) SELECT id FROM t1;
+INSERT INTO t1 (id) SELECT id FROM t2; /* 8 */
+INSERT INTO t1 (id) SELECT id FROM t2; /* 12 */
+INSERT INTO t1 (id) SELECT id FROM t2; /* 16 */
+INSERT INTO t1 (id) SELECT id FROM t2; /* 20 */
+INSERT INTO t1 (id) SELECT id FROM t2; /* 24 */
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+1 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+2 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+4 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+8 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+16 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+32 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+64 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+128 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+256 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+512 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+1024 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+2048 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+4096 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+INSERT INTO t2 (id) SELECT id+8192 FROM t1;
+INSERT INTO t1 SELECT id FROM t2;
+DELETE FROM t2;
+#INSERT INTO t2 (id) SELECT id+16384 FROM t1;
+#INSERT INTO t1 SELECT id FROM t2;
+#DELETE FROM t2;
+#INSERT INTO t2 (id) SELECT id+32768 FROM t1;
+#INSERT INTO t1 SELECT id FROM t2;
+#DELETE FROM t2;
+#INSERT INTO t2 (id) SELECT id+65536 FROM t1;
+#INSERT INTO t1 SELECT id FROM t2;
+#DELETE FROM t2;
+INSERT INTO t2 SELECT id FROM t1 ORDER BY id*rand();
+
+# SELECT '++++++++++++++++++++++++++++++++++++++++++++++++++';
+
+SELECT SUM(DISTINCT id) sm FROM t1;
+SELECT SUM(DISTINCT id) sm FROM t2;
+SELECT SUM(DISTINCT id) sm FROM t1 group by id % 13;
+
+# this limit for max_heap_table_size is set to force testing the case, when
+# all distinct sum values can not fit in memory and must be stored in a
+# temporary table
+
+SET max_heap_table_size=16384;
+
+# to check that max_heap_table_size was actually set (hard limit for minimum
+# max_heap_table_size is set in mysqld.cc):
+
+SHOW variables LIKE 'max_heap_table_size';
+
+SELECT SUM(DISTINCT id) sm FROM t1;
+SELECT SUM(DISTINCT id) sm FROM t2;
+SELECT SUM(DISTINCT id) sm FROM t1 GROUP BY id % 13;
+
+DROP TABLE t1;
+DROP TABLE t2;
diff --git a/mysql-test/t/system_mysql_db_fix.test b/mysql-test/t/system_mysql_db_fix.test
index 6c44535e3b7..1122803fd8f 100644
--- a/mysql-test/t/system_mysql_db_fix.test
+++ b/mysql-test/t/system_mysql_db_fix.test
@@ -68,7 +68,7 @@ INSERT INTO user VALUES ('localhost','', '','N','N','N','N','N','N','N','N','
-- disable_query_log
-DROP TABLE db, host, user, func, tables_priv, columns_priv, help_category, help_keyword, help_relation, help_topic, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type;
+DROP TABLE db, host, user, func, tables_priv, columns_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type;
-- enable_query_log
diff --git a/mysql-test/t/user_var.test b/mysql-test/t/user_var.test
index 601724e68c8..412003aa547 100644
--- a/mysql-test/t/user_var.test
+++ b/mysql-test/t/user_var.test
@@ -111,7 +111,7 @@ insert into t1 values (@var1);
create table t2 (c char(30)) charset=ucs2;
set @v=convert('abc' using ucs2);
insert into t2 values (@v);
-show binlog events from 79;
+show binlog events from 95;
# more important than SHOW BINLOG EVENTS, mysqlbinlog (where we
# absolutely need variables names to be quoted and strings to be
# escaped).
diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test
index a480ecb570a..8c318497c22 100644
--- a/mysql-test/t/variables.test
+++ b/mysql-test/t/variables.test
@@ -5,8 +5,20 @@
drop table if exists t1,t2;
--enable_warnings
-set @`test`=1,@TEST=3,@select=2,@t5=1.23456;
-select @test,@`select`,@TEST,@not_used;
+# case insensitivity tests (new in 5.0)
+set @`test`=1;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+set @TEST=2;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+set @"tEST"=3;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+set @`TeST`=4;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+select @`teST`:=5;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+
+set @select=2,@t5=1.23456;
+select @`select`,@not_used;
set @test_int=10,@test_double=1e-10,@test_string="abcdeghi",@test_string2="abcdefghij",@select=NULL;
select @test_int,@test_double,@test_string,@test_string2,@select;
set @test_int="hello",@test_double="hello",@test_string="hello",@test_string2="hello";
diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test
new file mode 100644
index 00000000000..e73133afa67
--- /dev/null
+++ b/mysql-test/t/view.test
@@ -0,0 +1,1108 @@
+--disable_warnings
+drop table if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6;
+drop view if exists t1,t2,`t1a``b`,v1,v2,v3,v4,v5,v6;
+drop database if exists mysqltest;
+--enable_warnings
+use test;
+
+#
+# some basic test of views and its functionality
+#
+
+# create view on unexistence table
+-- error 1146
+create view v1 (c,d) as select a,b from t1;
+
+create temporary table t1 (a int, b int);
+#view on temporary table
+-- error 1351
+create view v1 (c) as select b+1 from t1;
+drop table t1;
+
+create table t1 (a int, b int);
+insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10);
+
+#view with variable
+-- error 1350
+create view v1 (c,d) as select a,b+@@global.max_user_connections from t1;
+
+# simple view
+create view v1 (c) as select b+1 from t1;
+select c from v1;
+
+#tamporary table should not shade (hide) table of view
+create temporary table t1 (a int, b int);
+# this is empty
+select * from t1;
+# but this based on normal t1
+select c from v1;
+show create table v1;
+show create view v1;
+-- error 1346
+show create view t1;
+drop table t1;
+
+# try to use fields from underlaid table
+-- error 1054
+select a from v1;
+-- error 1054
+select v1.a from v1;
+-- error 1054
+select b from v1;
+-- error 1054
+select v1.b from v1;
+
+# view with different algorithms (explain out put are differ)
+explain extended select c from v1;
+create algorithm=temptable view v2 (c) as select b+1 from t1;
+show create table v2;
+select c from v2;
+explain extended select c from v2;
+
+# try to use underlaid table fields in VIEW creation process
+-- error 1054
+create view v3 (c) as select a+1 from v1;
+-- error 1054
+create view v3 (c) as select b+1 from v1;
+
+
+# VIEW on VIEW test with mixing different algorithms on different order
+create view v3 (c) as select c+1 from v1;
+select c from v3;
+explain extended select c from v3;
+create algorithm=temptable view v4 (c) as select c+1 from v2;
+select c from v4;
+explain extended select c from v4;
+create view v5 (c) as select c+1 from v2;
+select c from v5;
+explain extended select c from v5;
+create algorithm=temptable view v6 (c) as select c+1 from v1;
+select c from v6;
+explain extended select c from v6;
+
+# show table/table status test
+show tables;
+--replace_column 12 # 13 #
+show table status;
+
+drop view v1,v2,v3,v4,v5,v6;
+
+#
+# alter/create view test
+#
+
+# view with subqueries of different types
+create view v1 (c,d,e,f) as select a,b,
+a in (select a+2 from t1), a = all (select a from t1) from t1;
+create view v2 as select c, d from v1;
+select * from v1;
+select * from v2;
+
+# try to create VIEW with name of existing VIEW
+-- error 1050
+create view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1;
+
+# 'or replace' should work in this case
+create or replace view v1 (c,d,e,f) as select a,b, a in (select a+2 from t1), a = all (select a from t1) from t1;
+
+# try to ALTER unexisting VIEW
+drop view v2;
+-- error 1146
+alter view v2 as select c, d from v1;
+
+# 'or replace' on unexisting view
+create or replace view v2 as select c, d from v1;
+
+# alter view on existing view
+alter view v1 (c,d) as select a,max(b) from t1 group by a;
+
+# check that created view works
+select * from v1;
+select * from v2;
+
+# simple test of grants
+grant create view on test.* to test@localhost;
+show grants for test@localhost;
+revoke create view on test.* from test@localhost;
+show grants for test@localhost;
+
+#try to drop unexisten VIEW
+-- error 1051
+drop view v100;
+
+#try to drop table with DROP VIEW
+-- error 1346
+drop view t1;
+
+#try to drop VIEW with DROP TABLE
+-- error 1051
+drop table v1;
+
+#try to drop table with DROP VIEW
+
+drop view v1,v2;
+drop table t1;
+
+#
+# outer left join with merged views
+#
+create table t1 (a int);
+insert into t1 values (1), (2), (3);
+
+create view v1 (a) as select a+1 from t1;
+create view v2 (a) as select a-1 from t1;
+
+select * from t1 natural left join v1;
+select * from v2 natural left join t1;
+select * from v2 natural left join v1;
+
+drop view v1, v2;
+drop table t1;
+
+
+#
+# grant create view test
+#
+connect (root,localhost,root,,test);
+connection root;
+--disable_warnings
+create database mysqltest;
+--enable_warnings
+
+create table mysqltest.t1 (a int, b int);
+create table mysqltest.t2 (a int, b int);
+
+grant select on mysqltest.t1 to mysqltest_1@localhost;
+grant create view,select on test.* to mysqltest_1@localhost;
+
+connect (user1,localhost,mysqltest_1,,test);
+connection user1;
+
+create view v1 as select * from mysqltest.t1;
+# no CRETE VIEW privilege
+-- error 1142
+create view mysqltest.v2 as select * from mysqltest.t1;
+# no SELECT privilege
+-- error 1142
+create view v2 as select * from mysqltest.t2;
+
+connection root;
+revoke all privileges on mysqltest.t1 from mysqltest_1@localhost;
+revoke all privileges on test.* from mysqltest_1@localhost;
+
+drop database mysqltest;
+drop view test.v1;
+
+#
+# grants per columns
+#
+# MERGE algorithm
+--disable_warnings
+create database mysqltest;
+--enable_warnings
+
+create table mysqltest.t1 (a int, b int);
+create view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1;
+grant select (c) on mysqltest.v1 to mysqltest_1@localhost;
+
+connection user1;
+select c from mysqltest.v1;
+# there are not privilege ob column 'd'
+-- error 1143
+select d from mysqltest.v1;
+
+connection root;
+revoke all privileges on mysqltest.v1 from mysqltest_1@localhost;
+delete from mysql.user where user='mysqltest_1';
+drop database mysqltest;
+
+# TEMPORARY TABLE algorithm
+--disable_warnings
+create database mysqltest;
+--enable_warnings
+
+create table mysqltest.t1 (a int, b int);
+create algorithm=temptable view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1;
+grant select (c) on mysqltest.v1 to mysqltest_1@localhost;
+
+connection user1;
+select c from mysqltest.v1;
+# there are not privilege ob column 'd'
+-- error 1143
+select d from mysqltest.v1;
+
+connection root;
+revoke all privileges on mysqltest.v1 from mysqltest_1@localhost;
+delete from mysql.user where user='mysqltest_1';
+drop database mysqltest;
+
+#
+# EXPLAIN rights
+#
+connection root;
+--disable_warnings
+create database mysqltest;
+--enable_warnings
+#prepare views and tables
+create table mysqltest.t1 (a int, b int);
+create table mysqltest.t2 (a int, b int);
+create view mysqltest.v1 (c,d) as select a+1,b+1 from mysqltest.t1;
+create algorithm=temptable view mysqltest.v2 (c,d) as select a+1,b+1 from mysqltest.t1;
+create view mysqltest.v3 (c,d) as select a+1,b+1 from mysqltest.t2;
+create algorithm=temptable view mysqltest.v4 (c,d) as select a+1,b+1 from mysqltest.t2;
+grant select on mysqltest.v1 to mysqltest_1@localhost;
+grant select on mysqltest.v2 to mysqltest_1@localhost;
+grant select on mysqltest.v3 to mysqltest_1@localhost;
+grant select on mysqltest.v4 to mysqltest_1@localhost;
+
+connection user1;
+# all selects works
+select c from mysqltest.v1;
+select c from mysqltest.v2;
+select c from mysqltest.v3;
+select c from mysqltest.v4;
+# test of show coluns
+show columns from mysqltest.v1;
+show columns from mysqltest.v2;
+# but explain/show do not
+-- error 1344
+explain select c from mysqltest.v1;
+-- error 1344
+show create table mysqltest.v1;
+-- error 1344
+explain select c from mysqltest.v2;
+-- error 1344
+show create table mysqltest.v2;
+-- error 1344
+explain select c from mysqltest.v3;
+-- error 1344
+show create table mysqltest.v3;
+-- error 1344
+explain select c from mysqltest.v4;
+-- error 1344
+show create table mysqltest.v4;
+
+# allow to see one of underlaing table
+connection root;
+grant select on mysqltest.t1 to mysqltest_1@localhost;
+connection user1;
+# EXPLAIN of view on above table works
+explain select c from mysqltest.v1;
+show create table mysqltest.v1;
+explain select c from mysqltest.v2;
+show create table mysqltest.v2;
+# but other EXPLAINs do not
+-- error 1344
+explain select c from mysqltest.v3;
+-- error 1344
+show create table mysqltest.v3;
+-- error 1344
+explain select c from mysqltest.v4;
+-- error 1344
+show create table mysqltest.v4;
+
+# allow to see any view in mysqltest database
+connection root;
+grant show view on mysqltest.* to mysqltest_1@localhost;
+connection user1;
+explain select c from mysqltest.v1;
+show create table mysqltest.v1;
+explain select c from mysqltest.v2;
+show create table mysqltest.v2;
+explain select c from mysqltest.v3;
+show create table mysqltest.v3;
+explain select c from mysqltest.v4;
+show create table mysqltest.v4;
+
+connection root;
+revoke all privileges on mysqltest.* from mysqltest_1@localhost;
+delete from mysql.user where user='mysqltest_1';
+drop database mysqltest;
+
+#
+# QUERY CHECHE options for VIEWs
+#
+set GLOBAL query_cache_size=1355776;
+flush status;
+create table t1 (a int, b int);
+
+# queries with following views should not be in query cache
+create view v1 (c,d) as select sql_no_cache a,b from t1;
+create view v2 (c,d) as select a+rand(),b from t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+select * from v1;
+select * from v2;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+select * from v1;
+select * from v2;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+
+drop view v1,v2;
+
+# SQL_CACHE option
+set query_cache_type=demand;
+flush status;
+# query with view will be cached, but direct acess to table will not
+create view v1 (c,d) as select sql_cache a,b from t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+select * from v1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+select * from t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+select * from v1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+select * from t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+drop view v1;
+set query_cache_type=default;
+
+drop table t1;
+set GLOBAL query_cache_size=default;
+
+
+#
+# DISTINCT option for VIEW
+#
+create table t1 (a int);
+insert into t1 values (1), (2), (3), (1), (2), (3);
+create view v1 as select distinct a from t1;
+select * from v1;
+explain select * from v1;
+select * from t1;
+drop view v1;
+drop table t1;
+
+#
+# syntax compatibility
+#
+create table t1 (a int);
+create view v1 as select distinct a from t1 WITH CHECK OPTION;
+create view v2 as select distinct a from t1 WITH CASCADED CHECK OPTION;
+create view v3 as select distinct a from t1 WITH LOCAL CHECK OPTION;
+drop view v3 RESTRICT;
+drop view v2 CASCADE;
+drop view v1;
+drop table t1;
+
+#
+# aliases
+#
+create table t1 (a int, b int);
+insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10);
+create view v1 (c) as select b+1 from t1;
+select test.c from v1 test;
+create algorithm=temptable view v2 (c) as select b+1 from t1;
+select test.c from v2 test;
+select test1.* from v1 test1, v2 test2 where test1.c=test2.c;
+select test2.* from v1 test1, v2 test2 where test1.c=test2.c;
+drop table t1;
+drop view v1,v2;
+
+#
+# LIMIT clasuse test
+#
+create table t1 (a int);
+insert into t1 values (1), (2), (3), (4);
+create view v1 as select a+1 from t1 order by 1 desc limit 2;
+select * from v1;
+explain select * from v1;
+drop view v1;
+drop table t1;
+
+#
+# CREATE ... SELECT view test
+#
+create table t1 (a int);
+insert into t1 values (1), (2), (3), (4);
+create view v1 as select a+1 from t1;
+create table t2 select * from v1;
+show columns from t2;
+select * from t2;
+drop view v1;
+drop table t1,t2;
+
+#
+# simple view + simple update
+#
+create table t1 (a int, b int, primary key(a));
+insert into t1 values (10,2), (20,3), (30,4), (40,5), (50,10);
+create view v1 (a,c) as select a, b+1 from t1;
+create algorithm=temptable view v2 (a,c) as select a, b+1 from t1;
+# try to update expression
+-- error 1347
+update v1 set c=a+c;
+# try to update VIEW with forced TEMPORARY TABLE algorithm
+-- error 1288
+update v2 set a=a+c;
+# updatable field of updateable view
+update v1 set a=a+c;
+select * from v1;
+select * from t1;
+drop table t1;
+drop view v1,v2;
+
+#
+# simple view + simple multi-update
+#
+create table t1 (a int, b int, primary key(a));
+insert into t1 values (10,2), (20,3), (30,4), (40,5), (50,10);
+create table t2 (x int);
+insert into t2 values (10), (20);
+create view v1 (a,c) as select a, b+1 from t1;
+create algorithm=temptable view v2 (a,c) as select a, b+1 from t1;
+# try to update expression
+-- error 1347
+update t2,v1 set v1.c=v1.a+v1.c where t2.x=v1.a;
+# try to update VIEW with forced TEMPORARY TABLE algorithm
+-- error 1288
+update t2,v2 set v2.a=v2.v2.a+c where t2.x=v2.a;
+# updatable field of updateable view
+update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.a;
+select * from v1;
+select * from t1;
+drop table t1,t2;
+drop view v1,v2;
+
+#
+# UPDATE privileges on VIEW columns and whole VIEW
+#
+connection root;
+--disable_warnings
+create database mysqltest;
+--enable_warnings
+
+create table mysqltest.t1 (a int, b int, primary key(a));
+insert into mysqltest.t1 values (10,2), (20,3), (30,4), (40,5), (50,10);
+create table mysqltest.t2 (x int);
+insert into mysqltest.t2 values (3), (4), (5), (6);
+create view mysqltest.v1 (a,c) as select a, b+1 from mysqltest.t1;
+create view mysqltest.v2 (a,c) as select a, b from mysqltest.t1;
+create view mysqltest.v3 (a,c) as select a, b+1 from mysqltest.t1;
+
+grant update (a) on mysqltest.v2 to mysqltest_1@localhost;
+grant update on mysqltest.v1 to mysqltest_1@localhost;
+grant select on mysqltest.* to mysqltest_1@localhost;
+
+connection user1;
+use mysqltest;
+# update with rights on VIEW column
+update t2,v1 set v1.a=v1.a+v1.c where t2.x=v1.c;
+select * from t1;
+update v1 set a=a+c;
+select * from t1;
+# update with rights on whole VIEW
+update t2,v2 set v2.a=v2.a+v2.c where t2.x=v2.c;
+select * from t1;
+update v2 set a=a+c;
+select * from t1;
+# no rights on column
+-- error 1143
+update t2,v2 set v2.c=v2.a+v2.c where t2.x=v2.c;
+-- error 1143
+update v2 set c=a+c;
+# no rights for view
+-- error 1143
+update t2,v3 set v3.a=v3.a+v3.c where t2.x=v3.c;
+-- error 1142
+update v3 set a=a+c;
+
+use test;
+connection root;
+REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
+drop database mysqltest;
+
+#
+# MEREGE VIEW with WHERE clause
+#
+create table t1 (a int, b int, primary key(b));
+insert into t1 values (1,20), (2,30), (3,40), (4,50), (5,100);
+create view v1 (c) as select b from t1 where a<3;
+# simple select and explaint to be sure that it is MERGE
+select * from v1;
+explain extended select * from v1;
+# update test
+update v1 set c=c+1;
+select * from t1;
+# join of such VIEWs test
+create view v2 (c) as select b from t1 where a>=3;
+select * from v1, v2;
+drop view v1, v2;
+drop table t1;
+
+#
+# simple view + simple delete
+#
+create table t1 (a int, b int, primary key(a));
+insert into t1 values (1,2), (2,3), (3,4), (4,5), (5,10);
+create view v1 (a,c) as select a, b+1 from t1;
+create algorithm=temptable view v2 (a,c) as select a, b+1 from t1;
+# try to update VIEW with forced TEMPORARY TABLE algorithm
+-- error 1288
+delete from v2 where c < 4;
+# updatable field of updateable view
+delete from v1 where c < 4;
+select * from v1;
+select * from t1;
+drop table t1;
+drop view v1,v2;
+
+#
+# simple view + simple multi-delete
+#
+create table t1 (a int, b int, primary key(a));
+insert into t1 values (1,2), (2,3), (3,4), (4,5), (5,10);
+create table t2 (x int);
+insert into t2 values (1), (2), (3), (4);
+create view v1 (a,c) as select a, b+1 from t1;
+create algorithm=temptable view v2 (a,c) as select a, b+1 from t1;
+# try to update VIEW with forced TEMPORARY TABLE algorithm
+-- error 1288
+delete v2 from t2,v2 where t2.x=v2.a;
+# updatable field of updateable view
+delete v1 from t2,v1 where t2.x=v1.a;
+select * from v1;
+select * from t1;
+drop table t1,t2;
+drop view v1,v2;
+
+#
+# DELETE privileges on VIEW
+#
+connection root;
+--disable_warnings
+create database mysqltest;
+--enable_warnings
+
+create table mysqltest.t1 (a int, b int, primary key(a));
+insert into mysqltest.t1 values (1,2), (2,3), (3,4), (4,5), (5,10);
+create table mysqltest.t2 (x int);
+insert into mysqltest.t2 values (3), (4), (5), (6);
+create view mysqltest.v1 (a,c) as select a, b+1 from mysqltest.t1;
+create view mysqltest.v2 (a,c) as select a, b+1 from mysqltest.t1;
+
+grant delete on mysqltest.v1 to mysqltest_1@localhost;
+grant select on mysqltest.* to mysqltest_1@localhost;
+
+connection user1;
+use mysqltest;
+# update with rights on VIEW column
+delete from v1 where c < 4;
+select * from t1;
+delete v1 from t2,v1 where t2.x=v1.c;
+select * from t1;
+# no rights for view
+-- error 1142
+delete v2 from t2,v2 where t2.x=v2.c;
+-- error 1142
+delete from v2 where c < 4;
+
+use test;
+connection root;
+REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
+drop database mysqltest;
+
+#
+# key presence check
+#
+create table t1 (a int, b int, c int, primary key(a,b));
+insert into t1 values (10,2,-1), (20,3,-2), (30,4,-3), (40,5,-4), (50,10,-5);
+create view v1 (x,y) as select a, b from t1;
+create view v2 (x,y) as select a, c from t1;
+set sql_updatable_view_key=YES;
+update v1 set x=x+1;
+-- error 1288
+update v2 set x=x+1;
+set sql_updatable_view_key=LIMIT1;
+update v1 set x=x+1;
+update v2 set x=x+1;
+update v1 set x=x+1 limit 1;
+-- error 1288
+update v2 set x=x+1 limit 1;
+set sql_updatable_view_key=NO;
+update v1 set x=x+1 limit 1;
+update v2 set x=x+1 limit 1;
+set sql_updatable_view_key=DEFAULT;
+select * from t1;
+drop table t1;
+drop view v1,v2;
+
+#
+# simple insert
+#
+create table t1 (a int, b int, c int, primary key(a,b));
+insert into t1 values (10,2,-1), (20,3,-2);
+create view v1 (x,y,z) as select c, b, a from t1;
+create view v2 (x,y) as select b, a from t1;
+create view v3 (x,y,z) as select b, a, b from t1;
+create view v4 (x,y,z) as select c+1, b, a from t1;
+create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1;
+# try insert to VIEW with fields duplicate
+-- error 1288
+insert into v3 values (-60,4,30);
+# try insert to VIEW with expression in SELECT list
+-- error 1288
+insert into v4 values (-60,4,30);
+# try insert to VIEW using temporary table algorithm
+-- error 1288
+insert into v5 values (-60,4,30);
+insert into v1 values (-60,4,30);
+insert into v1 (z,y,x) values (50,6,-100);
+insert into v2 values (5,40);
+select * from t1;
+drop table t1;
+drop view v1,v2,v3,v4,v5;
+
+#
+# insert ... select
+#
+create table t1 (a int, b int, c int, primary key(a,b));
+insert into t1 values (10,2,-1), (20,3,-2);
+create table t2 (a int, b int, c int, primary key(a,b));
+insert into t2 values (30,4,-60);
+create view v1 (x,y,z) as select c, b, a from t1;
+create view v2 (x,y) as select b, a from t1;
+create view v3 (x,y,z) as select b, a, b from t1;
+create view v4 (x,y,z) as select c+1, b, a from t1;
+create algorithm=temptable view v5 (x,y,z) as select c, b, a from t1;
+# try insert to VIEW with fields duplicate
+-- error 1288
+insert into v3 select c, b, a from t2;
+# try insert to VIEW with expression in SELECT list
+-- error 1288
+insert into v4 select c, b, a from t2;
+# try insert to VIEW using temporary table algorithm
+-- error 1288
+insert into v5 select c, b, a from t2;
+insert into v1 select c, b, a from t2;
+insert into v1 (z,y,x) select a+20,b+2,-100 from t2;
+insert into v2 select b+1, a+10 from t2;
+select * from t1;
+drop table t1, t2;
+drop view v1,v2,v3,v4,v5;
+
+#
+# insert privileges on VIEW
+#
+connection root;
+--disable_warnings
+create database mysqltest;
+--enable_warnings
+
+create table mysqltest.t1 (a int, b int, primary key(a));
+insert into mysqltest.t1 values (1,2), (2,3);
+create table mysqltest.t2 (x int, y int);
+insert into mysqltest.t2 values (3,4);
+create view mysqltest.v1 (a,c) as select a, b from mysqltest.t1;
+create view mysqltest.v2 (a,c) as select a, b from mysqltest.t1;
+
+grant insert on mysqltest.v1 to mysqltest_1@localhost;
+grant select on mysqltest.* to mysqltest_1@localhost;
+
+connection user1;
+use mysqltest;
+# update with rights on VIEW column
+insert into v1 values (5,6);
+select * from t1;
+insert into v1 select x,y from t2;
+select * from t1;
+# no rights for view
+-- error 1142
+insert into v2 values (5,6);
+-- error 1142
+insert into v2 select x,y from t2;
+
+use test;
+connection root;
+REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
+drop database mysqltest;
+
+#
+# outer join based on VIEW with WHERE clause
+#
+create table t1 (a int, primary key(a));
+insert into t1 values (1), (2), (3);
+create view v1 (x) as select a from t1 where a > 1;
+select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x);
+drop table t1;
+drop view v1;
+
+#
+# merging WHERE condition on VIEW on VIEW
+#
+create table t1 (a int, primary key(a));
+insert into t1 values (1), (2), (3), (200);
+create view v1 (x) as select a from t1 where a > 1;
+create view v2 (y) as select x from v1 where x < 100;
+select * from v2;
+drop table t1;
+drop view v1,v2;
+
+#
+# VIEW on non-updatable view
+#
+create table t1 (a int, primary key(a));
+insert into t1 values (1), (2), (3), (200);
+create ALGORITHM=TEMPTABLE view v1 (x) as select a from t1;
+create view v2 (y) as select x from v1;
+-- error 1288
+update v2 set y=10 where y=2;
+drop table t1;
+drop view v1,v2;
+
+#
+# auto_increment field out of VIEW
+#
+create table t1 (a int not null auto_increment, b int not null, primary key(a), unique(b));
+create view v1 (x) as select b from t1;
+insert into v1 values (1);
+select last_insert_id();
+insert into t1 (b) values (2);
+select last_insert_id();
+select * from t1;
+drop view v1;
+drop table t1;
+
+#
+# test of CREATE VIEW privileges if we have limited privileges
+#
+connection root;
+--disable_warnings
+create database mysqltest;
+--enable_warnings
+
+create table mysqltest.t1 (a int, b int);
+create table mysqltest.t2 (a int, b int);
+
+grant update on mysqltest.t1 to mysqltest_1@localhost;
+grant update(b) on mysqltest.t2 to mysqltest_1@localhost;
+grant create view,update on test.* to mysqltest_1@localhost;
+
+connection user1;
+
+create view v1 as select * from mysqltest.t1;
+create view v2 as select b from mysqltest.t2;
+# There are not rights on mysqltest.v1
+--error 1142
+create view mysqltest.v1 as select * from mysqltest.t1;
+# There are not any rights on mysqltest.t2.a
+-- error 1143
+create view v3 as select a from mysqltest.t2;
+
+# give CRETEA VIEW privileges but without any privileges for result colemn
+connection root;
+create table mysqltest.v3 (b int);
+grant create view on mysqltest.v3 to mysqltest_1@localhost;
+drop table mysqltest.v3;
+connection user1;
+-- error 1143
+create view mysqltest.v3 as select b from mysqltest.t2;
+
+# give UPDATE privileges -> create works
+connection root;
+create table mysqltest.v3 (b int);
+grant create view, update on mysqltest.v3 to mysqltest_1@localhost;
+drop table mysqltest.v3;
+connection user1;
+create view mysqltest.v3 as select b from mysqltest.t2;
+
+
+# If give other privileges for VIEW then underlaying table have =>
+# creation prohibited
+connection root;
+grant select(b) on mysqltest.v3 to mysqltest_1@localhost;
+drop view mysqltest.v3;
+connection user1;
+-- error 1142
+create view mysqltest.v3 as select b from mysqltest.t2;
+
+# Expression need select privileges
+-- error 1143
+create view v4 as select b+1 from mysqltest.t2;
+
+connection root;
+grant create view,update,select on test.* to mysqltest_1@localhost;
+connection user1;
+-- error 1143
+create view v4 as select b+1 from mysqltest.t2;
+
+connection root;
+grant update,select(b) on mysqltest.t2 to mysqltest_1@localhost;
+connection user1;
+create view v4 as select b+1 from mysqltest.t2;
+
+connection root;
+REVOKE ALL PRIVILEGES, GRANT OPTION FROM mysqltest_1@localhost;
+drop database mysqltest;
+drop view v1,v2;
+
+#
+# VIEW fields quoting
+#
+set sql_mode='ansi';
+create table t1 ("a*b" int);
+create view v1 as select "a*b" from t1;
+show create view v1;
+drop view v1;
+drop table t1;
+set sql_mode=default;
+
+#
+# VIEW without tables
+#
+create table t1 (t_column int);
+create view v1 as select 'a';
+select * from v1, t1;
+drop view v1;
+drop table t1;
+
+#
+# quote mark inside table name
+#
+create table `t1a``b` (col1 char(2));
+create view v1 as select * from `t1a``b`;
+select * from v1;
+describe v1;
+drop view v1;
+drop table `t1a``b`;
+
+#
+# Changing of underlaying table
+#
+create table t1 (col1 char(5),col2 char(5));
+create view v1 as select * from t1;
+drop table t1;
+create table t1 (col1 char(5),newcol2 char(5));
+-- error 1355
+insert into v1 values('a','aa');
+drop table t1;
+-- error 1355
+select * from v1;
+drop view v1;
+
+#
+# check of duplication of column names
+#
+-- error 1060
+create view v1 (a,a) as select 'a','a';
+
+#
+# SP variables inside view test
+#
+delimiter //;
+create procedure p1 () begin declare v int; create view v1 as select v; end;//
+delimiter ;//
+-- error 1350
+call p1();
+drop procedure p1;
+
+#
+# updateablity should be transitive
+#
+create table t1 (col1 int,col2 char(22));
+insert into t1 values(5,'Hello, world of views');
+create view v1 as select * from t1;
+create view v2 as select * from v1;
+update v2 set col2='Hello, view world';
+select * from t1;
+drop view v2, v1;
+drop table t1;
+
+#
+# check 'use index' on view with temporary table
+#
+create table t1 (a int, b int);
+create view v1 as select a, sum(b) from t1 group by a;
+-- error 1072
+select b from v1 use index (some_index) where b=1;
+drop view v1;
+drop table t1;
+
+#
+# using VIEW fields several times in query resolved via temporary tables
+#
+create table t1 (col1 char(5),col2 char(5));
+create view v1 (col1,col2) as select col1,col2 from t1;
+insert into v1 values('s1','p1'),('s1','p2'),('s1','p3'),('s1','p4'),('s2','p1'),('s3','p2'),('s4','p4');
+select distinct first.col2 from t1 first where first.col2 in (select second.col2 from t1 second where second.col1<>first.col1);
+select distinct first.col2 from v1 first where first.col2 in (select second.col2 from t1 second where second.col1<>first.col1);
+drop view v1;
+drop table t1;
+
+#
+# Test of view updatebility in prepared statement
+#
+create table t1 (a int);
+create view v1 as select a from t1;
+insert into t1 values (1);
+
+#update
+SET @v0 = '2';
+PREPARE stmt FROM 'UPDATE v1 SET a = ?';
+EXECUTE stmt USING @v0;
+DEALLOCATE PREPARE stmt;
+
+#insert without field list
+SET @v0 = '3';
+PREPARE stmt FROM 'insert into v1 values (?)';
+EXECUTE stmt USING @v0;
+DEALLOCATE PREPARE stmt;
+
+#insert with field list
+SET @v0 = '4';
+PREPARE stmt FROM 'insert into v1 (a) values (?)';
+EXECUTE stmt USING @v0;
+DEALLOCATE PREPARE stmt;
+
+select * from t1;
+
+drop view v1;
+drop table t1;
+
+#
+# error on preparation
+#
+-- error 1096
+CREATE VIEW v02 AS SELECT * FROM DUAL;
+SHOW TABLES;
+
+#
+# EXISTS with UNION VIEW
+#
+CREATE VIEW v1 AS SELECT EXISTS (SELECT 1 UNION SELECT 2);
+select * from v1;
+drop view v1;
+
+#
+# using VIEW where table is required
+#
+create table t1 (col1 int,col2 char(22));
+create view v1 as select * from t1;
+-- error 1346
+create index i1 on v1 (col1);
+drop view v1;
+drop table t1;
+
+#
+# connection_id(), pi(), current_user(), version() representation test
+#
+CREATE VIEW v1 (f1,f2,f3,f4) AS SELECT connection_id(), pi(), current_user(), version();
+SHOW CREATE VIEW v1;
+drop view v1;
+
+#
+# VIEW built over UNION
+#
+create table t1 (s1 int);
+create table t2 (s2 int);
+insert into t1 values (1), (2);
+insert into t2 values (2), (3);
+create view v1 as select * from t1,t2 union all select * from t1,t2;
+select * from v1;
+drop view v1;
+drop tables t1, t2;
+
+#
+# Aggregate functions in view list
+#
+create table t1 (col1 int);
+insert into t1 values (1);
+create view v1 as select count(*) from t1;
+insert into t1 values (null);
+select * from v1;
+drop view v1;
+drop table t1;
+
+#
+# Showing VIEW with VIEWs in subquery
+#
+create table t1 (a int);
+create table t2 (a int);
+create view v1 as select a from t1;
+create view v2 as select a from t2 where a in (select a from v1);
+show create view v2;
+drop view v2, v1;
+drop table t1, t2;
+
+#
+# SHOW VIEW view with name with spaces
+#
+CREATE VIEW `v 1` AS select 5 AS `5`;
+show create view `v 1`;
+drop view `v 1`;
+
+#
+# Removing database with .frm archives
+#
+create database mysqltest;
+create table mysqltest.t1 (a int, b int);
+create view mysqltest.v1 as select a from mysqltest.t1;
+alter view mysqltest.v1 as select b from mysqltest.t1;
+alter view mysqltest.v1 as select a from mysqltest.t1;
+drop database mysqltest;
+
+#
+# VIEW with full text
+#
+CREATE TABLE t1 (c1 int not null auto_increment primary key, c2 varchar(20), fulltext(c2));
+insert into t1 (c2) VALUES ('real Beer'),('Water'),('Kossu'),('Coca-Cola'),('Vodka'),('Wine'),('almost real Beer');
+select * from t1 WHERE match (c2) against ('Beer');
+CREATE VIEW v1 AS SELECT * from t1 WHERE match (c2) against ('Beer');
+select * from v1;
+drop view v1;
+drop table t1;
+
+#
+# distinct in temporary table with a VIEW
+#
+create table t1 (a int);
+insert into t1 values (1),(1),(2),(2),(3),(3);
+create view v1 as select a from t1;
+select distinct a from v1;
+select distinct a from v1 limit 2;
+select distinct a from t1 limit 2;
+prepare stmt1 from "select distinct a from v1 limit 2";
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+drop view v1;
+drop table t1;
+
+#
+# aggregate function of aggregate function
+#
+create table t1 (tg_column bigint);
+create view v1 as select count(tg_column) as vg_column from t1;
+select avg(vg_column) from v1;
+drop view v1;
+drop table t1;
+
+#
+# VIEW of VIEW with column renaming
+#
+create table t1 (col1 bigint not null, primary key (col1));
+create table t2 (col1 bigint not null, key (col1));
+create view v1 as select * from t1;
+create view v2 as select * from t2;
+insert into v1 values (1);
+insert into v2 values (1);
+create view v3 (a,b) as select v1.col1 as a, v2.col1 as b from v1, v2 where v1.col1 = v2.col1;
+select * from v3;
+show create view v3;
+drop view v3, v2, v1;
+drop table t2, t1;
diff --git a/mysql-test/t/view_skip_grants-master.opt b/mysql-test/t/view_skip_grants-master.opt
new file mode 100644
index 00000000000..5699a3387b8
--- /dev/null
+++ b/mysql-test/t/view_skip_grants-master.opt
@@ -0,0 +1 @@
+--skip-grant-tables
diff --git a/mysql-test/t/view_skip_grants.test b/mysql-test/t/view_skip_grants.test
new file mode 100644
index 00000000000..bfbaec44eb1
--- /dev/null
+++ b/mysql-test/t/view_skip_grants.test
@@ -0,0 +1,14 @@
+--disable_warnings
+drop table if exists t1,v1;
+drop view if exists t1,v1;
+--enable_warnings
+use test;
+
+#
+# test that we can create VIEW if privileges check switched off
+#
+create table t1 (field1 INT);
+CREATE VIEW v1 AS SELECT field1 FROM t1;
+
+drop view v1;
+drop table t1
diff --git a/mysys/mf_getdate.c b/mysys/mf_getdate.c
index 189d43e782a..b12e68cc1f9 100644
--- a/mysys/mf_getdate.c
+++ b/mysys/mf_getdate.c
@@ -19,11 +19,20 @@
#include "mysys_priv.h"
#include <m_string.h>
- /*
- If flag & 1 Return date and time
- If flag & 2 Return short date format YYMMDD
- if flag & 4 Return time in HHMMDD format.
- */
+/*
+ get date as string
+
+ SYNOPSIS
+ get_date()
+ to - string where date will be written
+ flag - format of date:
+ If flag & GETDATE_TIME Return date and time
+ If flag & GETDATE_SHORT_DATE Return short date format YYMMDD
+ If flag & GETDATE_HHMMSSTIME Return time in HHMMDD format.
+ If flag & GETDATE_GMT Date/time in GMT
+ If flag & GETDATE_FIXEDLENGTH Return fixed length date/time
+ date - for conversion
+*/
void get_date(register my_string to, int flag, time_t date)
@@ -36,27 +45,36 @@ void get_date(register my_string to, int flag, time_t date)
skr=date ? (time_t) date : time((time_t*) 0);
#if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT)
- localtime_r(&skr,&tm_tmp);
+ if (flag & GETDATE_GMT)
+ localtime_r(&skr,&tm_tmp);
+ else
+ gmtime_r(&skr,&tm_tmp);
start_time= &tm_tmp;
#else
- start_time=localtime(&skr);
+ if (flag & GETDATE_GMT)
+ start_time= localtime(&skr);
+ else
+ gmtime(&skr,&tm_tmp);
#endif
- if (flag & 2)
+ if (flag & GETDATE_SHORT_DATE)
sprintf(to,"%02d%02d%02d",
start_time->tm_year % 100,
start_time->tm_mon+1,
start_time->tm_mday);
else
- sprintf(to,"%d-%02d-%02d",
+ sprintf(to, ((flag & GETDATE_FIXEDLENGTH) ?
+ "%4d-%02d-%02d" : "%d-%02d-%02d"),
start_time->tm_year+1900,
start_time->tm_mon+1,
start_time->tm_mday);
- if (flag & 1)
- sprintf(strend(to)," %2d:%02d:%02d",
+ if (flag & GETDATE_DATE_TIME)
+ sprintf(strend(to),
+ ((flag & GETDATE_FIXEDLENGTH) ?
+ " %02d:%02d:%02d" : " %2d:%02d:%02d"),
start_time->tm_hour,
start_time->tm_min,
start_time->tm_sec);
- else if (flag & 4)
+ else if (flag & GETDATE_HHMMSSTIME)
sprintf(strend(to),"%02d%02d%02d",
start_time->tm_hour,
start_time->tm_min,
diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c
index 3755bcdb53d..1f3db84304e 100644
--- a/mysys/mf_iocache2.c
+++ b/mysys/mf_iocache2.c
@@ -65,6 +65,13 @@ my_off_t my_b_append_tell(IO_CACHE* info)
return res;
}
+my_off_t my_b_safe_tell(IO_CACHE *info)
+{
+ if (unlikely(info->type == SEQ_READ_APPEND))
+ return my_b_append_tell(info);
+ return my_b_tell(info);
+}
+
/*
Make next read happen at the given position
For write cache, make next write happen at the given position
diff --git a/mysys/my_bit.c b/mysys/my_bit.c
index 55dd72f5f76..01c9b5ea68d 100644
--- a/mysys/my_bit.c
+++ b/mysys/my_bit.c
@@ -71,3 +71,8 @@ uint my_count_bits(ulonglong v)
#endif
}
+uint my_count_bits_ushort(ushort v)
+{
+ return nbits[v];
+}
+
diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c
index 3a09255b0b0..a596fcb6fd9 100644
--- a/mysys/my_bitmap.c
+++ b/mysys/my_bitmap.c
@@ -28,6 +28,9 @@
* when both arguments are bitmaps, they must be of the same size
* bitmap_intersect() is an exception :)
(for for Bitmap::intersect(ulonglong map2buff))
+
+ If THREAD is defined all bitmap operations except bitmap_init/bitmap_free
+ are thread-safe.
TODO:
Make assembler THREAD safe versions of these using test-and-set instructions
@@ -329,3 +332,66 @@ void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2)
bitmap_unlock(map);
}
+
+/*
+ SYNOPSIS
+ bitmap_bits_set()
+ map
+ RETURN
+ Number of set bits in the bitmap.
+*/
+
+uint bitmap_bits_set(const MY_BITMAP *map)
+{
+ uchar *m= map->bitmap;
+ uchar *end= m + map->bitmap_size;
+ uint res= 0;
+
+ DBUG_ASSERT(map->bitmap);
+ bitmap_lock((MY_BITMAP *)map);
+ while (m < end)
+ {
+ res+= my_count_bits_ushort(*m++);
+ }
+ bitmap_unlock((MY_BITMAP *)map);
+ return res;
+}
+
+
+/*
+ SYNOPSIS
+ bitmap_get_first()
+ map
+ RETURN
+ Number of first unset bit in the bitmap or MY_BIT_NONE if all bits are set.
+*/
+
+uint bitmap_get_first(const MY_BITMAP *map)
+{
+ uchar *bitmap=map->bitmap;
+ uint bit_found = MY_BIT_NONE;
+ uint bitmap_size=map->bitmap_size*8;
+ uint i;
+
+ DBUG_ASSERT(map->bitmap);
+ bitmap_lock((MY_BITMAP *)map);
+ for (i=0; i < bitmap_size ; i++, bitmap++)
+ {
+ if (*bitmap != 0xff)
+ { /* Found slot with free bit */
+ uint b;
+ for (b=0; ; b++)
+ {
+ if (!(*bitmap & (1 << b)))
+ {
+ bit_found = (i*8)+b;
+ break;
+ }
+ }
+ break; /* Found bit */
+ }
+ }
+ bitmap_unlock((MY_BITMAP *)map);
+ return bit_found;
+}
+
diff --git a/netware/my_manage.h b/netware/my_manage.h
index ada02378ee4..480eefbe55a 100644
--- a/netware/my_manage.h
+++ b/netware/my_manage.h
@@ -26,17 +26,41 @@
******************************************************************************/
#include <stdlib.h>
+#ifndef __WIN__
#include <unistd.h>
+#endif
/******************************************************************************
macros
******************************************************************************/
+#ifdef __WIN__
+#define PATH_MAX _MAX_PATH
+#define NAME_MAX _MAX_FNAME
+#define kill(A,B) TerminateProcess((HANDLE)A,0)
+#define NOT_NEED_PID 0
+#define MASTER_PID 1
+#define SLAVE_PID 2
+#define mysqld_timeout 60000
+
+intptr_t master_server;
+intptr_t slave_server;
+int pid_mode;
+bool run_server;
+char win_args[1024];
+bool skip_first_param;
+#endif
+
#define ARG_BUF 10
#define TRY_MAX 5
+#ifdef __NETWARE__
+#define strstr(A,B) strindex(A,B)
+#endif
+
+
/******************************************************************************
structures
@@ -53,6 +77,8 @@ typedef struct
} arg_list_t;
+
+typedef int pid_t;
/******************************************************************************
global variables
@@ -66,7 +92,7 @@ typedef struct
******************************************************************************/
void init_args(arg_list_t *);
-void add_arg(arg_list_t *, char *, ...);
+void add_arg(arg_list_t *, const char *, ...);
void free_args(arg_list_t *);
int sleep_until_file_exists(char *);
@@ -80,8 +106,12 @@ pid_t get_server_pid(char *);
void kill_server(pid_t pid);
void del_tree(char *);
-int removef(char *, ...);
+int removef(const char *, ...);
void get_basedir(char *, char *);
+char mysqladmin_file[PATH_MAX];
+
#endif /* _MY_MANAGE */
+
+
diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh
index 5165f63955c..87473d6bd17 100644
--- a/scripts/make_binary_distribution.sh
+++ b/scripts/make_binary_distribution.sh
@@ -96,7 +96,7 @@ do
fi
done
-# Non platform-specific bin dir files:
+# Non platform-specific bin files:
BIN_FILES="extra/comp_err$BS extra/replace$BS extra/perror$BS \
extra/resolveip$BS extra/my_print_defaults$BS \
extra/resolve_stack_dump$BS extra/mysql_waitpid$BS \
@@ -107,18 +107,18 @@ BIN_FILES="extra/comp_err$BS extra/replace$BS extra/perror$BS \
client/mysql$BS client/mysqlshow$BS client/mysqladmin$BS \
client/mysqldump$BS client/mysqlimport$BS \
client/mysqltest$BS client/mysqlcheck$BS \
- client/mysqlbinlog$BS
+ client/mysqlbinlog$BS \
";
-# Platform-specific bin dir files:
+# Platform-specific bin files:
if [ $BASE_SYSTEM = "netware" ] ; then
BIN_FILES="$BIN_FILES \
netware/mysqld_safe$BS netware/mysql_install_db$BS \
netware/init_db.sql netware/test_db.sql netware/mysql_explain_log$BS \
- netware/mysqlhotcopy$BS netware/libmysql$BS netware/init_secure_db.sql
+ netware/mysqlhotcopy$BS netware/libmysql$BS netware/init_secure_db.sql \
";
+# For all other platforms:
else
- # For all other platforms:
BIN_FILES="$BIN_FILES \
client/mysqlmanagerc \
client/mysqlmanager-pwgen tools/mysqlmanager \
@@ -257,7 +257,9 @@ fi
# Make safe_mysqld a symlink to mysqld_safe for backwards portability
# To be removed in MySQL 4.1
-(cd $BASE/bin ; ln -s mysqld_safe safe_mysqld )
+if [ $BASE_SYSTEM != "netware" ] ; then
+ (cd $BASE/bin ; ln -s mysqld_safe safe_mysqld )
+fi
# Clean up if we did this from a bk tree
if [ -d $BASE/sql-bench/SCCS ] ; then
diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh
index f524b322388..5a2a45c4b3d 100644
--- a/scripts/mysql_create_system_tables.sh
+++ b/scripts/mysql_create_system_tables.sh
@@ -41,6 +41,7 @@ c_hk=""
i_ht=""
c_tzn="" c_tz="" c_tzt="" c_tztt="" c_tzls=""
i_tzn="" i_tz="" i_tzt="" i_tztt="" i_tzls=""
+c_p=""
# Check for old tables
if test ! -f $mdata/db.frm
@@ -66,14 +67,16 @@ then
c_d="$c_d Alter_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_d="$c_d Create_tmp_table_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_d="$c_d Lock_tables_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_d="$c_d Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_d="$c_d Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_d="$c_d PRIMARY KEY Host (Host,Db,User),"
c_d="$c_d KEY User (User)"
c_d="$c_d ) engine=MyISAM"
c_d="$c_d CHARACTER SET utf8 COLLATE utf8_bin"
c_d="$c_d comment='Database privileges';"
- i_d="INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y');
- INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y');"
+ i_d="INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y');
+ INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y');"
fi
if test ! -f $mdata/host.frm
@@ -97,6 +100,8 @@ then
c_h="$c_h Alter_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_h="$c_h Create_tmp_table_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_h="$c_h Lock_tables_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_h="$c_h Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_h="$c_h Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_h="$c_h PRIMARY KEY Host (Host,Db)"
c_h="$c_h ) engine=MyISAM"
c_h="$c_h CHARACTER SET utf8 COLLATE utf8_bin"
@@ -134,6 +139,8 @@ then
c_u="$c_u Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Repl_slave_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Repl_client_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_u="$c_u Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
+ c_u="$c_u Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u ssl_type enum('','ANY','X509', 'SPECIFIED') DEFAULT '' NOT NULL,"
c_u="$c_u ssl_cipher BLOB NOT NULL,"
c_u="$c_u x509_issuer BLOB NOT NULL,"
@@ -148,24 +155,24 @@ then
if test "$1" = "test"
then
- i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- REPLACE INTO user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ REPLACE INTO user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
INSERT INTO user (host,user) values ('localhost','');
INSERT INTO user (host,user) values ('$hostname','');"
else
- i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);"
+ i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);"
if test "$windows" = "0"
then
i_u="$i_u
- INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
INSERT INTO user (host,user) values ('$hostname','');
INSERT INTO user (host,user) values ('localhost','');"
else
i_u="$i_u
- INSERT INTO user VALUES ('%','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- INSERT INTO user VALUES ('%','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','','','','',0,0,0);"
+ INSERT INTO user VALUES ('%','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+ INSERT INTO user VALUES ('%','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','','','','',0,0,0);"
fi
fi
fi
@@ -629,6 +636,50 @@ then
fi
fi
+if test ! -f $mdata/proc.frm
+then
+ c_p="$c_p CREATE TABLE proc ("
+ c_p="$c_p db char(64) binary DEFAULT '' NOT NULL,"
+ c_p="$c_p name char(64) DEFAULT '' NOT NULL,"
+ c_p="$c_p type enum('FUNCTION','PROCEDURE') NOT NULL,"
+ c_p="$c_p specific_name char(64) DEFAULT '' NOT NULL,"
+ c_p="$c_p language enum('SQL') DEFAULT 'SQL' NOT NULL,"
+ c_p="$c_p sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL,"
+ c_p="$c_p is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL,"
+ c_p="$c_p security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL,"
+ c_p="$c_p param_list blob DEFAULT '' NOT NULL,"
+ c_p="$c_p returns char(64) DEFAULT '' NOT NULL,"
+ c_p="$c_p body blob DEFAULT '' NOT NULL,"
+ c_p="$c_p definer char(77) binary DEFAULT '' NOT NULL,"
+ c_p="$c_p created timestamp,"
+ c_p="$c_p modified timestamp,"
+ c_p="$c_p sql_mode set("
+ c_p="$c_p 'REAL_AS_FLOAT',"
+ c_p="$c_p 'PIPES_AS_CONCAT',"
+ c_p="$c_p 'ANSI_QUOTES',"
+ c_p="$c_p 'IGNORE_SPACE',"
+ c_p="$c_p 'NOT_USED',"
+ c_p="$c_p 'ONLY_FULL_GROUP_BY',"
+ c_p="$c_p 'NO_UNSIGNED_SUBTRACTION',"
+ c_p="$c_p 'NO_DIR_IN_CREATE',"
+ c_p="$c_p 'POSTGRESQL',"
+ c_p="$c_p 'ORACLE',"
+ c_p="$c_p 'MSSQL',"
+ c_p="$c_p 'DB2',"
+ c_p="$c_p 'MAXDB',"
+ c_p="$c_p 'NO_KEY_OPTIONS',"
+ c_p="$c_p 'NO_TABLE_OPTIONS',"
+ c_p="$c_p 'NO_FIELD_OPTIONS',"
+ c_p="$c_p 'MYSQL323',"
+ c_p="$c_p 'MYSQL40',"
+ c_p="$c_p 'ANSI',"
+ c_p="$c_p 'NO_AUTO_VALUE_ON_ZERO'"
+ c_p="$c_p ) DEFAULT 0 NOT NULL,"
+ c_p="$c_p comment char(64) binary DEFAULT '' NOT NULL,"
+ c_p="$c_p PRIMARY KEY (db,name,type)"
+ c_p="$c_p ) comment='Stored Procedures';"
+fi
+
cat << END_OF_DATA
use mysql;
$c_d
@@ -661,5 +712,8 @@ $c_tztt
$i_tztt
$c_tzls
$i_tzls
+
+$c_p
+
END_OF_DATA
diff --git a/scripts/mysql_fix_privilege_tables.sh b/scripts/mysql_fix_privilege_tables.sh
index c9e8e0c4dfd..89b96f2bc6e 100644
--- a/scripts/mysql_fix_privilege_tables.sh
+++ b/scripts/mysql_fix_privilege_tables.sh
@@ -7,13 +7,14 @@ password=""
host="localhost"
user="root"
sql_only=0
-basedir=""
+basedir="@prefix@"
verbose=0
args=""
port=""
socket=""
database="mysql"
bindir=""
+pkgdatadir="@pkgdatadir@"
file=mysql_fix_privilege_tables.sql
@@ -85,34 +86,23 @@ done
parse_arguments `$print_defaults $defaults mysql_install_db mysql_fix_privilege_tables`
parse_arguments PICK-ARGS-FROM-ARGV "$@"
-if test -z "$basedir"
+if test -z "$password"
then
- basedir=@prefix@
- if test -z "$bindir"
- then
- bindir=@bindir@
- fi
- execdir=@libexecdir@
- pkgdatadir=@pkgdatadir@
-else
- if test -z "$bindir"
- then
- bindir="$basedir/bin"
- fi
- if test -x "$basedir/libexec/mysqld"
- then
- execdir="$basedir/libexec"
- elif test -x "@libexecdir@/mysqld"
- then
- execdir="@libexecdir@"
- else
- execdir="$basedir/bin"
- fi
+ password=$old_style_password
fi
-if test -z "$password"
+# Find where 'mysql' command is located
+
+if test -z "$bindir"
then
- password=$old_style_password
+ for i in @bindir@ $basedir/bin client
+ do
+ if test -f $i/mysql
+ then
+ bindir=$i
+ break
+ fi
+ done
fi
cmd="$bindir/mysql -f --user=$user --host=$host"
@@ -134,7 +124,7 @@ fi
# Find where first mysql_fix_privilege_tables.sql is located
for i in $basedir/support-files $basedir/share $basedir/share/mysql \
- $basedir/scripts @pkgdatadir@ . ./scripts
+ $basedir/scripts $pkgdatadir . ./scripts
do
if test -f $i/$file
then
@@ -163,7 +153,8 @@ s_echo "This script updates all the mysql privilege tables to be usable by"
s_echo "MySQL 4.0 and above."
s_echo ""
s_echo "This is needed if you want to use the new GRANT functions,"
-s_echo "CREATE AGGREGATE FUNCTION, or the more secure passwords in 4.1"
+s_echo "CREATE AGGREGATE FUNCTION, stored procedures, or"
+s_echo "more secure passwords in 4.1"
s_echo ""
if test $verbose = 1
diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql
index a60d987f8b5..18dfe14bc45 100644
--- a/scripts/mysql_fix_privilege_tables.sql
+++ b/scripts/mysql_fix_privilege_tables.sql
@@ -144,6 +144,19 @@ alter table user comment='Users and global privileges';
alter table func comment='User defined functions';
alter table tables_priv comment='Table privileges';
alter table columns_priv comment='Column privileges';
+#
+# Create VIEWs privileges (v5.0)
+#
+ALTER TABLE db ADD Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Lock_tables_priv;
+ALTER TABLE host ADD Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Lock_tables_priv;
+ALTER TABLE user ADD Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Repl_client_priv;
+
+#
+# Show VIEWs privileges (v5.0)
+#
+ALTER TABLE db ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_view_priv;
+ALTER TABLE host ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_view_priv;
+ALTER TABLE user ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_view_priv;
#
# Create some possible missing tables
@@ -218,3 +231,52 @@ Correction int signed NOT NULL,
PRIMARY KEY TranTime (Transition_time)
) CHARACTER SET utf8 comment='Leap seconds information for time zones';
+
+#
+# Create proc table if it doesn't exists
+#
+
+CREATE TABLE IF NOT EXISTS proc (
+ db char(64) binary DEFAULT '' NOT NULL,
+ name char(64) DEFAULT '' NOT NULL,
+ type enum('FUNCTION','PROCEDURE') NOT NULL,
+ specific_name char(64) DEFAULT '' NOT NULL,
+ language enum('SQL') DEFAULT 'SQL' NOT NULL,
+ sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL,
+ is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL,
+ security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL,
+ param_list blob DEFAULT '' NOT NULL,
+ returns char(64) DEFAULT '' NOT NULL,
+ body blob DEFAULT '' NOT NULL,
+ definer char(77) binary DEFAULT '' NOT NULL,
+ created timestamp,
+ modified timestamp,
+ sql_mode set(
+ 'REAL_AS_FLOAT',
+ 'PIPES_AS_CONCAT',
+ 'ANSI_QUOTES',
+ 'IGNORE_SPACE',
+ 'NOT_USED',
+ 'ONLY_FULL_GROUP_BY',
+ 'NO_UNSIGNED_SUBTRACTION',
+ 'NO_DIR_IN_CREATE',
+ 'POSTGRESQL',
+ 'ORACLE',
+ 'MSSQL',
+ 'DB2',
+ 'MAXDB',
+ 'NO_KEY_OPTIONS',
+ 'NO_TABLE_OPTIONS',
+ 'NO_FIELD_OPTIONS',
+ 'MYSQL323',
+ 'MYSQL40',
+ 'ANSI',
+ 'NO_AUTO_VALUE_ON_ZERO'
+ ) DEFAULT 0 NOT NULL,
+ comment char(64) binary DEFAULT '' NOT NULL,
+ PRIMARY KEY (db,name,type)
+) comment='Stored Procedures';
+
+# Correct the name fields to not binary
+ALTER TABLE proc MODIFY name char(64) DEFAULT '' NOT NULL,
+ MODIFY specific_name char(64) DEFAULT '' NOT NULL;
diff --git a/sql-bench/limits/mysql-4.0.cfg b/sql-bench/limits/mysql-4.0.cfg
index 35bdc9fa842..6a23be36fce 100644
--- a/sql-bench/limits/mysql-4.0.cfg
+++ b/sql-bench/limits/mysql-4.0.cfg
@@ -84,7 +84,7 @@ alter_drop_unique=with drop key # Alter table drop unique
###< alter table crash_q drop constraint u1 restrict
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'constraint u1 restrict' at line 1
###
- ###< alter table crash_q drop key c1
+ ###< alter table crash_q drop key u1
###> OK
alter_modify_col=yes # Alter table modify column
###< alter table crash_q modify c1 CHAR(20)
@@ -395,7 +395,7 @@ date_format_inresult=iso # Date format in result
###> OK
###
###< select a from crash_me_d
- ###> 2003-08-27
+ ###> 2003-12-24
###< delete from crash_me_d
###> OK
###< insert into crash_me_d values( sysdate() )
@@ -664,14 +664,14 @@ func_extra_add_months=no # Function ADD_MONTHS
###
###<select add_months('1997-01-01',1) from crash_me_d
###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('1997-01-01',1) from crash_me_d' at line 1
-func_extra_adddate=no # Function ADDDATE
+func_extra_adddate=yes # Function ADDDATE
###
###<select ADDDATE('2002-12-01',3) from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '3) from crash_me_d' at line 1
-func_extra_addtime=no # Function ADDTIME
+ ###>2002-12-04
+func_extra_addtime=yes # Function ADDTIME
###
###<select ADDTIME('20:02:12','00:00:03')
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('20:02:12','00:00:03')' at line 1
+ ###>20:02:15
func_extra_alpha=no # Function ALPHA
###
###<select alpha('Aâ',2)
@@ -754,10 +754,10 @@ func_extra_cosh=no # Function COSH
###
###<select cosh(0)
###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(0)' at line 1
-func_extra_date=no # Function DATE
+func_extra_date=yes # Function DATE
###
###<select date('1963-08-16') from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('1963-08-16') from crash_me_d' at line 1
+ ###>1963-08-16
func_extra_date_format=yes # Function DATE_FORMAT
###
###<select date_format('1997-01-02 03:04:05','M W D Y y m d h i s w') from crash_me_d
@@ -769,11 +769,11 @@ func_extra_dateadd=no # Function DATEADD
func_extra_datediff=no # Function DATEDIFF
###
###<select datediff(month,'Oct 21 1997','Nov 30 1997') from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(month,'Oct 21 1997','Nov 30 1997') from crash_me_d' at line 1
-func_extra_datediff2arg=no # Function DATEDIFF (2 arg)
+ ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near ''Nov 30 1997') from crash_me_d' at line 1
+func_extra_datediff2arg=yes # Function DATEDIFF (2 arg)
###
###<select DATEDIFF('2002-12-04','2002-12-01') from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('2002-12-04','2002-12-01') from crash_me_d' at line 1
+ ###>3
func_extra_datename=no # Function DATENAME
###
###<select datename(month,'Nov 30 1997') from crash_me_d
@@ -782,10 +782,10 @@ func_extra_datepart=no # Function DATEPART
###
###<select datepart(month,'July 20 1997') from crash_me_d
###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(month,'July 20 1997') from crash_me_d' at line 1
-func_extra_day=no # Function DAY
+func_extra_day=yes # Function DAY
###
###<select DAY('2002-12-01') from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('2002-12-01') from crash_me_d' at line 1
+ ###>1
func_extra_decode=no # Function DECODE
###
###<select DECODE('S-103','T72',1,'S-103',2,'Leopard',3)
@@ -801,7 +801,7 @@ func_extra_elt=yes # Function ELT
func_extra_encrypt=yes # Function ENCRYPT
###
###<select encrypt('hello')
- ###>tHrzZO8Aq1FG6
+ ###>1Wi1s0yzcGqMY
func_extra_expand2arg=no # Function EXPAND
###
###<select expand('abcd',6)
@@ -878,10 +878,10 @@ func_extra_interval=yes # Function INTERVAL
###
###<select interval(55,10,20,30,40,50,60,70,80,90,100)
###>5
-func_extra_last_day=no # Function LAST_DAY
+func_extra_last_day=yes # Function LAST_DAY
###
###<select last_day('1997-04-01') from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('1997-04-01') from crash_me_d' at line 1
+ ###>1997-04-30
func_extra_last_insert_id=yes # Function LAST_INSERT_ID
###
###<select last_insert_id()
@@ -931,14 +931,14 @@ func_extra_ltrim2arg=no # Function LTRIM (2 arg)
###
###<select ltrim('..abcd..','.')
###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near ''.')' at line 1
-func_extra_makedate=no # Function MAKEDATE
+func_extra_makedate=yes # Function MAKEDATE
###
###<select MAKEDATE(1963,228) from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(1963,228) from crash_me_d' at line 1
-func_extra_maketime=no # Function MAKETIME
+ ###>1963-08-16
+func_extra_maketime=yes # Function MAKETIME
###
###<select MAKETIME(20,02,12)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(20,02,12)' at line 1
+ ###>20:02:12
func_extra_mapchar=no # Function MAPCHAR
###
###<select mapchar('Aâ')
@@ -947,10 +947,11 @@ func_extra_mdy=no # Function MDY
###
###<select mdy(7,1,1998) from crash_me_d
###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(7,1,1998) from crash_me_d' at line 1
-func_extra_microsecond=no # Function MICROSECOND
+func_extra_microsecond=error # Function MICROSECOND
###
###<select MICROSECOND('19630816200212111111')
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('19630816200212111111')' at line 1
+ ###>11
+ ###We expected '111111' but got '11'
func_extra_mid=yes # Function SUBSTRING as MID
###
###<select mid('hello',3,2)
@@ -968,7 +969,7 @@ func_extra_noround=no # Function NOROUND
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(22.6)' at line 1
func_extra_not=yes # Function NOT in SELECT
###
- ###<select not 0
+ ###<select not false
###>1
func_extra_not_between=yes # Function NOT BETWEEN in SELECT
###
@@ -985,11 +986,11 @@ func_extra_num=no # Function NUM
func_extra_odbc_convert=no # Function ODBC CONVERT
###
###<select convert(5,SQL_CHAR)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SQL_CHAR)' at line 1
+ ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
func_extra_password=yes # Function PASSWORD
###
###<select password('hello')
- ###>70de51425df9d787
+ ###>*6B4F89A54E2D27ECD7E8DA05B4AB8FD9D1D8B119
func_extra_paste=no # Function PASTE
###
###<select paste('ABCDEFG',3,2,'1234')
@@ -1074,18 +1075,18 @@ func_extra_stuff=no # Function STUFF
###
###<select stuff('abc',2,3,'xyz')
###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('abc',2,3,'xyz')' at line 1
-func_extra_subdate=no # Function SUBDATE
+func_extra_subdate=yes # Function SUBDATE
###
###<select SUBDATE('2002-12-04',3) from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '3) from crash_me_d' at line 1
-func_extra_substr2arg=no # Function SUBSTR (2 arg)
+ ###>2002-12-01
+func_extra_substr2arg=yes # Function SUBSTR (2 arg)
###
###<select substr('abcd',2)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('abcd',2)' at line 1
-func_extra_substr3arg=no # Function SUBSTR (3 arg)
+ ###>bcd
+func_extra_substr3arg=yes # Function SUBSTR (3 arg)
###
###<select substr('abcd',2,2)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('abcd',2,2)' at line 1
+ ###>bc
func_extra_substrb=no # Function SUBSTRB
###
###<select SUBSTRB('ABCDEFG',5,4.2)
@@ -1094,14 +1095,14 @@ func_extra_substring_index=yes # Function SUBSTRING_INDEX
###
###<select substring_index('www.tcx.se','.',-2)
###>tcx.se
-func_extra_subtime=no # Function SUBTIME
+func_extra_subtime=yes # Function SUBTIME
###
###<select SUBTIME('20:02:15','00:00:03')
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('20:02:15','00:00:03')' at line 1
+ ###>20:02:12
func_extra_sysdate=yes # Function SYSDATE
###
###<select sysdate()
- ###>2003-08-27 19:55:21
+ ###>2003-12-24 09:46:11
func_extra_tail=no # Function TAIL
###
###<select tail('ABCDEFG',3)
@@ -1110,22 +1111,23 @@ func_extra_tanh=no # Function TANH
###
###<select tanh(1)
###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(1)' at line 1
-func_extra_time=no # Function TIME
+func_extra_time=yes # Function TIME
###
###<select time('20:02:12')
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('20:02:12')' at line 1
+ ###>20:02:12
func_extra_time_to_sec=yes # Function TIME_TO_SEC
###
###<select time_to_sec('01:23:21')
###>5001
-func_extra_timediff=no # Function TIMEDIFF
+func_extra_timediff=yes # Function TIMEDIFF
###
###<select TIMEDIFF('20:02:15','20:02:12')
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('20:02:15','20:02:12')' at line 1
-func_extra_timestamp=no # Function TIMESTAMP
+ ###>00:00:03
+func_extra_timestamp=error # Function TIMESTAMP
###
###<select timestamp('19630816','00200212')
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('19630816','00200212')' at line 1
+ ###>1963-08-16 20:02:12
+ ###We expected '19630816200212000000' but got '1963-08-16 20:02:12'
func_extra_to_days=yes # Function TO_DAYS
###
###<select to_days('1996-01-01') from crash_me_d
@@ -1166,7 +1168,7 @@ func_extra_uid=no # Function UID
func_extra_unix_timestamp=yes # Function UNIX_TIMESTAMP
###
###<select unix_timestamp()
- ###>1062003321
+ ###>1072251971
func_extra_userenv=no # Function USERENV
###
###<select userenv
@@ -1178,15 +1180,15 @@ func_extra_value=no # Function VALUE
func_extra_version=yes # Function VERSION
###
###<select version()
- ###>4.0.15-debug-log
+ ###>5.0.0-alpha-debug-log
func_extra_weekday=yes # Function WEEKDAY
###
###<select weekday('1997-11-29') from crash_me_d
###>5
-func_extra_weekofyear=no # Function WEEKOFYEAR
+func_extra_weekofyear=yes # Function WEEKOFYEAR
###
###<select WEEKOFYEAR('1963-08-16') from crash_me_d
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '('1963-08-16') from crash_me_d' at line 1
+ ###>33
func_extra_|=yes # Function | (bitwise or)
###
###<select 1 | 2
@@ -1246,11 +1248,11 @@ func_odbc_cot=yes # Function COT
func_odbc_curdate=yes # Function CURDATE
###
###<select curdate()
- ###>2003-08-27
+ ###>2003-12-24
func_odbc_curtime=yes # Function CURTIME
###
###<select curtime()
- ###>19:55:21
+ ###>09:46:11
func_odbc_database=yes # Function DATABASE
###
###<select database()
@@ -1397,7 +1399,7 @@ func_odbc_monthname=yes # Function MONTHNAME
func_odbc_now=yes # Function NOW
###
###<select now()
- ###>2003-08-27 19:55:21
+ ###>2003-12-24 09:46:11
func_odbc_pi=yes # Function PI
###
###<select pi()
@@ -1476,20 +1478,19 @@ func_odbc_tan=yes # Function TAN
###
###<select tan(1)
###>1.557408
-func_odbc_timestampadd=no # Function TIMESTAMPADD
+func_odbc_timestampadd=yes # Function TIMESTAMPADD
###
###<select timestampadd(SQL_TSI_SECOND,1,'1997-01-01 00:00:00')
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(SQL_TSI_SECOND,1,'1997-01-01 00:00:00')' at line 1
- ###
- ###<select {fn timestampadd(SQL_TSI_SECOND,1,{ts '1997-01-01 00:00:00'}) }
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(SQL_TSI_SECOND,1,{ts '1997-01-01 00:00:00'}) }' at line 1
-func_odbc_timestampdiff=no # Function TIMESTAMPDIFF
+ ###>1997-01-01 00:00:01
+func_odbc_timestampdiff=error # Function TIMESTAMPDIFF
###
###<select timestampdiff(SQL_TSI_SECOND,'1997-01-01 00:00:02', '1997-01-01 00:00:01')
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(SQL_TSI_SECOND,'1997-01-01 00:00:02', '1997-01-01 00:00:01')'
+ ###>-1
+ ###We expected '1' but got '-1'
###
###<select {fn timestampdiff(SQL_TSI_SECOND,{ts '1997-01-01 00:00:02'}, {ts '1997-01-01 00:00:01'}) }
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(SQL_TSI_SECOND,{ts '1997-01-01 00:00:02'}, {ts '1997-01-01 00:
+ ###>-1
+ ###We expected '1' but got '-1'
func_odbc_truncate=yes # Function TRUNCATE
###
###<select truncate(18.18,-1)
@@ -1556,15 +1557,15 @@ func_sql_concat_as_||=error # Function concatenation with ||
func_sql_current_date=yes # Function CURRENT_DATE
###
###<select current_date
- ###>2003-08-27
+ ###>2003-12-24
func_sql_current_time=yes # Function CURRENT_TIME
###
###<select current_time
- ###>19:55:21
+ ###>09:46:11
func_sql_current_timestamp=yes # Function CURRENT_TIMESTAMP
###
###<select current_timestamp
- ###>2003-08-27 19:55:21
+ ###>2003-12-24 09:46:11
func_sql_current_user=with_parenthesis # CURRENT_USER
###< select CURRENT_USER
###> execute error:Unknown column 'CURRENT_USER' in 'field list'
@@ -1585,11 +1586,11 @@ func_sql_extract_sql=yes # Function EXTRACT
func_sql_localtime=yes # Function LOCALTIME
###
###<select localtime
- ###>2003-08-27 19:55:21
+ ###>2003-12-24 09:46:11
func_sql_localtimestamp=yes # Function LOCALTIMESTAMP
###
###<select localtimestamp
- ###>2003-08-27 19:55:21
+ ###>2003-12-24 09:46:11
func_sql_lower=yes # Function LOWER
###
###<select LOWER('ABC')
@@ -1682,22 +1683,22 @@ func_where_between=yes # Function BETWEEN
###
###<select a from crash_me where 5 between 4 and 6
###>1
-func_where_eq_all=no # Function = ALL
+func_where_eq_all=yes # Function = ALL
###
###<select a from crash_me where b =all (select b from crash_me)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'all (select b from crash_me)' at line 1
-func_where_eq_any=no # Function = ANY
+ ###>1
+func_where_eq_any=yes # Function = ANY
###
###<select a from crash_me where b =any (select b from crash_me)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(select b from crash_me)' at line 1
-func_where_eq_some=no # Function = SOME
+ ###>1
+func_where_eq_some=yes # Function = SOME
###
###<select a from crash_me where b =some (select b from crash_me)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(select b from crash_me)' at line 1
-func_where_exists=no # Function EXISTS
+ ###>1
+func_where_exists=yes # Function EXISTS
###
###<select a from crash_me where exists (select * from crash_me)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'exists (select * from crash_me)' at line 1
+ ###>1
func_where_in_num=yes # Function IN on numbers
###
###<select a from crash_me where 2 in (3,2,5,9,5,1)
@@ -1726,10 +1727,10 @@ func_where_not_between=yes # Function NOT BETWEEN
###
###<select a from crash_me where 7 not between 4 and 6
###>1
-func_where_not_exists=no # Function NOT EXISTS
+func_where_not_exists=yes # Function NOT EXISTS
###
###<select a from crash_me where not exists (select * from crash_me where a = 2)
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'exists (select * from crash_me where a = 2)' at line 1
+ ###>1
func_where_not_like=yes # Function NOT LIKE
###
###<select a from crash_me where b not like 'b%'
@@ -1800,10 +1801,10 @@ group_func_extra_stddev=yes # Group function STDDEV
###
###<select stddev(a),a from crash_me group by a
###>0.0000
-group_func_extra_variance=no # Group function VARIANCE
+group_func_extra_variance=yes # Group function VARIANCE
###
###<select variance(a),a from crash_me group by a
- ###> execute failed:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(a),a from crash_me group by a' at line 1
+ ###>0.0000
group_func_sql_any=no # Group function ANY
###
###<select any(a),a from crash_me group by a
@@ -1867,13 +1868,9 @@ group_on_unused=yes # Group on unused column
###> OK
###
###As far as all queries returned OK, result is YES
-has_true_false=no # TRUE and FALSE
- ###< select (1=1)=true
- ###> execute error:Unknown column 'true' in 'field list'
+has_true_false=yes # TRUE and FALSE
###< select (1=1)=true
###> OK
- ###< select (1=1)=true
- ###> execute error:Unknown column 'true' in 'field list'
having=yes # Having
###<select a from crash_me group by a having a > 0
###>1
@@ -2013,10 +2010,10 @@ intersect_incompat=no # intersect (incompatible lists)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'select * from crash_me2' at line 1
###
###As far as some queries didnt return OK, result is NO
-join_tables=31 # tables in join
+join_tables=61 # tables in join
###We are trying (example with N=5):
###select crash_me.a,t0.a,t1.a,t2.a,t3.a,t4.a from crash_me,crash_me t0,crash_me t1,crash_me t2,crash_me t3,crash_me t4
- ### 32:FAIL 7:OK 19:OK 25:OK 28:OK 30:OK 31:FAIL
+ ### 32:OK 48:OK 56:OK 60:OK 62:FAIL 61:FAIL
left_outer_join=yes # left outer join
###< select crash_me.a from crash_me left join crash_me2 ON crash_me.a=crash_me2.a
###> OK
@@ -2071,32 +2068,32 @@ logical_value=1 # Value of logical operation (1=1)
###>1
max_big_expressions=10 # big expressions
###We are trying (example with N=5):
- ###select 0+(1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+...(14308)
+ ###select 0+(1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+...(14328)
### 50:FAIL 10:OK 30:FAIL 14:FAIL 11:FAIL
-max_char_size=255 # max char() size
+max_char_size=1048543 # max char() size
###We are trying (example with N=5):
###create table crash_q (q char(5))
###insert into crash_q values ('aaaaa')
###select * from crash_q
- ### 524287:FAIL 104858:FAIL 20972:FAIL 4195:FAIL 839:FAIL 168:OK 503:FAIL 235:OK 369:FAIL 262:FAIL 241:OK 251:OK 256:FAIL 252:OK 254:OK 255:OK
+ ### 524287:OK 786431:OK 917503:OK 983039:OK 1015807:OK 1032191:OK 1040383:OK 1044479:OK 1046527:OK 1047551:OK 1048063:OK 1048319:OK 1048447:OK 1048511:OK 1048543:OK 1048559:FAIL 1048546:FAIL 1048544:FAIL
max_column_name=64 # column name length
###We are trying (example with N=5):
###create table crash_q (qaaaaa integer)
###insert into crash_q (qaaaaa) values(1)
###select qaaaaa from crash_q
### 256:FAIL 51:OK 153:FAIL 72:FAIL 55:OK 63:OK 67:FAIL 64:FAIL
-max_columns=3398 # Columns in table
+max_columns=2599 # Columns in table
###We are trying (example with N=5):
###create table crash_q (a integer ,a0 integer,a1 integer,a2 integer,a3 integer,a4 integer)
- ### 4096:FAIL 819:OK 2457:OK 3276:OK 3686:FAIL 3358:OK 3522:FAIL 3391:OK 3456:FAIL 3404:FAIL 3394:OK 3399:FAIL 3395:OK 3397:OK 3398:FAIL
+ ### 4096:FAIL 819:OK 2457:OK 3276:FAIL 2621:FAIL 2490:OK 2555:OK 2588:OK 2604:FAIL 2591:OK 2597:OK 2600:FAIL 2598:OK 2599:FAIL
max_conditions=85660 # OR and AND in WHERE
###We are trying (example with N=5):
###select a from crash_me where a=1 and b='a' or a=0 and b='0' or a=1 and b='1' or a=2 and b='2' or a=3 and b='3' or a=4 and b='4'
### 27592:OK 41389:OK 48287:FAIL 42769:OK 45528:FAIL 43321:FAIL 42880:FAIL 42791:OK 42835:FAIL 42800:OK 42817:OK 42826:OK 42830:OK 42832:FAIL 42831:FAIL
-max_expressions=1450 # simple expressions
+max_expressions=1452 # simple expressions
###We are trying (example with N=5):
###select 1+1+1+1+1+1
- ### 5000:FAIL 1000:OK 3000:FAIL 1400:OK 2200:FAIL 1560:FAIL 1432:OK 1496:FAIL 1445:OK 1470:FAIL 1450:OK 1460:FAIL 1452:FAIL 1451:FAIL
+ ### 5000:FAIL 1000:OK 3000:FAIL 1400:OK 2200:FAIL 1560:FAIL 1432:OK 1496:FAIL 1445:OK 1470:FAIL 1450:OK 1460:FAIL 1452:OK 1456:FAIL 1453:FAIL
max_index=32 # max index
### max_unique_index=32 ,so max_index must be same
### max_unique_index=32 ,so max_index must be same
@@ -2115,6 +2112,7 @@ max_index_part_length=255 # max index part length
###create table crash_q (q char(5) not null,unique(q))
###insert into crash_q (q) values ('aaaaa')
###select q from crash_q
+ ### 524271:FAIL 104854:FAIL 20971:FAIL 4194:FAIL 839:FAIL 168:OK 503:FAIL 235:OK 369:FAIL 262:FAIL 241:OK 251:OK 256:FAIL 252:OK 254:OK 255:OK
max_index_parts=16 # index parts
###We are trying (example with N=5):
###create table crash_q (q0 integer not null,q1 integer not null,q2 integer not null,q3 integer not null,q4 integer not nul...(1263)
@@ -2126,11 +2124,12 @@ max_index_varchar_part_length=255 # index varchar part length
###create table crash_q (q varchar(5) not null,unique(q))
###insert into crash_q (q) values ('aaaaa')
###select q from crash_q
+ ### 524271:FAIL 104854:FAIL 20971:FAIL 4194:FAIL 839:FAIL 168:OK 503:FAIL 235:OK 369:FAIL 262:FAIL 241:OK 251:OK 256:FAIL 252:OK 254:OK 255:OK
max_row_length=65534 # max table row length (without blobs)
###We are trying (example with N=5):
###create table crash_q (q0 char(5) not null)
###insert into crash_q values ('aaaaa')
- ### 433245:FAIL 86649:FAIL 17330:OK 51989:OK 69319:FAIL 55455:OK 62387:OK 65853:FAIL 63080:OK 64466:OK 65159:OK 65506:OK 65679:FAIL 65541:FAIL 65513:OK 65527:OK 65534:OK 65537:FAIL 65535:FAIL
+ ### 331372:FAIL 66275:FAIL 13255:OK 39765:OK 53020:OK 59647:OK 62961:OK 64618:OK 65446:OK 65860:FAIL 65529:OK 65694:FAIL 65562:FAIL 65536:FAIL 65531:OK 65533:OK 65534:OK 65535:FAIL
max_row_length_with_null=65502 # table row length with nulls (without blobs)
###We are trying (example with N=5):
###create table crash_q (q0 char(5) )
@@ -2139,10 +2138,10 @@ max_row_length_with_null=65502 # table row length with nulls (without blobs)
max_select_alias_name=+512 # select alias name length
###We are trying (example with N=5):
###select b as aaaaa from crash_me
-max_stack_expression=1450 # stacked expressions
+max_stack_expression=1452 # stacked expressions
###We are trying (example with N=5):
###select 1+(1+(1+(1+(1+(1)))))
- ### 1000:OK 1500:FAIL 1100:OK 1300:OK 1400:OK 1450:OK 1475:FAIL 1455:FAIL 1451:FAIL
+ ### 1000:OK 1500:FAIL 1100:OK 1300:OK 1400:OK 1450:OK 1475:FAIL 1455:FAIL 1451:OK 1453:FAIL 1452:OK
max_table_alias_name=+512 # table alias name length
###We are trying (example with N=5):
###select aaaaa.b from crash_me aaaaa
@@ -2164,12 +2163,12 @@ max_unique_index=32 # unique indexes
###insert into crash_q (q,q1,q2,q3,q4,q5) values (1,1,1,1,1,1)
###select q from crash_q
### 32:OK 48:FAIL 35:FAIL 33:FAIL
-max_varchar_size=255 # max varchar() size
+max_varchar_size=1048543 # max varchar() size
###We are trying (example with N=5):
###create table crash_q (q varchar(5))
###insert into crash_q values ('aaaaa')
###select * from crash_q
- ### 524287:FAIL 104858:FAIL 20972:FAIL 4195:FAIL 839:FAIL 168:OK 503:FAIL 235:OK 369:FAIL 262:FAIL 241:OK 251:OK 256:FAIL 252:OK 254:OK 255:OK
+ ### 524287:OK 786431:OK 917503:OK 983039:OK 1015807:OK 1032191:OK 1040383:OK 1044479:OK 1046527:OK 1047551:OK 1048063:OK 1048319:OK 1048447:OK 1048511:OK 1048543:OK 1048559:FAIL 1048546:FAIL 1048544:FAIL
minus=no # minus
###< select * from crash_me minus select * from crash_me3
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'select * from crash_me3' at line 1
@@ -2426,13 +2425,13 @@ psm_functions=no # PSM functions (ANSI SQL)
###< create table crash_q (a int)
###> OK
###< create function crash_func(in a1 int, in b1 int) returns int language sql deterministic contains sql begin return a1 * b1; end
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(in a1 int, in b1 int) returns int language sql deterministic c
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'in a1 int, in b1 int) returns int language sql deterministic co
###< insert into crash_q values(crash_func(2,4))
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(2,4))' at line 1
###< select a,crash_func(a,2) from crash_q
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(a,2) from crash_q' at line 1
###< drop function crash_func cascade
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'cascade' at line 1
+ ###> execute error:Failed to DROP FUNCTION crash_func
###< drop table crash_q
###> OK
###
@@ -2443,7 +2442,7 @@ psm_modules=no # PSM modules (ANSI SQL)
###< create module crash_m declare procedure crash_proc(in a1 int, in b1 int) language sql modifies sql data begin declare c1 int; set c1 = a1 + b1; insert into crash_q(a,b) values (a1,c1); end; declare procedure crash_proc2(INOUT a int, in b int) contains sql set a = b + 10; end module
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'module crash_m declare procedure crash_proc(in a1 int, in b1 in
###< call crash_proc(1,10)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'call crash_proc(1,10)' at line 1
+ ###> execute error:PROCEDURE crash_proc does not exist
###< drop module crash_m cascade
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'module crash_m cascade' at line 1
###< drop table crash_q cascade
@@ -2454,11 +2453,11 @@ psm_procedures=no # PSM procedures (ANSI SQL)
###< create table crash_q (a int,b int)
###> OK
###< create procedure crash_proc(in a1 int, in b1 int) language sql modifies sql data begin declare c1 int; set c1 = a1 + b1; insert into crash_q(a,b) values (a1,c1); end
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'procedure crash_proc(in a1 int, in b1 int) language sql modifie
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'sql data begin declare c1 int; set c1 = a1 + b1; insert into cr
###< call crash_proc(1,10)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'call crash_proc(1,10)' at line 1
+ ###> execute error:PROCEDURE crash_proc does not exist
###< drop procedure crash_proc
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'procedure crash_proc' at line 1
+ ###> execute error:Failed to DROP PROCEDURE crash_proc
###< drop table crash_q
###> OK
###
@@ -2502,6 +2501,9 @@ quote_with_"=yes # Allows ' and " as string markers
###> OK
###
###As far as all queries returned OK, result is YES
+recursive_subqueries=+64 # recursive subqueries
+ ###We are trying (example with N=5):
+ ###select a from crash_me where a in (select a from crash_me where a in (select a from crash_me where a in (select a from c...(82)
remember_end_space=no # Remembers end space in char()
###< create table crash_q (a char(10))
###> OK
@@ -2651,13 +2653,13 @@ reserved_word_ansi-92/99_authorization=no # Keyword AUTHORIZATION
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_before=no # Keyword BEFORE
+reserved_word_ansi-92/99_before=yes # Keyword BEFORE
###< create table crash_me10 (BEFORE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'BEFORE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_begin=no # Keyword BEGIN
###< create table crash_me10 (BEGIN int not null)
###> OK
@@ -2700,13 +2702,13 @@ reserved_word_ansi-92/99_by=yes # Keyword BY
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_ansi-92/99_call=no # Keyword CALL
+reserved_word_ansi-92/99_call=yes # Keyword CALL
###< create table crash_me10 (CALL int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'CALL int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_cascade=yes # Keyword CASCADE
###< create table crash_me10 (CASCADE int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'CASCADE int not null)' at line 1
@@ -2770,13 +2772,13 @@ reserved_word_ansi-92/99_close=no # Keyword CLOSE
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_collate=no # Keyword COLLATE
+reserved_word_ansi-92/99_collate=yes # Keyword COLLATE
###< create table crash_me10 (COLLATE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'COLLATE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_collation=no # Keyword COLLATION
###< create table crash_me10 (COLLATION int not null)
###> OK
@@ -2812,13 +2814,13 @@ reserved_word_ansi-92/99_connect=no # Keyword CONNECT
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_connection=no # Keyword CONNECTION
+reserved_word_ansi-92/99_connection=yes # Keyword CONNECTION
###< create table crash_me10 (CONNECTION int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'CONNECTION int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_constraint=yes # Keyword CONSTRAINT
###< create table crash_me10 (CONSTRAINT int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'int not null)' at line 1
@@ -2833,13 +2835,13 @@ reserved_word_ansi-92/99_constraints=no # Keyword CONSTRAINTS
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_continue=no # Keyword CONTINUE
+reserved_word_ansi-92/99_continue=yes # Keyword CONTINUE
###< create table crash_me10 (CONTINUE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'CONTINUE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_corresponding=no # Keyword CORRESPONDING
###< create table crash_me10 (CORRESPONDING int not null)
###> OK
@@ -2896,13 +2898,13 @@ reserved_word_ansi-92/99_current_user=no # Keyword CURRENT_USER
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_cursor=no # Keyword CURSOR
+reserved_word_ansi-92/99_cursor=yes # Keyword CURSOR
###< create table crash_me10 (CURSOR int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'CURSOR int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_cycle=no # Keyword CYCLE
###< create table crash_me10 (CYCLE int not null)
###> OK
@@ -2952,13 +2954,13 @@ reserved_word_ansi-92/99_decimal=yes # Keyword DECIMAL
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_ansi-92/99_declare=no # Keyword DECLARE
+reserved_word_ansi-92/99_declare=yes # Keyword DECLARE
###< create table crash_me10 (DECLARE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_default=yes # Keyword DEFAULT
###< create table crash_me10 (DEFAULT int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'DEFAULT int not null)' at line 1
@@ -3078,13 +3080,13 @@ reserved_word_ansi-92/99_else=yes # Keyword ELSE
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_ansi-92/99_elseif=no # Keyword ELSEIF
+reserved_word_ansi-92/99_elseif=yes # Keyword ELSEIF
###< create table crash_me10 (ELSEIF int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'ELSEIF int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_end=no # Keyword END
###< create table crash_me10 (END int not null)
###> OK
@@ -3148,20 +3150,20 @@ reserved_word_ansi-92/99_external=no # Keyword EXTERNAL
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_false=no # Keyword FALSE
+reserved_word_ansi-92/99_false=yes # Keyword FALSE
###< create table crash_me10 (FALSE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'FALSE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_fetch=no # Keyword FETCH
+ ###As far as some queries didnt return OK, result is YES
+reserved_word_ansi-92/99_fetch=yes # Keyword FETCH
###< create table crash_me10 (FETCH int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'FETCH int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_first=no # Keyword FIRST
###< create table crash_me10 (FIRST int not null)
###> OK
@@ -3190,13 +3192,13 @@ reserved_word_ansi-92/99_foreign=yes # Keyword FOREIGN
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_ansi-92/99_found=no # Keyword FOUND
+reserved_word_ansi-92/99_found=yes # Keyword FOUND
###< create table crash_me10 (FOUND int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'FOUND int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_from=yes # Keyword FROM
###< create table crash_me10 (FROM int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM int not null)' at line 1
@@ -3428,13 +3430,13 @@ reserved_word_ansi-92/99_leading=yes # Keyword LEADING
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_ansi-92/99_leave=no # Keyword LEAVE
+reserved_word_ansi-92/99_leave=yes # Keyword LEAVE
###< create table crash_me10 (LEAVE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'LEAVE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_left=yes # Keyword LEFT
###< create table crash_me10 (LEFT int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'LEFT int not null)' at line 1
@@ -3458,7 +3460,7 @@ reserved_word_ansi-92/99_level=no # Keyword LEVEL
###As far as all queries returned OK, result is NO
reserved_word_ansi-92/99_like=yes # Keyword LIKE
###< create table crash_me10 (LIKE int not null)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIKE int not null)' at line 1
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'int not null)' at line 1
###< drop table crash_me10
###> execute error:Unknown table 'crash_me10'
###
@@ -3477,13 +3479,13 @@ reserved_word_ansi-92/99_local=no # Keyword LOCAL
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_loop=no # Keyword LOOP
+reserved_word_ansi-92/99_loop=yes # Keyword LOOP
###< create table crash_me10 (LOOP int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'LOOP int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_match=yes # Keyword MATCH
###< create table crash_me10 (MATCH int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'MATCH int not null)' at line 1
@@ -3701,13 +3703,13 @@ reserved_word_ansi-92/99_parameters=no # Keyword PARAMETERS
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_partial=yes # Keyword PARTIAL
+reserved_word_ansi-92/99_partial=no # Keyword PARTIAL
###< create table crash_me10 (PARTIAL int not null)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'PARTIAL int not null)' at line 1
+ ###> OK
###< drop table crash_me10
- ###> execute error:Unknown table 'crash_me10'
+ ###> OK
###
- ###As far as some queries didnt return OK, result is YES
+ ###As far as all queries returned OK, result is NO
reserved_word_ansi-92/99_precision=yes # Keyword PRECISION
###< create table crash_me10 (PRECISION int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'PRECISION int not null)' at line 1
@@ -3834,20 +3836,20 @@ reserved_word_ansi-92/99_restrict=yes # Keyword RESTRICT
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_ansi-92/99_return=no # Keyword RETURN
+reserved_word_ansi-92/99_return=yes # Keyword RETURN
###< create table crash_me10 (RETURN int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'RETURN int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_returns=yes # Keyword RETURNS
+ ###As far as some queries didnt return OK, result is YES
+reserved_word_ansi-92/99_returns=no # Keyword RETURNS
###< create table crash_me10 (RETURNS int not null)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'RETURNS int not null)' at line 1
+ ###> OK
###< drop table crash_me10
- ###> execute error:Unknown table 'crash_me10'
+ ###> OK
###
- ###As far as some queries didnt return OK, result is YES
+ ###As far as all queries returned OK, result is NO
reserved_word_ansi-92/99_revoke=yes # Keyword REVOKE
###< create table crash_me10 (REVOKE int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'REVOKE int not null)' at line 1
@@ -4009,34 +4011,34 @@ reserved_word_ansi-92/99_space=no # Keyword SPACE
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_sql=no # Keyword SQL
+reserved_word_ansi-92/99_sql=yes # Keyword SQL
###< create table crash_me10 (SQL int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SQL int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_sqlexception=no # Keyword SQLEXCEPTION
+ ###As far as some queries didnt return OK, result is YES
+reserved_word_ansi-92/99_sqlexception=yes # Keyword SQLEXCEPTION
###< create table crash_me10 (SQLEXCEPTION int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SQLEXCEPTION int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_sqlstate=no # Keyword SQLSTATE
+ ###As far as some queries didnt return OK, result is YES
+reserved_word_ansi-92/99_sqlstate=yes # Keyword SQLSTATE
###< create table crash_me10 (SQLSTATE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SQLSTATE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_sqlwarning=no # Keyword SQLWARNING
+ ###As far as some queries didnt return OK, result is YES
+reserved_word_ansi-92/99_sqlwarning=yes # Keyword SQLWARNING
###< create table crash_me10 (SQLWARNING int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SQLWARNING int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_structure=no # Keyword STRUCTURE
###< create table crash_me10 (STRUCTURE int not null)
###> OK
@@ -4135,13 +4137,13 @@ reserved_word_ansi-92/99_trigger=no # Keyword TRIGGER
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi-92/99_true=no # Keyword TRUE
+reserved_word_ansi-92/99_true=yes # Keyword TRUE
###< create table crash_me10 (TRUE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'TRUE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_under=no # Keyword UNDER
###< create table crash_me10 (UNDER int not null)
###> OK
@@ -4261,13 +4263,13 @@ reserved_word_ansi-92/99_where=yes # Keyword WHERE
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_ansi-92/99_while=no # Keyword WHILE
+reserved_word_ansi-92/99_while=yes # Keyword WHILE
###< create table crash_me10 (WHILE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHILE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi-92/99_with=yes # Keyword WITH
###< create table crash_me10 (WITH int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'WITH int not null)' at line 1
@@ -4387,13 +4389,13 @@ reserved_word_ansi92_extract=no # Keyword EXTRACT
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi92_insensitive=no # Keyword INSENSITIVE
+reserved_word_ansi92_insensitive=yes # Keyword INSENSITIVE
###< create table crash_me10 (INSENSITIVE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSENSITIVE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi92_lower=no # Keyword LOWER
###< create table crash_me10 (LOWER int not null)
###> OK
@@ -4492,13 +4494,13 @@ reserved_word_ansi92_replace=yes # Keyword REPLACE
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_ansi92_sensitive=no # Keyword SENSITIVE
+reserved_word_ansi92_sensitive=yes # Keyword SENSITIVE
###< create table crash_me10 (SENSITIVE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SENSITIVE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi92_similar=no # Keyword SIMILAR
###< create table crash_me10 (SIMILAR int not null)
###> OK
@@ -4646,13 +4648,13 @@ reserved_word_ansi99_clob=no # Keyword CLOB
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi99_condition=no # Keyword CONDITION
+reserved_word_ansi99_condition=yes # Keyword CONDITION
###< create table crash_me10 (CONDITION int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'CONDITION int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi99_constructor=no # Keyword CONSTRUCTOR
###< create table crash_me10 (CONSTRUCTOR int not null)
###> OK
@@ -4716,13 +4718,13 @@ reserved_word_ansi99_destructor=no # Keyword DESTRUCTOR
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi99_deterministic=no # Keyword DETERMINISTIC
+reserved_word_ansi99_deterministic=yes # Keyword DETERMINISTIC
###< create table crash_me10 (DETERMINISTIC int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'DETERMINISTIC int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi99_do=no # Keyword DO
###< create table crash_me10 (DO int not null)
###> OK
@@ -4744,13 +4746,13 @@ reserved_word_ansi99_every=no # Keyword EVERY
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi99_exit=no # Keyword EXIT
+reserved_word_ansi99_exit=yes # Keyword EXIT
###< create table crash_me10 (EXIT int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'EXIT int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi99_expand=no # Keyword EXPAND
###< create table crash_me10 (EXPAND int not null)
###> OK
@@ -4814,20 +4816,20 @@ reserved_word_ansi99_initialize=no # Keyword INITIALIZE
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi99_inout=no # Keyword INOUT
+reserved_word_ansi99_inout=yes # Keyword INOUT
###< create table crash_me10 (INOUT int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'INOUT int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
-reserved_word_ansi99_iterate=no # Keyword ITERATE
+ ###As far as some queries didnt return OK, result is YES
+reserved_word_ansi99_iterate=yes # Keyword ITERATE
###< create table crash_me10 (ITERATE int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'ITERATE int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi99_large=no # Keyword LARGE
###< create table crash_me10 (LARGE int not null)
###> OK
@@ -4898,13 +4900,13 @@ reserved_word_ansi99_ordinality=no # Keyword ORDINALITY
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi99_out=no # Keyword OUT
+reserved_word_ansi99_out=yes # Keyword OUT
###< create table crash_me10 (OUT int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'OUT int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi99_parameter=no # Keyword PARAMETER
###< create table crash_me10 (PARAMETER int not null)
###> OK
@@ -4961,13 +4963,13 @@ reserved_word_ansi99_redo=no # Keyword REDO
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi99_repeat=no # Keyword REPEAT
+reserved_word_ansi99_repeat=yes # Keyword REPEAT
###< create table crash_me10 (REPEAT int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'REPEAT int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi99_result=no # Keyword RESULT
###< create table crash_me10 (RESULT int not null)
###> OK
@@ -4989,13 +4991,13 @@ reserved_word_ansi99_sets=no # Keyword SETS
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi99_specific=no # Keyword SPECIFIC
+reserved_word_ansi99_specific=yes # Keyword SPECIFIC
###< create table crash_me10 (SPECIFIC int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SPECIFIC int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi99_specifictype=no # Keyword SPECIFICTYPE
###< create table crash_me10 (SPECIFICTYPE int not null)
###> OK
@@ -5052,13 +5054,13 @@ reserved_word_ansi99_treat=no # Keyword TREAT
###> OK
###
###As far as all queries returned OK, result is NO
-reserved_word_ansi99_undo=no # Keyword UNDO
+reserved_word_ansi99_undo=yes # Keyword UNDO
###< create table crash_me10 (UNDO int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'UNDO int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_ansi99_until=no # Keyword UNTIL
###< create table crash_me10 (UNTIL int not null)
###> OK
@@ -5955,13 +5957,13 @@ reserved_word_extra_soname=yes # Keyword SONAME
###> execute error:Unknown table 'crash_me10'
###
###As far as some queries didnt return OK, result is YES
-reserved_word_extra_spatial=no # Keyword SPATIAL
+reserved_word_extra_spatial=yes # Keyword SPATIAL
###< create table crash_me10 (SPATIAL int not null)
- ###> OK
+ ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'int not null)' at line 1
###< drop table crash_me10
- ###> OK
+ ###> execute error:Unknown table 'crash_me10'
###
- ###As far as all queries returned OK, result is NO
+ ###As far as some queries didnt return OK, result is YES
reserved_word_extra_sql_big_result=yes # Keyword SQL_BIG_RESULT
###< create table crash_me10 (SQL_BIG_RESULT int not null)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'SQL_BIG_RESULT int not null)' at line 1
@@ -6273,13 +6275,16 @@ select_limit3=yes # SELECT with LIMIT # OFFSET #
select_string_size=1048565 # constant string size in SELECT
###We are trying (example with N=5):
###select 'aaaaa'
-select_table_update=no # Update with sub select
+select_table_update=yes # Update with sub select
###< create table crash_q (a integer,b char(10))
###> OK
###< insert into crash_q values(1,'c')
###> OK
###< update crash_q set b= (select b from crash_me where crash_q.a = crash_me.a)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'select b from crash_me where crash_q.a = crash_me.a)' at line 1
+ ###> OK
+ ###
+ ###<select b from crash_q
+ ###>a
###
###< drop table crash_q
###> OK
@@ -6288,7 +6293,7 @@ select_without_from=yes # SELECT without FROM
###> OK
###
###As far as all queries returned OK, result is YES
-server_version=MySQL 4.0.20 debug/ # server version
+server_version=MySQL 5.0.0 alpha debug log/ # server version
simple_joins=yes # ANSI SQL simple joins
###< select crash_me.a from crash_me, crash_me t0
###> OK
@@ -6383,11 +6388,11 @@ storage_of_float=round # Storage of float values
###
###< drop table crash_q
###> OK
-subqueries=no # subqueries
+subqueries=yes # subqueries
###< select a from crash_me where crash_me.a in (select max(a) from crash_me)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'select max(a) from crash_me)' at line 1
+ ###> OK
###
- ###As far as some queries didnt return OK, result is NO
+ ###As far as all queries returned OK, result is YES
table_alias=yes # Table alias
###< select b.a from crash_me as b
###> OK
@@ -6455,21 +6460,7 @@ time_format_inresult=iso # Time format in result
###> OK
###
###< select a from crash_me_t
- ###> 19:55:21
- ###< delete from crash_me_t
- ###> OK
- ###< insert into crash_me_t values(CURRENT_TIME)
- ###> OK
- ###
- ###< select a from crash_me_t
- ###> 13:45:04
- ###< delete from crash_me_t
- ###> OK
- ###< insert into crash_me_t values(CURRENT_TIME)
- ###> OK
- ###
- ###< select a from crash_me_t
- ###> 13:47:18
+ ###> 09:46:11
###< delete from crash_me_t
###> OK
transactions=yes # transactions
@@ -6688,13 +6679,13 @@ type_extra_line=no # Type line
###> execute error:Unknown table 'crash_q'
###
###As far as some queries didnt return OK, result is NO
-type_extra_long=no # Type long
+type_extra_long=yes # Type long
###< create table crash_q (q long)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
+ ###> OK
###< drop table crash_q
- ###> execute error:Unknown table 'crash_q'
+ ###> OK
###
- ###As far as some queries didnt return OK, result is NO
+ ###As far as all queries returned OK, result is YES
type_extra_long_raw=no # Type long raw
###< create table crash_q (q long raw)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'raw)' at line 1
@@ -6807,20 +6798,20 @@ type_extra_path=no # Type path
###> execute error:Unknown table 'crash_q'
###
###As far as some queries didnt return OK, result is NO
-type_extra_point=no # Type point
+type_extra_point=yes # Type point
###< create table crash_q (q point)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'point)' at line 1
+ ###> OK
###< drop table crash_q
- ###> execute error:Unknown table 'crash_q'
+ ###> OK
###
- ###As far as some queries didnt return OK, result is NO
-type_extra_polygon=no # Type polygon
+ ###As far as all queries returned OK, result is YES
+type_extra_polygon=yes # Type polygon
###< create table crash_q (q polygon)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'polygon)' at line 1
+ ###> OK
###< drop table crash_q
- ###> execute error:Unknown table 'crash_q'
+ ###> OK
###
- ###As far as some queries didnt return OK, result is NO
+ ###As far as all queries returned OK, result is YES
type_extra_raw(1_arg)=no # Type raw(1 arg)
###< create table crash_q (q raw(16))
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'raw(16))' at line 1
@@ -6842,13 +6833,13 @@ type_extra_rowid=no # Type rowid
###> execute error:Unknown table 'crash_q'
###
###As far as some queries didnt return OK, result is NO
-type_extra_serial=no # Type serial
+type_extra_serial=yes # Type serial
###< create table crash_q (q serial)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'serial)' at line 1
+ ###> OK
###< drop table crash_q
- ###> execute error:Unknown table 'crash_q'
+ ###> OK
###
- ###As far as some queries didnt return OK, result is NO
+ ###As far as all queries returned OK, result is YES
type_extra_set(1_arg)=yes # Type set(1 arg)
###< create table crash_q (q set('red'))
###> OK
@@ -6884,13 +6875,13 @@ type_extra_text=yes # Type text
###> OK
###
###As far as all queries returned OK, result is YES
-type_extra_text(1_arg)=no # Type text(1 arg)
+type_extra_text(1_arg)=yes # Type text(1 arg)
###< create table crash_q (q text(10))
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(10))' at line 1
+ ###> OK
###< drop table crash_q
- ###> execute error:Unknown table 'crash_q'
+ ###> OK
###
- ###As far as some queries didnt return OK, result is NO
+ ###As far as all queries returned OK, result is YES
type_extra_timespan=no # Type timespan
###< create table crash_q (q timespan)
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'timespan)' at line 1
@@ -6975,13 +6966,13 @@ type_sql_bit_varying(1_arg)=no # Type bit varying(1 arg)
###> execute error:Unknown table 'crash_q'
###
###As far as some queries didnt return OK, result is NO
-type_sql_boolean=no # Type boolean
+type_sql_boolean=yes # Type boolean
###< create table crash_q (q boolean)
- ###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'boolean)' at line 1
+ ###> OK
###< drop table crash_q
- ###> execute error:Unknown table 'crash_q'
+ ###> OK
###
- ###As far as some queries didnt return OK, result is NO
+ ###As far as all queries returned OK, result is YES
type_sql_char(1_arg)=yes # Type char(1 arg)
###< create table crash_q (q char(1))
###> OK
@@ -7281,12 +7272,12 @@ unique_null_in_create=yes # unique null in create
###> OK
###
###As far as all queries returned OK, result is YES
-value_of_false=not supported # Value of FALSE
+value_of_false=0 # Value of FALSE
###<select FALSE
- ###> execute failed:Unknown column 'FALSE' in 'field list'
-value_of_true=not supported # Value of TRUE
+ ###>0
+value_of_true=1 # Value of TRUE
###<select TRUE
- ###> execute failed:Unknown column 'TRUE' in 'field list'
+ ###>1
views=no # views
###< create view crash_q as select a from crash_me
###> execute error:You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'view crash_q as select a from crash_me' at line 1
diff --git a/sql-common/client.c b/sql-common/client.c
index 04d4bc06102..41f0bb80844 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -57,7 +57,7 @@
my_bool net_flush(NET *net);
#else /*EMBEDDED_LIBRARY*/
-#define CLI_MYSQL_REAL_CONNECT mysql_real_connect
+#define CLI_MYSQL_REAL_CONNECT STDCALL mysql_real_connect
#endif /*EMBEDDED_LIBRARY*/
#include <my_sys.h>
#include <mysys_err.h>
@@ -1508,7 +1508,7 @@ static MYSQL_METHODS client_methods=
#endif
};
-MYSQL * STDCALL
+MYSQL *
CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
const char *passwd, const char *db,
uint port, const char *unix_socket,ulong client_flag)
diff --git a/sql/Makefile.am b/sql/Makefile.am
index d951aae91e1..7a55367c717 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -59,8 +59,12 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
log_event.h sql_repl.h slave.h \
stacktrace.h sql_sort.h sql_cache.h set_var.h \
spatial.h gstream.h client_settings.h tzfile.h \
- tztime.h examples/ha_example.h examples/ha_archive.h \
+ tztime.h \
+ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \
+ parse_file.h sql_view.h \
+ examples/ha_example.h examples/ha_archive.h \
examples/ha_tina.h
+
mysqld_SOURCES = sql_lex.cc sql_handler.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \
@@ -89,11 +93,13 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
slave.cc sql_repl.cc sql_union.cc sql_derived.cc \
client.c sql_client.cc mini_client_errors.c pack.c\
stacktrace.c repl_failsafe.h repl_failsafe.cc \
+ sql_olap.cc sql_view.cc \
gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \
tztime.cc my_time.c \
+ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
+ sp_cache.cc parse_file.cc \
examples/ha_example.cc examples/ha_archive.cc \
examples/ha_tina.cc
-
gen_lex_hash_SOURCES = gen_lex_hash.cc
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
mysql_tzinfo_to_sql_SOURCES = mysql_tzinfo_to_sql.cc
diff --git a/sql/filesort.cc b/sql/filesort.cc
index a84fa4fe6f4..fe2b3850197 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -48,7 +48,8 @@ static int merge_index(SORTPARAM *param,uchar *sort_buffer,
BUFFPEK *buffpek,
uint maxbuffer,IO_CACHE *tempfile,
IO_CACHE *outfile);
-static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count);
+static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count,
+ FILESORT_INFO *table_sort);
static uint sortlength(SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset);
static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield,
@@ -106,8 +107,16 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_PUSH(""); /* No DBUG here */
#endif
-
- outfile= table->sort.io_cache;
+ FILESORT_INFO table_sort;
+ /*
+ Don't use table->sort in filesort as it is also used by
+ QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end
+ when index_merge select has finished with it.
+ */
+ memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
+ table->sort.io_cache= NULL;
+
+ outfile= table_sort.io_cache;
my_b_clear(&tempfile);
my_b_clear(&buffpek_pointers);
buffpek=0;
@@ -128,14 +137,15 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
param.sort_length,
&param.addon_length);
}
- table->sort.addon_buf= 0;
- table->sort.addon_length= param.addon_length;
- table->sort.addon_field= param.addon_field;
- table->sort.unpack= unpack_addon_fields;
+
+ table_sort.addon_buf= 0;
+ table_sort.addon_length= param.addon_length;
+ table_sort.addon_field= param.addon_field;
+ table_sort.unpack= unpack_addon_fields;
if (param.addon_field)
{
param.res_length= param.addon_length;
- if (!(table->sort.addon_buf= (byte *) my_malloc(param.addon_length,
+ if (!(table_sort.addon_buf= (byte *) my_malloc(param.addon_length,
MYF(MY_WME))))
goto err;
}
@@ -212,7 +222,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
if (maxbuffer == 0) // The whole set is in memory
{
- if (save_index(&param,sort_keys,(uint) records))
+ if (save_index(&param,sort_keys,(uint) records, &table_sort))
goto err;
}
else
@@ -275,6 +285,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_POP(); /* Ok to DBUG */
#endif
+ memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
DBUG_PRINT("exit",("records: %ld",records));
DBUG_RETURN(error ? HA_POS_ERROR : records);
} /* filesort */
@@ -349,7 +360,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
byte *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
my_off_t record;
TABLE *sort_form;
- volatile my_bool *killed= &current_thd->killed;
+ volatile THD::killed_state *killed= &current_thd->killed;
handler *file;
DBUG_ENTER("find_all_keys");
DBUG_PRINT("info",("using: %s",(select?select->quick?"ranges":"where":"every row")));
@@ -378,12 +389,24 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
current_thd->variables.read_buff_size);
}
+ READ_RECORD read_record_info;
+ if (quick_select)
+ {
+ if (select->quick->reset())
+ DBUG_RETURN(HA_POS_ERROR);
+ init_read_record(&read_record_info, current_thd, select->quick->head,
+ select, 1, 1);
+ }
+
for (;;)
{
if (quick_select)
{
- if ((error=select->quick->get_next()))
- break;
+ if ((error= read_record_info.read_record(&read_record_info)))
+ {
+ error= HA_ERR_END_OF_FILE;
+ break;
+ }
file->position(sort_form->record[0]);
}
else /* Not quick-select */
@@ -411,6 +434,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
if (error && error != HA_ERR_RECORD_DELETED)
break;
}
+
if (*killed)
{
DBUG_PRINT("info",("Sort killed by user"));
@@ -434,9 +458,21 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
else
file->unlock_row();
}
- (void) file->extra(HA_EXTRA_NO_CACHE); /* End cacheing of records */
- if (!next_pos)
- file->ha_rnd_end();
+ if (quick_select)
+ {
+ /*
+ index_merge quick select uses table->sort when retrieving rows, so free
+ resoures it has allocated.
+ */
+ end_read_record(&read_record_info);
+ }
+ else
+ {
+ (void) file->extra(HA_EXTRA_NO_CACHE); /* End cacheing of records */
+ if (!next_pos)
+ file->ha_rnd_end();
+ }
+
DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos));
if (error != HA_ERR_END_OF_FILE)
{
@@ -691,8 +727,8 @@ static void make_sortkey(register SORTPARAM *param,
return;
}
-
-static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count)
+static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
+ FILESORT_INFO *table_sort)
{
uint offset,res_length;
byte *to;
@@ -703,7 +739,7 @@ static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count)
offset= param->rec_length-res_length;
if ((ha_rows) count > param->max_rows)
count=(uint) param->max_rows;
- if (!(to= param->sort_form->sort.record_pointers=
+ if (!(to= table_sort->record_pointers=
(byte*) my_malloc(res_length*count, MYF(MY_WME))))
DBUG_RETURN(1); /* purecov: inspected */
for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++)
@@ -783,6 +819,39 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
} /* read_to_buffer */
+/*
+ Put all room used by freed buffer to use in adjacent buffer. Note, that
+ we can't simply distribute memory evenly between all buffers, because
+ new areas must not overlap with old ones.
+ SYNOPSYS
+ reuse_freed_buff()
+ queue IN list of non-empty buffers, without freed buffer
+ reuse IN empty buffer
+ key_length IN key length
+*/
+
+void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
+{
+ uchar *reuse_end= reuse->base + reuse->max_keys * key_length;
+ for (uint i= 0; i < queue->elements; ++i)
+ {
+ BUFFPEK *bp= (BUFFPEK *) queue_element(queue, i);
+ if (bp->base + bp->max_keys * key_length == reuse->base)
+ {
+ bp->max_keys+= reuse->max_keys;
+ return;
+ }
+ else if (bp->base == reuse_end)
+ {
+ bp->base= reuse->base;
+ bp->max_keys+= reuse->max_keys;
+ return;
+ }
+ }
+ DBUG_ASSERT(0);
+}
+
+
/*
Merge buffers to one buffer
*/
@@ -798,18 +867,18 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
ha_rows max_rows,org_max_rows;
my_off_t to_start_filepos;
uchar *strpos;
- BUFFPEK *buffpek,**refpek;
+ BUFFPEK *buffpek;
QUEUE queue;
qsort2_cmp cmp;
- volatile my_bool *killed= &current_thd->killed;
- my_bool not_killable;
+ volatile THD::killed_state *killed= &current_thd->killed;
+ THD::killed_state not_killable;
DBUG_ENTER("merge_buffers");
statistic_increment(filesort_merge_passes, &LOCK_status);
if (param->not_killable)
{
killed= &not_killable;
- not_killable= 0;
+ not_killable= THD::NOT_KILLED;
}
error=0;
@@ -909,29 +978,8 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
if (!(error= (int) read_to_buffer(from_file,buffpek,
rec_length)))
{
- uchar *base= buffpek->base;
- ulong max_keys= buffpek->max_keys;
-
VOID(queue_remove(&queue,0));
-
- /* Put room used by buffer to use in other buffer */
- for (refpek= (BUFFPEK**) &queue_top(&queue);
- refpek <= (BUFFPEK**) &queue_end(&queue);
- refpek++)
- {
- buffpek= *refpek;
- if (buffpek->base+buffpek->max_keys*rec_length == base)
- {
- buffpek->max_keys+= max_keys;
- break;
- }
- else if (base+max_keys*rec_length == buffpek->base)
- {
- buffpek->base= base;
- buffpek->max_keys+= max_keys;
- break;
- }
- }
+ reuse_freed_buff(&queue, buffpek, rec_length);
break; /* One buffer have been removed */
}
else if (error == -1)
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
index 7cd534d60b3..b4f07073afa 100644
--- a/sql/ha_berkeley.cc
+++ b/sql/ha_berkeley.cc
@@ -2179,7 +2179,7 @@ static void print_msg(THD *thd, const char *table_name, const char *op_name,
protocol->store(msg_type);
protocol->store(msgbuf);
if (protocol->write())
- thd->killed=1;
+ thd->killed=THD::KILL_CONNECTION;
}
#endif
@@ -2561,4 +2561,29 @@ ha_rows ha_berkeley::estimate_number_of_rows()
return share->rows + HA_BERKELEY_EXTRA_ROWS;
}
+int ha_berkeley::cmp_ref(const byte *ref1, const byte *ref2)
+{
+ if (hidden_primary_key)
+ return memcmp(ref1, ref2, BDB_HIDDEN_PRIMARY_KEY_LENGTH);
+
+ int result;
+ Field *field;
+ KEY *key_info=table->key_info+table->primary_key;
+ KEY_PART_INFO *key_part=key_info->key_part;
+ KEY_PART_INFO *end=key_part+key_info->key_parts;
+
+ for (; key_part != end; key_part++)
+ {
+ field= key_part->field;
+ result= field->pack_cmp((const char*)ref1, (const char*)ref2,
+ key_part->length);
+ if (result)
+ return result;
+ ref1 += field->packed_col_length((const char*)ref1, key_part->length);
+ ref2 += field->packed_col_length((const char*)ref2, key_part->length);
+ }
+
+ return 0;
+}
+
#endif /* HAVE_BERKELEY_DB */
diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h
index 5cba3bebf10..975d70abf7b 100644
--- a/sql/ha_berkeley.h
+++ b/sql/ha_berkeley.h
@@ -156,6 +156,8 @@ class ha_berkeley: public handler
longlong get_auto_increment();
void print_error(int error, myf errflag);
uint8 table_cache_type() { return HA_CACHE_TBL_TRANSACT; }
+ bool primary_key_is_clustered() { return true; }
+ int cmp_ref(const byte *ref1, const byte *ref2);
};
extern bool berkeley_shared_data;
diff --git a/sql/ha_heap.h b/sql/ha_heap.h
index 9ca6b9b76b6..c326f570feb 100644
--- a/sql/ha_heap.h
+++ b/sql/ha_heap.h
@@ -91,5 +91,10 @@ class ha_heap: public handler
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
-
+ int cmp_ref(const byte *ref1, const byte *ref2)
+ {
+ HEAP_PTR ptr1=*(HEAP_PTR*)ref1;
+ HEAP_PTR ptr2=*(HEAP_PTR*)ref2;
+ return ptr1 < ptr2? -1 : (ptr1 > ptr2? 1 : 0);
+ }
};
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index d787159241a..3003425a489 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -802,6 +802,8 @@ ha_innobase::init_table_handle_for_HANDLER(void)
prebuilt->read_just_key = FALSE;
prebuilt->used_in_HANDLER = TRUE;
+
+ prebuilt->keep_other_fields_on_keyread = FALSE;
}
/*************************************************************************
@@ -2111,12 +2113,15 @@ build_template(
update field->query_id so that the formula
thd->query_id == field->query_id did not work. */
- if (templ_type == ROW_MYSQL_REC_FIELDS
- && !(fetch_all_in_key
- && dict_index_contains_col_or_prefix(index, i))
- && !(fetch_primary_key_cols
- && dict_table_col_in_clustered_key(index->table, i))
- && thd->query_id != field->query_id) {
+ ibool index_contains_field=
+ dict_index_contains_col_or_prefix(index, i);
+
+ if (templ_type == ROW_MYSQL_REC_FIELDS &&
+ ((prebuilt->read_just_key && !index_contains_field) ||
+ (!(fetch_all_in_key && index_contains_field) &&
+ !(fetch_primary_key_cols &&
+ dict_table_col_in_clustered_key(index->table, i)) &&
+ thd->query_id != field->query_id))) {
/* This field is not needed in the query, skip it */
@@ -4626,9 +4631,11 @@ ha_innobase::extra(
if (prebuilt->blob_heap) {
row_mysql_prebuilt_free_blob_heap(prebuilt);
}
+ prebuilt->keep_other_fields_on_keyread = 0;
prebuilt->read_just_key = 0;
break;
case HA_EXTRA_RESET_STATE:
+ prebuilt->keep_other_fields_on_keyread = 0;
prebuilt->read_just_key = 0;
break;
case HA_EXTRA_NO_KEYREAD:
@@ -4647,6 +4654,9 @@ ha_innobase::extra(
case HA_EXTRA_KEYREAD:
prebuilt->read_just_key = 1;
break;
+ case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
+ prebuilt->keep_other_fields_on_keyread = 1;
+ break;
default:/* Do nothing */
;
}
@@ -4695,6 +4705,7 @@ ha_innobase::start_stmt(
prebuilt->sql_stat_start = TRUE;
prebuilt->hint_need_to_fetch_extra_cols = 0;
prebuilt->read_just_key = 0;
+ prebuilt->keep_other_fields_on_keyread = FALSE;
if (!prebuilt->mysql_has_locked) {
/* This handle is for a temporary table created inside
@@ -4787,6 +4798,7 @@ ha_innobase::external_lock(
prebuilt->hint_need_to_fetch_extra_cols = 0;
prebuilt->read_just_key = 0;
+ prebuilt->keep_other_fields_on_keyread = FALSE;
if (lock_type == F_WRLCK) {
@@ -4946,7 +4958,8 @@ innodb_show_status(
field_list.push_back(new Item_empty_string("Status", flen));
- if (protocol->send_fields(&field_list, 1)) {
+ if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF)) {
my_free(str, MYF(0));
@@ -5255,6 +5268,55 @@ innobase_store_binlog_offset_and_flush_log(
log_buffer_flush_to_disk();
}
+
+int
+ha_innobase::cmp_ref(
+ const mysql_byte *ref1,
+ const mysql_byte *ref2)
+{
+ row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt;
+ enum_field_types mysql_type;
+ Field* field;
+ int result;
+
+ if (prebuilt->clust_index_was_generated)
+ return memcmp(ref1, ref2, DATA_ROW_ID_LEN);
+
+ /* Do type-aware comparison of Primary Key members. PK members
+ are always NOT NULL, so no checks for NULL are performed */
+ KEY_PART_INFO *key_part= table->key_info[table->primary_key].key_part;
+ KEY_PART_INFO *key_part_end=
+ key_part + table->key_info[table->primary_key].key_parts;
+ for (; key_part != key_part_end; ++key_part) {
+ field = key_part->field;
+ mysql_type = field->type();
+ if (mysql_type == FIELD_TYPE_TINY_BLOB
+ || mysql_type == FIELD_TYPE_MEDIUM_BLOB
+ || mysql_type == FIELD_TYPE_BLOB
+ || mysql_type == FIELD_TYPE_LONG_BLOB) {
+
+ ut_a(!ref1[1]);
+ ut_a(!ref2[1]);
+ byte len1= *ref1;
+ byte len2= *ref2;
+ ref1 += 2;
+ ref2 += 2;
+ result =
+ ((Field_blob*)field)->cmp((const char*)ref1, len1,
+ (const char*)ref2, len2);
+ } else {
+ result =
+ field->cmp((const char*)ref1, (const char*)ref2);
+ }
+
+ if (result)
+ return result;
+ ref1 += key_part->length;
+ ref2 += key_part->length;
+ }
+ return 0;
+}
+
char *ha_innobase::get_mysql_bin_log_name()
{
return trx_sys_mysql_bin_log_name;
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
index 6556931fa1a..1bfb86de944 100644
--- a/sql/ha_innodb.h
+++ b/sql/ha_innodb.h
@@ -166,9 +166,10 @@ class ha_innobase: public handler
void init_table_handle_for_HANDLER();
longlong get_auto_increment();
uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; }
-
- static char *get_mysql_bin_log_name();
+ static char *get_mysql_bin_log_name();
static ulonglong get_mysql_bin_log_pos();
+ bool primary_key_is_clustered() { return true; }
+ int cmp_ref(const byte *ref1, const byte *ref2);
};
extern uint innobase_init_flags, innobase_lock_type;
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index 3d2d25b3e7d..89288d6059f 100644
--- a/sql/ha_myisam.cc
+++ b/sql/ha_myisam.cc
@@ -87,9 +87,10 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type,
extern "C" {
-volatile my_bool *killed_ptr(MI_CHECK *param)
+volatile int *killed_ptr(MI_CHECK *param)
{
- return &(((THD *)(param->thd))->killed);
+ /* In theory Unsafe conversion, but should be ok for now */
+ return (int*) &(((THD *)(param->thd))->killed);
}
void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h
index 6fde84d6f6f..972d6b18e19 100644
--- a/sql/ha_myisam.h
+++ b/sql/ha_myisam.h
@@ -81,7 +81,6 @@ class ha_myisam: public handler
int index_first(byte * buf);
int index_last(byte * buf);
int index_next_same(byte *buf, const byte *key, uint keylen);
- int index_end() { ft_handler=NULL; return 0; }
int ft_init()
{
if (!ft_handler)
diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc
index 9aa6d039efb..895d025d6fc 100644
--- a/sql/ha_myisammrg.cc
+++ b/sql/ha_myisammrg.cc
@@ -347,7 +347,7 @@ void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info)
create_info->merge_list.elements++;
(*create_info->merge_list.next) = (byte*) ptr;
- create_info->merge_list.next= (byte**) &ptr->next;
+ create_info->merge_list.next= (byte**) &ptr->next_local;
}
*create_info->merge_list.next=0;
}
@@ -375,7 +375,7 @@ int ha_myisammrg::create(const char *name, register TABLE *form,
if (!(table_names= (char**) thd->alloc((create_info->merge_list.elements+1)*
sizeof(char*))))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- for (pos=table_names ; tables ; tables=tables->next)
+ for (pos= table_names; tables; tables= tables->next_local)
{
char *table_name;
TABLE **tbl= 0;
diff --git a/sql/handler.cc b/sql/handler.cc
index 15f30b25eb8..640c4f3710d 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1514,4 +1514,3 @@ int handler::index_read_idx(byte * buf, uint index, const byte * key,
error= ha_index_end();
return error;
}
-
diff --git a/sql/handler.h b/sql/handler.h
index 4bb1d66eef3..7e5e626f713 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -306,29 +306,33 @@ public:
int ha_index_init(uint idx)
{
+ DBUG_ENTER("ha_index_init");
DBUG_ASSERT(inited==NONE);
inited=INDEX;
- return index_init(idx);
+ DBUG_RETURN(index_init(idx));
}
int ha_index_end()
{
+ DBUG_ENTER("ha_index_end");
DBUG_ASSERT(inited==INDEX);
inited=NONE;
- return index_end();
+ DBUG_RETURN(index_end());
}
int ha_rnd_init(bool scan)
{
+ DBUG_ENTER("ha_rnd_init");
DBUG_ASSERT(inited==NONE || (inited==RND && scan));
inited=RND;
- return rnd_init(scan);
+ DBUG_RETURN(rnd_init(scan));
}
int ha_rnd_end()
{
+ DBUG_ENTER("ha_rnd_end");
DBUG_ASSERT(inited==RND);
inited=NONE;
- return rnd_end();
+ DBUG_RETURN(rnd_end());
}
- /* this is neseccary in many places, e.g. in HANDLER command */
+ /* this is necessary in many places, e.g. in HANDLER command */
int ha_index_or_rnd_end()
{
return inited == INDEX ? ha_index_end() : inited == RND ? ha_rnd_end() : 0;
@@ -363,6 +367,7 @@ public:
virtual int read_range_next();
int compare_key(key_range *range);
virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
+ void ft_end() { ft_handler=NULL; }
virtual FT_INFO *ft_init_ext(uint flags,uint inx,const byte *key,
uint keylen)
{ return NULL; }
@@ -497,10 +502,18 @@ public:
/* Type of table for caching query */
virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; }
- /*
- Is query with this table cachable (have sense only for ASKTRANSACT
- tables)
- */
+
+ /*
+ RETURN
+ true Primary key (if there is one) is clustered key covering all fields
+ false otherwise
+ */
+ virtual bool primary_key_is_clustered() { return FALSE; }
+
+ virtual int cmp_ref(const byte *ref1, const byte *ref2)
+ {
+ return memcmp(ref1, ref2, ref_length);
+ }
};
/* Some extern variables used with handlers */
diff --git a/sql/item.cc b/sql/item.cc
index e9ef3b6a763..ecbe2d22fa4 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -22,6 +22,8 @@
#include "mysql_priv.h"
#include <m_ctype.h>
#include "my_dir.h"
+#include "sp_rcontext.h"
+#include "sql_acl.h"
static void mark_as_dependent(THD *thd,
SELECT_LEX *last, SELECT_LEX *current,
@@ -41,7 +43,7 @@ void item_init(void)
}
Item::Item():
- fixed(0)
+ name_length(0), fixed(0)
{
marker= 0;
maybe_null=null_value=with_sum_func=unsigned_flag=0;
@@ -96,9 +98,9 @@ void Item::print_item_w_name(String *str)
print(str);
if (name)
{
- str->append(" AS `", 5);
- str->append(name);
- str->append('`');
+ THD *thd= current_thd;
+ str->append(" AS ", 4);
+ append_identifier(thd, str, name, strlen(name));
}
}
@@ -174,12 +176,15 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
{
/* Empty string, used by AS or internal function like last_insert_id() */
name= (char*) str;
+ name_length= 0;
return;
}
if (cs->ctype)
{
- // This will probably need a better implementation in the future:
- // a function in CHARSET_INFO structure.
+ /*
+ This will probably need a better implementation in the future:
+ a function in CHARSET_INFO structure.
+ */
while (length && !my_isgraph(cs,*str))
{ // Fix problem with yacc
length--;
@@ -189,12 +194,12 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
if (!my_charset_same(cs, system_charset_info))
{
uint32 res_length;
- name= sql_strmake_with_convert(str, length, cs,
+ name= sql_strmake_with_convert(str, name_length= length, cs,
MAX_ALIAS_NAME, system_charset_info,
&res_length);
}
else
- name=sql_strmake(str, min(length,MAX_ALIAS_NAME));
+ name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME)));
}
@@ -265,6 +270,34 @@ CHARSET_INFO *Item::default_charset()
return current_thd->variables.collation_connection;
}
+
+Item *
+Item_splocal::this_item()
+{
+ THD *thd= current_thd;
+
+ return thd->spcont->get_item(m_offset);
+}
+
+Item *
+Item_splocal::this_const_item() const
+{
+ THD *thd= current_thd;
+
+ return thd->spcont->get_item(m_offset);
+}
+
+Item::Type
+Item_splocal::type() const
+{
+ THD *thd= current_thd;
+
+ if (thd->spcont)
+ return thd->spcont->get_item(m_offset)->type();
+ return NULL_ITEM; // Anything but SUBSELECT_ITEM
+}
+
+
bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion)
{
nagg++;
@@ -351,7 +384,8 @@ bool DTCollation::aggregate(DTCollation &dt, bool superset_conversion)
}
Item_field::Item_field(Field *f)
- :Item_ident(NullS, f->table_name, f->field_name)
+ :Item_ident(NullS, f->table_name, f->field_name),
+ have_privileges(0), any_privileges(0)
{
set_field(f);
collation.set(DERIVATION_IMPLICIT);
@@ -360,7 +394,8 @@ Item_field::Item_field(Field *f)
Item_field::Item_field(THD *thd, Field *f)
:Item_ident(NullS, thd->strdup(f->table_name),
- thd->strdup(f->field_name))
+ thd->strdup(f->field_name)),
+ have_privileges(0), any_privileges(0)
{
set_field(f);
collation.set(DERIVATION_IMPLICIT);
@@ -371,7 +406,9 @@ Item_field::Item_field(THD *thd, Field *f)
Item_field::Item_field(THD *thd, Item_field *item)
:Item_ident(thd, item),
field(item->field),
- result_field(item->result_field)
+ result_field(item->result_field),
+ have_privileges(item->have_privileges),
+ any_privileges(item->any_privileges)
{
collation.set(DERIVATION_IMPLICIT);
}
@@ -414,6 +451,36 @@ const char *Item_ident::full_name() const
return tmp;
}
+void Item_ident::print(String *str)
+{
+ THD *thd= current_thd;
+ if (!table_name || !field_name)
+ {
+ const char *nm= field_name ? field_name : name ? name : "tmp_field";
+ append_identifier(thd, str, nm, strlen(nm));
+ return;
+ }
+ if (db_name && db_name[0])
+ {
+ append_identifier(thd, str, db_name, strlen(db_name));
+ str->append('.');
+ append_identifier(thd, str, table_name, strlen(table_name));
+ str->append('.');
+ append_identifier(thd, str, field_name, strlen(field_name));
+ }
+ else
+ {
+ if (table_name[0])
+ {
+ append_identifier(thd, str, table_name, strlen(table_name));
+ str->append('.');
+ append_identifier(thd, str, field_name, strlen(field_name));
+ }
+ else
+ append_identifier(thd, str, field_name, strlen(field_name));
+ }
+}
+
/* ARGSUSED */
String *Item_field::val_str(String *str)
{
@@ -1109,8 +1176,27 @@ bool Item_param::convert_str_value(THD *thd)
return rc;
}
-/* End of Item_param related */
+void Item_param::print(String *str)
+{
+ if (state == NO_VALUE)
+ {
+ str->append('?');
+ }
+ else
+ {
+ char buffer[80];
+ String tmp(buffer, sizeof(buffer), &my_charset_bin);
+ const String *res;
+ res= query_val_str(&tmp);
+ str->append(*res);
+ }
+}
+
+
+/****************************************************************************
+ Item_copy_string
+****************************************************************************/
void Item_copy_string::copy()
{
@@ -1224,10 +1310,10 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
DBUG_ASSERT(fixed == 0);
if (!field) // If field is not checked
{
- TABLE_LIST *where= 0;
bool upward_lookup= 0;
Field *tmp= (Field *)not_found_field;
- if ((tmp= find_field_in_tables(thd, this, tables, &where, 0)) ==
+ if ((tmp= find_field_in_tables(thd, this, tables, ref, 0,
+ !any_privileges)) ==
not_found_field)
{
/*
@@ -1263,12 +1349,11 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
it is primary INSERT st_select_lex => skip first table
resolving
*/
- table_list= table_list->next;
+ table_list= table_list->next_local;
}
Item_subselect *prev_subselect_item= prev_unit->item;
- enum_parsing_place place=
- prev_subselect_item->parsing_place;
+ enum_parsing_place place= prev_subselect_item->parsing_place;
/*
check table fields only if subquery used somewhere out of HAVING
or SELECT list or outer SELECT do not use groupping (i.e. tables
@@ -1278,15 +1363,16 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
place != SELECT_LIST) ||
(sl->with_sum_func == 0 && sl->group_list.elements == 0)) &&
(tmp= find_field_in_tables(thd, this,
- table_list, &where,
- 0)) != not_found_field)
- {
- if (!tmp)
- return -1;
- prev_subselect_item->used_tables_cache|= tmp->table->map;
- prev_subselect_item->const_item_cache= 0;
- break;
- }
+ table_list, ref,
+ 0, 1)) != not_found_field)
+ {
+ if (tmp && tmp != view_ref_found)
+ {
+ prev_subselect_item->used_tables_cache|= tmp->table->map;
+ prev_subselect_item->const_item_cache= 0;
+ }
+ break;
+ }
if (sl->resolve_mode == SELECT_LEX::SELECT_MODE &&
(refer= find_item_in_list(this, sl->item_list, &counter,
REPORT_EXCEPT_NOT_FOUND)) !=
@@ -1324,7 +1410,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
else
{
// Call to report error
- find_field_in_tables(thd, this, tables, &where, 1);
+ find_field_in_tables(thd, this, tables, ref, 1, 1);
}
return -1;
}
@@ -1362,8 +1448,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
{
Item_ref *rf;
*ref= rf= new Item_ref(ref, *ref,
- (where->db[0]?where->db:0),
- (char *)where->alias,
+ (cached_table->db[0]?cached_table->db:0),
+ (char *)cached_table->alias,
(char *)field_name);
if (!rf)
return 1;
@@ -1378,7 +1464,20 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
else if (!tmp)
return -1;
- set_field(tmp);
+ /*
+ if it is not expression from merged VIEW we will set this field.
+
+ We can leave expression substituted from view for next PS/SP rexecution
+ (i.e. do not register this substitution for reverting on cleupup()
+ (register_item_tree_changing())), because this subtree will be
+ fix_field'ed during setup_tables()->setup_ancestor() (i.e. before
+ all other expressions of query, and references on tables which do
+ not present in query will not make problems.
+
+ Also we suppose that view can't be changed during PS/SP life.
+ */
+ if (tmp != view_ref_found)
+ set_field(tmp);
}
else if (thd->set_query_id && field->query_id != thd->query_id)
{
@@ -1388,6 +1487,36 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
table->used_fields++;
table->used_keys.intersect(field->part_of_key);
}
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (any_privileges)
+ {
+ char *db, *tab;
+ if (cached_table->view)
+ {
+ db= cached_table->view_db.str;
+ tab= cached_table->view_name.str;
+ }
+ else
+ {
+ db= cached_table->db;
+ tab= cached_table->real_name;
+ }
+ if (!(have_privileges= (get_column_grant(thd, &field->table->grant,
+ db, tab, field_name) &
+ VIEW_ANY_ACL)))
+ {
+ my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
+ ER(ER_COLUMNACCESS_DENIED_ERROR),
+ MYF(0),
+ "ANY",
+ thd->priv_user,
+ thd->host_or_ip,
+ field_name,
+ tab);
+ return 1;
+ }
+ }
+#endif
fixed= 1;
return 0;
}
@@ -1884,13 +2013,13 @@ bool Item_field::send(Protocol *protocol, String *buffer)
Find field in select list having the same name
*/
-bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
+bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
{
DBUG_ASSERT(fixed == 0);
uint counter;
if (!ref)
{
- TABLE_LIST *where= 0, *table_list;
+ TABLE_LIST *table_list;
bool upward_lookup= 0;
SELECT_LEX_UNIT *prev_unit= thd->lex->current_select->master_unit();
SELECT_LEX *sl= prev_unit->outer_select();
@@ -1943,12 +2072,11 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
if (sl->resolve_mode == SELECT_LEX::INSERT_MODE && table_list)
{
// it is primary INSERT st_select_lex => skip first table resolving
- table_list= table_list->next;
+ table_list= table_list->next_local;
}
- enum_parsing_place place=
- prev_subselect_item->parsing_place;
+ enum_parsing_place place= prev_subselect_item->parsing_place;
/*
- check table fields only if subquery used somewhere out of HAVING
+ Check table fields only if subquery used somewhere out of HAVING
or SELECT list or outer SELECT do not use groupping (i.e. tables
are accessable)
*/
@@ -1956,14 +2084,18 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
place != SELECT_LIST) ||
(sl->with_sum_func == 0 && sl->group_list.elements == 0)) &&
(tmp= find_field_in_tables(thd, this,
- table_list, &where,
- 0)) != not_found_field)
- {
- prev_subselect_item->used_tables_cache|= tmp->table->map;
- prev_subselect_item->const_item_cache= 0;
- break;
- }
- // Reference is not found => depend from outer (or just error)
+ table_list, reference,
+ 0, 1)) != not_found_field)
+ {
+ if (tmp && tmp != view_ref_found)
+ {
+ prev_subselect_item->used_tables_cache|= tmp->table->map;
+ prev_subselect_item->const_item_cache= 0;
+ }
+ break;
+ }
+
+ // Reference is not found => depend from outer (or just error)
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
prev_subselect_item->const_item_cache= 0;
@@ -1998,12 +2130,26 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
else if (tmp != not_found_field)
{
ref= 0; // To prevent "delete *ref;" on ~Item_erf() of this item
- Item_field* fld;
- if (!((*reference)= fld= new Item_field(tmp)))
- return 1;
- register_item_tree_changing(reference);
- mark_as_dependent(thd, last, thd->lex->current_select, fld);
- return 0;
+ if (tmp != view_ref_found)
+ {
+ Item_field* fld;
+ if (!((*reference)= fld= new Item_field(tmp)))
+ return 1;
+ mark_as_dependent(thd, last, thd->lex->current_select, fld);
+ return 0;
+ register_item_tree_changing(reference);
+ }
+ /*
+ We can leave expression substituted from view for next PS/SP
+ rexecution (i.e. do not register this substitution for reverting
+ on cleupup() (register_item_tree_changing())), because this
+ subtree will be fix_field'ed during
+ setup_tables()->setup_ancestor() (i.e. before all other
+ expressions of query, and references on tables which do not
+ present in query will not make problems.
+
+ Also we suppose that view can't be changed during PS/SP life.
+ */
}
else
{
@@ -2068,6 +2214,7 @@ void Item_ref::cleanup()
{
DBUG_ENTER("Item_ref::cleanup");
Item_ident::cleanup();
+ result_field= 0;
if (hook_ptr)
*hook_ptr= orig_item;
DBUG_VOID_RETURN;
@@ -2083,6 +2230,51 @@ void Item_ref::print(String *str)
}
+bool Item_ref::send(Protocol *prot, String *tmp)
+{
+ if (result_field)
+ return prot->store(result_field);
+ return (*ref)->send(prot, tmp);
+}
+
+
+double Item_ref::val_result()
+{
+ if (result_field)
+ {
+ if ((null_value= result_field->is_null()))
+ return 0.0;
+ return result_field->val_real();
+ }
+ return val();
+}
+
+
+longlong Item_ref::val_int_result()
+{
+ if (result_field)
+ {
+ if ((null_value= result_field->is_null()))
+ return 0;
+ return result_field->val_int();
+ }
+ return val_int();
+}
+
+
+String *Item_ref::str_result(String* str)
+{
+ if (result_field)
+ {
+ if ((null_value= result_field->is_null()))
+ return 0;
+ str->set_charset(str_value.charset());
+ return result_field->val_str(str, &str_value);
+ }
+ return val_str(str);
+}
+
+
void Item_ref_null_helper::print(String *str)
{
str->append("<ref_null_helper>(", 18);
diff --git a/sql/item.h b/sql/item.h
index 23c5c844f21..4d3f1736b4e 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -117,6 +117,7 @@ public:
my_string name; /* Name from select */
Item *next;
uint32 max_length;
+ uint name_length; /* Length of name */
uint8 marker,decimals;
my_bool maybe_null; /* If item may be null */
my_bool null_value; /* if item is null */
@@ -136,7 +137,10 @@ public:
optimisation changes in prepared statements
*/
Item(THD *thd, Item *item);
- virtual ~Item() { name=0; } /*lint -e1509 */
+ virtual ~Item()
+ {
+ name=0;
+ } /*lint -e1509 */
void set_name(const char *str,uint length, CHARSET_INFO *cs);
void init_make_field(Send_field *tmp_field,enum enum_field_types type);
virtual void cleanup()
@@ -264,6 +268,9 @@ public:
virtual bool remove_dependence_processor(byte * arg) { return 0; }
virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; }
+ virtual Item *this_item() { return this; } /* For SPs mostly. */
+ virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
+
// Row emulation
virtual uint cols() { return 1; }
virtual Item* el(uint i) { return this; }
@@ -285,6 +292,104 @@ public:
};
+// A local SP variable (incl. parameters), used in runtime
+class Item_splocal : public Item
+{
+private:
+
+ uint m_offset;
+ LEX_STRING m_name;
+
+public:
+
+ Item_splocal(LEX_STRING name, uint offset)
+ : m_offset(offset), m_name(name)
+ {
+ Item::maybe_null= TRUE;
+ }
+
+ Item *this_item();
+ Item *this_const_item() const;
+
+ inline uint get_offset()
+ {
+ return m_offset;
+ }
+
+ // Abstract methods inherited from Item. Just defer the call to
+ // the item in the frame
+ enum Type type() const;
+
+ inline double val()
+ {
+ Item *it= this_item();
+ double ret= it->val();
+ Item::null_value= it->null_value;
+ return ret;
+ }
+
+ inline longlong val_int()
+ {
+ Item *it= this_item();
+ longlong ret= it->val_int();
+ Item::null_value= it->null_value;
+ return ret;
+ }
+
+ inline String *val_str(String *sp)
+ {
+ Item *it= this_item();
+ String *ret= it->val_str(sp);
+ Item::null_value= it->null_value;
+ return ret;
+ }
+
+ inline bool is_null()
+ {
+ Item *it= this_item();
+ bool ret= it->is_null();
+ Item::null_value= it->null_value;
+ return ret;
+ }
+
+ inline void make_field(Send_field *field)
+ {
+ Item *it= this_item();
+
+ it->set_name(m_name.str, m_name.length, system_charset_info);
+ it->make_field(field);
+ }
+
+ inline Item_result result_type() const
+ {
+ return this_const_item()->result_type();
+ }
+
+ inline bool const_item() const
+ {
+ return TRUE;
+ }
+
+ inline int save_in_field(Field *field, bool no_conversions)
+ {
+ return this_item()->save_in_field(field, no_conversions);
+ }
+
+ void print(String *str)
+ {
+ str->reserve(m_name.length+8);
+ str->append(m_name.str, m_name.length);
+ str->append('@');
+ str->qs_append(m_offset);
+ }
+
+ inline bool send(Protocol *protocol, String *str)
+ {
+ return this_item()->send(protocol, str);
+ }
+};
+
+
class Item_num: public Item
{
public:
@@ -331,6 +436,11 @@ public:
void register_item_tree_changing(Item **ref)
{ changed_during_fix_field= ref; }
bool remove_dependence_processor(byte * arg);
+ void print(String *str);
+
+ friend bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
+ const char *table_name, List_iterator<Item> *it,
+ bool any_privileges);
};
@@ -339,11 +449,18 @@ class Item_field :public Item_ident
void set_field(Field *field);
public:
Field *field,*result_field;
+ /*
+ if any_privileges set to TRUE then here real effective privileges will
+ be stored
+ */
+ uint have_privileges;
+ /* field need any privileges (for VIEW creation) */
+ bool any_privileges;
Item_field(const char *db_par,const char *table_name_par,
const char *field_name_par)
:Item_ident(db_par,table_name_par,field_name_par),
- field(0), result_field(0)
+ field(0), result_field(0), have_privileges(0), any_privileges(0)
{ collation.set(DERIVATION_IMPLICIT); }
// Constructor need to process subselect with temporary tables (see Item)
Item_field(THD *thd, Item_field *item);
@@ -524,7 +641,7 @@ public:
*/
virtual table_map used_tables() const
{ return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; }
- void print(String *str) { str->append('?'); }
+ void print(String *str);
/* parameter never equal to other parameter of other item */
bool eq(const Item *item, bool binary_cmp) const { return 0; }
};
@@ -558,6 +675,17 @@ public:
};
+class Item_static_int_func :public Item_int
+{
+ const char *func_name;
+public:
+ Item_static_int_func(const char *str_arg, longlong i, uint length)
+ :Item_int(NullS, i, length), func_name(str_arg)
+ {}
+ void print(String *str) { str->append(func_name); }
+};
+
+
class Item_uint :public Item_int
{
public:
@@ -613,6 +741,18 @@ public:
};
+class Item_static_real_func :public Item_real
+{
+ const char *func_name;
+public:
+ Item_static_real_func(const char *str, double val_arg, uint decimal_par,
+ uint length)
+ :Item_real(NullS, val_arg, decimal_par, length), func_name(str)
+ {}
+ void print(String *str) { str->append(func_name); }
+};
+
+
class Item_float :public Item_real
{
public:
@@ -692,6 +832,20 @@ public:
void cleanup() {}
};
+
+class Item_static_string_func :public Item_string
+{
+ const char *func_name;
+public:
+ Item_static_string_func(const char *name_par, const char *str, uint length,
+ CHARSET_INFO *cs,
+ Derivation dv= DERIVATION_COERCIBLE)
+ :Item_string(NullS, str, length, cs, dv), func_name(name_par)
+ {}
+ void print(String *str) { str->append(func_name); }
+};
+
+
/* for show tables */
class Item_datetime :public Item_string
@@ -776,15 +930,15 @@ public:
Item *orig_item; /* things in 'cleanup()' */
Item_ref(Item **hook, Item *original,const char *db_par,
const char *table_name_par, const char *field_name_par)
- :Item_ident(db_par,table_name_par,field_name_par),ref(0), hook_ptr(hook),
- orig_item(original) {}
- Item_ref(Item **item, Item **hook,
+ :Item_ident(db_par, table_name_par, field_name_par), result_field(0),
+ ref(0), hook_ptr(hook), orig_item(original) {}
+ Item_ref(Item **item, Item **hook,
const char *table_name_par, const char *field_name_par)
- :Item_ident(NullS,table_name_par,field_name_par),
+ :Item_ident(NullS, table_name_par, field_name_par), result_field(0),
ref(item), hook_ptr(hook), orig_item(hook ? *hook:0) {}
// Constructor need to process subselect with temporary tables (see Item)
Item_ref(THD *thd, Item_ref *item, Item **hook)
- :Item_ident(thd, item), ref(item->ref),
+ :Item_ident(thd, item), result_field(item->result_field), ref(item->ref),
hook_ptr(hook), orig_item(hook ? *hook : 0) {}
enum Type type() const { return REF_ITEM; }
bool eq(const Item *item, bool binary_cmp) const
@@ -816,7 +970,10 @@ public:
{
return (null_value=(*ref)->get_date_result(ltime,fuzzydate));
}
- bool send(Protocol *prot, String *tmp){ return (*ref)->send(prot, tmp); }
+ double val_result();
+ longlong val_int_result();
+ String *str_result(String* tmp);
+ bool send(Protocol *prot, String *tmp);
void make_field(Send_field *field) { (*ref)->make_field(field); }
bool fix_fields(THD *, struct st_table_list *, Item **);
int save_in_field(Field *field, bool no_conversions)
@@ -829,6 +986,7 @@ public:
return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables();
}
void set_result_field(Field *field) { result_field= field; }
+ Field *get_tmp_table_field() { return result_field; }
bool is_result_field() { return 1; }
void save_in_result_field(bool no_conversions)
{
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 85b22d1eddd..f6daf0f5ed4 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -217,11 +217,13 @@ void Item_bool_func2::fix_length_and_dec()
in its memory: it will be reused on each execute.
*/
Item_arena *arena= thd->current_arena, backup;
- if (arena->is_stmt_prepare())
+ if (!arena->is_stmt_prepare())
+ arena= 0;
+ else
thd->set_n_backup_item_arena(arena, &backup);
conv= new Item_func_conv_charset(args[weak],
args[strong]->collation.collation);
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
conv->collation.set(args[weak]->collation.derivation);
conv->fix_fields(thd, 0, &conv);
@@ -593,7 +595,7 @@ bool Item_in_optimizer::fix_left(THD *thd,
If it is preparation PS only then we do not know values of parameters =>
cant't get there values and do not need that values.
*/
- if (! thd->current_arena->is_stmt_prepare())
+ if (!thd->only_prepare())
cache->store(args[0]);
if (cache->cols() == 1)
{
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index f1a2b11aaa8..e7bef18e629 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -239,6 +239,40 @@ public:
Item *neg_transformer(THD *thd);
};
+
+/*
+ The class Item_func_trig_cond is used for guarded predicates
+ which are employed only for internal purposes.
+ A guarded predicates is an object consisting of an a regular or
+ a guarded predicate P and a pointer to a boolean guard variable g.
+ A guarded predicate P/g is evaluated to true if the value of the
+ guard g is false, otherwise it is evaluated to the same value that
+ the predicate P: val(P/g)= g ? val(P):true.
+ Guarded predicates allow us to include predicates into a conjunction
+ conditionally. Currently they are utilized for pushed down predicates
+ in queries with outer join operations.
+
+ In the future, probably, it makes sense to extend this class to
+ the objects consisting of three elements: a predicate P, a pointer
+ to a variable g and a firing value s with following evaluation
+ rule: val(P/g,s)= g==s? val(P) : true. It will allow us to build only
+ one item for the objects of the form P/g1/g2...
+
+ Objects of this class are built only for query execution after
+ the execution plan has been already selected. That's why this
+ class needs only val_int out of generic methods.
+*/
+
+class Item_func_trig_cond: public Item_bool_func
+{
+ bool *trig_var;
+public:
+ Item_func_trig_cond(Item *a, bool *f) : Item_bool_func(a) { trig_var= f; }
+ longlong val_int() { return *trig_var ? args[0]->val_int() : 1; }
+ enum Functype functype() const { return TRIG_COND_FUNC; };
+ const char *func_name() const { return "trigcond"; };
+};
+
class Item_func_not_all :public Item_func_not
{
bool abort_on_null;
@@ -824,7 +858,7 @@ public:
}
const char *func_name() const { return "isnotnull"; }
optimize_type select_optimize() const { return OPTIMIZE_NULL; }
- table_map not_null_tables() const { return 0; }
+ table_map not_null_tables() const { return used_tables(); }
Item *neg_transformer(THD *thd);
void print(String *str);
CHARSET_INFO *compare_collation() { return args[0]->collation.collation; }
diff --git a/sql/item_create.cc b/sql/item_create.cc
index c98c7892c26..e21364045ba 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -18,10 +18,6 @@
#include "mysql_priv.h"
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
Item *create_func_abs(Item* a)
{
return new Item_func_abs(a);
@@ -77,12 +73,13 @@ Item *create_func_connection_id(void)
{
THD *thd=current_thd;
thd->lex->safe_to_cache_query= 0;
- return new Item_int(NullS,(longlong)
- ((thd->slave_thread) ?
- thd->variables.pseudo_thread_id :
- thd->thread_id),
- 10);
-}
+ return new Item_static_int_func("connection_id()",
+ (longlong)
+ ((thd->slave_thread) ?
+ thd->variables.pseudo_thread_id :
+ thd->thread_id),
+ 10);
+}
Item *create_func_conv(Item* a, Item *b, Item *c)
{
@@ -292,7 +289,7 @@ Item *create_func_period_diff(Item* a, Item *b)
Item *create_func_pi(void)
{
- return new Item_real("pi()",M_PI,6,8);
+ return new Item_static_real_func("pi()", M_PI, 6, 8);
}
Item *create_func_pow(Item* a, Item *b)
@@ -308,13 +305,9 @@ Item *create_func_current_user()
length= (uint) (strxmov(buff, thd->priv_user, "@", thd->priv_host, NullS) -
buff);
- return new Item_string(NullS, thd->memdup(buff, length), length,
- system_charset_info);
-}
-
-Item *create_func_quarter(Item* a)
-{
- return new Item_func_quarter(a);
+ return new Item_static_string_func("current_user()",
+ thd->memdup(buff, length), length,
+ system_charset_info);
}
Item *create_func_radians(Item *a)
@@ -438,7 +431,7 @@ Item *create_func_uuid(void)
Item *create_func_version(void)
{
- return new Item_string(NullS,server_version,
+ return new Item_static_string_func("version()", server_version,
(uint) strlen(server_version),
system_charset_info, DERIVATION_IMPLICIT);
}
diff --git a/sql/item_create.h b/sql/item_create.h
index 7577627ef04..d48aed5284a 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -72,7 +72,6 @@ Item *create_func_period_diff(Item* a, Item *b);
Item *create_func_pi(void);
Item *create_func_pow(Item* a, Item *b);
Item *create_func_current_user(void);
-Item *create_func_quarter(Item* a);
Item *create_func_radians(Item *a);
Item *create_func_release_lock(Item* a);
Item *create_func_repeat(Item* a, Item *b);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index ef845bb8266..7c5b584e645 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -28,6 +28,9 @@
#include <time.h>
#include <ft_global.h>
+#include "sp_head.h"
+#include "sp_rcontext.h"
+#include "sp.h"
bool check_reserved_words(LEX_STRING *name)
{
@@ -786,7 +789,7 @@ double Item_func_log2::val()
double value=args[0]->val();
if ((null_value=(args[0]->null_value || value <= 0.0)))
return 0.0;
- return log(value) / log(2.0);
+ return log(value) / M_LN2;
}
double Item_func_log10::val()
@@ -1587,11 +1590,16 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
const_item_cache&=item->const_item();
f_args.arg_type[i]=item->result_type();
}
+ //TODO: why all folowing memory is not allocated with 1 call of sql_alloc?
if (!(buffers=new String[arg_count]) ||
!(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) ||
- !(f_args.lengths=(ulong*) sql_alloc(arg_count * sizeof(long))) ||
- !(f_args.maybe_null=(char*) sql_alloc(arg_count * sizeof(char))) ||
- !(num_buffer= (char*) sql_alloc(ALIGN_SIZE(sizeof(double))*arg_count)))
+ !(f_args.lengths= (ulong*) sql_alloc(arg_count * sizeof(long))) ||
+ !(f_args.maybe_null= (char*) sql_alloc(arg_count * sizeof(char))) ||
+ !(num_buffer= (char*) sql_alloc(arg_count *
+ ALIGN_SIZE(sizeof(double)))) ||
+ !(f_args.attributes= (char**) sql_alloc(arg_count * sizeof(char *))) ||
+ !(f_args.attribute_lengths= (ulong*) sql_alloc(arg_count *
+ sizeof(long))))
{
free_udf(u_d);
DBUG_RETURN(1);
@@ -1610,8 +1618,10 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
for (uint i=0; i < arg_count; i++)
{
f_args.args[i]=0;
- f_args.lengths[i]=arguments[i]->max_length;
- f_args.maybe_null[i]=(char) arguments[i]->maybe_null;
+ f_args.lengths[i]= arguments[i]->max_length;
+ f_args.maybe_null[i]= (char) arguments[i]->maybe_null;
+ f_args.attributes[i]= arguments[i]->name;
+ f_args.attribute_lengths[i]= arguments[i]->name_length;
switch(arguments[i]->type()) {
case Item::STRING_ITEM: // Constant string !
@@ -3253,6 +3263,137 @@ longlong Item_func_is_used_lock::val_int()
}
+longlong Item_func_row_count::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ THD *thd= current_thd;
+
+ return thd->row_count_func;
+}
+
+
+Item_func_sp::Item_func_sp(sp_name *name)
+ :Item_func(), m_name(name), m_sp(NULL)
+{
+ m_name->init_qname(current_thd);
+}
+
+Item_func_sp::Item_func_sp(sp_name *name, List<Item> &list)
+ :Item_func(list), m_name(name), m_sp(NULL)
+{
+ m_name->init_qname(current_thd);
+}
+
+const char *
+Item_func_sp::func_name() const
+{
+ return m_name->m_name.str;
+}
+
+int
+Item_func_sp::execute(Item **itp)
+{
+ DBUG_ENTER("Item_func_sp::execute");
+ THD *thd= current_thd;
+ int res;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ st_sp_security_context save_ctx;
+#endif
+
+ if (! m_sp)
+ m_sp= sp_find_function(thd, m_name);
+ if (! m_sp)
+ {
+ my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0),
+ "FUNCTION", m_name->m_qname);
+ DBUG_RETURN(-1);
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_change_security_context(thd, m_sp, &save_ctx);
+#endif
+
+ res= m_sp->execute_function(thd, args, arg_count, itp);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, m_sp, &save_ctx);
+#endif
+
+ DBUG_RETURN(res);
+}
+
+enum enum_field_types
+Item_func_sp::field_type() const
+{
+ DBUG_ENTER("Item_func_sp::field_type");
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, m_name);
+ if (m_sp)
+ {
+ DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns));
+ DBUG_RETURN(m_sp->m_returns);
+ }
+ my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0),
+ "FUNCTION", m_name->m_qname);
+ DBUG_RETURN(MYSQL_TYPE_STRING);
+}
+
+Item_result
+Item_func_sp::result_type() const
+{
+ DBUG_ENTER("Item_func_sp::result_type");
+ DBUG_PRINT("info", ("m_sp = %p", m_sp));
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, m_name);
+ if (m_sp)
+ {
+ DBUG_RETURN(m_sp->result());
+ }
+ my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0),
+ "FUNCTION", m_name->m_qname);
+ DBUG_RETURN(STRING_RESULT);
+}
+
+void
+Item_func_sp::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_func_sp::fix_length_and_dec");
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, m_name);
+ if (! m_sp)
+ {
+ my_printf_error(ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), MYF(0),
+ "FUNCTION", m_name->m_qname);
+ }
+ else
+ {
+ switch (m_sp->result()) {
+ case STRING_RESULT:
+ maybe_null= 1;
+ max_length= MAX_BLOB_WIDTH;
+ break;
+ case REAL_RESULT:
+ decimals= NOT_FIXED_DEC;
+ max_length= float_length(decimals);
+ break;
+ case INT_RESULT:
+ decimals= 0;
+ max_length= 21;
+ break;
+ case ROW_RESULT:
+ default:
+ // This case should never be choosen
+ DBUG_ASSERT(0);
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
longlong Item_func_found_rows::val_int()
{
DBUG_ASSERT(fixed == 1);
diff --git a/sql/item_func.h b/sql/item_func.h
index d45f7244e55..76d0346531e 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -47,7 +47,9 @@ public:
SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC,
SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING,
SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN,
- NOT_FUNC, NOT_ALL_FUNC, NOW_FUNC};
+ NOT_FUNC, NOT_ALL_FUNC,
+ NOW_FUNC, TRIG_COND_FUNC,
+ GUSERVAR_FUNC};
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL };
enum Type type() const { return FUNC_ITEM; }
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
@@ -958,6 +960,8 @@ class Item_func_get_user_var :public Item_func
public:
Item_func_get_user_var(LEX_STRING a):
Item_func(), name(a) {}
+ enum Functype functype() const { return GUSERVAR_FUNC; }
+ LEX_STRING get_name() { return name; }
double val();
longlong val_int();
String *val_str(String* str);
@@ -1076,6 +1080,82 @@ enum Cast_target
};
+class Item_func_row_count :public Item_int_func
+{
+public:
+ Item_func_row_count() :Item_int_func() {}
+ longlong val_int();
+ const char *func_name() const { return "row_count"; }
+ void fix_length_and_dec() { decimals= 0; maybe_null=0; }
+};
+
+
+/*
+ *
+ * Stored FUNCTIONs
+ *
+ */
+
+class sp_head;
+class sp_name;
+
+class Item_func_sp :public Item_func
+{
+private:
+ sp_name *m_name;
+ mutable sp_head *m_sp;
+
+ int execute(Item **itp);
+
+public:
+
+ Item_func_sp(sp_name *name);
+
+ Item_func_sp(sp_name *name, List<Item> &list);
+
+ virtual ~Item_func_sp()
+ {}
+
+ const char *func_name() const;
+
+ enum enum_field_types field_type() const;
+
+ Item_result result_type() const;
+
+ longlong val_int()
+ {
+ return (longlong)Item_func_sp::val();
+ }
+
+ double val()
+ {
+ Item *it;
+
+ if (execute(&it))
+ {
+ null_value= 1;
+ return 0.0;
+ }
+ return it->val();
+ }
+
+ String *val_str(String *str)
+ {
+ Item *it;
+
+ if (execute(&it))
+ {
+ null_value= 1;
+ return NULL;
+ }
+ return it->val_str(str);
+ }
+
+ void fix_length_and_dec();
+
+};
+
+
class Item_func_found_rows :public Item_int_func
{
public:
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 8d140efac5f..6d86d7d4b2d 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -132,11 +132,12 @@ Item_subselect::select_transformer(JOIN *join)
bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref)
{
+ char const *save_where= thd_param->where;
+ int res;
+
DBUG_ASSERT(fixed == 0);
engine->set_thd((thd= thd_param));
-
- char const *save_where= thd->where;
- int res;
+ arena= thd->current_arena;
if (check_stack_overrun(thd, (gptr)&res))
return 1;
@@ -318,6 +319,8 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
/* Juggle with current arena only if we're in prepared statement prepare */
Item_arena *arena= join->thd->current_arena;
Item_arena backup;
+ if (!arena->is_stmt_prepare())
+ arena= 0; // For easier test
if (!select_lex->master_unit()->first_select()->next_select() &&
!select_lex->table_list.elements &&
@@ -352,7 +355,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
if (join->conds || join->having)
{
Item *cond;
- if (arena->is_stmt_prepare())
+ if (arena)
thd->set_n_backup_item_arena(arena, &backup);
if (!join->having)
@@ -366,14 +369,14 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
new Item_null())))
goto err;
}
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
return RES_REDUCE;
}
return RES_OK;
err:
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
return RES_ERROR;
}
@@ -643,6 +646,7 @@ Item_subselect::trans_res
Item_in_subselect::single_value_transformer(JOIN *join,
Comp_creator *func)
{
+ Item_subselect::trans_res result= RES_ERROR;
DBUG_ENTER("Item_in_subselect::single_value_transformer");
if (changed)
@@ -654,7 +658,10 @@ Item_in_subselect::single_value_transformer(JOIN *join,
Item_arena *arena= join->thd->current_arena, backup;
thd->where= "scalar IN/ALL/ANY subquery";
- if (arena->is_stmt_prepare())
+
+ if (!arena->is_stmt_prepare())
+ arena= 0; // For easier test
+ else
thd->set_n_backup_item_arena(arena, &backup);
if (select_lex->item_list.elements > 1)
@@ -762,18 +769,17 @@ Item_in_subselect::single_value_transformer(JOIN *join,
}
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
- Item *item;
-
- item= (Item*) select_lex->item_list.head();
if (join->having || select_lex->with_sum_func ||
select_lex->group_list.elements)
{
- item= func->create(expr,
- new Item_ref_null_helper(this,
- select_lex->ref_pointer_array,
- (char *)"<ref>",
- this->full_name()));
+ bool tmp;
+ Item *item= func->create(expr,
+ new Item_ref_null_helper(this,
+ select_lex->
+ ref_pointer_array,
+ (char *)"<ref>",
+ this->full_name()));
/*
AND and comparison functions can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last
@@ -781,21 +787,22 @@ Item_in_subselect::single_value_transformer(JOIN *join,
*/
select_lex->having= join->having= and_items(join->having, item);
select_lex->having_fix_field= 1;
- if (join->having->fix_fields(thd, join->tables_list, 0))
- {
- select_lex->having_fix_field= 0;
- goto err;
- }
+ tmp= join->having->fix_fields(thd, join->tables_list, 0);
select_lex->having_fix_field= 0;
+ if (tmp)
+ goto err;
}
else
{
+ Item *item= (Item*) select_lex->item_list.head();
+
select_lex->item_list.empty();
select_lex->item_list.push_back(new Item_int("Not_used",
(longlong) 1, 21));
select_lex->ref_pointer_array[0]= select_lex->item_list.head();
if (select_lex->table_list.elements)
{
+ bool tmp;
Item *having= item, *orig_item= item;
item= func->create(expr, item);
if (!abort_on_null && orig_item->maybe_null)
@@ -811,12 +818,10 @@ Item_in_subselect::single_value_transformer(JOIN *join,
new Item_cond_and(having, join->having) :
having);
select_lex->having_fix_field= 1;
- if (join->having->fix_fields(thd, join->tables_list, 0))
- {
- select_lex->having_fix_field= 0;
+ tmp= join->having->fix_fields(thd, join->tables_list, 0);
+ select_lex->having_fix_field= 0;
+ if (tmp)
goto err;
- }
- select_lex->having_fix_field= 0;
item= new Item_cond_or(item,
new Item_func_isnull(orig_item));
}
@@ -832,6 +837,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
}
else
{
+ bool tmp;
if (select_lex->master_unit()->first_select()->next_select())
{
/*
@@ -841,24 +847,21 @@ Item_in_subselect::single_value_transformer(JOIN *join,
*/
select_lex->having=
join->having=
- func->create(expr,
+ func->create(expr,
new Item_null_helper(this, item,
(char *)"<no matter>",
(char *)"<result>"));
select_lex->having_fix_field= 1;
- if (join->having->fix_fields(thd, join->tables_list,
- 0))
- {
- select_lex->having_fix_field= 0;
+ tmp= join->having->fix_fields(thd, join->tables_list, 0);
+ select_lex->having_fix_field= 0;
+ if (tmp)
goto err;
- }
- select_lex->having_fix_field= 0;
}
else
{
// it is single select without tables => possible optimization
item= func->create(left_expr, item);
- // fix_field of item will be done in time of substituting
+ // fix_field of item will be done in time of substituting
substitution= item;
have_to_be_excluded= 1;
if (thd->lex->describe)
@@ -868,42 +871,40 @@ Item_in_subselect::single_value_transformer(JOIN *join,
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff);
}
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
- DBUG_RETURN(RES_REDUCE);
+ result= RES_REDUCE;
+ goto end;
}
}
}
ok:
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
- DBUG_RETURN(RES_OK);
+ result= RES_OK;
err:
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
- DBUG_RETURN(RES_ERROR);
+ DBUG_RETURN(result);
}
Item_subselect::trans_res
Item_in_subselect::row_value_transformer(JOIN *join)
{
+ Item *item= 0;
+ SELECT_LEX *select_lex= join->select_lex;
DBUG_ENTER("Item_in_subselect::row_value_transformer");
if (changed)
{
DBUG_RETURN(RES_OK);
}
- Item_arena *arena= join->thd->current_arena, backup;
- Item *item= 0;
-
thd->where= "row IN/ALL/ANY subquery";
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
- SELECT_LEX *select_lex= join->select_lex;
+ Item_arena *arena= join->thd->current_arena, backup;
+ if (!arena->is_stmt_prepare())
+ arena= 0;
+ else
+ thd->set_n_backup_item_arena(arena, &backup);
if (select_lex->item_list.elements != left_expr->cols())
{
@@ -985,12 +986,12 @@ Item_in_subselect::row_value_transformer(JOIN *join)
if (join->conds->fix_fields(thd, join->tables_list, 0))
goto err;
}
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_OK);
err:
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(RES_ERROR);
}
@@ -1053,13 +1054,7 @@ subselect_single_select_engine(st_select_lex *select,
{
select_lex= select;
SELECT_LEX_UNIT *unit= select_lex->master_unit();
- unit->offset_limit_cnt= unit->global_parameters->offset_limit;
- unit->select_limit_cnt= unit->global_parameters->select_limit+
- unit->global_parameters ->offset_limit;
- if (unit->select_limit_cnt < unit->global_parameters->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // no limit
- if (unit->select_limit_cnt == HA_POS_ERROR)
- select_lex->options&= ~OPTION_FOUND_ROWS;
+ unit->set_limit(unit->global_parameters, select_lex);
unit->item= item;
this->select_lex= select_lex;
}
@@ -1408,7 +1403,7 @@ void subselect_uniquesubquery_engine::exclude()
table_map subselect_engine::calc_const_tables(TABLE_LIST *table)
{
table_map map= 0;
- for (; table; table= table->next)
+ for(; table; table= table->next_local)
{
TABLE *tbl= table->table;
if (tbl && tbl->const_table)
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index f570d89f28f..e870feddedf 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -26,7 +26,7 @@ class JOIN;
class select_subselect;
class subselect_engine;
class Item_bool_func2;
-class Statement;
+class Item_arena;
/* base class for subselects */
@@ -36,6 +36,8 @@ class Item_subselect :public Item_result_field
protected:
/* thread handler, will be assigned in fix_fields only */
THD *thd;
+ /* Item_arena used or 0 */
+ Item_arena *arena;
/* substitution instead of subselect in case of optimization */
Item *substitution;
/* unit of subquery */
@@ -209,7 +211,6 @@ public:
Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
Item_in_subselect()
:Item_exists_subselect(), abort_on_null(0), transformed(0), upper_not(0)
-
{}
subs_type substype() { return IN_SUBS; }
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 79c1be57625..13b6329daae 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -77,7 +77,7 @@ Item_sum::Item_sum(THD *thd, Item_sum *item):
*/
bool Item_sum::save_args_for_prepared_statement(THD *thd)
{
- if (thd->current_arena->is_stmt_prepare())
+ if (thd->current_arena->is_stmt_prepare() && args_copy == 0)
return save_args(thd->current_arena);
return 0;
}
@@ -330,6 +330,122 @@ double Item_sum_sum::val()
}
+/* Item_sum_sum_distinct */
+
+Item_sum_sum_distinct::Item_sum_sum_distinct(Item *item)
+ :Item_sum_num(item), sum(0.0), tree(0)
+{
+ /*
+ quick_group is an optimizer hint, which means that GROUP BY can be
+ handled with help of index on grouped columns.
+ By setting quick_group to zero we force creation of temporary table
+ to perform GROUP BY.
+ */
+ quick_group= 0;
+}
+
+
+Item_sum_sum_distinct::Item_sum_sum_distinct(THD *thd,
+ Item_sum_sum_distinct *original)
+ :Item_sum_num(thd, original), sum(0.0), tree(0)
+{
+ quick_group= 0;
+}
+
+
+Item_sum_sum_distinct::~Item_sum_sum_distinct()
+{
+ delete tree;
+}
+
+
+Item *
+Item_sum_sum_distinct::copy_or_same(THD *thd)
+{
+ return new (&thd->mem_root) Item_sum_sum_distinct(thd, this);
+}
+
+C_MODE_START
+
+static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2)
+{
+ return memcmp(key1, key2, *(uint *) arg);
+}
+
+C_MODE_END
+
+bool Item_sum_sum_distinct::setup(THD *thd)
+{
+ SELECT_LEX *select_lex= thd->lex->current_select;
+ /* what does it mean??? */
+ if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
+ return 1;
+
+ DBUG_ASSERT(tree == 0); /* setup can not be called twice */
+
+ /*
+ Uniques handles all unique elements in a tree until they can't fit in.
+ Then thee tree is dumped to the temporary file.
+ See class Unique for details.
+ */
+ null_value= maybe_null= 1;
+ /*
+ TODO: if underlying item result fits in 4 bytes we can take advantage
+ of it and have tree of long/ulong. It gives 10% performance boost
+ */
+ static uint key_length= sizeof(double);
+ tree= new Unique(simple_raw_key_cmp, &key_length, key_length,
+ thd->variables.max_heap_table_size);
+ return tree == 0;
+}
+
+void Item_sum_sum_distinct::clear()
+{
+ DBUG_ASSERT(tree); /* we always have a tree */
+ null_value= 1;
+ tree->reset();
+}
+
+bool Item_sum_sum_distinct::add()
+{
+ /* args[0]->val() may reset args[0]->null_value */
+ double val= args[0]->val();
+ if (!args[0]->null_value)
+ {
+ DBUG_ASSERT(tree);
+ null_value= 0;
+ if (val)
+ return tree->unique_add(&val);
+ }
+ return 0;
+}
+
+C_MODE_START
+
+static int sum_sum_distinct(void *element, element_count num_of_dups,
+ void *item_sum_sum_distinct)
+{
+ ((Item_sum_sum_distinct *)
+ (item_sum_sum_distinct))->add(* (double *) element);
+ return 0;
+}
+
+C_MODE_END
+
+double Item_sum_sum_distinct::val()
+{
+ /*
+ We don't have a tree only if 'setup()' hasn't been called;
+ this is the case of sql_select.cc:return_zero_rows.
+ */
+ sum= 0.0;
+ if (tree)
+ tree->walk(sum_sum_distinct, (void *) this);
+ return sum;
+}
+
+/* end of Item_sum_sum_distinct */
+
Item *Item_sum_count::copy_or_same(THD* thd)
{
return new (&thd->mem_root) Item_sum_count(thd, this);
@@ -1121,11 +1237,6 @@ String *Item_variance_field::val_str(String *str)
#include "sql_select.h"
-int simple_raw_key_cmp(void* arg, byte* key1, byte* key2)
-{
- return memcmp(key1, key2, *(uint*) arg);
-}
-
int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
{
Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
@@ -1976,7 +2087,7 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
result_field= 0;
null_value= 1;
max_length= group_concat_max_len;
- thd->allow_sum_func= 1;
+ thd->allow_sum_func= 1;
if (!(tmp_table_param= new TMP_TABLE_PARAM))
return 1;
tables_list= tables;
@@ -1990,7 +2101,6 @@ bool Item_func_group_concat::setup(THD *thd)
List<Item> list;
SELECT_LEX *select_lex= thd->lex->current_select;
uint const_fields;
- byte *record;
qsort_cmp2 compare_key;
DBUG_ENTER("Item_func_group_concat::setup");
@@ -1999,7 +2109,7 @@ bool Item_func_group_concat::setup(THD *thd)
/*
push all not constant fields to list and create temp table
- */
+ */
const_fields= 0;
always_null= 0;
for (uint i= 0; i < arg_count_field; i++)
@@ -2017,15 +2127,15 @@ bool Item_func_group_concat::setup(THD *thd)
}
if (always_null)
DBUG_RETURN(0);
-
+
List<Item> all_fields(list);
- if (arg_count_order)
+ if (arg_count_order)
{
bool hidden_group_fields;
setup_group(thd, args, tables_list, list, all_fields, *order,
&hidden_group_fields);
}
-
+
count_field_types(tmp_table_param,all_fields,0);
if (table)
{
@@ -2055,7 +2165,6 @@ bool Item_func_group_concat::setup(THD *thd)
table->no_rows= 1;
key_length= table->reclength;
- record= table->record[0];
/* Offset to first result field in table */
field_list_offset= table->fields - (list.elements - const_fields);
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 5081d592654..9046a215c86 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -23,13 +23,15 @@
#include <my_tree.h>
+class Item_arena;
+
class Item_sum :public Item_result_field
{
public:
enum Sumfunctype
- { COUNT_FUNC,COUNT_DISTINCT_FUNC,SUM_FUNC,AVG_FUNC,MIN_FUNC,
- MAX_FUNC, UNIQUE_USERS_FUNC,STD_FUNC,VARIANCE_FUNC,SUM_BIT_FUNC,
- UDF_SUM_FUNC, GROUP_CONCAT_FUNC
+ { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC,
+ MIN_FUNC, MAX_FUNC, UNIQUE_USERS_FUNC, STD_FUNC, VARIANCE_FUNC,
+ SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC
};
Item **args, *tmp_args[2];
@@ -150,6 +152,39 @@ class Item_sum_sum :public Item_sum_num
};
+/*
+ Item_sum_sum_distinct - SELECT SUM(DISTINCT expr) FROM ...
+ support. See also: MySQL manual, chapter 'Adding New Functions To MySQL'
+ and comments in item_sum.cc.
+*/
+
+class Unique;
+
+class Item_sum_sum_distinct :public Item_sum_num
+{
+ double sum;
+ Unique *tree;
+private:
+ Item_sum_sum_distinct(THD *thd, Item_sum_sum_distinct *item);
+public:
+ Item_sum_sum_distinct(Item *item_par);
+ ~Item_sum_sum_distinct();
+
+ bool setup(THD *thd);
+ void clear();
+ bool add();
+ double val();
+
+ inline void add(double val) { sum+= val; }
+ enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; }
+ void reset_field() {} // not used
+ void update_field() {} // not used
+ const char *func_name() const { return "sum_distinct"; }
+ Item *copy_or_same(THD* thd);
+ virtual void no_rows_in_result() {}
+};
+
+
class Item_sum_count :public Item_sum_int
{
longlong count;
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 5d9a6dd9490..c558c935090 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1071,9 +1071,15 @@ static bool get_interval_value(Item *args,interval_type int_type,
case INTERVAL_YEAR:
interval->year= (ulong) value;
break;
+ case INTERVAL_QUARTER:
+ interval->month= (ulong)(value*3);
+ break;
case INTERVAL_MONTH:
interval->month= (ulong) value;
break;
+ case INTERVAL_WEEK:
+ interval->day= (ulong)(value*7);
+ break;
case INTERVAL_DAY:
interval->day= (ulong) value;
break;
@@ -1845,6 +1851,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
break;
}
case INTERVAL_DAY:
+ case INTERVAL_WEEK:
period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
sign * (long) interval.day);
/* Daynumber from year 0 to 9999-12-31 */
@@ -1861,6 +1868,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
ltime->day=28; // Was leap-year
break;
case INTERVAL_YEAR_MONTH:
+ case INTERVAL_QUARTER:
case INTERVAL_MONTH:
period= (ltime->year*12 + sign * (long) interval.year*12 +
ltime->month-1 + sign * (long) interval.month);
@@ -1924,12 +1932,13 @@ longlong Item_date_add_interval::val_int()
static const char *interval_names[]=
{
- "year", "month", "day", "hour", "minute",
- "second", "microsecond", "year_month",
- "day_hour", "day_minute", "day_second",
- "hour_minute", "hour_second", "minute_second",
- "day_microsecond", "hour_microsecond",
- "minute_microsecond", "second_microsecond"
+ "year", "quarter", "month", "day", "hour",
+ "minute", "week", "second", "microsecond",
+ "year_month", "day_hour", "day_minute",
+ "day_second", "hour_minute", "hour_second",
+ "minute_second", "day_microsecond",
+ "hour_microsecond", "minute_microsecond",
+ "second_microsecond"
};
void Item_date_add_interval::print(String *str)
@@ -1960,7 +1969,9 @@ void Item_extract::fix_length_and_dec()
switch (int_type) {
case INTERVAL_YEAR: max_length=4; date_value=1; break;
case INTERVAL_YEAR_MONTH: max_length=6; date_value=1; break;
+ case INTERVAL_QUARTER: max_length=2; date_value=1; break;
case INTERVAL_MONTH: max_length=2; date_value=1; break;
+ case INTERVAL_WEEK: max_length=2; date_value=1; break;
case INTERVAL_DAY: max_length=2; date_value=1; break;
case INTERVAL_DAY_HOUR: max_length=9; date_value=0; break;
case INTERVAL_DAY_MINUTE: max_length=11; date_value=0; break;
@@ -1984,6 +1995,8 @@ longlong Item_extract::val_int()
{
DBUG_ASSERT(fixed == 1);
TIME ltime;
+ uint year;
+ ulong week_format;
long neg;
if (date_value)
{
@@ -2005,7 +2018,13 @@ longlong Item_extract::val_int()
switch (int_type) {
case INTERVAL_YEAR: return ltime.year;
case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month;
+ case INTERVAL_QUARTER: return ltime.month/3 + 1;
case INTERVAL_MONTH: return ltime.month;
+ case INTERVAL_WEEK:
+ {
+ week_format= current_thd->variables.default_week_format;
+ return calc_week(&ltime, week_mode(week_format), &year);
+ }
case INTERVAL_DAY: return ltime.day;
case INTERVAL_DAY_HOUR: return (long) (ltime.day*100L+ltime.hour)*neg;
case INTERVAL_DAY_MINUTE: return (long) (ltime.day*10000L+
@@ -2396,6 +2415,79 @@ void Item_func_add_time::print(String *str)
/*
+ SYNOPSIS
+ calc_time_diff()
+ l_time1 TIME/DATE/DATETIME value
+ l_time2 TIME/DATE/DATETIME value
+ l_sign Can be 1 (operation of addition)
+ or -1 (substraction)
+ seconds_out Returns count of seconds bitween
+ l_time1 and l_time2
+ microseconds_out Returns count of microseconds bitween
+ l_time1 and l_time2.
+
+ DESCRIPTION
+ Calculates difference in seconds(seconds_out)
+ and microseconds(microseconds_out)
+ bitween two TIME/DATE/DATETIME values.
+
+ RETURN VALUES
+ Rertuns sign of difference.
+ 1 means negative result
+ 0 means positive result
+
+*/
+
+bool calc_time_diff(TIME *l_time1,TIME *l_time2, int l_sign,
+ longlong *seconds_out, long *microseconds_out)
+{
+ long days;
+ bool neg;
+ longlong seconds= *seconds_out;
+ long microseconds= *microseconds_out;
+
+ /*
+ We suppose that if first argument is MYSQL_TIMESTAMP_TIME
+ the second argument should be TIMESTAMP_TIME also.
+ We should check it before calc_time_diff call.
+ */
+ if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value
+ days= l_time1->day - l_sign*l_time2->day;
+ else // DateTime value
+ days= (calc_daynr((uint) l_time1->year,
+ (uint) l_time1->month,
+ (uint) l_time1->day) -
+ l_sign*calc_daynr((uint) l_time2->year,
+ (uint) l_time2->month,
+ (uint) l_time2->day));
+
+ microseconds= l_time1->second_part - l_sign*l_time2->second_part;
+ seconds= ((longlong) days*86400L + l_time1->hour*3600L +
+ l_time1->minute*60L + l_time1->second + microseconds/1000000L -
+ (longlong)l_sign*(l_time2->hour*3600L+l_time2->minute*60L+l_time2->second));
+
+ neg= 0;
+ if (seconds < 0)
+ {
+ seconds= -seconds;
+ neg= 1;
+ }
+ else if (seconds == 0 && microseconds < 0)
+ {
+ microseconds= -microseconds;
+ neg= 1;
+ }
+ if (microseconds < 0)
+ {
+ microseconds+= 1000000L;
+ seconds--;
+ }
+ *seconds_out= seconds;
+ *microseconds_out= microseconds;
+ return neg;
+}
+
+/*
TIMEDIFF(t,s) is a time function that calculates the
time value between a start and end time.
@@ -2408,7 +2500,6 @@ String *Item_func_timediff::val_str(String *str)
DBUG_ASSERT(fixed == 1);
longlong seconds;
long microseconds;
- long days;
int l_sign= 1;
TIME l_time1 ,l_time2, l_time3;
@@ -2421,40 +2512,16 @@ String *Item_func_timediff::val_str(String *str)
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
- if (l_time1.time_type == MYSQL_TIMESTAMP_TIME) // Time value
- days= l_time1.day - l_sign*l_time2.day;
- else // DateTime value
- days= (calc_daynr((uint) l_time1.year,
- (uint) l_time1.month,
- (uint) l_time1.day) -
- l_sign*calc_daynr((uint) l_time2.year,
- (uint) l_time2.month,
- (uint) l_time2.day));
-
- microseconds= l_time1.second_part - l_sign*l_time2.second_part;
- seconds= ((longlong) days*86400L + l_time1.hour*3600L +
- l_time1.minute*60L + l_time1.second + microseconds/1000000L -
- (longlong)l_sign*(l_time2.hour*3600L+l_time2.minute*60L+
- l_time2.second));
+ l_time3.neg= calc_time_diff(&l_time1,&l_time2, l_sign,
+ &seconds, &microseconds);
- l_time3.neg= 0;
- if (seconds < 0)
- {
- seconds= -seconds;
- l_time3.neg= 1;
- }
- else if (seconds == 0 && microseconds < 0)
- {
- microseconds= -microseconds;
- l_time3.neg= 1;
- }
- if (microseconds < 0)
- {
- microseconds+= 1000000L;
- seconds--;
- }
+ /*
+ For MYSQL_TIMESTAMP_TIME only:
+ If both argumets are negative values and diff between them
+ is negative we need to swap sign as result should be positive.
+ */
if ((l_time2.neg == l_time1.neg) && l_time1.neg)
- l_time3.neg= l_time3.neg ? 0 : 1;
+ l_time3.neg= 1-l_time3.neg; // Swap sign of result
calc_time_from_sec(&l_time3, (long) seconds, microseconds);
@@ -2523,6 +2590,165 @@ longlong Item_func_microsecond::val_int()
}
+longlong Item_func_timestamp_diff::val_int()
+{
+ TIME ltime1, ltime2;
+ longlong seconds;
+ long microseconds;
+ long months= 0;
+ int neg= 1;
+
+ null_value= 0;
+ if (args[0]->get_date(&ltime1, 0) ||
+ args[1]->get_date(&ltime2, 0))
+ goto null_date;
+
+ if (calc_time_diff(&ltime2,&ltime1, 1,
+ &seconds, &microseconds))
+ neg= -1;
+
+ if (int_type == INTERVAL_YEAR ||
+ int_type == INTERVAL_QUARTER ||
+ int_type == INTERVAL_MONTH)
+ {
+ uint year;
+ uint year_beg, year_end, month_beg, month_end;
+ uint diff_days= (uint) (seconds/86400L);
+ uint diff_years= 0;
+ if (neg == -1)
+ {
+ year_beg= ltime2.year;
+ year_end= ltime1.year;
+ month_beg= ltime2.month;
+ month_end= ltime1.month;
+ }
+ else
+ {
+ year_beg= ltime1.year;
+ year_end= ltime2.year;
+ month_beg= ltime1.month;
+ month_end= ltime2.month;
+ }
+ /* calc years*/
+ for (year= year_beg;year < year_end; year++)
+ {
+ uint days=calc_days_in_year(year);
+ if (days > diff_days)
+ break;
+ diff_days-= days;
+ diff_years++;
+ }
+
+ /* calc months; Current year is in the 'year' variable */
+ month_beg--; /* Change months to be 0-11 for easier calculation */
+ month_end--;
+
+ months= 12*diff_years;
+ while (month_beg != month_end)
+ {
+ uint m_days= (uint) days_in_month[month_beg];
+ if (month_beg == 1)
+ {
+ /* This is only calculated once so there is no reason to cache it*/
+ uint leap= (uint) ((year & 3) == 0 && (year%100 ||
+ (year%400 == 0 && year)));
+ m_days+= leap;
+ }
+ if (m_days > diff_days)
+ break;
+ diff_days-= m_days;
+ months++;
+ if (month_beg++ == 11) /* if we wrap to next year */
+ {
+ month_beg= 0;
+ year++;
+ }
+ }
+ if (neg == -1)
+ months= -months;
+ }
+
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ return months/12;
+ case INTERVAL_QUARTER:
+ return months/3;
+ case INTERVAL_MONTH:
+ return months;
+ case INTERVAL_WEEK:
+ return seconds/86400L/7L*neg;
+ case INTERVAL_DAY:
+ return seconds/86400L*neg;
+ case INTERVAL_HOUR:
+ return seconds/3600L*neg;
+ case INTERVAL_MINUTE:
+ return seconds/60L*neg;
+ case INTERVAL_SECOND:
+ return seconds*neg;
+ case INTERVAL_MICROSECOND:
+ {
+ longlong max_sec= LONGLONG_MAX/1000000;
+ if (max_sec > seconds ||
+ max_sec == seconds && LONGLONG_MAX%1000000 >= microseconds)
+ return (longlong) (seconds*1000000L+microseconds)*neg;
+ goto null_date;
+ }
+ default:
+ break;
+ }
+
+null_date:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_timestamp_diff::print(String *str)
+{
+ str->append(func_name());
+ str->append('(');
+
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ str->append("YEAR");
+ break;
+ case INTERVAL_QUARTER:
+ str->append("QUARTER");
+ break;
+ case INTERVAL_MONTH:
+ str->append("MONTH");
+ break;
+ case INTERVAL_WEEK:
+ str->append("WEEK");
+ break;
+ case INTERVAL_DAY:
+ str->append("DAY");
+ break;
+ case INTERVAL_HOUR:
+ str->append("HOUR");
+ break;
+ case INTERVAL_MINUTE:
+ str->append("MINUTE");
+ break;
+ case INTERVAL_SECOND:
+ str->append("SECOND");
+ break;
+ case INTERVAL_MICROSECOND:
+ str->append("SECOND_FRAC");
+ break;
+ default:
+ break;
+ }
+
+ for (uint i=0 ; i < 2 ; i++)
+ {
+ str->append(',');
+ args[i]->print(str);
+ }
+ str->append(')');
+}
+
+
String *Item_func_get_format::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 2254fc830c9..5f71045ef27 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -593,11 +593,11 @@ public:
enum interval_type
{
- INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE,
- INTERVAL_SECOND, INTERVAL_MICROSECOND ,INTERVAL_YEAR_MONTH,
- INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND,
- INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND,
- INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
+ INTERVAL_YEAR, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR,
+ INTERVAL_MINUTE, INTERVAL_WEEK, INTERVAL_SECOND, INTERVAL_MICROSECOND ,
+ INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE,
+ INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND,
+ INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND
};
@@ -833,6 +833,23 @@ public:
};
+class Item_func_timestamp_diff :public Item_int_func
+{
+ const interval_type int_type;
+public:
+ Item_func_timestamp_diff(Item *a,Item *b,interval_type type_arg)
+ :Item_int_func(a,b), int_type(type_arg) {}
+ const char *func_name() const { return "timestamp_diff"; }
+ longlong val_int();
+ void fix_length_and_dec()
+ {
+ decimals=0;
+ maybe_null=1;
+ }
+ void print(String *str);
+};
+
+
enum date_time_format
{
USA_FORMAT, JIS_FORMAT, ISO_FORMAT, EUR_FORMAT, INTERNAL_FORMAT
diff --git a/sql/lex.h b/sql/lex.h
index c64a7069c32..4c44d53d5b1 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -65,6 +65,7 @@ static SYMBOL symbols[] = {
{ "AGAINST", SYM(AGAINST)},
{ "AGGREGATE", SYM(AGGREGATE_SYM)},
{ "ALL", SYM(ALL)},
+ { "ALGORITHM", SYM(ALGORITHM_SYM)},
{ "ALTER", SYM(ALTER)},
{ "ANALYZE", SYM(ANALYZE_SYM)},
{ "AND", SYM(AND_SYM)},
@@ -72,6 +73,7 @@ static SYMBOL symbols[] = {
{ "AS", SYM(AS)},
{ "ASC", SYM(ASC)},
{ "ASCII", SYM(ASCII_SYM)},
+ { "ASENSITIVE", SYM(ASENSITIVE_SYM)},
{ "AUTO_INCREMENT", SYM(AUTO_INC)},
{ "AVG", SYM(AVG_SYM)},
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH)},
@@ -93,7 +95,9 @@ static SYMBOL symbols[] = {
{ "BY", SYM(BY)},
{ "BYTE", SYM(BYTE_SYM)},
{ "CACHE", SYM(CACHE_SYM)},
+ { "CALL", SYM(CALL_SYM)},
{ "CASCADE", SYM(CASCADE)},
+ { "CASCADED", SYM(CASCADED)},
{ "CASE", SYM(CASE_SYM)},
{ "CHANGE", SYM(CHANGE)},
{ "CHANGED", SYM(CHANGED)},
@@ -114,7 +118,10 @@ static SYMBOL symbols[] = {
{ "COMMITTED", SYM(COMMITTED_SYM)},
{ "COMPRESSED", SYM(COMPRESSED_SYM)},
{ "CONCURRENT", SYM(CONCURRENT)},
+ { "CONDITION", SYM(CONDITION_SYM)},
+ { "CONNECTION", SYM(CONNECTION_SYM)},
{ "CONSTRAINT", SYM(CONSTRAINT)},
+ { "CONTINUE", SYM(CONTINUE_SYM)},
{ "CONVERT", SYM(CONVERT_SYM)},
{ "CREATE", SYM(CREATE)},
{ "CROSS", SYM(CROSS)},
@@ -123,6 +130,7 @@ static SYMBOL symbols[] = {
{ "CURRENT_TIME", SYM(CURTIME)},
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM)},
{ "CURRENT_USER", SYM(CURRENT_USER)},
+ { "CURSOR", SYM(CURSOR_SYM)},
{ "DATA", SYM(DATA_SYM)},
{ "DATABASE", SYM(DATABASE)},
{ "DATABASES", SYM(DATABASES)},
@@ -136,13 +144,16 @@ static SYMBOL symbols[] = {
{ "DEALLOCATE", SYM(DEALLOCATE_SYM)},
{ "DEC", SYM(DECIMAL_SYM)},
{ "DECIMAL", SYM(DECIMAL_SYM)},
+ { "DECLARE", SYM(DECLARE_SYM)},
{ "DEFAULT", SYM(DEFAULT)},
+ { "DEFINER", SYM(DEFINER_SYM)},
{ "DELAYED", SYM(DELAYED_SYM)},
{ "DELAY_KEY_WRITE", SYM(DELAY_KEY_WRITE_SYM)},
{ "DELETE", SYM(DELETE_SYM)},
{ "DESC", SYM(DESC)},
{ "DESCRIBE", SYM(DESCRIBE)},
{ "DES_KEY_FILE", SYM(DES_KEY_FILE)},
+ { "DETERMINISTIC", SYM(DETERMINISTIC_SYM)},
{ "DIRECTORY", SYM(DIRECTORY_SYM)},
{ "DISABLE", SYM(DISABLE_SYM)},
{ "DISCARD", SYM(DISCARD)},
@@ -157,6 +168,7 @@ static SYMBOL symbols[] = {
{ "DUPLICATE", SYM(DUPLICATE_SYM)},
{ "DYNAMIC", SYM(DYNAMIC_SYM)},
{ "ELSE", SYM(ELSE)},
+ { "ELSEIF", SYM(ELSEIF_SYM)},
{ "ENABLE", SYM(ENABLE_SYM)},
{ "ENCLOSED", SYM(ENCLOSED)},
{ "END", SYM(END)},
@@ -169,11 +181,13 @@ static SYMBOL symbols[] = {
{ "EVENTS", SYM(EVENTS_SYM)},
{ "EXECUTE", SYM(EXECUTE_SYM)},
{ "EXISTS", SYM(EXISTS)},
+ { "EXIT", SYM(EXIT_SYM)},
{ "EXPANSION", SYM(EXPANSION_SYM)},
{ "EXPLAIN", SYM(DESCRIBE)},
{ "EXTENDED", SYM(EXTENDED_SYM)},
{ "FALSE", SYM(FALSE_SYM)},
{ "FAST", SYM(FAST_SYM)},
+ { "FETCH", SYM(FETCH_SYM)},
{ "FIELDS", SYM(COLUMNS)},
{ "FILE", SYM(FILE_SYM)},
{ "FIRST", SYM(FIRST_SYM)},
@@ -185,14 +199,17 @@ static SYMBOL symbols[] = {
{ "FOR", SYM(FOR_SYM)},
{ "FORCE", SYM(FORCE_SYM)},
{ "FOREIGN", SYM(FOREIGN)},
+ { "FOUND", SYM(FOUND_SYM)},
+ { "FRAC_SECOND", SYM(FRAC_SECOND_SYM)},
{ "FROM", SYM(FROM)},
{ "FULL", SYM(FULL)},
{ "FULLTEXT", SYM(FULLTEXT_SYM)},
- { "FUNCTION", SYM(UDF_SYM)},
+ { "FUNCTION", SYM(FUNCTION_SYM)},
{ "GEOMETRY", SYM(GEOMETRY_SYM)},
{ "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)},
{ "GET_FORMAT", SYM(GET_FORMAT)},
{ "GLOBAL", SYM(GLOBAL_SYM)},
+ { "GOTO", SYM(GOTO_SYM)},
{ "GRANT", SYM(GRANT)},
{ "GRANTS", SYM(GRANTS)},
{ "GROUP", SYM(GROUP)},
@@ -217,6 +234,8 @@ static SYMBOL symbols[] = {
{ "INNER", SYM(INNER_SYM)},
{ "INNOBASE", SYM(INNOBASE_SYM)},
{ "INNODB", SYM(INNOBASE_SYM)},
+ { "INOUT", SYM(INOUT_SYM)},
+ { "INSENSITIVE", SYM(INSENSITIVE_SYM)},
{ "INSERT", SYM(INSERT)},
{ "INSERT_METHOD", SYM(INSERT_METHOD)},
{ "INT", SYM(INT_SYM)},
@@ -232,12 +251,17 @@ static SYMBOL symbols[] = {
{ "IS", SYM(IS)},
{ "ISOLATION", SYM(ISOLATION)},
{ "ISSUER", SYM(ISSUER_SYM)},
+ { "ITERATE", SYM(ITERATE_SYM)},
+ { "INVOKER", SYM(INVOKER_SYM)},
{ "JOIN", SYM(JOIN_SYM)},
{ "KEY", SYM(KEY_SYM)},
{ "KEYS", SYM(KEYS)},
{ "KILL", SYM(KILL_SYM)},
+ { "LABEL", SYM(LABEL_SYM)},
+ { "LANGUAGE", SYM(LANGUAGE_SYM)},
{ "LAST", SYM(LAST_SYM)},
{ "LEADING", SYM(LEADING)},
+ { "LEAVE", SYM(LEAVE_SYM)},
{ "LEAVES", SYM(LEAVES)},
{ "LEFT", SYM(LEFT)},
{ "LEVEL", SYM(LEVEL_SYM)},
@@ -255,6 +279,7 @@ static SYMBOL symbols[] = {
{ "LONG", SYM(LONG_SYM)},
{ "LONGBLOB", SYM(LONGBLOB)},
{ "LONGTEXT", SYM(LONGTEXT)},
+ { "LOOP", SYM(LOOP_SYM)},
{ "LOW_PRIORITY", SYM(LOW_PRIORITY)},
{ "MASTER", SYM(MASTER_SYM)},
{ "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM)},
@@ -280,6 +305,7 @@ static SYMBOL symbols[] = {
{ "MEDIUMBLOB", SYM(MEDIUMBLOB)},
{ "MEDIUMINT", SYM(MEDIUMINT)},
{ "MEDIUMTEXT", SYM(MEDIUMTEXT)},
+ { "MERGE", SYM(MERGE_SYM)},
{ "MICROSECOND", SYM(MICROSECOND_SYM)},
{ "MIDDLEINT", SYM(MEDIUMINT)}, /* For powerbuilder */
{ "MINUTE", SYM(MINUTE_SYM)},
@@ -293,6 +319,7 @@ static SYMBOL symbols[] = {
{ "MULTILINESTRING", SYM(MULTILINESTRING)},
{ "MULTIPOINT", SYM(MULTIPOINT)},
{ "MULTIPOLYGON", SYM(MULTIPOLYGON)},
+ { "NAME", SYM(NAME_SYM)},
{ "NAMES", SYM(NAMES_SYM)},
{ "NATIONAL", SYM(NATIONAL_SYM)},
{ "NATURAL", SYM(NATURAL)},
@@ -318,6 +345,7 @@ static SYMBOL symbols[] = {
{ "OPTIONALLY", SYM(OPTIONALLY)},
{ "OR", SYM(OR_SYM)},
{ "ORDER", SYM(ORDER_SYM)},
+ { "OUT", SYM(OUT_SYM)},
{ "OUTER", SYM(OUTER)},
{ "OUTFILE", SYM(OUTFILE)},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM)},
@@ -334,6 +362,7 @@ static SYMBOL symbols[] = {
{ "PROCESS" , SYM(PROCESS)},
{ "PROCESSLIST", SYM(PROCESSLIST_SYM)},
{ "PURGE", SYM(PURGE)},
+ { "QUARTER", SYM(QUARTER_SYM)},
{ "QUERY", SYM(QUERY_SYM)},
{ "QUICK", SYM(QUICK)},
{ "RAID0", SYM(RAID_0_SYM)},
@@ -353,11 +382,13 @@ static SYMBOL symbols[] = {
{ "REPEATABLE", SYM(REPEATABLE_SYM)},
{ "REPLACE", SYM(REPLACE)},
{ "REPLICATION", SYM(REPLICATION)},
+ { "REPEAT", SYM(REPEAT_SYM)},
{ "REQUIRE", SYM(REQUIRE_SYM)},
{ "RESET", SYM(RESET_SYM)},
{ "RESTORE", SYM(RESTORE_SYM)},
{ "RESTRICT", SYM(RESTRICT)},
- { "RETURNS", SYM(UDF_RETURNS_SYM)},
+ { "RETURN", SYM(RETURN_SYM)},
+ { "RETURNS", SYM(RETURNS_SYM)},
{ "REVOKE", SYM(REVOKE)},
{ "RIGHT", SYM(RIGHT)},
{ "RLIKE", SYM(REGEXP)}, /* Like in mSQL2 */
@@ -370,7 +401,9 @@ static SYMBOL symbols[] = {
{ "SAVEPOINT", SYM(SAVEPOINT_SYM)},
{ "SECOND", SYM(SECOND_SYM)},
{ "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM)},
+ { "SECURITY", SYM(SECURITY_SYM)},
{ "SELECT", SYM(SELECT_SYM)},
+ { "SENSITIVE", SYM(SENSITIVE_SYM)},
{ "SEPARATOR", SYM(SEPARATOR_SYM)},
{ "SERIAL", SYM(SERIAL_SYM)},
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM)},
@@ -387,6 +420,11 @@ static SYMBOL symbols[] = {
{ "SONAME", SYM(UDF_SONAME_SYM)},
{ "SOUNDS", SYM(SOUNDS_SYM)},
{ "SPATIAL", SYM(SPATIAL_SYM)},
+ { "SPECIFIC", SYM(SPECIFIC_SYM)},
+ { "SQL", SYM(SQL_SYM)},
+ { "SQLEXCEPTION", SYM(SQLEXCEPTION_SYM)},
+ { "SQLSTATE", SYM(SQLSTATE_SYM)},
+ { "SQLWARNING", SYM(SQLWARNING_SYM)},
{ "SQL_BIG_RESULT", SYM(SQL_BIG_RESULT)},
{ "SQL_BUFFER_RESULT", SYM(SQL_BUFFER_RESULT)},
{ "SQL_CACHE", SYM(SQL_CACHE_SYM)},
@@ -394,6 +432,15 @@ static SYMBOL symbols[] = {
{ "SQL_NO_CACHE", SYM(SQL_NO_CACHE_SYM)},
{ "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT)},
{ "SQL_THREAD", SYM(SQL_THREAD)},
+ { "SQL_TSI_FRAC_SECOND", SYM(FRAC_SECOND_SYM)},
+ { "SQL_TSI_SECOND", SYM(SECOND_SYM)},
+ { "SQL_TSI_MINUTE", SYM(MINUTE_SYM)},
+ { "SQL_TSI_HOUR", SYM(HOUR_SYM)},
+ { "SQL_TSI_DAY", SYM(DAY_SYM)},
+ { "SQL_TSI_WEEK", SYM(WEEK_SYM)},
+ { "SQL_TSI_MONTH", SYM(MONTH_SYM)},
+ { "SQL_TSI_QUARTER", SYM(QUARTER_SYM)},
+ { "SQL_TSI_YEAR", SYM(YEAR_SYM)},
{ "SSL", SYM(SSL_SYM)},
{ "START", SYM(START_SYM)},
{ "STARTING", SYM(STARTING)},
@@ -409,11 +456,14 @@ static SYMBOL symbols[] = {
{ "TABLES", SYM(TABLES)},
{ "TABLESPACE", SYM(TABLESPACE)},
{ "TEMPORARY", SYM(TEMPORARY)},
+ { "TEMPTABLE", SYM(TEMPTABLE_SYM)},
{ "TERMINATED", SYM(TERMINATED)},
{ "TEXT", SYM(TEXT_SYM)},
{ "THEN", SYM(THEN_SYM)},
{ "TIME", SYM(TIME_SYM)},
{ "TIMESTAMP", SYM(TIMESTAMP)},
+ { "TIMESTAMPADD", SYM(TIMESTAMP_ADD)},
+ { "TIMESTAMPDIFF", SYM(TIMESTAMP_DIFF)},
{ "TINYBLOB", SYM(TINYBLOB)},
{ "TINYINT", SYM(TINYINT)},
{ "TINYTEXT", SYM(TINYTEXT)},
@@ -425,6 +475,7 @@ static SYMBOL symbols[] = {
{ "TYPE", SYM(TYPE_SYM)},
{ "TYPES", SYM(TYPES_SYM)},
{ "UNCOMMITTED", SYM(UNCOMMITTED_SYM)},
+ { "UNDO", SYM(UNDO_SYM)},
{ "UNICODE", SYM(UNICODE_SYM)},
{ "UNION", SYM(UNION_SYM)},
{ "UNIQUE", SYM(UNIQUE_SYM)},
@@ -449,8 +500,11 @@ static SYMBOL symbols[] = {
{ "VARIABLES", SYM(VARIABLES)},
{ "VARYING", SYM(VARYING)},
{ "WARNINGS", SYM(WARNINGS)},
+ { "WEEK", SYM(WEEK_SYM)},
{ "WHEN", SYM(WHEN_SYM)},
{ "WHERE", SYM(WHERE)},
+ { "WHILE", SYM(WHILE_SYM)},
+ { "VIEW", SYM(VIEW_SYM)},
{ "WITH", SYM(WITH)},
{ "WORK", SYM(WORK_SYM)},
{ "WRITE", SYM(WRITE_SYM)},
@@ -634,14 +688,13 @@ static SYMBOL sql_functions[] = {
{ "POSITION", SYM(POSITION_SYM)},
{ "POW", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
{ "POWER", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
- { "QUARTER", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quarter)},
{ "QUOTE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quote)},
{ "RADIANS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)},
{ "RAND", SYM(RAND)},
{ "RELEASE_LOCK", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_release_lock)},
- { "REPEAT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_repeat)},
{ "REVERSE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_reverse)},
{ "ROUND", SYM(ROUND)},
+ { "ROW_COUNT", SYM(ROW_COUNT_SYM)},
{ "RPAD", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_rpad)},
{ "RTRIM", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_rtrim)},
{ "SEC_TO_TIME", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sec_to_time)},
@@ -684,7 +737,6 @@ static SYMBOL sql_functions[] = {
{ "UUID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_uuid)},
{ "VARIANCE", SYM(VARIANCE_SYM)},
{ "VERSION", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)},
- { "WEEK", SYM(WEEK_SYM)},
{ "WEEKDAY", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)},
{ "WEEKOFYEAR", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekofyear)},
{ "WITHIN", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_within)},
diff --git a/sql/lock.cc b/sql/lock.cc
index 215059b8a46..debfb900c23 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -152,7 +152,7 @@ retry:
thd->proc_info=0;
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
if (sql_lock)
{
mysql_unlock_tables(thd,sql_lock);
@@ -578,7 +578,7 @@ void unlock_table_name(THD *thd, TABLE_LIST *table_list)
static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
{
- for (; table_list ; table_list=table_list->next)
+ for (; table_list ; table_list=table_list->next_local)
{
if (table_list->table && table_is_used(table_list->table,0))
return 1;
@@ -632,7 +632,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list)
bool got_all_locks=1;
TABLE_LIST *lock_table;
- for (lock_table=table_list ; lock_table ; lock_table=lock_table->next)
+ for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
{
int got_lock;
if ((got_lock=lock_table_name(thd,lock_table)) < 0)
@@ -675,7 +675,9 @@ end:
void unlock_table_names(THD *thd, TABLE_LIST *table_list,
TABLE_LIST *last_table)
{
- for (TABLE_LIST *table=table_list ; table != last_table ; table=table->next)
+ for (TABLE_LIST *table= table_list;
+ table != last_table;
+ table= table->next_local)
unlock_table_name(thd,table);
pthread_cond_broadcast(&COND_refresh);
}
diff --git a/sql/log.cc b/sql/log.cc
index 08c1b31ed0d..16381c8e26c 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -35,7 +35,7 @@
#include "message.h"
#endif
-MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
+MYSQL_LOG mysql_log, mysql_slow_log, mysql_bin_log;
ulong sync_binlog_counter= 0;
static bool test_if_number(const char *str,
@@ -129,7 +129,8 @@ static int find_uniq_filename(char *name)
MYSQL_LOG::MYSQL_LOG()
:bytes_written(0), last_time(0), query_start(0), name(0),
file_id(1), open_count(1), log_type(LOG_CLOSED), write_error(0), inited(0),
- need_start_event(1)
+ need_start_event(1), description_event_for_exec(0),
+ description_event_for_queue(0)
{
/*
We don't want to initialize LOCK_Log here as such initialization depends on
@@ -157,6 +158,8 @@ void MYSQL_LOG::cleanup()
{
inited= 0;
close(LOG_CLOSE_INDEX);
+ delete description_event_for_queue;
+ delete description_event_for_exec;
(void) pthread_mutex_destroy(&LOCK_log);
(void) pthread_mutex_destroy(&LOCK_index);
(void) pthread_cond_destroy(&update_cond);
@@ -226,7 +229,8 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
const char *new_name, const char *index_file_name_arg,
enum cache_type io_cache_type_arg,
bool no_auto_events_arg,
- ulong max_size_arg)
+ ulong max_size_arg,
+ bool null_created_arg)
{
char buff[512];
File file= -1, index_file_nr= -1;
@@ -324,8 +328,8 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
if (my_b_safe_write(&log_file, (byte*) BINLOG_MAGIC,
BIN_LOG_HEADER_SIZE))
goto err;
- bytes_written += BIN_LOG_HEADER_SIZE;
- write_file_name_to_index_file=1;
+ bytes_written+= BIN_LOG_HEADER_SIZE;
+ write_file_name_to_index_file= 1;
}
if (!my_b_inited(&index_file))
@@ -355,10 +359,51 @@ bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
}
if (need_start_event && !no_auto_events)
{
- need_start_event=0;
- Start_log_event s;
+ /*
+ In 4.x we set need_start_event=0 here, but in 5.0 we want a Start event
+ even if this is not the very first binlog.
+ */
+ Format_description_log_event s(BINLOG_VERSION);
+ if (!s.is_valid())
+ goto err;
s.set_log_pos(this);
- s.write(&log_file);
+ if (null_created_arg)
+ s.created= 0;
+ if (s.write(&log_file))
+ goto err;
+ bytes_written+= s.get_event_len();
+ }
+ if (description_event_for_queue &&
+ description_event_for_queue->binlog_version>=4)
+ {
+ /*
+ This is a relay log written to by the I/O slave thread.
+ Write the event so that others can later know the format of this relay
+ log.
+ Note that this event is very close to the original event from the
+ master (it has binlog version of the master, event types of the
+ master), so this is suitable to parse the next relay log's event. It
+ has been produced by
+ Format_description_log_event::Format_description_log_event(char*
+ buf,).
+ Why don't we want to write the description_event_for_queue if this event
+ is for format<4 (3.23 or 4.x): this is because in that case, the
+ description_event_for_queue describes the data received from the master,
+ but not the data written to the relay log (*conversion*), which is in
+ format 4 (slave's).
+ */
+ /*
+ Set 'created' to 0, so that in next relay logs this event does not trigger
+ cleaning actions on the slave in
+ Format_description_log_event::exec_event().
+ Set 'log_pos' to 0 to show that it's an artificial event.
+ */
+ description_event_for_queue->created= 0;
+ description_event_for_queue->log_pos= 0;
+
+ if (description_event_for_queue->write(&log_file))
+ goto err;
+ bytes_written+= description_event_for_queue->get_event_len();
}
if (flush_io_cache(&log_file) ||
my_sync(log_file.file, MYF(MY_WME)))
@@ -654,7 +699,7 @@ bool MYSQL_LOG::reset_logs(THD* thd)
if (!thd->slave_thread)
need_start_event=1;
open(save_name, save_log_type, 0, index_file_name,
- io_cache_type, no_auto_events, max_size);
+ io_cache_type, no_auto_events, max_size, 0);
my_free((gptr) save_name, MYF(0));
err:
@@ -836,13 +881,13 @@ int MYSQL_LOG::purge_logs(const char *to_log,
while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
!log_in_use(log_info.log_file_name))
{
- ulong tmp;
- LINT_INIT(tmp);
+ ulong file_size;
+ LINT_INIT(file_size);
if (decrease_log_space) //stat the file we want to delete
{
MY_STAT s;
if (my_stat(log_info.log_file_name,&s,MYF(0)))
- tmp= s.st_size;
+ file_size= s.st_size;
else
{
/*
@@ -850,7 +895,7 @@ int MYSQL_LOG::purge_logs(const char *to_log,
of space that deletion will free. In most cases,
deletion won't work either, so it's not a problem.
*/
- tmp= 0;
+ file_size= 0;
}
}
/*
@@ -859,7 +904,7 @@ int MYSQL_LOG::purge_logs(const char *to_log,
*/
DBUG_PRINT("info",("purging %s",log_info.log_file_name));
if (!my_delete(log_info.log_file_name, MYF(0)) && decrease_log_space)
- *decrease_log_space-= tmp;
+ *decrease_log_space-= file_size;
if (find_next_log(&log_info, 0) || exit_loop)
break;
}
@@ -1044,8 +1089,17 @@ void MYSQL_LOG::new_file(bool need_lock)
Note that at this point, log_type != LOG_CLOSED (important for is_open()).
*/
+ /*
+ new_file() is only used for rotation (in FLUSH LOGS or because size >
+ max_binlog_size or max_relay_log_size).
+ If this is a binary log, the Format_description_log_event at the beginning of
+ the new file should have created=0 (to distinguish with the
+ Format_description_log_event written at server startup, which should
+ trigger temp tables deletion on slaves.
+ */
+
open(old_name, save_log_type, new_name_ptr, index_file_name, io_cache_type,
- no_auto_events, max_size);
+ no_auto_events, max_size, 1);
if (this == &mysql_bin_log)
report_pos_in_innodb();
my_free(old_name,MYF(0));
@@ -1129,7 +1183,7 @@ err:
/*
Write to normal (not rotable) log
- This is the format for the 'normal', 'slow' and 'update' logs.
+ This is the format for the 'normal' log.
*/
bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
@@ -1220,11 +1274,15 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
inline bool sync_binlog(IO_CACHE *cache)
{
- return (sync_binlog_period &&
- (sync_binlog_period == ++sync_binlog_counter) &&
- (sync_binlog_counter= 0, my_sync(cache->file, MYF(MY_WME))));
+ if (sync_binlog_period == ++sync_binlog_counter && sync_binlog_period)
+ {
+ sync_binlog_counter= 0;
+ return my_sync(cache->file, MYF(MY_WME));
+ }
+ return 0;
}
+
/*
Write an event to the binary log
*/
@@ -1292,7 +1350,7 @@ bool MYSQL_LOG::write(Log_event* event_info)
if (thd)
{
-#if MYSQL_VERSION_ID < 50000
+ /* NOTE: CHARSET AND TZ REPL WILL BE REWRITTEN SHORTLY */
/*
To make replication of charsets working in 4.1 we are writing values
of charset related variables before every statement in the binlog,
@@ -1338,7 +1396,6 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
if (e.write(file))
goto err;
}
-#endif
if (thd->last_insert_id_used)
{
@@ -1391,22 +1448,6 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
goto err;
}
#endif
-
- /*
- If the user has set FOREIGN_KEY_CHECKS=0 we wrap every SQL
- command in the binlog inside:
- SET FOREIGN_KEY_CHECKS=0;
- <command>;
- SET FOREIGN_KEY_CHECKS=1;
- */
-
- if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS)
- {
- Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=0", 24, 0);
- e.set_log_pos(this);
- if (e.write(file))
- goto err;
- }
}
/* Write the SQL command */
@@ -1415,28 +1456,6 @@ COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u",
if (event_info->write(file))
goto err;
- /* Write log events to reset the 'run environment' of the SQL command */
-
- if (thd)
- {
- if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS)
- {
- Query_log_event e(thd, "SET FOREIGN_KEY_CHECKS=1", 24, 0);
- e.set_log_pos(this);
- if (e.write(file))
- goto err;
- }
-#if MYSQL_VERSION_ID < 40100
- if (thd->variables.convert_set)
- {
- Query_log_event e(thd, "SET CHARACTER SET DEFAULT", 25, 0);
- e.set_log_pos(this);
- if (e.write(file))
- goto err;
- }
-#endif
- }
-
/*
Tell for transactional table handlers up to which position in the
binlog file we wrote. The table handler can store this info, and
@@ -1701,8 +1720,7 @@ err:
/*
- Write update log in a format suitable for incremental backup
- This is also used by the slow query log.
+ Write to the slow query log.
*/
bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
@@ -1718,11 +1736,6 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
int tmp_errno=0;
char buff[80],*end;
end=buff;
- if (!(thd->options & OPTION_UPDATE_LOG))
- {
- VOID(pthread_mutex_unlock(&LOCK_log));
- return 0;
- }
if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT) || query_start_arg)
{
current_time=time(NULL);
@@ -1883,6 +1896,7 @@ void MYSQL_LOG::close(uint exiting)
Stop_log_event s;
s.set_log_pos(this);
s.write(&log_file);
+ bytes_written+= s.get_event_len();
signal_update();
}
#endif /* HAVE_REPLICATION */
diff --git a/sql/log_event.cc b/sql/log_event.cc
index ef77aa6603b..97e041774f8 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -225,9 +225,25 @@ static char *str_to_hex(char *to, char *from, uint len)
return p; // pointer to end 0 of 'to'
}
+/*
+ Prints a "session_var=value" string. Used by mysqlbinlog to print some SET
+ commands just before it prints a query.
+*/
+
+static void print_set_option(FILE* file, uint32 bits_changed, uint32 option,
+ uint32 flags, const char* name, bool* need_comma)
+{
+ if (bits_changed & option)
+ {
+ if (*need_comma)
+ fprintf(file,", ");
+ fprintf(file,"%s=%d", name, (bool)(flags & option));
+ *need_comma= 1;
+ }
+}
/**************************************************************************
- Log_event methods
+ Log_event methods (= the parent class of all events)
**************************************************************************/
/*
@@ -237,7 +253,7 @@ static char *str_to_hex(char *to, char *from, uint len)
const char* Log_event::get_type_str()
{
switch(get_type_code()) {
- case START_EVENT: return "Start";
+ case START_EVENT_V3: return "Start_v3";
case STOP_EVENT: return "Stop";
case QUERY_EVENT: return "Query";
case ROTATE_EVENT: return "Rotate";
@@ -251,6 +267,7 @@ const char* Log_event::get_type_str()
case EXEC_LOAD_EVENT: return "Exec_load";
case RAND_EVENT: return "RAND";
case USER_VAR_EVENT: return "User var";
+ case FORMAT_DESCRIPTION_EVENT: return "Format_desc";
default: return "Unknown"; /* impossible */
}
}
@@ -294,24 +311,66 @@ Log_event::Log_event()
Log_event::Log_event()
*/
-Log_event::Log_event(const char* buf, bool old_format)
+Log_event::Log_event(const char* buf,
+ const Format_description_log_event* description_event)
:temp_buf(0), cached_event_len(0), cache_stmt(0)
{
+#ifndef MYSQL_CLIENT
+ thd = 0;
+#endif
when = uint4korr(buf);
server_id = uint4korr(buf + SERVER_ID_OFFSET);
- if (old_format)
+ if (description_event->binlog_version==1)
{
- log_pos=0;
- flags=0;
+ log_pos= 0;
+ flags= 0;
+ return;
}
- else
+ /* 4.0 or newer */
+ log_pos= uint4korr(buf + LOG_POS_OFFSET);
+ /*
+ If the log is 4.0 (so here it can only be a 4.0 relay log read by the SQL
+ thread or a 4.0 master binlog read by the I/O thread), log_pos is the
+ beginning of the event: we transform it into the end of the event, which is
+ more useful.
+ But how do you know that the log is 4.0: you know it if description_event is
+ version 3 *and* you are not reading a Format_desc (remember that mysqlbinlog
+ starts by assuming that 5.0 logs are in 4.0 format, until it finds a
+ Format_desc).
+ */
+ if (description_event->binlog_version==3 &&
+ buf[EVENT_TYPE_OFFSET]<FORMAT_DESCRIPTION_EVENT &&
+ /*
+ If log_pos=0, don't change it. log_pos==0 is a marker to mean
+ "don't change rli->group_master_log_pos" (see
+ inc_group_relay_log_pos()). As it is unreal log_pos, adding the event
+ len's is nonsense. For example, a fake Rotate event should
+ not have its log_pos (which is 0) changed or it will modify
+ Exec_master_log_pos in SHOW SLAVE STATUS, displaying a nonsense value of
+ (a non-zero offset which does not exist in the master's binlog, so which
+ will cause problems if the user uses this value in CHANGE MASTER).
+ */
+ log_pos)
+ log_pos+= uint4korr(buf + EVENT_LEN_OFFSET);
+ flags= uint2korr(buf + FLAGS_OFFSET);
+ if ((buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) ||
+ (buf[EVENT_TYPE_OFFSET] == ROTATE_EVENT))
{
- log_pos = uint4korr(buf + LOG_POS_OFFSET);
- flags = uint2korr(buf + FLAGS_OFFSET);
+ /*
+ These events always have a header which stops here (i.e. their header is
+ FROZEN).
+ */
+ /*
+ Initialization to zero of all other Log_event members as they're not
+ specified. Currently there are no such members; in the future there will
+ be an event UID (but Format_description and Rotate don't need this UID, as
+ they are not propagated through --log-slave-updates (remember the UID is
+ used to not play a query twice when you have two masters which are slaves
+ of a 3rd master). Then we are done.
+ */
+ return;
}
-#ifndef MYSQL_CLIENT
- thd = 0;
-#endif
+ /* otherwise, go on with reading the header from buf (nothing now) */
}
#ifndef MYSQL_CLIENT
@@ -364,10 +423,10 @@ int Log_event::exec_event(struct st_relay_log_info* rli)
has already been updated.
*/
if ((thd->options & OPTION_BEGIN) && opt_using_transactions)
- rli->inc_event_relay_log_pos(get_event_len());
+ rli->inc_event_relay_log_pos();
else
{
- rli->inc_group_relay_log_pos(get_event_len(),log_pos);
+ rli->inc_group_relay_log_pos(log_pos);
flush_relay_log_info(rli);
/*
Note that Rotate_log_event::exec_event() does not call this function,
@@ -429,7 +488,7 @@ void Log_event::init_show_field_list(List<Item>* field_list)
field_list->push_back(new Item_empty_string("Event_type", 20));
field_list->push_back(new Item_return_int("Server_id", 10,
MYSQL_TYPE_LONG));
- field_list->push_back(new Item_return_int("Orig_log_pos", 11,
+ field_list->push_back(new Item_return_int("End_log_pos", 11,
MYSQL_TYPE_LONGLONG));
field_list->push_back(new Item_empty_string("Info", 20));
}
@@ -452,6 +511,12 @@ int Log_event::write(IO_CACHE* file)
int Log_event::write_header(IO_CACHE* file)
{
+ /*
+ Header will be of size LOG_EVENT_HEADER_LEN for all events, except for
+ FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT, where it will be
+ LOG_EVENT_MINIMAL_HEADER_LEN (remember these 2 have a frozen header, because
+ we read them before knowing the format).
+ */
char buf[LOG_EVENT_HEADER_LEN];
char* pos = buf;
int4store(pos, (ulong) when); // timestamp
@@ -459,19 +524,45 @@ int Log_event::write_header(IO_CACHE* file)
*pos++ = get_type_code(); // event type code
int4store(pos, server_id);
pos += 4;
- long tmp=get_data_size() + LOG_EVENT_HEADER_LEN;
+ long tmp; // total length of the event
+ switch (get_type_code())
+ {
+ case FORMAT_DESCRIPTION_EVENT:
+ case ROTATE_EVENT:
+ tmp= get_data_size() + LOG_EVENT_MINIMAL_HEADER_LEN;
+ break;
+ default:
+ tmp= get_data_size() + LOG_EVENT_HEADER_LEN;
+ break;
+ }
int4store(pos, tmp);
pos += 4;
int4store(pos, log_pos);
pos += 4;
int2store(pos, flags);
pos += 2;
+ switch (get_type_code())
+ {
+ case FORMAT_DESCRIPTION_EVENT:
+ case ROTATE_EVENT:
+ break;
+ default:
+ /*
+ Other data to print in the header (nothing now); in that case increment
+ pos.
+ */
+ break;
+ }
return (my_b_safe_write(file, (byte*) buf, (uint) (pos - buf)));
}
/*
Log_event::read_log_event()
+
+ This needn't be format-tolerant, because we only read
+ LOG_EVENT_MINIMAL_HEADER_LEN (we just want to read the event's length).
+
*/
#ifndef MYSQL_CLIENT
@@ -480,7 +571,7 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
{
ulong data_len;
int result=0;
- char buf[LOG_EVENT_HEADER_LEN];
+ char buf[LOG_EVENT_MINIMAL_HEADER_LEN];
DBUG_ENTER("read_log_event");
if (log_lock)
@@ -500,24 +591,25 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet,
goto end;
}
data_len= uint4korr(buf + EVENT_LEN_OFFSET);
- if (data_len < LOG_EVENT_HEADER_LEN ||
+ if (data_len < LOG_EVENT_MINIMAL_HEADER_LEN ||
data_len > current_thd->variables.max_allowed_packet)
{
DBUG_PRINT("error",("data_len: %ld", data_len));
- result= ((data_len < LOG_EVENT_HEADER_LEN) ? LOG_READ_BOGUS :
+ result= ((data_len < LOG_EVENT_MINIMAL_HEADER_LEN) ? LOG_READ_BOGUS :
LOG_READ_TOO_LARGE);
goto end;
}
packet->append(buf, sizeof(buf));
- data_len-= LOG_EVENT_HEADER_LEN;
+ data_len-= LOG_EVENT_MINIMAL_HEADER_LEN;
if (data_len)
{
if (packet->append(file, data_len))
{
/*
- Here we should never hit EOF in a non-error condition.
+ Here if we hit EOF it's really an error: as data_len is >=0
+ there's supposed to be more bytes available.
EOF means we are reading the event partially, which should
- never happen.
+ never happen: either we read badly or the binlog is truncated.
*/
result= file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO;
/* Implicit goto end; */
@@ -545,24 +637,42 @@ end:
Log_event::read_log_event()
NOTE:
- Allocates memory; The caller is responsible for clean-up
+ Allocates memory; The caller is responsible for clean-up.
*/
#ifndef MYSQL_CLIENT
Log_event* Log_event::read_log_event(IO_CACHE* file,
pthread_mutex_t* log_lock,
- bool old_format)
+ const Format_description_log_event *description_event)
#else
-Log_event* Log_event::read_log_event(IO_CACHE* file, bool old_format)
+Log_event* Log_event::read_log_event(IO_CACHE* file,
+ const Format_description_log_event *description_event)
#endif
{
- char head[LOG_EVENT_HEADER_LEN];
- uint header_size= old_format ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
+ DBUG_ASSERT(description_event);
+ char head[LOG_EVENT_MINIMAL_HEADER_LEN];
+ /*
+ First we only want to read at most LOG_EVENT_MINIMAL_HEADER_LEN, just to
+ check the event for sanity and to know its length; no need to really parse
+ it. We say "at most" because this could be a 3.23 master, which has header
+ of 13 bytes, whereas LOG_EVENT_MINIMAL_HEADER_LEN is 19 bytes (it's "minimal"
+ over the set {MySQL >=4.0}).
+ */
+ uint header_size= min(description_event->common_header_len,
+ LOG_EVENT_MINIMAL_HEADER_LEN);
LOCK_MUTEX;
+ DBUG_PRINT("info", ("my_b_tell=%lu", my_b_tell(file)));
if (my_b_read(file, (byte *) head, header_size))
{
+ DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \
+failed my_b_read"));
UNLOCK_MUTEX;
+ /*
+ No error here; it could be that we are at the file's end. However if the
+ next my_b_read() fails (below), it will be an error as we were able to
+ read the first bytes.
+ */
return 0;
}
@@ -596,7 +706,8 @@ Log_event* Log_event::read_log_event(IO_CACHE* file, bool old_format)
error = "read error";
goto err;
}
- if ((res = read_log_event(buf, data_len, &error, old_format)))
+ if ((res= read_log_event(buf, data_len, &error,
+ description_event)))
res->register_temp_buf(buf);
err:
@@ -623,13 +734,17 @@ Error in Log_event::read_log_event(): '%s', data_len: %d, event_type: %d",
/*
Log_event::read_log_event()
+ Binlog format tolerance is in (buf, event_len, description_event)
+ constructors.
*/
-Log_event* Log_event::read_log_event(const char* buf, int event_len,
- const char **error, bool old_format)
+Log_event* Log_event::read_log_event(const char* buf, uint event_len,
+ const char **error,
+ const Format_description_log_event *description_event)
{
- DBUG_ENTER("Log_event::read_log_event");
-
+ DBUG_ENTER("Log_event::read_log_event(char*,...)");
+ DBUG_ASSERT(description_event);
+ DBUG_PRINT("info", ("binlog_version=%d", description_event->binlog_version));
if (event_len < EVENT_LEN_OFFSET ||
(uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
{
@@ -641,64 +756,76 @@ Log_event* Log_event::read_log_event(const char* buf, int event_len,
switch(buf[EVENT_TYPE_OFFSET]) {
case QUERY_EVENT:
- ev = new Query_log_event(buf, event_len, old_format);
+ ev = new Query_log_event(buf, event_len, description_event);
break;
case LOAD_EVENT:
- ev = new Create_file_log_event(buf, event_len, old_format);
+ ev = new Create_file_log_event(buf, event_len, description_event);
break;
case NEW_LOAD_EVENT:
- ev = new Load_log_event(buf, event_len, old_format);
+ ev = new Load_log_event(buf, event_len, description_event);
break;
case ROTATE_EVENT:
- ev = new Rotate_log_event(buf, event_len, old_format);
+ ev = new Rotate_log_event(buf, event_len, description_event);
break;
#ifdef HAVE_REPLICATION
- case SLAVE_EVENT:
+ case SLAVE_EVENT: /* can never happen (unused event) */
ev = new Slave_log_event(buf, event_len);
break;
#endif /* HAVE_REPLICATION */
case CREATE_FILE_EVENT:
- ev = new Create_file_log_event(buf, event_len, old_format);
+ ev = new Create_file_log_event(buf, event_len, description_event);
break;
case APPEND_BLOCK_EVENT:
- ev = new Append_block_log_event(buf, event_len);
+ ev = new Append_block_log_event(buf, event_len, description_event);
break;
case DELETE_FILE_EVENT:
- ev = new Delete_file_log_event(buf, event_len);
+ ev = new Delete_file_log_event(buf, event_len, description_event);
break;
case EXEC_LOAD_EVENT:
- ev = new Execute_load_log_event(buf, event_len);
+ ev = new Execute_load_log_event(buf, event_len, description_event);
break;
- case START_EVENT:
- ev = new Start_log_event(buf, old_format);
+ case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
+ ev = new Start_log_event_v3(buf, description_event);
break;
#ifdef HAVE_REPLICATION
case STOP_EVENT:
- ev = new Stop_log_event(buf, old_format);
+ ev = new Stop_log_event(buf, description_event);
break;
#endif /* HAVE_REPLICATION */
case INTVAR_EVENT:
- ev = new Intvar_log_event(buf, old_format);
+ ev = new Intvar_log_event(buf, description_event);
break;
case RAND_EVENT:
- ev = new Rand_log_event(buf, old_format);
+ ev = new Rand_log_event(buf, description_event);
break;
case USER_VAR_EVENT:
- ev = new User_var_log_event(buf, old_format);
+ ev = new User_var_log_event(buf, description_event);
+ break;
+ case FORMAT_DESCRIPTION_EVENT:
+ ev = new Format_description_log_event(buf, event_len, description_event);
break;
default:
break;
}
+ /*
+ is_valid() are small event-specific sanity tests which are important; for
+ example there are some my_malloc() in constructors
+ (e.g. Query_log_event::Query_log_event(char*...)); when these my_malloc()
+ fail we can't return an error out of the constructor (because constructor is
+ "void") ; so instead we leave the pointer we wanted to allocate
+ (e.g. 'query') to 0 and we test it in is_valid(). Same for
+ Format_description_log_event, member 'post_header_len'.
+ */
if (!ev || !ev->is_valid())
{
delete ev;
#ifdef MYSQL_CLIENT
- if (!force_opt)
+ if (!force_opt) /* then mysqlbinlog dies */
{
*error= "Found invalid event in binary log";
DBUG_RETURN(0);
}
- ev= new Unknown_log_event(buf, old_format);
+ ev= new Unknown_log_event(buf, description_event);
#else
*error= "Found invalid event in binary log";
DBUG_RETURN(0);
@@ -719,7 +846,7 @@ void Log_event::print_header(FILE* file)
char llbuff[22];
fputc('#', file);
print_timestamp(file);
- fprintf(file, " server id %d log_pos %s ", server_id,
+ fprintf(file, " server id %d end_log_pos %s ", server_id,
llstr(log_pos,llbuff));
}
@@ -753,13 +880,36 @@ void Log_event::print_timestamp(FILE* file, time_t* ts)
/*
Log_event::set_log_pos()
+ Only used when we are writing an event which we created, to the BINlog. That
+ is, when we have parsed and executed a query; we then want to set the event's
+ log_pos to what it is going to be in the binlog after we write it. Note that
+ this is the position of the END of the event.
*/
#ifndef MYSQL_CLIENT
void Log_event::set_log_pos(MYSQL_LOG* log)
{
+ /*
+ Note that with a SEQ_READ_APPEND cache, my_b_tell() does not work well.
+ So this will give slightly wrong positions for the Format_desc/Rotate/Stop
+ events which the slave writes to its relay log. For example, the initial
+ Format_desc will have end_log_pos=91 instead of 95. Because after writing
+ the first 4 bytes of the relay log, my_b_tell() still reports 0. Because
+ my_b_append() does not update the counter which my_b_tell() later uses (one
+ should probably use my_b_append_tell() to work around this).
+ To get right positions even when writing to the relay log, we use the (new)
+ my_b_safe_tell().
+ Note that this raises a question on the correctness of all these
+ DBUG_ASSERT(my_b_tell()=rli->event_relay_log_pos).
+ If in a transaction, the log_pos which we calculate below is not very good
+ (because then my_b_safe_tell() returns start position of the BEGIN, so it's
+ like the statement was at the BEGIN's place), but it's not a very serious
+ problem (as the slave, when it is in a transaction, does not take those
+ end_log_pos into account (as it calls inc_event_relay_log_pos()). To be
+ fixed later, so that it looks less strange. But not bug.
+ */
if (!log_pos)
- log_pos = my_b_tell(&log->log_file);
+ log_pos = my_b_safe_tell(&log->log_file)+get_event_len();
}
#endif /* !MYSQL_CLIENT */
@@ -772,10 +922,15 @@ void Log_event::set_log_pos(MYSQL_LOG* log)
/*
Query_log_event::pack_info()
+ This (which is used only for SHOW BINLOG EVENTS) could be updated to
+ print SET @@session_var=. But this is not urgent, as SHOW BINLOG EVENTS is
+ only an information, it does not produce suitable queries to replay (for
+ example it does not print LOAD DATA INFILE).
*/
void Query_log_event::pack_info(Protocol *protocol)
{
+ // TODO: show the catalog ??
char *buf, *pos;
if (!(buf= my_malloc(9 + db_len + q_len, MYF(MY_WME))))
return;
@@ -813,7 +968,7 @@ int Query_log_event::write(IO_CACHE* file)
int Query_log_event::write_data(IO_CACHE* file)
{
- char buf[QUERY_HEADER_LEN];
+ uchar buf[QUERY_HEADER_LEN+1+4+1+8+1+1+FN_REFLEN], *start;
if (!query)
return -1;
@@ -859,10 +1014,61 @@ int Query_log_event::write_data(IO_CACHE* file)
int4store(buf + Q_EXEC_TIME_OFFSET, exec_time);
buf[Q_DB_LEN_OFFSET] = (char) db_len;
int2store(buf + Q_ERR_CODE_OFFSET, error_code);
+ int2store(buf + Q_STATUS_VARS_LEN_OFFSET, status_vars_len);
- return (my_b_safe_write(file, (byte*) buf, QUERY_HEADER_LEN) ||
- my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
- my_b_safe_write(file, (byte*) query, q_len)) ? -1 : 0;
+ /*
+ You MUST always write status vars in increasing order of code. This
+ guarantees that a slightly older slave will be able to parse those he
+ knows.
+ */
+ start= buf+QUERY_HEADER_LEN;
+ if (flags2_inited)
+ {
+ *(start++)= Q_FLAGS2_CODE;
+ int4store(start, flags2);
+ start+= 4;
+ }
+ if (sql_mode_inited)
+ {
+ *(start++)= Q_SQL_MODE_CODE;
+ int8store(start, sql_mode);
+ start+= 8;
+ }
+ if (catalog_len >= 0) // i.e. "catalog inited" (false for 4.0 events)
+ {
+ *(start++)= Q_CATALOG_CODE;
+ *(start++)= (uchar) catalog_len;
+ bmove(start, catalog, catalog_len);
+ start+= catalog_len;
+ /*
+ We write a \0 at the end. As we also have written the length, it's
+ apparently useless; but in fact it enables us to just do
+ catalog= a_pointer_to_the_buffer_of_the_read_event
+ later in the slave SQL thread.
+ If we didn't have the \0, we would need to memdup to build the catalog in
+ the slave SQL thread.
+ And still the interest of having the length too is that in the slave SQL
+ thread we immediately know at which position the catalog ends (no need to
+ search for '\0'. In other words: length saves search, \0 saves mem alloc,
+ at the cost of 1 redundant byte on the disk.
+ Note that this is only a fix until we change 'catalog' to LEX_STRING
+ (then we won't need the \0).
+ */
+ *(start++)= '\0';
+ }
+ /*
+ Here there could be code like
+ if (command-line-option-which-says-"log_this_variable")
+ {
+ *(start++)= Q_THIS_VARIABLE_CODE;
+ int4store(start, this_variable);
+ start+= 4;
+ }
+ */
+
+ return (my_b_safe_write(file, (byte*) buf, (start-buf)) ||
+ my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) ||
+ my_b_safe_write(file, (byte*) query, q_len)) ? -1 : 0;
}
@@ -875,60 +1081,178 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
ulong query_length, bool using_trans)
:Log_event(thd_arg, !thd_arg->tmp_table_used ?
0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans),
- data_buf(0), query(query_arg),
+ data_buf(0), query(query_arg), catalog(thd_arg->catalog),
db(thd_arg->db), q_len((uint32) query_length),
- error_code(thd_arg->killed ?
+ error_code((thd_arg->killed != THD::NOT_KILLED) ?
((thd_arg->system_thread & SYSTEM_THREAD_DELAYED_INSERT) ?
- 0 : ER_SERVER_SHUTDOWN) : thd_arg->net.last_errno),
+ 0 : thd->killed_errno()) : thd_arg->net.last_errno),
thread_id(thd_arg->thread_id),
/* save the original thread id; we already know the server id */
- slave_proxy_id(thd_arg->variables.pseudo_thread_id)
+ slave_proxy_id(thd_arg->variables.pseudo_thread_id),
+ flags2_inited(1), sql_mode_inited(1), flags2(0), sql_mode(0)
{
time_t end_time;
time(&end_time);
exec_time = (ulong) (end_time - thd->start_time);
+ catalog_len = (catalog) ? (uint32) strlen(catalog) : 0;
+ status_vars_len= 1+4+1+8+1+1+catalog_len+1;
db_len = (db) ? (uint32) strlen(db) : 0;
+ /*
+ If we don't use flags2 for anything else than options contained in
+ thd->options, it would be more efficient to flags2=thd_arg->options
+ (OPTIONS_WRITTEN_TO_BINLOG would be used only at reading time).
+ But it's likely that we don't want to use 32 bits for 3 bits; in the future
+ we will probably want to reclaim the 29 bits. So we need the &.
+ */
+ flags2= thd_arg->options & OPTIONS_WRITTEN_TO_BIN_LOG;
+ sql_mode= thd_arg->variables.sql_mode;
+ DBUG_PRINT("info",("Query_log_event has flags2=%lu sql_mode=%lu",flags2,sql_mode));
}
#endif /* MYSQL_CLIENT */
/*
Query_log_event::Query_log_event()
+ This is used by the SQL slave thread to prepare the event before execution.
*/
-Query_log_event::Query_log_event(const char* buf, int event_len,
- bool old_format)
- :Log_event(buf, old_format),data_buf(0), query(NULL), db(NULL)
+Query_log_event::Query_log_event(const char* buf, uint event_len,
+ const Format_description_log_event *description_event)
+ :Log_event(buf, description_event), data_buf(0), query(NULL),
+ db(NULL), catalog_len(-1), status_vars_len(0),
+ flags2_inited(0), sql_mode_inited(0)
{
ulong data_len;
- if (old_format)
+ uint8 common_header_len, post_header_len;
+ DBUG_ENTER("Query_log_event::Query_log_event(char*,...)");
+ common_header_len= description_event->common_header_len;
+ post_header_len= description_event->post_header_len[QUERY_EVENT-1];
+ DBUG_PRINT("info",("event_len=%ld, common_header_len=%d, post_header_len=%d",
+ event_len, common_header_len, post_header_len));
+
+ /*
+ We test if the event's length is sensible, and if so we compute data_len.
+ We cannot rely on QUERY_HEADER_LEN here as it would not be format-tolerant.
+ We use QUERY_HEADER_MINIMAL_LEN which is the same for 3.23, 4.0 & 5.0.
+ */
+ if (event_len < (uint)(common_header_len + post_header_len))
+ return;
+ data_len = event_len - (common_header_len + post_header_len);
+ buf+= common_header_len;
+
+ slave_proxy_id= thread_id = uint4korr(buf + Q_THREAD_ID_OFFSET);
+ exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET);
+ db_len = (uint)buf[Q_DB_LEN_OFFSET];
+ error_code = uint2korr(buf + Q_ERR_CODE_OFFSET);
+
+ /*
+ 5.0 format starts here.
+ Depending on the format, we may or not have affected/warnings etc
+ The remnent post-header to be parsed has length:
+ */
+ uint32 tmp= post_header_len - QUERY_HEADER_MINIMAL_LEN;
+ if (tmp)
{
- if ((uint)event_len < OLD_HEADER_LEN + QUERY_HEADER_LEN)
- return;
- data_len = event_len - (QUERY_HEADER_LEN + OLD_HEADER_LEN);
- buf += OLD_HEADER_LEN;
+ status_vars_len= uint2korr(buf + Q_STATUS_VARS_LEN_OFFSET);
+ DBUG_PRINT("info", ("Query_log_event has status_vars_len=%d",
+ status_vars_len));
+ tmp-= 2;
}
- else
+ /* we have parsed everything we know in the post header */
+#ifndef DBUG_OFF
+ if (tmp) /* this is probably a master newer than us */
+ DBUG_PRINT("info", ("Query_log_event has longer post header than we know\
+ (%d more bytes)", tmp));
+#endif
+
+ /*
+ Suppose you have a 4.0 master with --ansi and a 5.0 slave with --ansi.
+ The slave sets flags2 to 0 (because that's a 4.0 event); if we simply use
+ this value of 0, we will cancel --ansi on the slave, which is
+ unwanted. In this example 0 means "unset", not really "set to 0".
+ Fortunately we have flags2_inited==0 to distinguish between "unset" and
+ "set to 0". See below.
+ */
+
+ /* variable-part: the status vars; only in MySQL 5.0 */
+
+ const uchar *start= (uchar*) (buf+post_header_len);
+ const uchar *end= (uchar*) (start+status_vars_len);
+ /*
+ The place from which we will start string duplication.
+ */
+ const uchar *start_dup= end;
+
+ for (const uchar* pos=start;pos<end;)
{
- if ((uint)event_len < QUERY_EVENT_OVERHEAD)
- return;
- data_len = event_len - QUERY_EVENT_OVERHEAD;
- buf += LOG_EVENT_HEADER_LEN;
+ switch (*pos++) {
+ case Q_FLAGS2_CODE:
+ flags2_inited= 1;
+ flags2= uint4korr(pos);
+ DBUG_PRINT("info",("In Query_log_event, read flags2: %lu", flags2));
+ pos+= 4;
+ break;
+ case Q_SQL_MODE_CODE:
+ {
+#ifndef DBUG_OFF
+ char buff[22];
+#endif
+ sql_mode_inited= 1;
+ sql_mode= (ulong) uint8korr(pos); // QQ: Fix when sql_mode is ulonglong
+ DBUG_PRINT("info",("In Query_log_event, read sql_mode: %s",
+ llstr(sql_mode, buff)));
+ pos+= 8;
+ break;
+ }
+ case Q_CATALOG_CODE:
+ catalog_len= *pos;
+ /*
+ Now 'pos' points to beginning of catalog - 1.
+ The catalog must be included in the string which we will duplicate
+ later. If string status vars having a smaller code had been seen before
+ and so marked to-be-duplicated, start_dup would be != end and we would
+ not need (and want) to change start_dup (because this would cut the
+ previously marked status vars).
+ */
+ pos++;
+ if (start_dup==end)
+ start_dup= pos;
+ pos+= catalog_len+1; // counting the end '\0'
+ break;
+ default:
+ /* That's why you must write status vars in growing order of code */
+ DBUG_PRINT("info",("Query_log_event has unknown status vars (first has\
+ code: %u), skipping the rest of them", (uint) *(pos-1)));
+ pos= end;
+ }
}
-
- exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET);
- error_code = uint2korr(buf + Q_ERR_CODE_OFFSET);
-
- if (!(data_buf = (char*) my_malloc(data_len + 1, MYF(MY_WME))))
+
+ /* A 2nd variable part; this is common to all versions */
+
+ data_len-= (uint) (start_dup-start); /* cut not-to-be-duplicated head */
+ if (!(data_buf = (char*) my_strdup_with_length((byte*) start_dup,
+ data_len,
+ MYF(MY_WME))))
return;
-
- memcpy(data_buf, buf + Q_DATA_OFFSET, data_len);
- slave_proxy_id= thread_id= uint4korr(buf + Q_THREAD_ID_OFFSET);
- db = data_buf;
- db_len = (uint)buf[Q_DB_LEN_OFFSET];
- query=data_buf + db_len + 1;
- q_len = data_len - 1 - db_len;
- *((char*)query+q_len) = 0;
+
+ const char* tmp_buf= data_buf;
+ /* Now set event's pointers to point to bits of the new string */
+ if (catalog_len >= 0) // we have seen a catalog (zero-length or not)
+ {
+ catalog= tmp_buf;
+ tmp_buf+= (uint) (end-start_dup); /* "seek" to db */
+ }
+#ifndef DBUG_OFF
+ else
+ catalog= 0; // for DBUG_PRINT
+#endif
+ db= tmp_buf;
+ query= tmp_buf + db_len + 1;
+ q_len = data_buf + data_len - query;
+ /* This is used to detect wrong parsing. Could be removed in the future. */
+ DBUG_PRINT("info", ("catalog_len:%d catalog: '%s' db: '%s' q_len: %d",
+ catalog_len, catalog, db, q_len));
+ DBUG_VOID_RETURN;
}
@@ -937,8 +1261,10 @@ Query_log_event::Query_log_event(const char* buf, int event_len,
*/
#ifdef MYSQL_CLIENT
-void Query_log_event::print(FILE* file, bool short_form, char* last_db)
+void Query_log_event::print(FILE* file, bool short_form,
+ LAST_EVENT_INFO* last_event_info)
{
+ // TODO: print the catalog ??
char buff[40],*end; // Enough for SET TIMESTAMP
if (!short_form)
{
@@ -949,10 +1275,10 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db)
bool different_db= 1;
- if (db && last_db)
+ if (db && last_event_info->db)
{
- if (different_db= memcmp(last_db, db, db_len + 1))
- memcpy(last_db, db, db_len + 1);
+ if ((different_db = memcmp(last_event_info->db, db, db_len + 1)))
+ memcpy(last_event_info->db, db, db_len + 1);
}
if (db && db[0] && different_db)
@@ -963,6 +1289,67 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db)
my_fwrite(file, (byte*) buff, (uint) (end-buff),MYF(MY_NABP | MY_WME));
if (flags & LOG_EVENT_THREAD_SPECIFIC_F)
fprintf(file,"SET @@session.pseudo_thread_id=%lu;\n",(ulong)thread_id);
+ /*
+ Now the session variables;
+ it's more efficient to pass SQL_MODE as a number instead of a
+ comma-separated list.
+ FOREIGN_KEY_CHECKS, SQL_AUTO_IS_NULL, UNIQUE_CHECKS are session-only
+ variables (they have no global version; they're not listed in sql_class.h),
+ The tests below work for pure binlogs or pure relay logs. Won't work for
+ mixed relay logs but we don't create mixed relay logs (that is, there is no
+ relay log with a format change except within the 3 first events, which
+ mysqlbinlog handles gracefully). So this code should always be good.
+ */
+
+ uint32 tmp;
+
+ if (likely(flags2_inited)) /* likely as this will mainly read 5.0 logs */
+ {
+ /* tmp is a bitmask of bits which have changed. */
+ if (likely(last_event_info->flags2_inited))
+ /* All bits which have changed */
+ tmp= (last_event_info->flags2) ^ flags2;
+ else /* that's the first Query event we read */
+ {
+ last_event_info->flags2_inited= 1;
+ tmp= ~((uint32)0); /* all bits have changed */
+ }
+
+ if (unlikely(tmp)) /* some bits have changed */
+ {
+ bool need_comma= 0;
+ fprintf(file, "SET ");
+ print_set_option(file, tmp, OPTION_NO_FOREIGN_KEY_CHECKS, ~flags2,
+ "@@session.foreign_key_checks", &need_comma);
+ print_set_option(file, tmp, OPTION_AUTO_IS_NULL, flags2,
+ "@@session.sql_auto_is_null", &need_comma);
+ print_set_option(file, tmp, OPTION_RELAXED_UNIQUE_CHECKS, ~flags2,
+ "@@session.unique_checks", &need_comma);
+ fprintf(file,";\n");
+ last_event_info->flags2= flags2;
+ }
+ }
+
+ /*
+ If flags2_inited==0, this is an event from 3.23 or 4.0; nothing to print
+ (remember we don't produce mixed relay logs so there cannot be 5.0 events
+ before that one so there is nothing to reset).
+ */
+
+ if (likely(sql_mode_inited))
+ {
+ if (unlikely(!last_event_info->sql_mode_inited)) /* first Query event */
+ {
+ last_event_info->sql_mode_inited= 1;
+ last_event_info->sql_mode= ~sql_mode; // force a difference to force write
+ }
+ if (unlikely(last_event_info->sql_mode != sql_mode))
+ {
+ fprintf(file,"SET @@session.sql_mode=%lu;\n",(ulong)sql_mode);
+ last_event_info->sql_mode= sql_mode;
+ }
+ }
+
my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
fprintf(file, ";\n");
}
@@ -977,11 +1364,24 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db)
int Query_log_event::exec_event(struct st_relay_log_info* rli)
{
int expected_error,actual_error= 0;
+ /*
+ Colleagues: please never free(thd->catalog) in MySQL. This would lead to
+ bugs as here thd->catalog is a part of an alloced block, not an entire
+ alloced block (see Query_log_event::exec_event()). Same for thd->db.
+ Thank you.
+ */
+ thd->catalog= (char*) catalog;
thd->db= (char*) rewrite_db(db);
/*
- InnoDB internally stores the master log position it has processed so far;
- position to store is of the END of the current log event.
+ InnoDB internally stores the master log position it has executed so far,
+ i.e. the position just after the COMMIT event.
+ When InnoDB will want to store, the positions in rli won't have
+ been updated yet, so group_master_log_* will point to old BEGIN
+ and event_master_log* will point to the beginning of current COMMIT.
+ But log_pos of the COMMIT Query event is what we want, i.e. the pos of the
+ END of the current log event (COMMIT). We save it in rli so that InnoDB can
+ access it.
*/
#if MYSQL_VERSION_ID < 50000
rli->future_group_master_log_pos= log_pos + get_event_len() -
@@ -1001,9 +1401,33 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli)
thd->query_id = query_id++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->variables.pseudo_thread_id= thread_id; // for temp tables
-
mysql_log.write(thd,COM_QUERY,"%s",thd->query);
DBUG_PRINT("query",("%s",thd->query));
+
+ if (flags2_inited)
+ /*
+ all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG must
+ take their value from flags2.
+ */
+ thd->options= flags2|(thd->options & ~(ulong)OPTIONS_WRITTEN_TO_BIN_LOG);
+ /*
+ else, we are in a 3.23/4.0 binlog; we previously received a
+ Rotate_log_event which reset thd->options and sql_mode, so nothing to do.
+ */
+
+ /*
+ We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a
+ slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not
+ force us to ignore the dir too. Imagine you are a ring of machines, and
+ one has a disk problem so that you temporarily need IGNORE_DIR_IN_CREATE
+ on this machine; you don't want it to propagate elsewhere (you don't want
+ all slaves to start ignoring the dirs).
+ */
+ if (sql_mode_inited)
+ thd->variables.sql_mode=
+ (ulong) ((thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) |
+ (sql_mode & ~(ulong) MODE_NO_DIR_IN_CREATE));
+
if (ignored_error_code((expected_error= error_code)) ||
!check_expected_error(thd,rli,expected_error))
mysql_parse(thd, thd->query, q_len);
@@ -1031,27 +1455,27 @@ START SLAVE; . Query: '%s'", expected_error, thd->query);
}
goto end;
}
-
+
/*
If we expected a non-zero error code, and we don't get the same error
code, and none of them should be ignored.
*/
DBUG_PRINT("info",("expected_error: %d last_errno: %d",
- expected_error, thd->net.last_errno));
+ expected_error, thd->net.last_errno));
if ((expected_error != (actual_error= thd->net.last_errno)) &&
- expected_error &&
- !ignored_error_code(actual_error) &&
- !ignored_error_code(expected_error))
+ expected_error &&
+ !ignored_error_code(actual_error) &&
+ !ignored_error_code(expected_error))
{
slave_print_error(rli, 0,
- "\
+ "\
Query caused different errors on master and slave. \
Error on master: '%s' (%d), Error on slave: '%s' (%d). \
Default database: '%s'. Query: '%s'",
- ER_SAFE(expected_error),
- expected_error,
- actual_error ? thd->net.last_error: "no error",
- actual_error,
+ ER_SAFE(expected_error),
+ expected_error,
+ actual_error ? thd->net.last_error: "no error",
+ actual_error,
print_slave_db_safe(db), query);
thd->query_error= 1;
}
@@ -1059,7 +1483,7 @@ Default database: '%s'. Query: '%s'",
If we get the same error code as expected, or they should be ignored.
*/
else if (expected_error == actual_error ||
- ignored_error_code(actual_error))
+ ignored_error_code(actual_error))
{
DBUG_PRINT("info",("error ignored"));
clear_all_errors(thd, rli);
@@ -1071,16 +1495,48 @@ Default database: '%s'. Query: '%s'",
{
slave_print_error(rli,actual_error,
"Error '%s' on query. Default database: '%s'. Query: '%s'",
- (actual_error ? thd->net.last_error :
- "unexpected success or fatal error"),
+ (actual_error ? thd->net.last_error :
+ "unexpected success or fatal error"),
print_slave_db_safe(db), query);
thd->query_error= 1;
}
+
+ /*
+ TODO: compare the values of "affected rows" around here. Something
+ like:
+ if ((uint32) affected_in_event != (uint32) affected_on_slave)
+ {
+ sql_print_error("Slave: did not get the expected number of affected \
+ rows running query from master - expected %d, got %d (this numbers \
+ should have matched modulo 4294967296).", 0, ...);
+ thd->query_error = 1;
+ }
+ We may also want an option to tell the slave to ignore "affected"
+ mismatch. This mismatch could be implemented with a new ER_ code, and
+ to ignore it you would use --slave-skip-errors...
+
+ To do the comparison we need to know the value of "affected" which the
+ above mysql_parse() computed. And we need to know the value of
+ "affected" in the master's binlog. Both will be implemented later. The
+ important thing is that we now have the format ready to log the values
+ of "affected" in the binlog. So we can release 5.0.0 before effectively
+ logging "affected" and effectively comparing it.
+ */
} /* End of if (db_ok(... */
end:
VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->db= 0; // prevent db from being freed
+ /*
+ Probably we have set thd->query, thd->db, thd->catalog to point to places
+ in the data_buf of this event. Now the event is going to be deleted
+ probably, so data_buf will be freed, so the thd->... listed above will be
+ pointers to freed memory.
+ So we must set them to 0, so that those bad pointers values are not later
+ used. Note that "cleanup" queries (automatic DO RELEASE_LOCK() and DROP
+ TEMPORARY TABLE don't suffer from these assignments to 0 as DROP TEMPORARY
+ TABLE uses the db.table syntax).
+ */
+ thd->db= thd->catalog= 0; // prevent db from being freed
thd->query= 0; // just to be sure
thd->query_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
@@ -1092,15 +1548,23 @@ end:
/**************************************************************************
- Start_log_event methods
+ Start_log_event_v3 methods
**************************************************************************/
+#ifndef MYSQL_CLIENT
+Start_log_event_v3::Start_log_event_v3() :Log_event(), binlog_version(BINLOG_VERSION)
+{
+ created= when;
+ memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
+}
+#endif
+
/*
- Start_log_event::pack_info()
+ Start_log_event_v3::pack_info()
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-void Start_log_event::pack_info(Protocol *protocol)
+void Start_log_event_v3::pack_info(Protocol *protocol)
{
char buf[12 + ST_SERVER_VER_LEN + 14 + 22], *pos;
pos= strmov(buf, "Server ver: ");
@@ -1113,35 +1577,43 @@ void Start_log_event::pack_info(Protocol *protocol)
/*
- Start_log_event::print()
+ Start_log_event_v3::print()
*/
#ifdef MYSQL_CLIENT
-void Start_log_event::print(FILE* file, bool short_form, char* last_db)
+void Start_log_event_v3::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
- if (short_form)
- return;
-
- print_header(file);
- fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version,
- server_version);
- print_timestamp(file);
- if (created)
- fprintf(file," at startup");
- fputc('\n', file);
+ if (!short_form)
+ {
+ print_header(file);
+ fprintf(file, "\tStart: binlog v %d, server v %s created ", binlog_version,
+ server_version);
+ print_timestamp(file);
+ if (created)
+ fprintf(file," at startup");
+ fputc('\n', file);
+ }
+#ifdef WHEN_WE_HAVE_THE_RESET_CONNECTION_SQL_COMMAND
+ /*
+ This is for mysqlbinlog: like in replication, we want to delete the stale
+ tmp files left by an unclean shutdown of mysqld (temporary tables). Probably
+ this can be done with RESET CONNECTION (syntax to be defined).
+ */
+ fprintf(file,"RESET CONNECTION;\n");
+#endif
fflush(file);
}
#endif /* MYSQL_CLIENT */
/*
- Start_log_event::Start_log_event()
+ Start_log_event_v3::Start_log_event_v3()
*/
-Start_log_event::Start_log_event(const char* buf,
- bool old_format)
- :Log_event(buf, old_format)
+Start_log_event_v3::Start_log_event_v3(const char* buf,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event)
{
- buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
+ buf += description_event->common_header_len;
binlog_version = uint2korr(buf+ST_BINLOG_VER_OFFSET);
memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
ST_SERVER_VER_LEN);
@@ -1150,12 +1622,12 @@ Start_log_event::Start_log_event(const char* buf,
/*
- Start_log_event::write_data()
+ Start_log_event_v3::write_data()
*/
-int Start_log_event::write_data(IO_CACHE* file)
+int Start_log_event_v3::write_data(IO_CACHE* file)
{
- char buff[START_HEADER_LEN];
+ char buff[START_V3_HEADER_LEN];
int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
int4store(buff + ST_CREATED_OFFSET,created);
@@ -1163,7 +1635,7 @@ int Start_log_event::write_data(IO_CACHE* file)
}
/*
- Start_log_event::exec_event()
+ Start_log_event_v3::exec_event()
The master started
@@ -1182,23 +1654,29 @@ int Start_log_event::write_data(IO_CACHE* file)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Start_log_event::exec_event(struct st_relay_log_info* rli)
+int Start_log_event_v3::exec_event(struct st_relay_log_info* rli)
{
- DBUG_ENTER("Start_log_event::exec_event");
-
+ DBUG_ENTER("Start_log_event_v3::exec_event");
/*
If the I/O thread has not started, mi->old_format is BINLOG_FORMAT_CURRENT
(that's what the MASTER_INFO constructor does), so the test below is not
perfect at all.
*/
- switch (rli->mi->old_format) {
- case BINLOG_FORMAT_CURRENT:
- /*
- This is 4.x, so a Start_log_event is only at master startup,
- so we are sure the master has restarted and cleared his temp tables.
+ switch (rli->relay_log.description_event_for_exec->binlog_version)
+ {
+ case 3:
+ case 4:
+ /*
+ This can either be 4.x (then a Start_log_event_v3 is only at master
+ startup so we are sure the master has restarted and cleared his temp
+ tables; the event always has 'created'>0) or 5.0 (then we have to test
+ 'created').
*/
- close_temporary_tables(thd);
- cleanup_load_tmpdir();
+ if (created)
+ {
+ close_temporary_tables(thd);
+ cleanup_load_tmpdir();
+ }
/*
As a transaction NEVER spans on 2 or more binlogs:
if we have an active transaction at this point, the master died while
@@ -1206,8 +1684,12 @@ int Start_log_event::exec_event(struct st_relay_log_info* rli)
cache to the binlog. As the write was started, the transaction had been
committed on the master, so we lack of information to replay this
transaction on the slave; all we can do is stop with error.
+ Note: this event could be sent by the master to inform us of the format
+ of its binlog; in other words maybe it is not at its original place when
+ it comes to us; we'll know this by checking log_pos ("artificial" events
+ have log_pos == 0).
*/
- if (thd->options & OPTION_BEGIN)
+ if (log_pos && (thd->options & OPTION_BEGIN))
{
slave_print_error(rli, 0, "\
Rolling back unfinished transaction (no COMMIT or ROLLBACK) from relay log. \
@@ -1221,33 +1703,270 @@ binary log.");
Now the older formats; in that case load_tmpdir is cleaned up by the I/O
thread.
*/
- case BINLOG_FORMAT_323_LESS_57:
- /*
- Cannot distinguish a Start_log_event generated at master startup and
- one generated by master FLUSH LOGS, so cannot be sure temp tables
- have to be dropped. So do nothing.
- */
- break;
- case BINLOG_FORMAT_323_GEQ_57:
+ case 1:
+ if (strncmp(rli->relay_log.description_event_for_exec->server_version,
+ "3.23.57",7) >= 0 && created)
+ {
+ /*
+ Can distinguish, based on the value of 'created': this event was
+ generated at master startup.
+ */
+ close_temporary_tables(thd);
+ }
/*
- Can distinguish, based on the value of 'created',
- which was generated at master startup.
+ Otherwise, can't distinguish a Start_log_event generated at master startup
+ and one generated by master FLUSH LOGS, so cannot be sure temp tables have
+ to be dropped. So do nothing.
*/
- if (created)
- close_temporary_tables(thd);
break;
default:
/* this case is impossible */
- return 1;
+ DBUG_RETURN(1);
}
-
DBUG_RETURN(Log_event::exec_event(rli));
}
#endif /* defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) */
-/**************************************************************************
- Load_log_event methods
-**************************************************************************/
+/***************************************************************************
+ Format_description_log_event methods
+****************************************************************************/
+
+/*
+ Format_description_log_event 1st ctor.
+
+ SYNOPSIS
+ Format_description_log_event::Format_description_log_event
+ binlog_version the binlog version for which we want to build
+ an event. Can be 1 (=MySQL 3.23), 3 (=4.0.x
+ x>=2 and 4.1) or 4 (MySQL 5.0). Note that the
+ old 4.0 (binlog version 2) is not supported;
+ it should not be used for replication with
+ 5.0.
+
+ DESCRIPTION
+ Ctor. Can be used to create the event to write to the binary log (when the
+ server starts or when FLUSH LOGS), or to create artificial events to parse
+ binlogs from MySQL 3.23 or 4.x.
+ When in a client, only the 2nd use is possible.
+
+ TODO
+ Update this code with the new event for LOAD DATA, once they are pushed (in
+ 4.1 or 5.0). If it's in 5.0, only the "case 4" block should be updated.
+
+*/
+
+Format_description_log_event::Format_description_log_event(uint8 binlog_ver,
+ const char* server_ver)
+ : Start_log_event_v3()
+{
+ created= when;
+ binlog_version= binlog_ver;
+ switch(binlog_ver)
+ {
+ case 4: /* MySQL 5.0 */
+ memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
+ common_header_len= LOG_EVENT_HEADER_LEN;
+ number_of_event_types= LOG_EVENT_TYPES;
+ /* we'll catch my_malloc() error in is_valid() */
+ post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8),
+ MYF(0));
+ /*
+ This long list of assignments is not beautiful, but I see no way to
+ make it nicer, as the right members are #defines, not array members, so
+ it's impossible to write a loop.
+ */
+ if (post_header_len)
+ {
+ post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
+ post_header_len[QUERY_EVENT-1]= QUERY_HEADER_LEN;
+ post_header_len[STOP_EVENT-1]= 0;
+ post_header_len[ROTATE_EVENT-1]= ROTATE_HEADER_LEN;
+ post_header_len[INTVAR_EVENT-1]= 0;
+ post_header_len[LOAD_EVENT-1]= LOAD_HEADER_LEN;
+ post_header_len[SLAVE_EVENT-1]= 0;
+ post_header_len[CREATE_FILE_EVENT-1]= CREATE_FILE_HEADER_LEN;
+ post_header_len[APPEND_BLOCK_EVENT-1]= APPEND_BLOCK_HEADER_LEN;
+ post_header_len[EXEC_LOAD_EVENT-1]= EXEC_LOAD_HEADER_LEN;
+ post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
+ post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
+ post_header_len[RAND_EVENT-1]= 0;
+ post_header_len[USER_VAR_EVENT-1]= 0;
+ post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
+ }
+ break;
+
+ case 1: /* 3.23 */
+ case 3: /* 4.0.x x>=2 */
+ /*
+ We build an artificial (i.e. not sent by the master) event, which
+ describes what those old master versions send.
+ */
+ if (binlog_ver==1)
+ strmov(server_version, server_ver ? server_ver : "3.23");
+ else
+ strmov(server_version, server_ver ? server_ver : "4.0");
+ common_header_len= binlog_ver==1 ? OLD_HEADER_LEN :
+ LOG_EVENT_MINIMAL_HEADER_LEN;
+ /*
+ The first new event in binlog version 4 is Format_desc. So any event type
+ after that does not exist in older versions. We use the events known by
+ version 3, even if version 1 had only a subset of them (this is not a
+ problem: it uses a few bytes for nothing but unifies code; it does not
+ make the slave detect less corruptions).
+ */
+ number_of_event_types= FORMAT_DESCRIPTION_EVENT - 1;
+ post_header_len=(uint8*) my_malloc(number_of_event_types*sizeof(uint8),
+ MYF(0));
+ if (post_header_len)
+ {
+ post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
+ post_header_len[QUERY_EVENT-1]= QUERY_HEADER_MINIMAL_LEN;
+ post_header_len[STOP_EVENT-1]= 0;
+ post_header_len[ROTATE_EVENT-1]= (binlog_ver==1) ? 0 : ROTATE_HEADER_LEN;
+ post_header_len[INTVAR_EVENT-1]= 0;
+ post_header_len[LOAD_EVENT-1]= LOAD_HEADER_LEN;
+ post_header_len[SLAVE_EVENT-1]= 0;
+ post_header_len[CREATE_FILE_EVENT-1]= CREATE_FILE_HEADER_LEN;
+ post_header_len[APPEND_BLOCK_EVENT-1]= APPEND_BLOCK_HEADER_LEN;
+ post_header_len[EXEC_LOAD_EVENT-1]= EXEC_LOAD_HEADER_LEN;
+ post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
+ post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-1];
+ post_header_len[RAND_EVENT-1]= 0;
+ post_header_len[USER_VAR_EVENT-1]= 0;
+ }
+ break;
+ default: /* Includes binlog version 2 i.e. 4.0.x x<=1 */
+ post_header_len= 0; /* will make is_valid() fail */
+ break;
+ }
+}
+
+Format_description_log_event::Format_description_log_event(const char* buf,
+ uint event_len,
+ const
+ Format_description_log_event*
+ description_event)
+ /*
+ The problem with this constructor is that the fixed header may have a length
+ different from this version, but we don't know this length as we have not
+ read the Format_description_log_event which says it, yet. This length is in
+ the post-header of the event, but we don't know where the post-header
+ starts.
+ So this type of event HAS to:
+ - either have the header's length at the beginning (in the header, at a
+ fixed position which will never be changed), not in the post-header. That
+ would make the header be "shifted" compared to other events.
+ - or have a header of size LOG_EVENT_MINIMAL_HEADER_LEN (19), in all future
+ versions, so that we know for sure.
+ I (Guilhem) chose the 2nd solution. Rotate has the same constraint (because
+ it is sent before Format_description_log_event).
+ */
+
+ :Start_log_event_v3(buf, description_event)
+{
+ DBUG_ENTER("Format_description_log_event::Format_description_log_event(char*,...)");
+ buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
+ if ((common_header_len=buf[ST_COMMON_HEADER_LEN_OFFSET]) < OLD_HEADER_LEN)
+ DBUG_VOID_RETURN; /* sanity check */
+ number_of_event_types=
+ event_len-(LOG_EVENT_MINIMAL_HEADER_LEN+ST_COMMON_HEADER_LEN_OFFSET+1);
+ DBUG_PRINT("info", ("common_header_len=%d number_of_event_types=%d",
+ common_header_len, number_of_event_types));
+ /* If alloc fails, we'll detect it in is_valid() */
+ post_header_len= (uint8*) my_memdup((byte*)buf+ST_COMMON_HEADER_LEN_OFFSET+1,
+ number_of_event_types*
+ sizeof(*post_header_len),
+ MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+int Format_description_log_event::write_data(IO_CACHE* file)
+{
+ /*
+ We don't call Start_log_event_v3::write_data() because this would make 2
+ my_b_safe_write().
+ */
+ char buff[FORMAT_DESCRIPTION_HEADER_LEN];
+ int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
+ memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
+ int4store(buff + ST_CREATED_OFFSET,created);
+ buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
+ memcpy(buff+ST_COMMON_HEADER_LEN_OFFSET+1, (byte*) post_header_len,
+ LOG_EVENT_TYPES);
+ return (my_b_safe_write(file, (byte*) buff, sizeof(buff)) ? -1 : 0);
+}
+
+/*
+ SYNOPSIS
+ Format_description_log_event::exec_event()
+
+ IMPLEMENTATION
+ Save the information which describes the binlog's format, to be able to
+ read all coming events.
+ Call Start_log_event_v3::exec_event().
+*/
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+int Format_description_log_event::exec_event(struct st_relay_log_info* rli)
+{
+ DBUG_ENTER("Format_description_log_event::exec_event");
+
+ /* save the information describing this binlog */
+ delete rli->relay_log.description_event_for_exec;
+ rli->relay_log.description_event_for_exec= this;
+
+ /*
+ If this event comes from ourselves, there is no cleaning task to perform,
+ we don't call Start_log_event_v3::exec_event() (this was just to update the
+ log's description event).
+ */
+ if (server_id == (uint32) ::server_id)
+ {
+ /*
+ Do not modify rli->group_master_log_pos, as this event did not exist on
+ the master. That is, just update the *relay log* coordinates; this is done
+ by passing log_pos=0 to inc_group_relay_log_pos, like we do in
+ Stop_log_event::exec_event().
+ If in a transaction, don't touch group_* coordinates.
+ */
+ if (thd->options & OPTION_BEGIN)
+ rli->inc_event_relay_log_pos();
+ else
+ {
+ rli->inc_group_relay_log_pos(0);
+ flush_relay_log_info(rli);
+ }
+ DBUG_RETURN(0);
+ }
+
+ /*
+ If the event was not requested by the slave i.e. the master sent it while
+ the slave asked for a position >4, the event will make
+ rli->group_master_log_pos advance. Say that the slave asked for position
+ 1000, and the Format_desc event's end is 95. Then in the beginning of
+ replication rli->group_master_log_pos will be 0, then 95, then jump to first
+ really asked event (which is >95). So this is ok.
+ */
+ DBUG_RETURN(Start_log_event_v3::exec_event(rli));
+}
+#endif
+
+ /**************************************************************************
+ Load_log_event methods
+ General note about Load_log_event: the binlogging of LOAD DATA INFILE is
+ going to be changed in 5.0 (or maybe in 4.1; not decided yet).
+ However, the 5.0 slave could still have to read such events (from a 4.x
+ master), convert them (which just means maybe expand the header, when 5.0
+ servers have a UID in events) (remember that whatever is after the header
+ will be like in 4.x, as this event's format is not modified in 5.0 as we
+ will use new types of events to log the new LOAD DATA INFILE features).
+ To be able to read/convert, we just need to not assume that the common
+ header is of length LOG_EVENT_HEADER_LEN (we must use the description
+ event).
+ Note that I (Guilhem) manually tested replication of a big LOAD DATA INFILE
+ between 3.23 and 5.0, and between 4.0 and 5.0, and it works fine (and the
+ positions displayed in SHOW SLAVE STATUS then are fine too).
+ **************************************************************************/
/*
Load_log_event::pack_info()
@@ -1482,15 +2201,25 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex,
constructed event.
*/
-Load_log_event::Load_log_event(const char *buf, int event_len,
- bool old_format)
- :Log_event(buf, old_format), num_fields(0), fields(0),
- field_lens(0), field_block_len(0),
+Load_log_event::Load_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event)
+ :Log_event(buf, description_event), num_fields(0), fields(0),
+ field_lens(0),field_block_len(0),
table_name(0), db(0), fname(0), local_fname(FALSE)
{
DBUG_ENTER("Load_log_event");
- if (event_len) // derived class, will call copy_log_event() itself
- copy_log_event(buf, event_len, old_format);
+ /*
+ I (Guilhem) manually tested replication of LOAD DATA INFILE for 3.23->5.0,
+ 4.0->5.0 and 5.0->5.0 and it works.
+ */
+ if (event_len)
+ copy_log_event(buf, event_len,
+ ((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
+ LOAD_HEADER_LEN +
+ description_event->common_header_len :
+ LOAD_HEADER_LEN + LOG_EVENT_HEADER_LEN),
+ description_event);
+ /* otherwise it's a derived class, will call copy_log_event() itself */
DBUG_VOID_RETURN;
}
@@ -1500,14 +2229,14 @@ Load_log_event::Load_log_event(const char *buf, int event_len,
*/
int Load_log_event::copy_log_event(const char *buf, ulong event_len,
- bool old_format)
+ int body_offset,
+ const Format_description_log_event *description_event)
{
+ DBUG_ENTER("Load_log_event::copy_log_event");
uint data_len;
char* buf_end = (char*)buf + event_len;
- uint header_len= old_format ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
- const char* data_head = buf + header_len;
- DBUG_ENTER("Load_log_event::copy_log_event");
-
+ /* this is the beginning of the post-header */
+ const char* data_head = buf + description_event->common_header_len;
slave_proxy_id= thread_id= uint4korr(data_head + L_THREAD_ID_OFFSET);
exec_time = uint4korr(data_head + L_EXEC_TIME_OFFSET);
skip_lines = uint4korr(data_head + L_SKIP_LINES_OFFSET);
@@ -1515,21 +2244,17 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
db_len = (uint)data_head[L_DB_LEN_OFFSET];
num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET);
- int body_offset = ((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
- LOAD_HEADER_LEN + header_len :
- get_data_body_offset());
-
if ((int) event_len < body_offset)
DBUG_RETURN(1);
/*
Sql_ex.init() on success returns the pointer to the first byte after
the sql_ex structure, which is the start of field lengths array.
*/
- if (!(field_lens=(uchar*)sql_ex.init((char*)buf + body_offset,
- buf_end,
- buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
+ if (!(field_lens= (uchar*)sql_ex.init((char*)buf + body_offset,
+ buf_end,
+ buf[EVENT_TYPE_OFFSET] != LOAD_EVENT)))
DBUG_RETURN(1);
-
+
data_len = event_len - body_offset;
if (num_fields > data_len) // simple sanity check against corruption
DBUG_RETURN(1);
@@ -1542,6 +2267,12 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
fname = db + db_len + 1;
fname_len = strlen(fname);
// null termination is accomplished by the caller doing buf[event_len]=0
+
+ /*
+ In 5.0 this event will have the same format, as we are planning to log LOAD
+ DATA INFILE in a completely different way (as a plain-text query) since 4.1
+ or 5.0 (Dmitri's WL#874)
+ */
DBUG_RETURN(0);
}
@@ -1551,13 +2282,13 @@ int Load_log_event::copy_log_event(const char *buf, ulong event_len,
*/
#ifdef MYSQL_CLIENT
-void Load_log_event::print(FILE* file, bool short_form, char* last_db)
+void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
- print(file, short_form, last_db, 0);
+ print(file, short_form, last_event_info, 0);
}
-void Load_log_event::print(FILE* file, bool short_form, char* last_db,
+void Load_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info,
bool commented)
{
DBUG_ENTER("Load_log_event::print");
@@ -1569,7 +2300,7 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db,
}
bool different_db= 1;
- if (db && last_db)
+ if (db && last_event_info->db)
{
/*
If the database is different from the one of the previous statement, we
@@ -1577,9 +2308,9 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db,
But if commented, the "use" is going to be commented so we should not
update the last_db.
*/
- if ((different_db= memcmp(last_db, db, db_len + 1)) &&
+ if ((different_db= memcmp(last_event_info->db, db, db_len + 1)) &&
!commented)
- memcpy(last_db, db, db_len + 1);
+ memcpy(last_event_info->db, db, db_len + 1);
}
if (db && db[0] && different_db)
@@ -1705,6 +2436,7 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
mysql_init_query(thd, 0, 0);
if (!use_rli_only_for_errors)
{
+ /* Saved for InnoDB, see comment in Query_log_event::exec_event() */
#if MYSQL_VERSION_ID < 50000
rli->future_group_master_log_pos= log_pos + get_event_len() -
(rli->mi->old_format ? (LOG_EVENT_HEADER_LEN - OLD_HEADER_LEN) : 0);
@@ -1712,8 +2444,8 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli,
rli->future_group_master_log_pos= log_pos;
#endif
}
-
- /*
+
+ /*
We test replicate_*_db rules. Note that we have already prepared the file
to load, even if we are going to ignore and delete it now. So it is
possible that we did a lot of disk writes for nothing. In other words, a
@@ -1852,7 +2584,7 @@ Slave: load data infile on table '%s' at log position %s in log \
thd->net.vio = 0;
VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->db= 0;
+ thd->db= thd->catalog= 0;
thd->query= 0;
thd->query_length= 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
@@ -1919,17 +2651,17 @@ void Rotate_log_event::pack_info(Protocol *protocol)
*/
#ifdef MYSQL_CLIENT
-void Rotate_log_event::print(FILE* file, bool short_form, char* last_db)
+void Rotate_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
char buf[22];
+
if (short_form)
return;
-
print_header(file);
fprintf(file, "\tRotate to ");
if (new_log_ident)
my_fwrite(file, (byte*) new_log_ident, (uint)ident_len,
- MYF(MY_NABP | MY_WME));
+ MYF(MY_NABP | MY_WME));
fprintf(file, " pos: %s", llstr(pos, buf));
fputc('\n', file);
fflush(file);
@@ -1941,31 +2673,22 @@ void Rotate_log_event::print(FILE* file, bool short_form, char* last_db)
Rotate_log_event::Rotate_log_event()
*/
-Rotate_log_event::Rotate_log_event(const char* buf, int event_len,
- bool old_format)
- :Log_event(buf, old_format),new_log_ident(NULL),alloced(0)
+Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event) ,new_log_ident(NULL),alloced(0)
{
+ DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)");
// The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
- int header_size = (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
+ uint8 header_size= description_event->common_header_len;
+ uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
uint ident_offset;
- DBUG_ENTER("Rotate_log_event");
-
if (event_len < header_size)
DBUG_VOID_RETURN;
-
buf += header_size;
- if (old_format)
- {
- ident_len = (uint)(event_len - OLD_HEADER_LEN);
- pos = 4;
- ident_offset = 0;
- }
- else
- {
- ident_len = (uint)(event_len - ROTATE_EVENT_OVERHEAD);
- pos = uint8korr(buf + R_POS_OFFSET);
- ident_offset = ROTATE_HEADER_LEN;
- }
+ pos = post_header_len ? uint8korr(buf + R_POS_OFFSET) : 4;
+ ident_len = (uint)(event_len -
+ (header_size+post_header_len));
+ ident_offset = post_header_len;
set_if_smaller(ident_len,FN_REFLEN-1);
if (!(new_log_ident= my_strdup_with_length((byte*) buf +
ident_offset,
@@ -1993,14 +2716,15 @@ int Rotate_log_event::write_data(IO_CACHE* file)
/*
Rotate_log_event::exec_event()
- Got a rotate log even from the master
+ Got a rotate log event from the master
IMPLEMENTATION
This is mainly used so that we can later figure out the logname and
position for the master.
- We can't rotate the slave as this will cause infinitive rotations
+ We can't rotate the slave's BINlog as this will cause infinitive rotations
in a A -> B -> A setup.
+ The NOTES below is a wrong comment which will disappear when 4.1 is merged.
RETURN VALUES
0 ok
@@ -2012,7 +2736,7 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
DBUG_ENTER("Rotate_log_event::exec_event");
pthread_mutex_lock(&rli->data_lock);
- rli->event_relay_log_pos += get_event_len();
+ rli->event_relay_log_pos= my_b_tell(rli->cur_log);
/*
If we are in a transaction: the only normal case is when the I/O thread was
copying a big transaction, then it was stopped and restarted: we have this
@@ -2024,15 +2748,28 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli)
COMMIT or ROLLBACK
In that case, we don't want to touch the coordinates which correspond to
the beginning of the transaction.
+ Starting from 5.0.0, there also are some rotates from the slave itself, in
+ the relay log.
*/
if (!(thd->options & OPTION_BEGIN))
{
memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
rli->notify_group_master_log_name_update();
- rli->group_master_log_pos = pos;
- rli->group_relay_log_pos = rli->event_relay_log_pos;
- DBUG_PRINT("info", ("group_master_log_pos: %lu",
+ rli->group_master_log_pos= pos;
+ rli->group_relay_log_pos= rli->event_relay_log_pos;
+ DBUG_PRINT("info", ("group_master_log_name: '%s' group_master_log_pos:\
+%lu",
+ rli->group_master_log_name,
(ulong) rli->group_master_log_pos));
+ /*
+ Reset thd->options and sql_mode, because this could be the signal of a
+ master's downgrade from 5.0 to 4.0.
+ However, no need to reset description_event_for_exec: indeed, if the next
+ master is 5.0 (even 5.0.1) we will soon get a Format_desc; if the next
+ master is 4.0 then the events are in the slave's format (conversion).
+ */
+ set_slave_thread_options(thd);
+ thd->variables.sql_mode= global_system_variables.sql_mode;
}
pthread_mutex_unlock(&rli->data_lock);
pthread_cond_broadcast(&rli->data_cond);
@@ -2066,12 +2803,13 @@ void Intvar_log_event::pack_info(Protocol *protocol)
Intvar_log_event::Intvar_log_event()
*/
-Intvar_log_event::Intvar_log_event(const char* buf, bool old_format)
- :Log_event(buf, old_format)
+Intvar_log_event::Intvar_log_event(const char* buf,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event)
{
- buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
- type = buf[I_TYPE_OFFSET];
- val = uint8korr(buf+I_VAL_OFFSET);
+ buf+= description_event->common_header_len;
+ type= buf[I_TYPE_OFFSET];
+ val= uint8korr(buf+I_VAL_OFFSET);
}
@@ -2107,7 +2845,7 @@ int Intvar_log_event::write_data(IO_CACHE* file)
*/
#ifdef MYSQL_CLIENT
-void Intvar_log_event::print(FILE* file, bool short_form, char* last_db)
+void Intvar_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
char llbuff[22];
const char *msg;
@@ -2150,7 +2888,7 @@ int Intvar_log_event::exec_event(struct st_relay_log_info* rli)
thd->next_insert_id = val;
break;
}
- rli->inc_event_relay_log_pos(get_event_len());
+ rli->inc_event_relay_log_pos();
return 0;
}
#endif
@@ -2173,12 +2911,13 @@ void Rand_log_event::pack_info(Protocol *protocol)
#endif
-Rand_log_event::Rand_log_event(const char* buf, bool old_format)
- :Log_event(buf, old_format)
+Rand_log_event::Rand_log_event(const char* buf,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event)
{
- buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
- seed1 = uint8korr(buf+RAND_SEED1_OFFSET);
- seed2 = uint8korr(buf+RAND_SEED2_OFFSET);
+ buf+= description_event->common_header_len;
+ seed1= uint8korr(buf+RAND_SEED1_OFFSET);
+ seed2= uint8korr(buf+RAND_SEED2_OFFSET);
}
@@ -2192,7 +2931,7 @@ int Rand_log_event::write_data(IO_CACHE* file)
#ifdef MYSQL_CLIENT
-void Rand_log_event::print(FILE* file, bool short_form, char* last_db)
+void Rand_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
char llbuff[22],llbuff2[22];
if (!short_form)
@@ -2212,7 +2951,7 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli)
{
thd->rand.seed1= (ulong) seed1;
thd->rand.seed2= (ulong) seed2;
- rli->inc_event_relay_log_pos(get_event_len());
+ rli->inc_event_relay_log_pos();
return 0;
}
#endif /* !MYSQL_CLIENT */
@@ -2283,10 +3022,11 @@ void User_var_log_event::pack_info(Protocol* protocol)
#endif /* !MYSQL_CLIENT */
-User_var_log_event::User_var_log_event(const char* buf, bool old_format)
- :Log_event(buf, old_format)
+User_var_log_event::User_var_log_event(const char* buf,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event)
{
- buf+= (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN;
+ buf+= description_event->common_header_len;
name_len= uint4korr(buf);
name= (char *) buf + UV_NAME_LEN_SIZE;
buf+= UV_NAME_LEN_SIZE + name_len;
@@ -2360,7 +3100,7 @@ int User_var_log_event::write_data(IO_CACHE* file)
*/
#ifdef MYSQL_CLIENT
-void User_var_log_event::print(FILE* file, bool short_form, char* last_db)
+void User_var_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
if (!short_form)
{
@@ -2493,7 +3233,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
e.update_hash(val, val_len, type, charset, DERIVATION_NONE);
free_root(&thd->mem_root,0);
- rli->inc_event_relay_log_pos(get_event_len());
+ rli->inc_event_relay_log_pos();
return 0;
}
#endif /* !MYSQL_CLIENT */
@@ -2505,7 +3245,7 @@ int User_var_log_event::exec_event(struct st_relay_log_info* rli)
#ifdef HAVE_REPLICATION
#ifdef MYSQL_CLIENT
-void Unknown_log_event::print(FILE* file, bool short_form, char* last_db)
+void Unknown_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
@@ -2535,7 +3275,7 @@ void Slave_log_event::pack_info(Protocol *protocol)
#ifndef MYSQL_CLIENT
Slave_log_event::Slave_log_event(THD* thd_arg,
struct st_relay_log_info* rli)
- :Log_event(thd_arg, 0, 0), mem_pool(0), master_host(0)
+ :Log_event(thd_arg, 0, 0) , mem_pool(0), master_host(0)
{
DBUG_ENTER("Slave_log_event");
if (!rli->inited) // QQ When can this happen ?
@@ -2576,7 +3316,7 @@ Slave_log_event::~Slave_log_event()
#ifdef MYSQL_CLIENT
-void Slave_log_event::print(FILE* file, bool short_form, char* last_db)
+void Slave_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
char llbuff[22];
if (short_form)
@@ -2622,12 +3362,13 @@ void Slave_log_event::init_from_mem_pool(int data_size)
}
-Slave_log_event::Slave_log_event(const char* buf, int event_len)
- :Log_event(buf,0),mem_pool(0),master_host(0)
+/* This code is not used, so has not been updated to be format-tolerant */
+Slave_log_event::Slave_log_event(const char* buf, uint event_len)
+ :Log_event(buf,0) /*unused event*/ ,mem_pool(0),master_host(0)
{
- event_len -= LOG_EVENT_HEADER_LEN;
- if (event_len < 0)
+ if (event_len < LOG_EVENT_HEADER_LEN)
return;
+ event_len -= LOG_EVENT_HEADER_LEN;
if (!(mem_pool = (char*) my_malloc(event_len + 1, MYF(MY_WME))))
return;
memcpy(mem_pool, buf + LOG_EVENT_HEADER_LEN, event_len);
@@ -2655,7 +3396,7 @@ int Slave_log_event::exec_event(struct st_relay_log_info* rli)
*/
#ifdef MYSQL_CLIENT
-void Stop_log_event::print(FILE* file, bool short_form, char* last_db)
+void Stop_log_event::print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
@@ -2677,7 +3418,7 @@ void Stop_log_event::print(FILE* file, bool short_form, char* last_db)
We used to clean up slave_load_tmpdir, but this is useless as it has been
cleared at the end of LOAD DATA INFILE.
So we have nothing to do here.
- The place were we must do this cleaning is in Start_log_event::exec_event(),
+ The place were we must do this cleaning is in Start_log_event_v3::exec_event(),
not here. Because if we come here, the master was sane.
*/
@@ -2691,8 +3432,13 @@ int Stop_log_event::exec_event(struct st_relay_log_info* rli)
could give false triggers in MASTER_POS_WAIT() that we have reached
the target position when in fact we have not.
*/
- rli->inc_group_relay_log_pos(get_event_len(), 0);
- flush_relay_log_info(rli);
+ if (thd->options & OPTION_BEGIN)
+ rli->inc_event_relay_log_pos();
+ else
+ {
+ rli->inc_group_relay_log_pos(0);
+ flush_relay_log_info(rli);
+ }
return 0;
}
#endif /* !MYSQL_CLIENT */
@@ -2772,28 +3518,42 @@ int Create_file_log_event::write_base(IO_CACHE* file)
Create_file_log_event ctor
*/
-Create_file_log_event::Create_file_log_event(const char* buf, int len,
- bool old_format)
- :Load_log_event(buf,0,old_format),fake_base(0),block(0),inited_from_old(0)
+Create_file_log_event::Create_file_log_event(const char* buf, uint len,
+ const Format_description_log_event* description_event)
+ :Load_log_event(buf,0,description_event),fake_base(0),block(0),inited_from_old(0)
{
- int block_offset;
- DBUG_ENTER("Create_file_log_event");
-
- /*
- We must make copy of 'buf' as this event may have to live over a
- rotate log entry when used in mysqlbinlog
- */
+ DBUG_ENTER("Create_file_log_event::Create_file_log_event(char*,...)");
+ uint block_offset;
+ uint header_len= description_event->common_header_len;
+ uint8 load_header_len= description_event->post_header_len[LOAD_EVENT-1];
+ uint8 create_file_header_len= description_event->post_header_len[CREATE_FILE_EVENT-1];
if (!(event_buf= my_memdup((byte*) buf, len, MYF(MY_WME))) ||
- (copy_log_event(event_buf, len, old_format)))
+ copy_log_event(event_buf,len,
+ ((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ?
+ load_header_len + header_len :
+ (fake_base ? (header_len+load_header_len) :
+ (header_len+load_header_len) +
+ create_file_header_len)),
+ description_event))
DBUG_VOID_RETURN;
-
- if (!old_format)
+ if (description_event->binlog_version!=1)
{
- file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN +
- + LOAD_HEADER_LEN + CF_FILE_ID_OFFSET);
+ file_id= uint4korr(buf +
+ header_len +
+ load_header_len + CF_FILE_ID_OFFSET);
// + 1 for \0 terminating fname
- block_offset = (LOG_EVENT_HEADER_LEN + Load_log_event::get_data_size() +
- CREATE_FILE_HEADER_LEN + 1);
+ /*
+ Note that it's ok to use get_data_size() below, because it is computed
+ with values we have already read from this event (because we called
+ copy_log_event()); we are not using slave's format info to decode master's
+ format, we are really using master's format info.
+ Anyway, both formats should be identical (except the common_header_len) as
+ these Load events are not changed between 4.0 and 5.0 (as logging of LOAD
+ DATA INFILE does not use Load_log_event in 5.0).
+ */
+ block_offset= description_event->common_header_len +
+ Load_log_event::get_data_size() +
+ create_file_header_len + 1;
if (len < block_offset)
return;
block = (char*)buf + block_offset;
@@ -2814,18 +3574,18 @@ Create_file_log_event::Create_file_log_event(const char* buf, int len,
#ifdef MYSQL_CLIENT
void Create_file_log_event::print(FILE* file, bool short_form,
- char* last_db, bool enable_local)
+ LAST_EVENT_INFO* last_event_info, bool enable_local)
{
if (short_form)
{
if (enable_local && check_fname_outside_temp_buf())
- Load_log_event::print(file, 1, last_db);
+ Load_log_event::print(file, 1, last_event_info);
return;
}
if (enable_local)
{
- Load_log_event::print(file, 1, last_db, !check_fname_outside_temp_buf());
+ Load_log_event::print(file, 1, last_event_info, !check_fname_outside_temp_buf());
/*
That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
SHOW BINLOG EVENTS we don't.
@@ -2838,9 +3598,9 @@ void Create_file_log_event::print(FILE* file, bool short_form,
void Create_file_log_event::print(FILE* file, bool short_form,
- char* last_db)
+ LAST_EVENT_INFO* last_event_info)
{
- print(file,short_form,last_db,0);
+ print(file,short_form,last_event_info,0);
}
#endif /* MYSQL_CLIENT */
@@ -2957,15 +3717,20 @@ Append_block_log_event::Append_block_log_event(THD* thd_arg, const char* db_arg,
Append_block_log_event ctor
*/
-Append_block_log_event::Append_block_log_event(const char* buf, int len)
- :Log_event(buf, 0),block(0)
+Append_block_log_event::Append_block_log_event(const char* buf, uint len,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event),block(0)
{
- DBUG_ENTER("Append_block_log_event");
- if ((uint)len < APPEND_BLOCK_EVENT_OVERHEAD)
+ DBUG_ENTER("Append_block_log_event::Append_block_log_event(char*,...)");
+ uint8 common_header_len= description_event->common_header_len;
+ uint8 append_block_header_len=
+ description_event->post_header_len[APPEND_BLOCK_EVENT-1];
+ uint total_header_len= common_header_len+append_block_header_len;
+ if (len < total_header_len)
DBUG_VOID_RETURN;
- file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET);
- block = (char*)buf + APPEND_BLOCK_EVENT_OVERHEAD;
- block_len = len - APPEND_BLOCK_EVENT_OVERHEAD;
+ file_id= uint4korr(buf + common_header_len + AB_FILE_ID_OFFSET);
+ block= (char*)buf + total_header_len;
+ block_len= len - total_header_len;
DBUG_VOID_RETURN;
}
@@ -2989,7 +3754,7 @@ int Append_block_log_event::write_data(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void Append_block_log_event::print(FILE* file, bool short_form,
- char* last_db)
+ LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
@@ -3075,12 +3840,15 @@ Delete_file_log_event::Delete_file_log_event(THD *thd_arg, const char* db_arg,
Delete_file_log_event ctor
*/
-Delete_file_log_event::Delete_file_log_event(const char* buf, int len)
- :Log_event(buf, 0),file_id(0)
+Delete_file_log_event::Delete_file_log_event(const char* buf, uint len,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event),file_id(0)
{
- if ((uint)len < DELETE_FILE_EVENT_OVERHEAD)
+ uint8 common_header_len= description_event->common_header_len;
+ uint8 delete_file_header_len= description_event->post_header_len[DELETE_FILE_EVENT-1];
+ if (len < (uint)(common_header_len + delete_file_header_len))
return;
- file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET);
+ file_id= uint4korr(buf + common_header_len + DF_FILE_ID_OFFSET);
}
@@ -3102,7 +3870,7 @@ int Delete_file_log_event::write_data(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void Delete_file_log_event::print(FILE* file, bool short_form,
- char* last_db)
+ LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
@@ -3165,12 +3933,15 @@ Execute_load_log_event::Execute_load_log_event(THD *thd_arg, const char* db_arg,
Execute_load_log_event ctor
*/
-Execute_load_log_event::Execute_load_log_event(const char* buf, int len)
- :Log_event(buf, 0), file_id(0)
+Execute_load_log_event::Execute_load_log_event(const char* buf, uint len,
+ const Format_description_log_event* description_event)
+ :Log_event(buf, description_event), file_id(0)
{
- if ((uint)len < EXEC_LOAD_EVENT_OVERHEAD)
+ uint8 common_header_len= description_event->common_header_len;
+ uint8 exec_load_header_len= description_event->post_header_len[EXEC_LOAD_EVENT-1];
+ if (len < (uint)(common_header_len+exec_load_header_len))
return;
- file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + EL_FILE_ID_OFFSET);
+ file_id= uint4korr(buf + common_header_len + EL_FILE_ID_OFFSET);
}
@@ -3192,7 +3963,7 @@ int Execute_load_log_event::write_data(IO_CACHE* file)
#ifdef MYSQL_CLIENT
void Execute_load_log_event::print(FILE* file, bool short_form,
- char* last_db)
+ LAST_EVENT_INFO* last_event_info)
{
if (short_form)
return;
@@ -3239,8 +4010,8 @@ int Execute_load_log_event::exec_event(struct st_relay_log_info* rli)
goto err;
}
if (!(lev = (Load_log_event*)Log_event::read_log_event(&file,
- (pthread_mutex_t*)0,
- (bool)0)) ||
+ (pthread_mutex_t*)0,
+ rli->relay_log.description_event_for_exec)) ||
lev->get_type_code() != NEW_LOAD_EVENT)
{
slave_print_error(rli,0, "Error in Exec_load event: file '%s' appears corrupted", fname);
diff --git a/sql/log_event.h b/sql/log_event.h
index 28d1f44df92..c9cce1d40ea 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -1,15 +1,15 @@
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
-
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
@@ -35,12 +35,42 @@
#define LOG_EVENT_OFFSET 4
-#define BINLOG_VERSION 3
+/*
+ 3 is MySQL 4.x; 4 is MySQL 5.0.0.
+ Compared to version 3, version 4 has:
+ - a different Start_log_event, which includes info about the binary log
+ (sizes of headers); this info is included for better compatibility if the
+ master's MySQL version is different from the slave's.
+ - all events have a unique ID (the triplet (server_id, timestamp at server
+ start, other) to be sure an event is not executed more than once in a
+ multimaster setup, example:
+ M1
+ / \
+ v v
+ M2 M3
+ \ /
+ v v
+ S
+ if a query is run on M1, it will arrive twice on S, so we need that S
+ remembers the last unique ID it has processed, to compare and know if the
+ event should be skipped or not. Example of ID: we already have the server id
+ (4 bytes), plus:
+ timestamp_when_the_master_started (4 bytes), a counter (a sequence number
+ which increments every time we write an event to the binlog) (3 bytes).
+ Q: how do we handle when the counter is overflowed and restarts from 0 ?
+
+ - Query and Load (Create or Execute) events may have a more precise timestamp
+ (with microseconds), number of matched/affected/warnings rows
+ and fields of session variables: SQL_MODE,
+ FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, SQL_AUTO_IS_NULL, the collations and
+ charsets, the PASSWORD() version (old/new/...).
+*/
+#define BINLOG_VERSION 4
/*
We could have used SERVER_VERSION_LENGTH, but this introduces an
obscure dependency - if somebody decided to change SERVER_VERSION_LENGTH
- this would have broken the replication protocol
+ this would break the replication protocol
*/
#define ST_SERVER_VER_LEN 50
@@ -49,6 +79,12 @@
TERMINATED etc).
*/
+/*
+ These are flags and structs to handle all the LOAD DATA INFILE options (LINES
+ TERMINATED etc).
+ DUMPFILE_FLAG is probably useless (DUMPFILE is a clause of SELECT, not of LOAD
+ DATA).
+*/
#define DUMPFILE_FLAG 0x1
#define OPT_ENCLOSED_FLAG 0x2
#define REPLACE_FLAG 0x4
@@ -92,11 +128,11 @@ struct sql_ex_info
char* escaped;
int cached_new_format;
uint8 field_term_len,enclosed_len,line_term_len,line_start_len, escaped_len;
- char opt_flags;
+ char opt_flags;
char empty_flags;
-
+
// store in new format even if old is possible
- void force_new_format() { cached_new_format = 1;}
+ void force_new_format() { cached_new_format = 1;}
int data_size()
{
return (new_format() ?
@@ -136,19 +172,31 @@ struct sql_ex_info
#define LOG_EVENT_HEADER_LEN 19 /* the fixed header length */
#define OLD_HEADER_LEN 13 /* the fixed header length in 3.23 */
+/*
+ Fixed header length, where 4.x and 5.0 agree. That is, 5.0 may have a longer
+ header (it will for sure when we have the unique event's ID), but at least
+ the first 19 bytes are the same in 4.x and 5.0. So when we have the unique
+ event's ID, LOG_EVENT_HEADER_LEN will be something like 26, but
+ LOG_EVENT_MINIMAL_HEADER_LEN will remain 19.
+*/
+#define LOG_EVENT_MINIMAL_HEADER_LEN 19
/* event-specific post-header sizes */
-#define QUERY_HEADER_LEN (4 + 4 + 1 + 2)
+// where 3.23, 4.x and 5.0 agree
+#define QUERY_HEADER_MINIMAL_LEN (4 + 4 + 1 + 2)
+// where 5.0 differs: 2 for len of N-bytes vars.
+#define QUERY_HEADER_LEN (QUERY_HEADER_MINIMAL_LEN + 2)
#define LOAD_HEADER_LEN (4 + 4 + 4 + 1 +1 + 4)
-#define START_HEADER_LEN (2 + ST_SERVER_VER_LEN + 4)
-#define ROTATE_HEADER_LEN 8
+#define START_V3_HEADER_LEN (2 + ST_SERVER_VER_LEN + 4)
+#define ROTATE_HEADER_LEN 8 // this is FROZEN (the Rotate post-header is frozen)
#define CREATE_FILE_HEADER_LEN 4
#define APPEND_BLOCK_HEADER_LEN 4
#define EXEC_LOAD_HEADER_LEN 4
#define DELETE_FILE_HEADER_LEN 4
+#define FORMAT_DESCRIPTION_HEADER_LEN (START_V3_HEADER_LEN+1+LOG_EVENT_TYPES)
-/*
- Event header offsets;
+/*
+ Event header offsets;
these point to places inside the fixed header.
*/
@@ -158,11 +206,12 @@ struct sql_ex_info
#define LOG_POS_OFFSET 13
#define FLAGS_OFFSET 17
-/* start event post-header */
+/* start event post-header (for v3 and v4) */
#define ST_BINLOG_VER_OFFSET 0
#define ST_SERVER_VER_OFFSET 2
#define ST_CREATED_OFFSET (ST_SERVER_VER_OFFSET + ST_SERVER_VER_LEN)
+#define ST_COMMON_HEADER_LEN_OFFSET (ST_CREATED_OFFSET + 4)
/* slave event post-header (this event is never written) */
@@ -176,7 +225,13 @@ struct sql_ex_info
#define Q_EXEC_TIME_OFFSET 4
#define Q_DB_LEN_OFFSET 8
#define Q_ERR_CODE_OFFSET 9
+#define Q_STATUS_VARS_LEN_OFFSET 11
#define Q_DATA_OFFSET QUERY_HEADER_LEN
+/* these are codes, not offsets; not more than 256 values (1 byte). */
+#define Q_FLAGS2_CODE 0
+#define Q_SQL_MODE_CODE 1
+#define Q_CATALOG_CODE 2
+
/* Intvar event post-header */
@@ -228,16 +283,6 @@ struct sql_ex_info
/* DF = "Delete File" */
#define DF_FILE_ID_OFFSET 0
-#define QUERY_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN)
-#define QUERY_DATA_OFFSET (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN)
-#define ROTATE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+ROTATE_HEADER_LEN)
-#define LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+LOAD_HEADER_LEN)
-#define CREATE_FILE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+\
- +LOAD_HEADER_LEN+CREATE_FILE_HEADER_LEN)
-#define DELETE_FILE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+DELETE_FILE_HEADER_LEN)
-#define EXEC_LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+EXEC_LOAD_HEADER_LEN)
-#define APPEND_BLOCK_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+APPEND_BLOCK_HEADER_LEN)
-
/* 4 bytes which all binlogs should begin with */
#define BINLOG_MAGIC "\xfe\x62\x69\x6e"
@@ -249,30 +294,69 @@ struct sql_ex_info
So they are now removed and their place may later be reused for other
flags. Then one must remember that Rotate events in 4.x have
LOG_EVENT_FORCED_ROTATE_F set, so one should not rely on the value of the
- replacing flag when reading a Rotate event.
+ replacing flag when reading a Rotate event.
I keep the defines here just to remember what they were.
*/
#ifdef TO_BE_REMOVED
#define LOG_EVENT_TIME_F 0x1
-#define LOG_EVENT_FORCED_ROTATE_F 0x2
+#define LOG_EVENT_FORCED_ROTATE_F 0x2
#endif
-/*
+/*
If the query depends on the thread (for example: TEMPORARY TABLE).
Currently this is used by mysqlbinlog to know it must print
SET @@PSEUDO_THREAD_ID=xx; before the query (it would not hurt to print it
for every query but this would be slow).
*/
-#define LOG_EVENT_THREAD_SPECIFIC_F 0x4
+#define LOG_EVENT_THREAD_SPECIFIC_F 0x4
+
+/*
+ OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must be written
+ to the binlog. OPTIONS_WRITTEN_TO_BINLOG could be written into the
+ Format_description_log_event, so that if later we don't want to replicate a
+ variable we did replicate, or the contrary, it's doable. But it should not be
+ too hard to decide once for all of what we replicate and what we don't, among
+ the fixed 32 bits of thd->options.
+ I (Guilhem) have read through every option's usage, and it looks like
+ OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only ones which alter
+ how the query modifies the table. It's good to replicate
+ OPTION_RELAXED_UNIQUE_CHECKS too because otherwise, the slave may insert data
+ slower than the master, in InnoDB.
+ OPTION_BIG_SELECTS is not needed (the slave thread runs with
+ max_join_size=HA_POS_ERROR) and OPTION_BIG_TABLES is not needed either, as
+ the manual says (because a too big in-memory temp table is automatically
+ written to disk).
+*/
+#define OPTIONS_WRITTEN_TO_BIN_LOG (OPTION_AUTO_IS_NULL | \
+OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS)
enum Log_event_type
{
- UNKNOWN_EVENT= 0, START_EVENT= 1, QUERY_EVENT= 2, STOP_EVENT= 3,
- ROTATE_EVENT= 4, INTVAR_EVENT= 5, LOAD_EVENT=6, SLAVE_EVENT= 7,
- CREATE_FILE_EVENT= 8, APPEND_BLOCK_EVENT= 9, EXEC_LOAD_EVENT= 10,
- DELETE_FILE_EVENT= 11, NEW_LOAD_EVENT= 12, RAND_EVENT= 13,
- USER_VAR_EVENT= 14
+ /*
+ Every time you update this enum (when you add a type), you have to
+ update the code of Format_description_log_event::Format_description_log_event().
+ Make sure you always insert new types ***BEFORE*** ENUM_END_EVENT.
+ */
+ UNKNOWN_EVENT= 0, START_EVENT_V3, QUERY_EVENT, STOP_EVENT, ROTATE_EVENT,
+ INTVAR_EVENT, LOAD_EVENT, SLAVE_EVENT, CREATE_FILE_EVENT,
+ APPEND_BLOCK_EVENT, EXEC_LOAD_EVENT, DELETE_FILE_EVENT,
+ /*
+ NEW_LOAD_EVENT is like LOAD_EVENT except that it has a longer sql_ex,
+ allowing multibyte TERMINATED BY etc; both types share the same class
+ (Load_log_event)
+ */
+ NEW_LOAD_EVENT,
+ RAND_EVENT, USER_VAR_EVENT,
+ FORMAT_DESCRIPTION_EVENT,
+ ENUM_END_EVENT /* end marker */
};
+/*
+ The number of types we handle in Format_description_log_event (UNKNOWN_EVENT
+ is not to be handled, it does not exist in binlogs, it does not have a
+ format).
+*/
+#define LOG_EVENT_TYPES (ENUM_END_EVENT-1)
+
enum Int_event_type
{
INVALID_INT_EVENT = 0, LAST_INSERT_ID_EVENT = 1, INSERT_ID_EVENT = 2
@@ -285,8 +369,33 @@ class MYSQL_LOG;
class THD;
#endif
+class Format_description_log_event;
+
struct st_relay_log_info;
+#ifdef MYSQL_CLIENT
+/*
+ A structure for mysqlbinlog to remember the last db, flags2, sql_mode etc; it
+ is passed to events' print() methods, so that they print only the necessary
+ USE and SET commands.
+*/
+typedef struct st_last_event_info
+{
+ // TODO: have the last catalog here ??
+ char db[FN_REFLEN+1]; // TODO: make this a LEX_STRING when thd->db is
+ bool flags2_inited;
+ uint32 flags2;
+ bool sql_mode_inited;
+ ulong sql_mode; /* must be same as THD.variables.sql_mode */
+ st_last_event_info()
+ : flags2_inited(0), flags2(0), sql_mode_inited(0), sql_mode(0)
+ {
+ db[0]= 0; /* initially, the db is unknown */
+ }
+} LAST_EVENT_INFO;
+#endif
+
+
/*****************************************************************************
Log_event class
@@ -297,7 +406,7 @@ struct st_relay_log_info;
class Log_event
{
public:
- /*
+ /*
The offset in the log where this event originally appeared (it is preserved
in relay logs, making SHOW SLAVE STATUS able to print coordinates of the
event in the master's binlog). Note: when a transaction is written by the
@@ -307,13 +416,13 @@ public:
may occur), except the COMMIT query which has its real offset.
*/
my_off_t log_pos;
- /*
+ /*
A temp buffer for read_log_event; it is later analysed according to the
event's type, and its content is distributed in the event-specific fields.
*/
- char *temp_buf;
+ char *temp_buf;
/*
- Timestamp on the master(for debugging and replication of NOW()/TIMESTAMP).
+ Timestamp on the master(for debugging and replication of NOW()/TIMESTAMP).
It is important for queries and LOAD DATA INFILE. This is set at the event's
creation time, except for Query and Load (et al.) events where this is set
at the query's execution time, which guarantees good replication (otherwise,
@@ -322,9 +431,9 @@ public:
time_t when;
/* The number of seconds the query took to run on the master. */
ulong exec_time;
- /*
+ /*
The master's server id (is preserved in the relay log; used to prevent from
- infinite loops in circular replication).
+ infinite loops in circular replication).
*/
uint32 server_id;
uint cached_event_len;
@@ -337,21 +446,26 @@ public:
uint16 flags;
bool cache_stmt;
+
#ifndef MYSQL_CLIENT
THD* thd;
- Log_event(THD* thd_arg, uint16 flags_arg, bool cache_stmt);
Log_event();
+ Log_event(THD* thd_arg, uint16 flags_arg, bool cache_stmt);
/*
read_log_event() functions read an event from a binlog or relay log; used by
SHOW BINLOG EVENTS, the binlog_dump thread on the master (reads master's
binlog), the slave IO thread (reads the event sent by binlog_dump), the
slave SQL thread (reads the event from the relay log).
+ If mutex is 0, the read will proceed without mutex.
+ We need the description_event to be able to parse the event (to know the
+ post-header's size); in fact in read_log_event we detect the event's type,
+ then call the specific event's constructor and pass description_event as an
+ argument.
*/
- // if mutex is 0, the read will proceed without mutex
static Log_event* read_log_event(IO_CACHE* file,
pthread_mutex_t* log_lock,
- bool old_format);
+ const Format_description_log_event *description_event);
static int read_log_event(IO_CACHE* file, String* packet,
pthread_mutex_t* log_lock);
/* set_log_pos() is used to fill log_pos with tell(log). */
@@ -379,13 +493,15 @@ public:
return thd ? thd->db : 0;
}
#else
+ Log_event() : temp_buf(0) {}
// avoid having to link mysqlbinlog against libpthread
- static Log_event* read_log_event(IO_CACHE* file, bool old_format);
+ static Log_event* read_log_event(IO_CACHE* file,
+ const Format_description_log_event *description_event);
/* print*() functions are used by mysqlbinlog */
- virtual void print(FILE* file, bool short_form = 0, char* last_db = 0) = 0;
+ virtual void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0) = 0;
void print_timestamp(FILE* file, time_t *ts = 0);
void print_header(FILE* file);
-#endif
+#endif
static void *operator new(size_t size)
{
@@ -395,7 +511,7 @@ public:
{
my_free((gptr) ptr, MYF(MY_WME|MY_ALLOW_ZERO_PTR));
}
-
+
int write(IO_CACHE* file);
int write_header(IO_CACHE* file);
virtual int write_data(IO_CACHE* file)
@@ -405,9 +521,9 @@ public:
virtual int write_data_body(IO_CACHE* file __attribute__((unused)))
{ return 0; }
virtual Log_event_type get_type_code() = 0;
- virtual bool is_valid() = 0;
+ virtual bool is_valid() const = 0;
inline bool get_cache_stmt() { return cache_stmt; }
- Log_event(const char* buf, bool old_format);
+ Log_event(const char* buf, const Format_description_log_event* description_event);
virtual ~Log_event() { free_temp_buf();}
void register_temp_buf(char* buf) { temp_buf = buf; }
void free_temp_buf()
@@ -419,18 +535,37 @@ public:
}
}
virtual int get_data_size() { return 0;}
- virtual int get_data_body_offset() { return 0; }
int get_event_len()
{
- return (cached_event_len ? cached_event_len :
- (cached_event_len = LOG_EVENT_HEADER_LEN + get_data_size()));
+ /*
+ We don't re-use the cached event's length anymore (we did in 4.x) because
+ this leads to nasty problems: when the 5.0 slave reads an event from a 4.0
+ master, it caches the event's length, then this event is converted before
+ it goes into the relay log, so it would be written to the relay log with
+ its old length, which is garbage.
+ */
+ return (cached_event_len=(LOG_EVENT_HEADER_LEN + get_data_size()));
}
- static Log_event* read_log_event(const char* buf, int event_len,
- const char **error, bool old_format);
+ static Log_event* read_log_event(const char* buf, uint event_len,
+ const char **error,
+ const Format_description_log_event
+ *description_event);
/* returns the human readable name of the event's type */
const char* get_type_str();
};
+/*
+ One class for each type of event.
+ Two constructors for each class:
+ - one to create the event for logging (when the server acts as a master),
+ called after an update to the database is done,
+ which accepts parameters like the query, the database, the options for LOAD
+ DATA INFILE...
+ - one to create the event from a packet (when the server acts as a slave),
+ called before reproducing the update, which accepts parameters (like a
+ buffer). Used to read from the master, from the relay log, and in
+ mysqlbinlog. This constructor must be format-tolerant.
+*/
/*****************************************************************************
@@ -445,6 +580,7 @@ protected:
char* data_buf;
public:
const char* query;
+ const char* catalog;
const char* db;
/*
If we already know the length of the query string
@@ -455,13 +591,59 @@ public:
uint32 db_len;
uint16 error_code;
ulong thread_id;
- /*
+ /*
For events created by Query_log_event::exec_event (and
Load_log_event::exec_event()) we need the *original* thread id, to be able
to log the event with the original (=master's) thread id (fix for
BUG#1686).
*/
ulong slave_proxy_id;
+
+ /*
+ Binlog format 3 and 4 start to differ (as far as class members are
+ concerned) from here.
+ */
+
+ int catalog_len; // <= 255 char; -1 means uninited
+
+ /*
+ We want to be able to store a variable number of N-bit status vars:
+ (generally N=32; but N=64 for SQL_MODE) a user may want to log the number of
+ affected rows (for debugging) while another does not want to lose 4 bytes in
+ this.
+ The storage on disk is the following:
+ status_vars_len is part of the post-header,
+ status_vars are in the variable-length part, after the post-header, before
+ the db & query.
+ status_vars on disk is a sequence of pairs (code, value) where 'code' means
+ 'sql_mode', 'affected' etc. Sometimes 'value' must be a short string, so its
+ first byte is its length. For now the order of status vars is:
+ flags2 - sql_mode - catalog.
+ We should add the same thing to Load_log_event, but in fact
+ LOAD DATA INFILE is going to be logged with a new type of event (logging of
+ the plain text query), so Load_log_event would be frozen, so no need. The
+ new way of logging LOAD DATA INFILE would use a derived class of
+ Query_log_event, so automatically benefit from the work already done for
+ status variables in Query_log_event.
+ */
+ uint16 status_vars_len;
+
+ /*
+ 'flags2' is a second set of flags (on top of those in Log_event), for
+ session variables. These are thd->options which is & against a mask
+ (OPTIONS_WRITTEN_TO_BINLOG).
+ flags2_inited helps make a difference between flags2==0 (3.23 or 4.x
+ master, we don't know flags2, so use the slave server's global options) and
+ flags2==0 (5.0 master, we know this has a meaning of flags all down which
+ must influence the query).
+ */
+ bool flags2_inited;
+ bool sql_mode_inited;
+
+ uint32 flags2;
+ /* In connections sql_mode is 32 bits now but will be 64 bits soon */
+ ulong sql_mode;
+
#ifndef MYSQL_CLIENT
Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length,
@@ -472,10 +654,11 @@ public:
int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
#endif
- Query_log_event(const char* buf, int event_len, bool old_format);
+ Query_log_event(const char* buf, uint event_len,
+ const Format_description_log_event *description_event);
~Query_log_event()
{
if (data_buf)
@@ -486,14 +669,11 @@ public:
Log_event_type get_type_code() { return QUERY_EVENT; }
int write(IO_CACHE* file);
int write_data(IO_CACHE* file); // returns 0 on success, -1 on error
- bool is_valid() { return query != 0; }
+ bool is_valid() const { return query != 0; }
int get_data_size()
{
- return (q_len + db_len + 2
- + 4 // thread_id
- + 4 // exec_time
- + 2 // error_code
- );
+ /* Note that the "1" below is the db's length. */
+ return (q_len + db_len + 1 + status_vars_len + QUERY_HEADER_LEN);
}
};
@@ -504,6 +684,7 @@ public:
Slave Log Event class
Note that this class is currently not used at all; no code writes a
Slave_log_event (though some code in repl_failsafe.cc reads Slave_log_event).
+ So it's not a problem if this code is not maintained.
****************************************************************************/
class Slave_log_event: public Log_event
@@ -519,18 +700,18 @@ public:
int master_log_len;
uint16 master_port;
-#ifndef MYSQL_CLIENT
+#ifndef MYSQL_CLIENT
Slave_log_event(THD* thd_arg, struct st_relay_log_info* rli);
void pack_info(Protocol* protocol);
int exec_event(struct st_relay_log_info* rli);
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
-#endif
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
+#endif
- Slave_log_event(const char* buf, int event_len);
+ Slave_log_event(const char* buf, uint event_len);
~Slave_log_event();
int get_data_size();
- bool is_valid() { return master_host != 0; }
+ bool is_valid() const { return master_host != 0; }
Log_event_type get_type_code() { return SLAVE_EVENT; }
int write_data(IO_CACHE* file );
};
@@ -546,12 +727,18 @@ public:
class Load_log_event: public Log_event
{
protected:
- int copy_log_event(const char *buf, ulong event_len, bool old_format);
+ int copy_log_event(const char *buf, ulong event_len,
+ int body_offset, const Format_description_log_event* description_event);
public:
ulong thread_id;
ulong slave_proxy_id;
uint32 table_name_len;
+ /*
+ No need to have a catalog, as these events can only come from 4.x.
+ TODO: this may become false if Dmitri pushes his new LOAD DATA INFILE in
+ 5.0 only (not in 4.x).
+ */
uint32 db_len;
uint32 fname_len;
uint32 num_fields;
@@ -582,7 +769,7 @@ public:
#ifndef MYSQL_CLIENT
String field_lens_buf;
String fields_buf;
-
+
Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg,
const char* table_name_arg,
List<Item>& fields_arg, enum enum_duplicates handle_dup,
@@ -595,57 +782,68 @@ public:
{
return exec_event(thd->slave_net,rli,0);
}
- int exec_event(NET* net, struct st_relay_log_info* rli,
+ int exec_event(NET* net, struct st_relay_log_info* rli,
bool use_rli_only_for_errors);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
- void print(FILE* file, bool short_form, char* last_db, bool commented);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info = 0);
+ void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool commented);
#endif
- Load_log_event(const char* buf, int event_len, bool old_format);
+ /*
+ Note that for all the events related to LOAD DATA (Load_log_event,
+ Create_file/Append/Exec/Delete, we pass description_event; however as
+ logging of LOAD DATA is going to be changed in 4.1 or 5.0, this is only used
+ for the common_header_len (post_header_len will not be changed).
+ */
+ Load_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event);
~Load_log_event()
{}
Log_event_type get_type_code()
{
return sql_ex.new_format() ? NEW_LOAD_EVENT: LOAD_EVENT;
}
- int write_data_header(IO_CACHE* file);
- int write_data_body(IO_CACHE* file);
- bool is_valid() { return table_name != 0; }
+ int write_data_header(IO_CACHE* file);
+ int write_data_body(IO_CACHE* file);
+ bool is_valid() const { return table_name != 0; }
int get_data_size()
{
- return (table_name_len + 2 + db_len + 2 + fname_len
- + 4 // thread_id
- + 4 // exec_time
- + 4 // skip_lines
- + 4 // field block len
+ return (table_name_len + db_len + 2 + fname_len
+ + LOAD_HEADER_LEN
+ sql_ex.data_size() + field_block_len + num_fields);
}
- int get_data_body_offset() { return LOAD_EVENT_OVERHEAD; }
};
extern char server_version[SERVER_VERSION_LENGTH];
/*****************************************************************************
- Start Log Event class
+ Start Log Event_v3 class
+
+ Start_log_event_v3 is the Start_log_event of binlog format 3 (MySQL 3.23 and
+ 4.x).
+ Format_description_log_event derives from Start_log_event_v3; it is the
+ Start_log_event of binlog format 4 (MySQL 5.0), that is, the event that
+ describes the other events' header/postheader lengths. This event is sent by
+ MySQL 5.0 whenever it starts sending a new binlog if the requested position
+ is >4 (otherwise if ==4 the event will be sent naturally).
****************************************************************************/
-class Start_log_event: public Log_event
+class Start_log_event_v3: public Log_event
{
public:
- /*
+ /*
If this event is at the start of the first binary log since server startup
'created' should be the timestamp when the event (and the binary log) was
- created.
+ created.
In the other case (i.e. this event is at the start of a binary log created
by FLUSH LOGS or automatic rotation), 'created' should be 0.
This "trick" is used by MySQL >=4.0.14 slaves to know if they must drop the
stale temporary tables or not.
Note that when 'created'!=0, it is always equal to the event's timestamp;
indeed Start_log_event is written only in log.cc where the first
- constructor below is called, in which 'created' is set to 'when'.
+ constructor below is called, in which 'created' is set to 'when'.
So in fact 'created' is a useless variable. When it is 0
we can read the actual value from timestamp ('when') and when it is
non-zero we can read the same value from timestamp ('when'). Conclusion:
@@ -660,27 +858,81 @@ public:
char server_version[ST_SERVER_VER_LEN];
#ifndef MYSQL_CLIENT
- Start_log_event() :Log_event(), binlog_version(BINLOG_VERSION)
- {
- created = (time_t) when;
- memcpy(server_version, ::server_version, ST_SERVER_VER_LEN);
- }
+ Start_log_event_v3();
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
-#endif
+ Start_log_event_v3() {}
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
+#endif
+
+ Start_log_event_v3(const char* buf,
+ const Format_description_log_event* description_event);
+ ~Start_log_event_v3() {}
+ Log_event_type get_type_code() { return START_EVENT_V3;}
+ int write_data(IO_CACHE* file);
+ bool is_valid() const { return 1; }
+ int get_data_size()
+ {
+ return START_V3_HEADER_LEN; //no variable-sized part
+ }
+};
+
+/*
+ For binlog version 4.
+ This event is saved by threads which read it, as they need it for future
+ use (to decode the ordinary events).
+*/
+
+class Format_description_log_event: public Start_log_event_v3
+{
+public:
+ /*
+ The size of the fixed header which _all_ events have
+ (for binlogs written by this version, this is equal to
+ LOG_EVENT_HEADER_LEN), except FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT
+ (those have a header of size LOG_EVENT_MINIMAL_HEADER_LEN).
+ */
+ uint8 common_header_len;
+ uint8 number_of_event_types;
+ /* The list of post-headers' lengthes */
+ uint8 *post_header_len;
+
+ Format_description_log_event(uint8 binlog_ver, const char* server_ver=0);
+
+#ifndef MYSQL_CLIENT
+#ifdef HAVE_REPLICATION
+ int exec_event(struct st_relay_log_info* rli);
+#endif /* HAVE_REPLICATION */
+#endif
- Start_log_event(const char* buf, bool old_format);
- ~Start_log_event() {}
- Log_event_type get_type_code() { return START_EVENT;}
+ Format_description_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event);
+ ~Format_description_log_event() { my_free((gptr)post_header_len, MYF(0)); }
+ Log_event_type get_type_code() { return FORMAT_DESCRIPTION_EVENT;}
int write_data(IO_CACHE* file);
- bool is_valid() { return 1; }
+ bool is_valid() const
+ {
+ return ((common_header_len >= ((binlog_version==1) ? OLD_HEADER_LEN :
+ LOG_EVENT_MINIMAL_HEADER_LEN)) &&
+ (post_header_len != NULL));
+ }
+ int get_event_len()
+ {
+ int i= LOG_EVENT_MINIMAL_HEADER_LEN + get_data_size();
+ DBUG_PRINT("info",("event_len=%d",i));
+ return i;
+ }
int get_data_size()
{
- return START_HEADER_LEN;
+ /*
+ The vector of post-header lengths is considered as part of the
+ post-header, because in a given version it never changes (contrary to the
+ query in a Query_log_event).
+ */
+ return FORMAT_DESCRIPTION_HEADER_LEN;
}
};
@@ -698,7 +950,7 @@ public:
ulonglong val;
uchar type;
-#ifndef MYSQL_CLIENT
+#ifndef MYSQL_CLIENT
Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg)
:Log_event(thd_arg,0,0),val(val_arg),type(type_arg)
{}
@@ -707,23 +959,26 @@ public:
int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
-#endif
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
+#endif
- Intvar_log_event(const char* buf, bool old_format);
+ Intvar_log_event(const char* buf, const Format_description_log_event* description_event);
~Intvar_log_event() {}
Log_event_type get_type_code() { return INTVAR_EVENT;}
const char* get_var_type_name();
int get_data_size() { return 9; /* sizeof(type) + sizeof(val) */;}
int write_data(IO_CACHE* file);
- bool is_valid() { return 1; }
+ bool is_valid() const { return 1; }
};
/*****************************************************************************
Rand Log Event class
- Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.
+ Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.0.
+ 4.1.1 does not need it (it's repeatable again) so this event needn't be
+ written in 4.1.1 for PASSWORD() (but the fact that it is written is just a
+ waste, it does not cause bugs).
****************************************************************************/
class Rand_log_event: public Log_event
@@ -741,15 +996,15 @@ class Rand_log_event: public Log_event
int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
#endif
- Rand_log_event(const char* buf, bool old_format);
+ Rand_log_event(const char* buf, const Format_description_log_event* description_event);
~Rand_log_event() {}
Log_event_type get_type_code() { return RAND_EVENT;}
int get_data_size() { return 16; /* sizeof(ulonglong) * 2*/ }
int write_data(IO_CACHE* file);
- bool is_valid() { return 1; }
+ bool is_valid() const { return 1; }
};
/*****************************************************************************
@@ -759,6 +1014,9 @@ class Rand_log_event: public Log_event
Every time a query uses the value of a user variable, a User_var_log_event is
written before the Query_log_event, to set the user variable.
+ Every time a query uses the value of a user variable, a User_var_log_event is
+ written before the Query_log_event, to set the user variable.
+
****************************************************************************/
class User_var_log_event: public Log_event
{
@@ -780,10 +1038,10 @@ public:
void pack_info(Protocol* protocol);
int exec_event(struct st_relay_log_info* rli);
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
#endif
- User_var_log_event(const char* buf, bool old_format);
+ User_var_log_event(const char* buf, const Format_description_log_event* description_event);
~User_var_log_event() {}
Log_event_type get_type_code() { return USER_VAR_EVENT;}
int get_data_size()
@@ -793,7 +1051,7 @@ public:
UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE + val_len);
}
int write_data(IO_CACHE* file);
- bool is_valid() { return 1; }
+ bool is_valid() const { return 1; }
};
/*****************************************************************************
@@ -811,15 +1069,15 @@ public:
{}
int exec_event(struct st_relay_log_info* rli);
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
-#endif
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
+#endif
- Stop_log_event(const char* buf, bool old_format):
- Log_event(buf, old_format)
+ Stop_log_event(const char* buf, const Format_description_log_event* description_event):
+ Log_event(buf, description_event)
{}
~Stop_log_event() {}
Log_event_type get_type_code() { return STOP_EVENT;}
- bool is_valid() { return 1; }
+ bool is_valid() const { return 1; }
};
#endif /* HAVE_REPLICATION */
@@ -839,7 +1097,7 @@ public:
ulonglong pos;
uint ident_len;
bool alloced;
-#ifndef MYSQL_CLIENT
+#ifndef MYSQL_CLIENT
Rotate_log_event(THD* thd_arg, const char* new_log_ident_arg,
uint ident_len_arg = 0,
ulonglong pos_arg = LOG_EVENT_OFFSET)
@@ -852,18 +1110,23 @@ public:
int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
#endif
- Rotate_log_event(const char* buf, int event_len, bool old_format);
+ Rotate_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event);
~Rotate_log_event()
{
if (alloced)
my_free((gptr) new_log_ident, MYF(0));
}
Log_event_type get_type_code() { return ROTATE_EVENT;}
+ int get_event_len()
+ {
+ return (LOG_EVENT_MINIMAL_HEADER_LEN + get_data_size());
+ }
int get_data_size() { return ident_len + ROTATE_HEADER_LEN;}
- bool is_valid() { return new_log_ident != 0; }
+ bool is_valid() const { return new_log_ident != 0; }
int write_data(IO_CACHE* file);
};
@@ -882,7 +1145,7 @@ protected:
our Load part - used on the slave when writing event out to
SQL_LOAD-*.info file
*/
- bool fake_base;
+ bool fake_base;
public:
char* block;
const char *event_buf;
@@ -902,11 +1165,12 @@ public:
int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
- void print(FILE* file, bool short_form, char* last_db, bool enable_local);
-#endif
-
- Create_file_log_event(const char* buf, int event_len, bool old_format);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
+ void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool enable_local);
+#endif
+
+ Create_file_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event);
~Create_file_log_event()
{
my_free((char*) event_buf, MYF(MY_ALLOW_ZERO_PTR));
@@ -922,12 +1186,7 @@ public:
Load_log_event::get_data_size() +
4 + 1 + block_len);
}
- int get_data_body_offset()
- {
- return (fake_base ? LOAD_EVENT_OVERHEAD:
- LOAD_EVENT_OVERHEAD + CREATE_FILE_HEADER_LEN);
- }
- bool is_valid() { return inited_from_old || block != 0; }
+ bool is_valid() const { return inited_from_old || block != 0; }
int write_data_header(IO_CACHE* file);
int write_data_body(IO_CACHE* file);
/*
@@ -969,14 +1228,15 @@ public:
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
#endif
-
- Append_block_log_event(const char* buf, int event_len);
+
+ Append_block_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event);
~Append_block_log_event() {}
Log_event_type get_type_code() { return APPEND_BLOCK_EVENT;}
int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;}
- bool is_valid() { return block != 0; }
+ bool is_valid() const { return block != 0; }
int write_data(IO_CACHE* file);
const char* get_db() { return db; }
};
@@ -991,7 +1251,7 @@ class Delete_file_log_event: public Log_event
public:
uint file_id;
const char* db; /* see comment in Append_block_log_event */
-
+
#ifndef MYSQL_CLIENT
Delete_file_log_event(THD* thd, const char* db_arg, bool using_trans);
#ifdef HAVE_REPLICATION
@@ -999,15 +1259,16 @@ public:
int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
- void print(FILE* file, bool short_form, char* last_db, bool enable_local);
-#endif
-
- Delete_file_log_event(const char* buf, int event_len);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
+ void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool enable_local);
+#endif
+
+ Delete_file_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event);
~Delete_file_log_event() {}
Log_event_type get_type_code() { return DELETE_FILE_EVENT;}
int get_data_size() { return DELETE_FILE_HEADER_LEN ;}
- bool is_valid() { return file_id != 0; }
+ bool is_valid() const { return file_id != 0; }
int write_data(IO_CACHE* file);
const char* get_db() { return db; }
};
@@ -1021,7 +1282,7 @@ class Execute_load_log_event: public Log_event
{
public:
uint file_id;
- const char* db; /* see comment in Append_block_log_event */
+ const char* db; /* see comment in Append_block_log_event */
#ifndef MYSQL_CLIENT
Execute_load_log_event(THD* thd, const char* db_arg, bool using_trans);
@@ -1030,14 +1291,15 @@ public:
int exec_event(struct st_relay_log_info* rli);
#endif /* HAVE_REPLICATION */
#else
- void print(FILE* file, bool short_form = 0, char* last_db = 0);
-#endif
-
- Execute_load_log_event(const char* buf, int event_len);
+ void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0);
+#endif
+
+ Execute_load_log_event(const char* buf, uint event_len,
+ const Format_description_log_event* description_event);
~Execute_load_log_event() {}
Log_event_type get_type_code() { return EXEC_LOAD_EVENT;}
int get_data_size() { return EXEC_LOAD_HEADER_LEN ;}
- bool is_valid() { return file_id != 0; }
+ bool is_valid() const { return file_id != 0; }
int write_data(IO_CACHE* file);
const char* get_db() { return db; }
};
@@ -1046,14 +1308,19 @@ public:
class Unknown_log_event: public Log_event
{
public:
- Unknown_log_event(const char* buf, bool old_format):
- Log_event(buf, old_format)
+ /*
+ Even if this is an unknown event, we still pass description_event to
+ Log_event's ctor, this way we can extract maximum information from the
+ event's header (the unique ID for example).
+ */
+ Unknown_log_event(const char* buf, const Format_description_log_event* description_event):
+ Log_event(buf, description_event)
{}
~Unknown_log_event() {}
- void print(FILE* file, bool short_form= 0, char* last_db= 0);
+ void print(FILE* file, bool short_form= 0, LAST_EVENT_INFO* last_event_info= 0);
Log_event_type get_type_code() { return UNKNOWN_EVENT;}
- bool is_valid() { return 1; }
+ bool is_valid() const { return 1; }
};
-#endif
+#endif
#endif /* _log_event_h */
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index b1b184eb9eb..c668e152df5 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -14,6 +14,15 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+/*
+ Mostly this file is used in the server. But a little part of it is used in
+ mysqlbinlog too (definition of SELECT_DISTINCT and others).
+ The consequence is that 90% of the file is wrapped in #ifndef MYSQL_CLIENT,
+ except the part which must be in the server and in the client.
+*/
+
+#ifndef MYSQL_CLIENT
+
#include <my_global.h>
#include <mysql_version.h>
#include <mysql_embed.h>
@@ -55,7 +64,7 @@ char *sql_strmake_with_convert(const char *str, uint32 arg_length,
CHARSET_INFO *from_cs,
uint32 max_res_length,
CHARSET_INFO *to_cs, uint32 *result_length);
-void kill_one_thread(THD *thd, ulong id);
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query);
bool net_request_file(NET* net, const char* fname);
char* query_table_status(THD *thd,const char *db,const char *table_name);
@@ -112,6 +121,26 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
#define TIME_FOR_COMPARE 5 // 5 compares == one read
/*
+ Number of comparisons of table rowids equivalent to reading one row from a
+ table.
+*/
+#define TIME_FOR_COMPARE_ROWID (TIME_FOR_COMPARE*2)
+
+/*
+ For sequential disk seeks the cost formula is:
+ DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST * #blocks_to_skip
+
+ The cost of average seek
+ DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*BLOCKS_IN_AVG_SEEK =1.0.
+*/
+#define DISK_SEEK_BASE_COST ((double)0.5)
+
+#define BLOCKS_IN_AVG_SEEK 128
+
+#define DISK_SEEK_PROP_COST ((double)0.5/BLOCKS_IN_AVG_SEEK)
+
+
+/*
Number of rows in a reference table when refereed through a not unique key.
This value is only used when we don't know anything about the key
distribution.
@@ -173,8 +202,15 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
#define TEST_SIGINT 1024 /* Allow sigint on threads */
#define TEST_SYNCHRONIZATION 2048 /* get server to do sleep in some
places */
+#endif
+
+/*
+ This is included in the server and in the client.
+ Options for select set by the yacc parser (stored in lex->options).
+ None of the 32 defines below should have its value changed, or this will
+ break replication.
+*/
-/* options for select set by the yacc parser (stored in lex->options) */
#define SELECT_DISTINCT (1L << 0)
#define SELECT_STRAIGHT_JOIN (1L << 1)
#define SELECT_DESCRIBE (1L << 2)
@@ -212,6 +248,9 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
#define OPTION_RELAXED_UNIQUE_CHECKS (1L << 27)
#define SELECT_NO_UNLOCK (1L << 28)
+/* The rest of the file is included in the server only */
+#ifndef MYSQL_CLIENT
+
/* Bits for different SQL modes modes (including ANSI mode) */
#define MODE_REAL_AS_FLOAT 1
#define MODE_PIPES_AS_CONCAT 2
@@ -233,6 +272,7 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
#define MODE_MYSQL40 (MODE_MYSQL323*2)
#define MODE_ANSI (MODE_MYSQL40*2)
#define MODE_NO_AUTO_VALUE_ON_ZERO (MODE_ANSI*2)
+#define MODE_NO_BACKSLASH_ESCAPES (MODE_NO_AUTO_VALUE_ON_ZERO*2)
#define RAID_BLOCK_SIZE 1024
@@ -352,6 +392,7 @@ inline THD *_current_thd(void)
#include "sql_list.h"
#include "sql_map.h"
#include "handler.h"
+#include "parse_file.h"
#include "table.h"
#include "field.h" /* Field definitions */
#include "protocol.h"
@@ -365,12 +406,16 @@ void free_items(Item *item);
void cleanup_items(Item *item);
class THD;
void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
-int check_one_table_access(THD *thd, ulong privilege,
+bool check_one_table_access(THD *thd, ulong privilege,
TABLE_LIST *tables);
+bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
bool check_merge_table_access(THD *thd, char *db,
TABLE_LIST *table_list);
int multi_update_precheck(THD *thd, TABLE_LIST *tables);
int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count);
+int mysql_multi_update_prepare(THD *thd);
+int mysql_multi_delete_prepare(THD *thd);
+int mysql_insert_select_prepare(THD *thd);
int insert_select_precheck(THD *thd, TABLE_LIST *tables);
int update_precheck(THD *thd, TABLE_LIST *tables);
int delete_precheck(THD *thd, TABLE_LIST *tables);
@@ -430,7 +475,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
my_bool drop_temporary);
int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool log_query);
+ bool drop_temporary, bool drop_view, bool log_query);
int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables,
bool if_exists, bool drop_temporary,
bool log_query);
@@ -443,7 +488,7 @@ bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
bool is_update_query(enum enum_sql_command command);
bool alloc_query(THD *thd, char *packet, ulong packet_length);
void mysql_init_select(LEX *lex);
-void mysql_init_query(THD *thd, uchar *buf, uint length);
+void mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly=0);
bool mysql_new_select(LEX *lex, bool move_down);
void create_select_for_variable(const char *var_name);
void mysql_init_multi_delete(LEX *lex);
@@ -454,7 +499,7 @@ extern "C" pthread_handler_decl(handle_one_connection,arg);
extern "C" pthread_handler_decl(handle_bootstrap,arg);
void end_thread(THD *thd,bool put_in_cache);
void flush_thread_cache();
-void mysql_execute_command(THD *thd);
+int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
bool dispatch_command(enum enum_server_command command, THD *thd,
char* packet, uint packet_length);
@@ -530,7 +575,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
bool tmp_table, uint select_field_count);
TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
- const char *db, const char *name,
+ TABLE_LIST *create_table,
List<create_field> *extra_fields,
List<Key> *keys,
List<Item> *items,
@@ -556,7 +601,6 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys);
int mysql_drop_index(THD *thd, TABLE_LIST *table_list,
ALTER_INFO *alter_info);
int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
- TABLE_LIST *update_table_list,
Item **conds, uint order_num, ORDER *order);
int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
List<Item> &values,COND *conds,
@@ -567,8 +611,7 @@ int mysql_multi_update(THD *thd, TABLE_LIST *table_list,
COND *conds, ulong options,
enum enum_duplicates handle_duplicates,
SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex);
-int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
- TABLE_LIST *insert_table_list, TABLE *table,
+int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
List<Item> &fields, List_item *values,
List<Item> &update_fields,
List<Item> &update_values, enum_duplicates duplic);
@@ -580,7 +623,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order,
ha_rows rows, ulong options);
int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
-TABLE *open_table(THD *thd,const char *db,const char *table,const char *alias,
+TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
bool *refresh);
TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table);
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
@@ -596,11 +639,17 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name);
void execute_init_command(THD *thd, sys_var_str *init_command_var,
rw_lock_t *var_mutex);
extern const Field *not_found_field;
+extern const Field *view_ref_found;
Field *find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
- TABLE_LIST **where, bool report_error);
-Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
- bool check_grant,bool allow_rowid,
- uint *cached_field_index_ptr);
+ Item **ref, bool report_error,
+ bool check_privileges);
+Field *
+find_field_in_table(THD *thd, TABLE_LIST *table_list,
+ const char *name, const char *item_name,
+ uint length, Item **ref,
+ bool check_grants_table, bool check_grants_view,
+ bool allow_rowid,
+ uint *cached_field_index_ptr);
#ifdef HAVE_OPENSSL
#include <openssl/des.h>
struct st_des_keyblock
@@ -658,11 +707,11 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
LEX_STRING *name=NULL);
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name);
+void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length);
void mysql_stmt_free(THD *thd, char *packet);
void mysql_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
-int check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
- List<Item> &values, ulong counter);
+void reset_stmt_for_execute(THD *thd, LEX *lex);
/* sql_error.cc */
MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, uint code,
@@ -707,8 +756,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
List<String> *index_list);
bool insert_fields(THD *thd,TABLE_LIST *tables,
const char *db_name, const char *table_name,
- List_iterator<Item> *it);
-bool setup_tables(TABLE_LIST *tables);
+ List_iterator<Item> *it, bool any_privileges);
+bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds);
int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
List<Item> *sum_func_list, uint wild_num);
int setup_fields(THD *thd, Item** ref_pointer_array, TABLE_LIST *tables,
@@ -729,11 +778,10 @@ void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
bool close_thread_table(THD *thd, TABLE **table_ptr);
void close_temporary_tables(THD *thd);
-TABLE_LIST * find_table_in_list(TABLE_LIST *table,
- const char *db_name, const char *table_name);
-TABLE_LIST * find_real_table_in_list(TABLE_LIST *table,
- const char *db_name,
- const char *table_name);
+TABLE_LIST *find_table_in_list(TABLE_LIST *table,
+ uint offset_to_list,
+ const char *db_name,
+ const char *table_name);
TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name);
bool close_temporary_table(THD *thd, const char *db, const char *table_name);
void close_temporary(TABLE *table, bool delete_table=1);
@@ -749,6 +797,23 @@ int fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors);
int fill_record(Field **field,List<Item> &values, bool ignore_errors);
OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild);
+inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table,
+ const char *db_name,
+ const char *table_name)
+{
+ return find_table_in_list(table, offsetof(TABLE_LIST, next_global),
+ db_name, table_name);
+}
+
+inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table,
+ const char *db_name,
+ const char *table_name)
+{
+ return find_table_in_list(table, offsetof(TABLE_LIST, next_local),
+ db_name, table_name);
+}
+
+
/* sql_calc.cc */
bool eval_const_cond(COND *cond);
@@ -771,6 +836,8 @@ extern "C" pthread_handler_decl(handle_manager, arg);
void print_where(COND *cond,const char *info);
void print_cached_tables(void);
void TEST_filesort(SORT_FIELD *sortorder,uint s_length);
+void print_plan(JOIN* join, double read_time, double record_count,
+ uint idx, const char *info);
#endif
void mysql_print_status(THD *thd);
/* key.cc */
@@ -847,6 +914,7 @@ extern char language[LIBLEN],reg_ext[FN_EXTLEN];
extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
extern char log_error_file[FN_REFLEN];
+extern double last_query_cost;
extern double log_10[32];
extern ulonglong log_10_int[20];
extern ulonglong keybuff_size;
@@ -892,6 +960,7 @@ extern bool using_update_log, opt_large_files, server_id_supplied;
extern bool opt_log, opt_update_log, opt_bin_log, opt_slow_log, opt_error_log;
extern bool opt_disable_networking, opt_skip_show_db;
extern bool volatile abort_loop, shutdown_in_progress, grant_option;
+extern bool mysql_proc_table_exists;
extern uint volatile thread_count, thread_running, global_read_lock;
extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types;
extern my_bool opt_safe_show_db, opt_local_infile;
@@ -904,7 +973,7 @@ extern char *shared_memory_base_name, *mysqld_unix_port;
extern bool opt_enable_shared_memory;
extern char *default_tz_name;
-extern MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
+extern MYSQL_LOG mysql_log,mysql_slow_log,mysql_bin_log;
extern FILE *bootstrap_file;
extern pthread_key(MEM_ROOT*,THR_MALLOC);
extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open,
@@ -1114,6 +1183,8 @@ extern int yyparse(void *thd);
SQL_CRYPT *get_crypt_for_frm(void);
#endif
+#include "sql_view.h"
+
/* Some inline functions for more speed */
inline bool add_item_to_list(THD *thd, Item *item)
@@ -1212,3 +1283,5 @@ bool check_stack_overrun(THD *thd,char *dummy);
inline void kill_delayed_threads(void) {}
#define check_stack_overrun(A, B) 0
#endif
+
+#endif /* MYSQL_CLIENT */
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 90b6d6319bf..81651862255 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -23,6 +23,7 @@
#include "repl_failsafe.h"
#include "stacktrace.h"
#include "mysqld_suffix.h"
+#include "mysys_err.h"
#ifdef HAVE_BERKELEY_DB
#include "ha_berkeley.h"
#endif
@@ -40,6 +41,8 @@
#include <thr_alarm.h>
#include <ft_global.h>
#include <errmsg.h>
+#include "sp_rcontext.h"
+#include "sp_cache.h"
#define mysqld_charset &my_charset_latin1
@@ -89,7 +92,7 @@ extern "C" { // Because of SCO 3.2V4.2
#if defined(OS2)
# include <sys/un.h>
-#elif !defined( __WIN__)
+#elif !defined(__WIN__)
# ifndef __NETWARE__
#include <sys/resource.h>
# endif /* __NETWARE__ */
@@ -216,7 +219,7 @@ const char *sql_mode_names[] =
"NO_DIR_IN_CREATE",
"POSTGRESQL", "ORACLE", "MSSQL", "DB2", "MAXDB", "NO_KEY_OPTIONS",
"NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI",
- "NO_AUTO_VALUE_ON_ZERO", NullS
+ "NO_AUTO_VALUE_ON_ZERO", "NO_BACKSLASH_ESCAPES", NullS
};
TYPELIB sql_mode_typelib= { array_elements(sql_mode_names)-1,"",
sql_mode_names };
@@ -309,6 +312,7 @@ ulong expire_logs_days = 0;
ulong rpl_recovery_rank=0;
ulong my_bind_addr; /* the address we bind to */
volatile ulong cached_thread_count= 0;
+double last_query_cost= -1; /* -1 denotes that no query was compiled yet */
double log_10[32]; /* 10 potences */
time_t start_time;
@@ -565,7 +569,7 @@ static void close_connections(void)
/* Abort listening to new connections */
DBUG_PRINT("quit",("Closing sockets"));
- if ( !opt_disable_networking )
+ if (!opt_disable_networking )
{
if (ip_sock != INVALID_SOCKET)
{
@@ -578,7 +582,7 @@ static void close_connections(void)
if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe)
{
HANDLE temp;
- DBUG_PRINT( "quit", ("Closing named pipes") );
+ DBUG_PRINT("quit", ("Closing named pipes") );
/* Create connection to the handle named pipe handler to break the loop */
if ((temp = CreateFile(pipe_name,
@@ -620,7 +624,7 @@ static void close_connections(void)
{
DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
tmp->thread_id));
- tmp->killed=1;
+ tmp->killed= THD::KILL_CONNECTION;
if (tmp->mysys_var)
{
tmp->mysys_var->abort=1;
@@ -744,7 +748,7 @@ void kill_mysql(void)
}
#endif
#elif defined(OS2)
- pthread_cond_signal( &eventShutdown); // post semaphore
+ pthread_cond_signal(&eventShutdown); // post semaphore
#elif defined(HAVE_PTHREAD_KILL)
if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
{
@@ -803,6 +807,7 @@ static void __cdecl kill_server(int sig_ptr)
unireg_abort(1); /* purecov: inspected */
else
unireg_end();
+
#ifdef __NETWARE__
pthread_join(select_thread, NULL); // wait for main thread
#endif /* __NETWARE__ */
@@ -894,7 +899,6 @@ void clean_up(bool print_message)
mysql_log.cleanup();
mysql_slow_log.cleanup();
- mysql_update_log.cleanup();
mysql_bin_log.cleanup();
#ifdef HAVE_REPLICATION
@@ -1137,7 +1141,7 @@ static void server_init(void)
DBUG_ENTER("server_init");
#ifdef __WIN__
- if ( !opt_disable_networking )
+ if (!opt_disable_networking)
{
WSADATA WsaData;
if (SOCKET_ERROR == WSAStartup (0x0101, &WsaData))
@@ -1212,7 +1216,7 @@ static void server_init(void)
sql_perror("Can't start server : Set security descriptor");
unireg_abort(1);
}
- saPipeSecurity.nLength = sizeof( SECURITY_ATTRIBUTES );
+ saPipeSecurity.nLength = sizeof(SECURITY_ATTRIBUTES);
saPipeSecurity.lpSecurityDescriptor = &sdPipeDescriptor;
saPipeSecurity.bInheritHandle = FALSE;
if ((hPipe= CreateNamedPipe(pipe_name,
@@ -1232,9 +1236,9 @@ static void server_init(void)
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL );
- MessageBox( NULL, (LPTSTR) lpMsgBuf, "Error from CreateNamedPipe",
- MB_OK|MB_ICONINFORMATION );
- LocalFree( lpMsgBuf );
+ MessageBox(NULL, (LPTSTR) lpMsgBuf, "Error from CreateNamedPipe",
+ MB_OK|MB_ICONINFORMATION);
+ LocalFree(lpMsgBuf);
unireg_abort(1);
}
}
@@ -1439,7 +1443,7 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
THD *thd=current_thd;
DBUG_ENTER("abort_thread");
if (thd)
- thd->killed=1;
+ thd->killed= THD::KILL_CONNECTION;
DBUG_VOID_RETURN;
}
#endif
@@ -1455,7 +1459,7 @@ static void init_signals(void)
{
int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT } ;
for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++)
- signal( signals[i], kill_server) ;
+ signal(signals[i], kill_server) ;
#if defined(__WIN__)
signal(SIGBREAK,SIG_IGN); //ignore SIGBREAK for NT
#else
@@ -2090,6 +2094,10 @@ extern "C" int my_message_sql(uint error, const char *str,
DBUG_PRINT("error", ("Message: '%s'", str));
if ((thd= current_thd))
{
+ if (thd->spcont && thd->spcont->find_handler(error))
+ {
+ DBUG_RETURN(0);
+ }
/*
thd->lex->current_select == 0 if lex structure is not inited
(not query command (COM_QUERY))
@@ -2164,10 +2172,10 @@ extern "C" pthread_handler_decl(handle_shutdown,arg)
my_thread_init();
// wait semaphore
- pthread_cond_wait( &eventShutdown, NULL);
+ pthread_cond_wait(&eventShutdown, NULL);
// close semaphore and kill server
- pthread_cond_destroy( &eventShutdown);
+ pthread_cond_destroy(&eventShutdown);
/*
Exit main loop on main thread, so kill will be done from
@@ -2213,7 +2221,7 @@ bool open_log(MYSQL_LOG *log, const char *hostname,
}
return log->open(opt_name, type, 0, index_file_name,
(read_append) ? SEQ_READ_APPEND : WRITE_CACHE,
- no_auto_events, max_size);
+ no_auto_events, max_size, 0);
}
@@ -2303,7 +2311,6 @@ static int init_common_variables(const char *conf_file_name, int argc,
before MY_INIT(). So we do it here.
*/
mysql_log.init_pthread_objects();
- mysql_update_log.init_pthread_objects();
mysql_slow_log.init_pthread_objects();
mysql_bin_log.init_pthread_objects();
@@ -2441,6 +2448,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_rpl_status, NULL);
#endif
+ sp_cache_init();
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
@@ -2499,9 +2507,55 @@ static int init_server_components()
LOG_NORMAL, 0, 0, 0);
if (opt_update_log)
{
- open_log(&mysql_update_log, glob_hostname, opt_update_logname, "",
- NullS, LOG_NEW, 0, 0, 0);
- using_update_log=1;
+ /*
+ Update log is removed since 5.0. But we still accept the option.
+ The idea is if the user already uses the binlog and the update log,
+ we completely ignore any option/variable related to the update log, like
+ if the update log did not exist. But if the user uses only the update log,
+ then we translate everything into binlog for him (with warnings).
+ Implementation of the above :
+ - If mysqld is started with --log-update and --log-bin,
+ ignore --log-update (print a warning), push a warning when SQL_LOG_UPDATE
+ is used, and turn off --sql-bin-update-same.
+ This will completely ignore SQL_LOG_UPDATE
+ - If mysqld is started with --log-update only,
+ change it to --log-bin (with the filename passed to log-update,
+ plus '-bin') (print a warning), push a warning when SQL_LOG_UPDATE is
+ used, and turn on --sql-bin-update-same.
+ This will translate SQL_LOG_UPDATE to SQL_LOG_BIN.
+
+ Note that we tell the user that --sql-bin-update-same is deprecated and
+ does nothing, and we don't take into account if he used this option or
+ not; but internally we give this variable a value to have the behaviour we
+ want (i.e. have SQL_LOG_UPDATE influence SQL_LOG_BIN or not).
+ As sql-bin-update-same, log-update and log-bin cannot be changed by the
+ user after starting the server (they are not variables), the user will not
+ later interfere with the settings we do here.
+ */
+ if (opt_bin_log)
+ {
+ opt_sql_bin_update= 0;
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log.");
+ }
+ else
+ {
+ opt_sql_bin_update= 1;
+ opt_bin_log= 1;
+ if (opt_update_logname)
+ {
+ // as opt_bin_log==0, no need to free opt_bin_logname
+ if (!(opt_bin_logname= my_strdup(opt_update_logname, MYF(MY_WME))))
+ exit(EXIT_OUT_OF_MEMORY);
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin='%s' instead.",opt_bin_logname);
+ }
+ else
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin instead.");
+ }
}
if (opt_slow_log)
open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log",
@@ -2763,7 +2817,7 @@ int main(int argc, char **argv)
if (_cust_check_startup())
{
/ * _cust_check_startup will report startup failure error * /
- exit( 1 );
+ exit(1);
}
#endif
@@ -2941,7 +2995,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
handle_connections_methods();
#else
#ifdef __WIN__
- if ( !have_tcpip || opt_disable_networking)
+ if (!have_tcpip || opt_disable_networking)
{
sql_print_error("TCP/IP unavailable or disabled with --skip-networking; no available interfaces");
unireg_abort(1);
@@ -3057,7 +3111,8 @@ default_service_handling(char **argv,
const char *servicename,
const char *displayname,
const char *file_path,
- const char *extra_opt)
+ const char *extra_opt,
+ const char *account_name)
{
char path_and_service[FN_REFLEN+FN_REFLEN+32], *pos, *end;
end= path_and_service + sizeof(path_and_service)-3;
@@ -3076,12 +3131,12 @@ default_service_handling(char **argv,
if (Service.got_service_option(argv, "install"))
{
- Service.Install(1, servicename, displayname, path_and_service);
+ Service.Install(1, servicename, displayname, path_and_service, account_name);
return 0;
}
if (Service.got_service_option(argv, "install-manual"))
{
- Service.Install(0, servicename, displayname, path_and_service);
+ Service.Install(0, servicename, displayname, path_and_service, account_name);
return 0;
}
if (Service.got_service_option(argv, "remove"))
@@ -3116,7 +3171,7 @@ int main(int argc, char **argv)
if (argc == 2)
{
if (!default_service_handling(argv, MYSQL_SERVICENAME, MYSQL_SERVICENAME,
- file_path, ""))
+ file_path, "", NULL))
return 0;
if (Service.IsService(argv[1])) /* Start an optional service */
{
@@ -3135,7 +3190,7 @@ int main(int argc, char **argv)
}
else if (argc == 3) /* install or remove any optional service */
{
- if (!default_service_handling(argv, argv[2], argv[2], file_path, ""))
+ if (!default_service_handling(argv, argv[2], argv[2], file_path, "", NULL))
return 0;
if (Service.IsService(argv[2]))
{
@@ -3153,15 +3208,39 @@ int main(int argc, char **argv)
return 0;
}
}
- else if (argc == 4)
+ else if (argc >= 4)
{
- /*
- Install an optional service with optional config file
- mysqld --install-manual mysqldopt --defaults-file=c:\miguel\my.ini
- */
- if (!default_service_handling(argv, argv[2], argv[2], file_path,
- argv[3]))
- return 0;
+ const char *defaults_file = "--defaults-file";
+ const char *service = "--local-service";
+ char extra_opt[FN_REFLEN] = "";
+ char *account_name = NULL;
+ char *option;
+ int index;
+ for (index = 3; index < argc; index++)
+ {
+ option= argv[index];
+ /*
+ Install an optional service with optional config file
+ mysqld --install-manual mysqldopt --defaults-file=c:\miguel\my.ini
+ */
+ if (strncmp(option, defaults_file, strlen(defaults_file)) == 0)
+ {
+ strmov(extra_opt, option);
+ }
+ else
+ /*
+ Install an optional service as local service
+ mysqld --install-manual mysqldopt --local-service
+ */
+ if (strncmp(option, service, strlen(service)) == 0)
+ {
+ account_name=(char*)malloc(27);
+ strmov(account_name, "NT AUTHORITY\\LocalService\0");
+ }
+ }
+
+ if (!default_service_handling(argv, argv[2], argv[2], file_path, extra_opt, account_name))
+ return 0;
}
else if (argc == 1 && Service.IsService(MYSQL_SERVICENAME))
{
@@ -3302,7 +3381,7 @@ static void create_new_thread(THD *thd)
("Can't create thread to handle request (error %d)",
error));
thread_count--;
- thd->killed=1; // Safety
+ thd->killed= THD::KILL_CONNECTION; // Safety
(void) pthread_mutex_unlock(&LOCK_thread_count);
statistic_increment(aborted_connects,&LOCK_status);
net_printf(thd,ER_CANT_CREATE_THREAD,error);
@@ -3576,25 +3655,27 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg)
while (!abort_loop)
{
/* wait for named pipe connection */
- fConnected = ConnectNamedPipe( hPipe, NULL );
+ fConnected = ConnectNamedPipe(hPipe, NULL);
if (abort_loop)
break;
if (!fConnected)
fConnected = GetLastError() == ERROR_PIPE_CONNECTED;
if (!fConnected)
{
- CloseHandle( hPipe );
- if ((hPipe = CreateNamedPipe(pipe_name,
- PIPE_ACCESS_DUPLEX,
- PIPE_TYPE_BYTE |
- PIPE_READMODE_BYTE |
- PIPE_WAIT,
- PIPE_UNLIMITED_INSTANCES,
- (int) global_system_variables.net_buffer_length,
- (int) global_system_variables.net_buffer_length,
- NMPWAIT_USE_DEFAULT_WAIT,
- &saPipeSecurity )) ==
- INVALID_HANDLE_VALUE )
+ CloseHandle(hPipe);
+ if ((hPipe= CreateNamedPipe(pipe_name,
+ PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_BYTE |
+ PIPE_READMODE_BYTE |
+ PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ (int) global_system_variables.
+ net_buffer_length,
+ (int) global_system_variables.
+ net_buffer_length,
+ NMPWAIT_USE_DEFAULT_WAIT,
+ &saPipeSecurity)) ==
+ INVALID_HANDLE_VALUE)
{
sql_perror("Can't create new named pipe!");
break; // Abort
@@ -3621,8 +3702,8 @@ extern "C" pthread_handler_decl(handle_connections_namedpipes,arg)
if (!(thd = new THD))
{
- DisconnectNamedPipe( hConnectedPipe );
- CloseHandle( hConnectedPipe );
+ DisconnectNamedPipe(hConnectedPipe);
+ CloseHandle(hConnectedPipe);
continue;
}
if (!(thd->net.vio = vio_new_win32pipe(hConnectedPipe)) ||
@@ -3984,7 +4065,10 @@ enum options_mysqld
OPT_TIME_FORMAT,
OPT_DATETIME_FORMAT,
OPT_LOG_QUERIES_NOT_USING_INDEXES,
- OPT_DEFAULT_TIME_ZONE
+ OPT_DEFAULT_TIME_ZONE,
+ OPT_OPTIMIZER_SEARCH_DEPTH,
+ OPT_OPTIMIZER_PRUNE_LEVEL,
+ OPT_SQL_UPDATABLE_VIEW_KEY
};
@@ -4243,13 +4327,10 @@ Disable with --skip-isam.",
(gptr*) &opt_slow_logname, (gptr*) &opt_slow_logname, 0, GET_STR, OPT_ARG,
0, 0, 0, 0, 0, 0},
{"log-update", OPT_UPDATE_LOG,
- "Log updates to file.# where # is a unique number if not given.",
+ "The update log is deprecated since version 5.0, is replaced by the binary \
+log and this option justs turns on --log-bin instead.",
(gptr*) &opt_update_logname, (gptr*) &opt_update_logname, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
- {"log-warnings", 'W', "Log some non-critical warnings to the error log file. Use this option twice or --log-warnings=2 if you also want 'Aborted connections' warnings.",
- (gptr*) &global_system_variables.log_warnings,
- (gptr*) &max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG, 1, 0, ~0L,
- 0, 0, 0},
{"low-priority-updates", OPT_LOW_PRIORITY_UPDATES,
"INSERT/DELETE/UPDATE has lower priority than selects.",
(gptr*) &global_system_variables.low_priority_updates,
@@ -4511,9 +4592,9 @@ replicating a LOAD DATA INFILE command.",
0},
#endif /* HAVE_REPLICATION */
{"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME,
- "If set, setting SQL_LOG_BIN to a value will automatically set SQL_LOG_UPDATE to the same value and vice versa.",
- (gptr*) &opt_sql_bin_update, (gptr*) &opt_sql_bin_update, 0, GET_BOOL,
- NO_ARG, 0, 0, 0, 0, 0, 0},
+ "The update log is deprecated since version 5.0, is replaced by the binary \
+log and this option does nothing anymore.",
+ 0, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"sql-mode", OPT_SQL_MODE,
"Syntax: sql-mode=option[,option[,option...]] where option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.",
(gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,
@@ -4535,7 +4616,7 @@ replicating a LOAD DATA INFILE command.",
0, 0, 0, 0, 0},
{"tmpdir", 't',
"Path for temporary files. Several paths may be specified, separated by a "
-#if defined( __WIN__) || defined(OS2) || defined(__NETWARE__)
+#if defined(__WIN__) || defined(OS2) || defined(__NETWARE__)
"semicolon (;)"
#else
"colon (:)"
@@ -4917,6 +4998,16 @@ The minimum value for this variable is 4096.",
"If this is not 0, then mysqld will use this value to reserve file descriptors to use with setrlimit(). If this value is 0 then mysqld will reserve max_connections*5 or max_connections + table_cache*2 (whichever is larger) number of files.",
(gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG,
REQUIRED_ARG, 0, 0, OS_FILE_LIMIT, 0, 1, 0},
+ {"optimizer_prune_level", OPT_OPTIMIZER_PRUNE_LEVEL,
+ "Controls the heuristic(s) applied during query optimization to prune less-promising partial plans from the optimizer search space. Meaning: 0 - do not apply any heuristic, thus perform exhaustive search; 1 - prune plans based on number of retrieved rows.",
+ (gptr*) &global_system_variables.optimizer_prune_level,
+ (gptr*) &max_system_variables.optimizer_prune_level,
+ 0, GET_ULONG, OPT_ARG, 1, 0, 1, 0, 1, 0},
+ {"optimizer_search_depth", OPT_OPTIMIZER_SEARCH_DEPTH,
+ "Maximum depth of search performed by the query optimizer. Values larger than the number of relations in a query result in better query plans, but take longer to compile a query. Smaller values than the number of tables in a relation result in faster optimization, but may produce very bad query plans. If set to 0, the system will automatically pick a reasonable value; if set to MAX_TABLES+2, the optimizer will switch to the original find_best (used for testing/comparison).",
+ (gptr*) &global_system_variables.optimizer_search_depth,
+ (gptr*) &max_system_variables.optimizer_search_depth,
+ 0, GET_ULONG, OPT_ARG, MAX_TABLES+1, 0, MAX_TABLES+2, 0, 1, 0},
{"preload_buffer_size", OPT_PRELOAD_BUFFER_SIZE,
"The size of the buffer that is allocated when preloading indexes",
(gptr*) &global_system_variables.preload_buff_size,
@@ -5016,6 +5107,11 @@ The minimum value for this variable is 4096.",
(gptr*) &max_system_variables.sortbuff_size, 0, GET_ULONG, REQUIRED_ARG,
MAX_SORT_MEMORY, MIN_SORT_MEMORY+MALLOC_OVERHEAD*2, ~0L, MALLOC_OVERHEAD,
1, 0},
+ {"sql_updatable_view_key", OPT_SQL_UPDATABLE_VIEW_KEY,
+ "0 = NO = Don't check presence of key in updatable VIEW. 1 = YES = Prohibit update of VIEW which does not contain key of underlying table. 2 = LIMIT1 = Same as YES but prohibited only operation with LIMIT 1 (usually get from GUI tools).",
+ (gptr*) &global_system_variables.sql_updatable_view_key,
+ (gptr*) &max_system_variables.sql_updatable_view_key,
+ 0, GET_ULONG, REQUIRED_ARG, 1, 0, 2, 0, 1, 0},
#ifdef HAVE_BERKELEY_DB
{"sync-bdb-logs", OPT_BDB_SYNC,
"Synchronously flush logs. Enabled by default",
@@ -5208,6 +5304,7 @@ struct show_var_st status_vars[]= {
SHOW_KEY_CACHE_LONG},
{"Key_writes", (char*) &dflt_key_cache_var.global_cache_write,
SHOW_KEY_CACHE_LONG},
+ {"Last_query_cost", (char*) &last_query_cost, SHOW_DOUBLE},
{"Max_used_connections", (char*) &max_used_connections, SHOW_LONG},
{"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_CONST},
{"Open_files", (char*) &my_file_opened, SHOW_LONG_CONST},
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index c2da47b480e..36a10370508 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -129,6 +129,7 @@ my_bool my_net_init(NET *net, Vio* vio)
net->buff_end=net->buff+net->max_packet;
net->vio = vio;
net->no_send_ok = 0;
+ net->no_send_eof = 0;
net->error=0; net->return_errno=0; net->return_status=0;
net->pkt_nr=net->compress_pkt_nr=0;
net->write_pos=net->read_pos = net->buff;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index f11ed31950a..e0e2b5c8045 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -23,6 +23,19 @@
*/
+/*
+ Classes in this file are used in the following way:
+ 1. For a selection condition a tree of SEL_IMERGE/SEL_TREE/SEL_ARG objects
+ is created. #of rows in table and index statistics are ignored at this
+ step.
+ 2. Created SEL_TREE and index stats data are used to construct a
+ TABLE_READ_PLAN-derived object (TRP_*). Several 'candidate' table read
+ plans may be created.
+ 3. The least expensive table read plan is used to create a tree of
+ QUICK_SELECT_I-derived objects which are later used for row retrieval.
+ QUICK_RANGEs are also created in this step.
+*/
+
#ifdef __GNUC__
#pragma implementation // gcc: Class implementation
#endif
@@ -167,8 +180,7 @@ public:
min_value=arg->max_value;
min_flag=arg->max_flag & NEAR_MAX ? 0 : NEAR_MIN;
}
- void store(uint length,char **min_key,uint min_key_flag,
- char **max_key, uint max_key_flag)
+ void store_min(uint length,char **min_key,uint min_key_flag)
{
if ((min_flag & GEOM_FLAG) ||
(!(min_flag & NO_MIN_RANGE) &&
@@ -183,6 +195,11 @@ public:
memcpy(*min_key,min_value,length);
(*min_key)+= length;
}
+ }
+ void store(uint length,char **min_key,uint min_key_flag,
+ char **max_key, uint max_key_flag)
+ {
+ store_min(length, min_key, min_key_flag);
if (!(max_flag & NO_MAX_RANGE) &&
!(max_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
{
@@ -267,31 +284,77 @@ public:
SEL_ARG *clone_tree();
};
+class SEL_IMERGE;
+
class SEL_TREE :public Sql_alloc
{
public:
enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type;
SEL_TREE(enum Type type_arg) :type(type_arg) {}
- SEL_TREE() :type(KEY) { bzero((char*) keys,sizeof(keys));}
+ SEL_TREE() :type(KEY)
+ {
+ keys_map.clear_all();
+ bzero((char*) keys,sizeof(keys));
+ }
SEL_ARG *keys[MAX_KEY];
+ key_map keys_map; /* bitmask of non-NULL elements in keys */
+
+ /*
+ Possible ways to read rows using index_merge. The list is non-empty only
+ if type==KEY. Currently can be non empty only if keys_map.is_clear_all().
+ */
+ List<SEL_IMERGE> merges;
+
+ /* The members below are filled/used only after get_mm_tree is done */
+ key_map ror_scans_map; /* bitmask of ROR scan-able elements in keys */
+ uint n_ror_scans; /* number of set bits in ror_scans_map */
+
+ struct st_ror_scan_info **ror_scans; /* list of ROR key scans */
+ struct st_ror_scan_info **ror_scans_end; /* last ROR scan */
+ /* Note that #records for each key scan is stored in table->quick_rows */
};
typedef struct st_qsel_param {
THD *thd;
TABLE *table;
- KEY_PART *key_parts,*key_parts_end,*key[MAX_KEY];
+ KEY_PART *key_parts,*key_parts_end;
+ KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
MEM_ROOT *mem_root;
table_map prev_tables,read_tables,current_table;
- uint baseflag, keys, max_key_part, range_count;
+ uint baseflag, max_key_part, range_count;
+
+ uint keys; /* number of keys used in the query */
+
+ /* used_key_no -> table_key_no translation table */
uint real_keynr[MAX_KEY];
+
char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
bool quick; // Don't calulate possible keys
COND *cond;
+
+ uint fields_bitmap_size;
+ MY_BITMAP needed_fields; /* bitmask of fields needed by the query */
+
+ key_map *needed_reg; /* ptr to SQL_SELECT::needed_reg */
+
+ uint *imerge_cost_buff; /* buffer for index_merge cost estimates */
+ uint imerge_cost_buff_size; /* size of the buffer */
+
+ /* TRUE if last checked tree->key can be used for ROR-scan */
+ bool is_ror_scan;
} PARAM;
+class TABLE_READ_PLAN;
+ class TRP_RANGE;
+ class TRP_ROR_INTERSECT;
+ class TRP_ROR_UNION;
+ class TRP_ROR_INDEX_MERGE;
+
+struct st_ror_scan_info;
+
static SEL_TREE * get_mm_parts(PARAM *param,COND *cond_func,Field *field,
Item_func::Functype type,Item *value,
Item_result cmp_type);
@@ -299,32 +362,277 @@ static SEL_ARG *get_mm_leaf(PARAM *param,COND *cond_func,Field *field,
KEY_PART *key_part,
Item_func::Functype type,Item *value);
static SEL_TREE *get_mm_tree(PARAM *param,COND *cond);
+
+static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
static ha_rows check_quick_select(PARAM *param,uint index,SEL_ARG *key_tree);
static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree,
char *min_key,uint min_key_flag,
char *max_key, uint max_key_flag);
-static QUICK_SELECT *get_quick_select(PARAM *param,uint index,
- SEL_ARG *key_tree);
+QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index,
+ SEL_ARG *key_tree,
+ MEM_ROOT *alloc = NULL);
+static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
+ bool index_read_must_be_used,
+ double read_time);
+static
+TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
+ double read_time,
+ bool *are_all_covering);
+static
+TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
+ SEL_TREE *tree,
+ double read_time);
+static
+TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
+ double read_time);
+static int get_index_merge_params(PARAM *param, key_map& needed_reg,
+ SEL_IMERGE *imerge, double *read_time,
+ ha_rows* imerge_rows);
+inline double get_index_only_read_time(const PARAM* param, ha_rows records,
+ int keynr);
+
#ifndef DBUG_OFF
-static void print_quick(QUICK_SELECT *quick,const key_map* needed_reg);
+static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,
+ const char *msg);
+static void print_ror_scans_arr(TABLE *table, const char *msg,
+ struct st_ror_scan_info **start,
+ struct st_ror_scan_info **end);
+static void print_rowid(byte* val, int len);
+static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg);
#endif
+
static SEL_TREE *tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
static SEL_TREE *tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2);
static SEL_ARG *key_or(SEL_ARG *key1,SEL_ARG *key2);
static SEL_ARG *key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag);
static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1);
-static bool get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
+bool get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
SEL_ARG *key_tree,char *min_key,uint min_key_flag,
char *max_key,uint max_key_flag);
static bool eq_tree(SEL_ARG* a,SEL_ARG *b);
static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
-static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length);
+static bool null_part_in_key(KEY_PART *key_part, const char *key,
+ uint length);
+bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param);
+
+
+/*
+ SEL_IMERGE is a list of possible ways to do index merge, i.e. it is
+ a condition in the following form:
+ (t_1||t_2||...||t_N) && (next)
+
+ where all t_i are SEL_TREEs, next is another SEL_IMERGE and no pair
+ (t_i,t_j) contains SEL_ARGS for the same index.
+
+ SEL_TREE contained in SEL_IMERGE always has merges=NULL.
+
+ This class relies on memory manager to do the cleanup.
+*/
+
+class SEL_IMERGE : public Sql_alloc
+{
+ enum { PREALLOCED_TREES= 10};
+public:
+ SEL_TREE *trees_prealloced[PREALLOCED_TREES];
+ SEL_TREE **trees; /* trees used to do index_merge */
+ SEL_TREE **trees_next; /* last of these trees */
+ SEL_TREE **trees_end; /* end of allocated space */
+
+ SEL_ARG ***best_keys; /* best keys to read in SEL_TREEs */
+
+ SEL_IMERGE() :
+ trees(&trees_prealloced[0]),
+ trees_next(trees),
+ trees_end(trees + PREALLOCED_TREES)
+ {}
+ int or_sel_tree(PARAM *param, SEL_TREE *tree);
+ int or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree);
+ int or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge);
+};
+
+
+/*
+ Add SEL_TREE to this index_merge without any checks,
+
+ NOTES
+ This function implements the following:
+ (x_1||...||x_N) || t = (x_1||...||x_N||t), where x_i, t are SEL_TREEs
+
+ RETURN
+ 0 - OK
+ -1 - Out of memory.
+*/
+
+int SEL_IMERGE::or_sel_tree(PARAM *param, SEL_TREE *tree)
+{
+ if (trees_next == trees_end)
+ {
+ const int realloc_ratio= 2; /* Double size for next round */
+ uint old_elements= (trees_end - trees);
+ uint old_size= sizeof(SEL_TREE**) * old_elements;
+ uint new_size= old_size * realloc_ratio;
+ SEL_TREE **new_trees;
+ if (!(new_trees= (SEL_TREE**)alloc_root(param->mem_root, new_size)))
+ return -1;
+ memcpy(new_trees, trees, old_size);
+ trees= new_trees;
+ trees_next= trees + old_elements;
+ trees_end= trees + old_elements * realloc_ratio;
+ }
+ *(trees_next++)= tree;
+ return 0;
+}
+
+
+/*
+ Perform OR operation on this SEL_IMERGE and supplied SEL_TREE new_tree,
+ combining new_tree with one of the trees in this SEL_IMERGE if they both
+ have SEL_ARGs for the same key.
+
+ SYNOPSIS
+ or_sel_tree_with_checks()
+ param PARAM from SQL_SELECT::test_quick_select
+ new_tree SEL_TREE with type KEY or KEY_SMALLER.
+
+ NOTES
+ This does the following:
+ (t_1||...||t_k)||new_tree =
+ either
+ = (t_1||...||t_k||new_tree)
+ or
+ = (t_1||....||(t_j|| new_tree)||...||t_k),
+
+ where t_i, y are SEL_TREEs.
+ new_tree is combined with the first t_j it has a SEL_ARG on common
+ key with. As a consequence of this, choice of keys to do index_merge
+ read may depend on the order of conditions in WHERE part of the query.
+
+ RETURN
+ 0 OK
+ 1 One of the trees was combined with new_tree to SEL_TREE::ALWAYS,
+ and (*this) should be discarded.
+ -1 An error occurred.
+*/
+
+int SEL_IMERGE::or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree)
+{
+ for (SEL_TREE** tree = trees;
+ tree != trees_next;
+ tree++)
+ {
+ if (sel_trees_can_be_ored(*tree, new_tree, param))
+ {
+ *tree = tree_or(param, *tree, new_tree);
+ if (!*tree)
+ return 1;
+ if (((*tree)->type == SEL_TREE::MAYBE) ||
+ ((*tree)->type == SEL_TREE::ALWAYS))
+ return 1;
+ /* SEL_TREE::IMPOSSIBLE is impossible here */
+ return 0;
+ }
+ }
+
+ /* New tree cannot be combined with any of existing trees. */
+ return or_sel_tree(param, new_tree);
+}
+
+
+/*
+ Perform OR operation on this index_merge and supplied index_merge list.
+
+ RETURN
+ 0 - OK
+ 1 - One of conditions in result is always TRUE and this SEL_IMERGE
+ should be discarded.
+ -1 - An error occurred
+*/
+
+int SEL_IMERGE::or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge)
+{
+ for (SEL_TREE** tree= imerge->trees;
+ tree != imerge->trees_next;
+ tree++)
+ {
+ if (or_sel_tree_with_checks(param, *tree))
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ Perform AND operation on two index_merge lists and store result in *im1.
+*/
+
+inline void imerge_list_and_list(List<SEL_IMERGE> *im1, List<SEL_IMERGE> *im2)
+{
+ im1->concat(im2);
+}
+
+
+/*
+ Perform OR operation on 2 index_merge lists, storing result in first list.
+
+ NOTES
+ The following conversion is implemented:
+ (a_1 &&...&& a_N)||(b_1 &&...&& b_K) = AND_i,j(a_i || b_j) =>
+ => (a_1||b_1).
+
+ i.e. all conjuncts except the first one are currently dropped.
+ This is done to avoid producing N*K ways to do index_merge.
+
+ If (a_1||b_1) produce a condition that is always TRUE, NULL is returned
+ and index_merge is discarded (while it is actually possible to try
+ harder).
+
+ As a consequence of this, choice of keys to do index_merge read may depend
+ on the order of conditions in WHERE part of the query.
+
+ RETURN
+ 0 OK, result is stored in *im1
+ other Error, both passed lists are unusable
+*/
+
+int imerge_list_or_list(PARAM *param,
+ List<SEL_IMERGE> *im1,
+ List<SEL_IMERGE> *im2)
+{
+ SEL_IMERGE *imerge= im1->head();
+ im1->empty();
+ im1->push_back(imerge);
+
+ return imerge->or_sel_imerge_with_checks(param, im2->head());
+}
+
+
+/*
+ Perform OR operation on index_merge list and key tree.
+
+ RETURN
+ 0 OK, result is stored in *im1.
+ other Error
+*/
+
+int imerge_list_or_tree(PARAM *param,
+ List<SEL_IMERGE> *im1,
+ SEL_TREE *tree)
+{
+ SEL_IMERGE *imerge;
+ List_iterator<SEL_IMERGE> it(*im1);
+ while((imerge= it++))
+ {
+ if (imerge->or_sel_tree_with_checks(param, tree))
+ it.remove();
+ }
+ return im1->is_empty();
+}
/***************************************************************************
-** Basic functions for SQL_SELECT and QUICK_SELECT
+** Basic functions for SQL_SELECT and QUICK_RANGE_SELECT
***************************************************************************/
/* make a select from mysql info
@@ -380,7 +688,7 @@ void SQL_SELECT::cleanup()
free_cond=0;
delete cond;
cond= 0;
- }
+ }
close_cached_file(&file);
}
@@ -392,11 +700,21 @@ SQL_SELECT::~SQL_SELECT()
#undef index // Fix for Unixware 7
-QUICK_SELECT::QUICK_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc)
- :dont_free(0),error(0),index(key_nr),max_used_key_length(0),
- used_key_parts(0), head(table), it(ranges),range(0)
+QUICK_SELECT_I::QUICK_SELECT_I()
+ :max_used_key_length(0),
+ used_key_parts(0)
+{}
+
+QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
+ bool no_alloc, MEM_ROOT *parent_alloc)
+ :dont_free(0),error(0),free_file(0),cur_range(NULL),range(0)
{
- if (!no_alloc)
+ index= key_nr;
+ head= table;
+ key_part_info= head->key_info[index].key_part;
+ my_init_dynamic_array(&ranges, sizeof(QUICK_RANGE*), 16, 16);
+
+ if (!no_alloc && !parent_alloc)
{
// Allocates everything through the internal memroot
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
@@ -404,21 +722,431 @@ QUICK_SELECT::QUICK_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc)
}
else
bzero((char*) &alloc,sizeof(alloc));
- file=head->file;
- record=head->record[0];
- init();
+ file= head->file;
+ record= head->record[0];
}
-QUICK_SELECT::~QUICK_SELECT()
+
+int QUICK_RANGE_SELECT::init()
{
+ DBUG_ENTER("QUICK_RANGE_SELECT::init");
+ if (file->inited == handler::NONE)
+ DBUG_RETURN(error= file->ha_index_init(index));
+ error= 0;
+ DBUG_RETURN(0);
+}
+
+
+void QUICK_RANGE_SELECT::range_end()
+{
+ if (file->inited != handler::NONE)
+ file->ha_index_end();
+}
+
+
+QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
+{
+ DBUG_ENTER("QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT");
if (!dont_free)
{
- if (file->inited)
- file->ha_index_end();
+ /* file is NULL for CPK scan on covering ROR-intersection */
+ if (file)
+ {
+ range_end();
+ file->extra(HA_EXTRA_NO_KEYREAD);
+ if (free_file)
+ {
+ DBUG_PRINT("info", ("Freeing separate handler %p (free=%d)", file,
+ free_file));
+ file->reset();
+ file->close();
+ }
+ }
+ delete_dynamic(&ranges); /* ranges are allocated in alloc */
free_root(&alloc,MYF(0));
}
+ DBUG_VOID_RETURN;
+}
+
+
+QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param,
+ TABLE *table)
+ :cur_quick_it(quick_selects),pk_quick_select(NULL),unique(NULL),
+ thd(thd_param)
+{
+ DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT");
+ index= MAX_KEY;
+ head= table;
+ bzero(&read_record, sizeof(read_record));
+ init_sql_alloc(&alloc,1024,0);
+ DBUG_VOID_RETURN;
+}
+
+int QUICK_INDEX_MERGE_SELECT::init()
+{
+ cur_quick_it.rewind();
+ cur_quick_select= cur_quick_it++;
+ return 0;
}
+int QUICK_INDEX_MERGE_SELECT::reset()
+{
+ int result;
+ DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::reset");
+ result= cur_quick_select->reset() || prepare_unique();
+ DBUG_RETURN(result);
+}
+
+bool
+QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range)
+{
+ /*
+ Save quick_select that does scan on clustered primary key as it will be
+ processed separately.
+ */
+ if (head->file->primary_key_is_clustered() &&
+ quick_sel_range->index == head->primary_key)
+ pk_quick_select= quick_sel_range;
+ else
+ return quick_selects.push_back(quick_sel_range);
+ return 0;
+}
+
+QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT()
+{
+ DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT");
+ delete unique;
+ quick_selects.delete_elements();
+ delete pk_quick_select;
+ free_root(&alloc,MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
+QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param,
+ TABLE *table,
+ bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc)
+ : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows)
+{
+ index= MAX_KEY;
+ head= table;
+ record= head->record[0];
+ if (!parent_alloc)
+ init_sql_alloc(&alloc,1024,0);
+ else
+ bzero(&alloc, sizeof(MEM_ROOT));
+ last_rowid= (byte*)alloc_root(parent_alloc? parent_alloc : &alloc,
+ head->file->ref_length);
+}
+
+
+/*
+ Do post-constructor initialization.
+ SYNOPSIS
+ QUICK_ROR_INTERSECT_SELECT::init()
+
+ RETURN
+ 0 OK
+ other Error code
+*/
+
+int QUICK_ROR_INTERSECT_SELECT::init()
+{
+ /* Check if last_rowid was successfully allocated in ctor */
+ return !last_rowid;
+}
+
+
+/*
+ Initialize this quick select to be a ROR-merged scan.
+
+ SYNOPSIS
+ QUICK_RANGE_SELECT::init_ror_merged_scan()
+ reuse_handler If TRUE, use head->file, otherwise create a separate
+ handler object
+
+ NOTES
+ This function creates and prepares for subsequent use a separate handler
+ object if it can't reuse head->file. The reason for this is that during
+ ROR-merge several key scans are performed simultaneously, and a single
+ handler is only capable of preserving context of a single key scan.
+
+ In ROR-merge the quick select doing merge does full records retrieval,
+ merged quick selects read only keys.
+
+ RETURN
+ 0 ROR child scan initialized, ok to use.
+ 1 error
+*/
+
+int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
+{
+ handler *save_file= file;
+ DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan");
+
+ if (reuse_handler)
+ {
+ DBUG_PRINT("info", ("Reusing handler %p", file));
+ if (file->extra(HA_EXTRA_KEYREAD) ||
+ file->extra(HA_EXTRA_RETRIEVE_ALL_COLS) |
+ init() || reset())
+ {
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+ }
+
+ /* Create a separate handler object for this quick select */
+ if (free_file)
+ {
+ /* already have own 'handler' object. */
+ DBUG_RETURN(0);
+ }
+
+ if (!(file= get_new_handler(head, head->db_type)))
+ goto failure;
+ DBUG_PRINT("info", ("Allocated new handler %p", file));
+ if (file->ha_open(head->path, head->db_stat, HA_OPEN_IGNORE_IF_LOCKED))
+ {
+ /* Caller will free the memory */
+ goto failure;
+ }
+
+ if (file->extra(HA_EXTRA_KEYREAD) ||
+ file->extra(HA_EXTRA_RETRIEVE_ALL_COLS) ||
+ init() || reset())
+ {
+ file->close();
+ goto failure;
+ }
+ free_file= TRUE;
+ last_rowid= file->ref;
+ DBUG_RETURN(0);
+
+failure:
+ file= save_file;
+ DBUG_RETURN(1);
+}
+
+
+/*
+ Initialize this quick select to be a part of a ROR-merged scan.
+ SYNOPSIS
+ QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan()
+ reuse_handler If TRUE, use head->file, otherwise create separate
+ handler object.
+ RETURN
+ 0 OK
+ other error code
+*/
+int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
+{
+ List_iterator_fast<QUICK_RANGE_SELECT> quick_it(quick_selects);
+ QUICK_RANGE_SELECT* quick;
+ DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan");
+
+ /* Initialize all merged "children" quick selects */
+ DBUG_ASSERT(!(need_to_fetch_row && !reuse_handler));
+ if (!need_to_fetch_row && reuse_handler)
+ {
+ quick= quick_it++;
+ /*
+ There is no use of this->file. Use it for the first of merged range
+ selects.
+ */
+ if (quick->init_ror_merged_scan(TRUE))
+ DBUG_RETURN(1);
+ quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
+ }
+ while((quick= quick_it++))
+ {
+ if (quick->init_ror_merged_scan(FALSE))
+ DBUG_RETURN(1);
+ quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
+ /* All merged scans share the same record buffer in intersection. */
+ quick->record= head->record[0];
+ }
+
+ if (need_to_fetch_row && head->file->ha_rnd_init(1))
+ {
+ DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Initialize quick select for row retrieval.
+ SYNOPSIS
+ reset()
+ RETURN
+ 0 OK
+ other Error code
+*/
+
+int QUICK_ROR_INTERSECT_SELECT::reset()
+{
+ DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::reset");
+ DBUG_RETURN(init_ror_merged_scan(TRUE));
+}
+
+
+/*
+ Add a merged quick select to this ROR-intersection quick select.
+
+ SYNOPSIS
+ QUICK_ROR_INTERSECT_SELECT::push_quick_back()
+ quick Quick select to be added. The quick select must return
+ rows in rowid order.
+ NOTES
+ This call can only be made before init() is called.
+
+ RETURN
+ FALSE OK
+ TRUE Out of memory.
+*/
+
+bool
+QUICK_ROR_INTERSECT_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick)
+{
+ return quick_selects.push_back(quick);
+}
+
+QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT()
+{
+ DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT");
+ quick_selects.delete_elements();
+ delete cpk_quick;
+ free_root(&alloc,MYF(0));
+ if (need_to_fetch_row && head->file->inited != handler::NONE)
+ head->file->ha_rnd_end();
+ DBUG_VOID_RETURN;
+}
+
+
+QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param,
+ TABLE *table)
+ :thd(thd_param)
+{
+ index= MAX_KEY;
+ head= table;
+ rowid_length= table->file->ref_length;
+ record= head->record[0];
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+ my_pthread_setspecific_ptr(THR_MALLOC,&alloc);
+}
+
+
+/*
+ Do post-constructor initialization.
+ SYNOPSIS
+ QUICK_ROR_UNION_SELECT::init()
+
+ RETURN
+ 0 OK
+ other Error code
+*/
+
+int QUICK_ROR_UNION_SELECT::init()
+{
+ if (init_queue(&queue, quick_selects.elements, 0,
+ FALSE , QUICK_ROR_UNION_SELECT::queue_cmp,
+ (void*) this))
+ {
+ bzero(&queue, sizeof(QUEUE));
+ return 1;
+ }
+
+ if (!(cur_rowid= (byte*)alloc_root(&alloc, 2*head->file->ref_length)))
+ return 1;
+ prev_rowid= cur_rowid + head->file->ref_length;
+ return 0;
+}
+
+
+/*
+ Comparison function to be used QUICK_ROR_UNION_SELECT::queue priority
+ queue.
+
+ SYNPOSIS
+ QUICK_ROR_UNION_SELECT::queue_cmp()
+ arg Pointer to QUICK_ROR_UNION_SELECT
+ val1 First merged select
+ val2 Second merged select
+*/
+int QUICK_ROR_UNION_SELECT::queue_cmp(void *arg, byte *val1, byte *val2)
+{
+ QUICK_ROR_UNION_SELECT *self= (QUICK_ROR_UNION_SELECT*)arg;
+ return self->head->file->cmp_ref(((QUICK_SELECT_I*)val1)->last_rowid,
+ ((QUICK_SELECT_I*)val2)->last_rowid);
+}
+
+
+/*
+ Initialize quick select for row retrieval.
+ SYNOPSIS
+ reset()
+
+ RETURN
+ 0 OK
+ other Error code
+*/
+
+int QUICK_ROR_UNION_SELECT::reset()
+{
+ QUICK_SELECT_I* quick;
+ int error;
+ DBUG_ENTER("QUICK_ROR_UNION_SELECT::reset");
+ have_prev_rowid= FALSE;
+ /*
+ Initialize scans for merged quick selects and put all merged quick
+ selects into the queue.
+ */
+ List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
+ while ((quick= it++))
+ {
+ if (quick->init_ror_merged_scan(FALSE))
+ DBUG_RETURN(1);
+ if ((error= quick->get_next()))
+ {
+ if (error == HA_ERR_END_OF_FILE)
+ continue;
+ DBUG_RETURN(error);
+ }
+ quick->save_last_pos();
+ queue_insert(&queue, (byte*)quick);
+ }
+
+ if (head->file->ha_rnd_init(1))
+ {
+ DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+bool
+QUICK_ROR_UNION_SELECT::push_quick_back(QUICK_SELECT_I *quick_sel_range)
+{
+ return quick_selects.push_back(quick_sel_range);
+}
+
+QUICK_ROR_UNION_SELECT::~QUICK_ROR_UNION_SELECT()
+{
+ DBUG_ENTER("QUICK_ROR_UNION_SELECT::~QUICK_ROR_UNION_SELECT");
+ delete_queue(&queue);
+ quick_selects.delete_elements();
+ if (head->file->inited != handler::NONE)
+ head->file->ha_rnd_end();
+ free_root(&alloc,MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
QUICK_RANGE::QUICK_RANGE()
:min_key(0),max_key(0),min_length(0),max_length(0),
flag(NO_MIN_RANGE | NO_MAX_RANGE)
@@ -582,28 +1310,221 @@ SEL_ARG *SEL_ARG::clone_tree()
return root;
}
+
+/*
+ Table rows retrieval plan. Range optimizer creates QUICK_SELECT_I-derived
+ objects from table read plans.
+*/
+class TABLE_READ_PLAN
+{
+public:
+ /*
+ Plan read cost, with or without cost of full row retrieval, depending
+ on plan creation parameters.
+ */
+ double read_cost;
+ ha_rows records; /* estimate of #rows to be examined */
+
+ /*
+ If TRUE, the scan returns rows in rowid order. This is used only for
+ scans that can be both ROR and non-ROR.
+ */
+ bool is_ror;
+
+ /*
+ Create quick select for this plan.
+ SYNOPSIS
+ make_quick()
+ param Parameter from test_quick_select
+ retrieve_full_rows If TRUE, created quick select will do full record
+ retrieval.
+ parent_alloc Memory pool to use, if any.
+
+ NOTES
+ retrieve_full_rows is ignored by some implementations.
+
+ RETURN
+ created quick select
+ NULL on any error.
+ */
+ virtual QUICK_SELECT_I *make_quick(PARAM *param,
+ bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc=NULL) = 0;
+
+ /* Table read plans are allocated on MEM_ROOT and are never deleted */
+ static void *operator new(size_t size, MEM_ROOT *mem_root)
+ { return (void*) alloc_root(mem_root, (uint) size); }
+ static void operator delete(void *ptr,size_t size) {}
+};
+
+class TRP_ROR_INTERSECT;
+class TRP_ROR_UNION;
+class TRP_INDEX_MERGE;
+
+
+/*
+ Plan for a QUICK_RANGE_SELECT scan.
+ TRP_RANGE::make_quick ignores retrieve_full_rows parameter because
+ QUICK_RANGE_SELECT doesn't distinguish between 'index only' scans and full
+ record retrieval scans.
+*/
+
+class TRP_RANGE : public TABLE_READ_PLAN
+{
+public:
+ SEL_ARG *key; /* set of intervals to be used in "range" method retrieval */
+ uint key_idx; /* key number in PARAM::key */
+
+ TRP_RANGE(SEL_ARG *key_arg, uint idx_arg)
+ : key(key_arg), key_idx(idx_arg)
+ {}
+
+ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc)
+ {
+ DBUG_ENTER("TRP_RANGE::make_quick");
+ QUICK_RANGE_SELECT *quick;
+ if ((quick= get_quick_select(param, key_idx, key, parent_alloc)))
+ {
+ quick->records= records;
+ quick->read_time= read_cost;
+ }
+ DBUG_RETURN(quick);
+ }
+};
+
+
+/* Plan for QUICK_ROR_INTERSECT_SELECT scan. */
+
+class TRP_ROR_INTERSECT : public TABLE_READ_PLAN
+{
+public:
+ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc);
+
+ /* Array of pointers to ROR range scans used in this intersection */
+ struct st_ror_scan_info **first_scan;
+ struct st_ror_scan_info **last_scan; /* End of the above array */
+ struct st_ror_scan_info *cpk_scan; /* Clustered PK scan, if there is one */
+ bool is_covering; /* TRUE if no row retrieval phase is necessary */
+ double index_scan_costs; /* SUM(cost(index_scan)) */
+};
+
+
+/*
+ Plan for QUICK_ROR_UNION_SELECT scan.
+ QUICK_ROR_UNION_SELECT always retrieves full rows, so retrieve_full_rows
+ is ignored by make_quick.
+*/
+
+class TRP_ROR_UNION : public TABLE_READ_PLAN
+{
+public:
+ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc);
+ TABLE_READ_PLAN **first_ror; /* array of ptrs to plans for merged scans */
+ TABLE_READ_PLAN **last_ror; /* end of the above array */
+};
+
+
+/*
+ Plan for QUICK_INDEX_MERGE_SELECT scan.
+ QUICK_ROR_INTERSECT_SELECT always retrieves full rows, so retrieve_full_rows
+ is ignored by make_quick.
+*/
+
+class TRP_INDEX_MERGE : public TABLE_READ_PLAN
+{
+public:
+ QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc);
+ TRP_RANGE **range_scans; /* array of ptrs to plans of merged scans */
+ TRP_RANGE **range_scans_end; /* end of the array */
+};
+
+
+/*
+ Fill param->needed_fields with bitmap of fields used in the query.
+ SYNOPSIS
+ fill_used_fields_bitmap()
+ param Parameter from test_quick_select function.
+
+ NOTES
+ Clustered PK members are not put into the bitmap as they are implicitly
+ present in all keys (and it is impossible to avoid reading them).
+ RETURN
+ 0 Ok
+ 1 Out of memory.
+*/
+
+static int fill_used_fields_bitmap(PARAM *param)
+{
+ TABLE *table= param->table;
+ param->fields_bitmap_size= (table->fields/8 + 1);
+ uchar *tmp;
+ uint pk;
+ if (!(tmp= (uchar*)alloc_root(param->mem_root,param->fields_bitmap_size)) ||
+ bitmap_init(&param->needed_fields, tmp, param->fields_bitmap_size*8,
+ FALSE))
+ return 1;
+
+ bitmap_clear_all(&param->needed_fields);
+ for (uint i= 0; i < table->fields; i++)
+ {
+ if (param->thd->query_id == table->field[i]->query_id)
+ bitmap_set_bit(&param->needed_fields, i+1);
+ }
+
+ pk= param->table->primary_key;
+ if (param->table->file->primary_key_is_clustered() && pk != MAX_KEY)
+ {
+ /* The table uses clustered PK and it is not internally generated */
+ KEY_PART_INFO *key_part= param->table->key_info[pk].key_part;
+ KEY_PART_INFO *key_part_end= key_part +
+ param->table->key_info[pk].key_parts;
+ for(;key_part != key_part_end; ++key_part)
+ {
+ bitmap_clear_bit(&param->needed_fields, key_part->fieldnr);
+ }
+ }
+ return 0;
+}
+
+
/*
Test if a key can be used in different ranges
SYNOPSIS
- SQL_SELECT::test_quick_select(thd,keys_to_use, prev_tables,
- limit, force_quick_range)
-
- Updates the following in the select parameter:
- needed_reg - Bits for keys with may be used if all prev regs are read
- quick - Parameter to use when reading records.
- In the table struct the following information is updated:
- quick_keys - Which keys can be used
- quick_rows - How many rows the key matches
-
- RETURN VALUES
- -1 if impossible select
- 0 if can't use quick_select
- 1 if found usable range
-
- TODO
- check if the function really needs to modify keys_to_use, and change the
- code to pass it by reference if not
+ SQL_SELECT::test_quick_select()
+ thd Current thread
+ keys_to_use Keys to use for range retrieval
+ prev_tables Tables assumed to be already read when the scan is
+ performed (but not read at the moment of this call)
+ limit Query limit
+ force_quick_range Prefer to use range (instead of full table scan) even
+ if it is more expensive.
+
+ NOTES
+ Updates the following in the select parameter:
+ needed_reg - Bits for keys with may be used if all prev regs are read
+ quick - Parameter to use when reading records.
+
+ In the table struct the following information is updated:
+ quick_keys - Which keys can be used
+ quick_rows - How many rows the key matches
+
+ TODO
+ Check if this function really needs to modify keys_to_use, and change the
+ code to pass it by reference if it doesn't.
+
+ In addition to force_quick_range other means can be (an usually are) used
+ to make this function prefer range over full table scan. Figure out if
+ force_quick_range is really needed.
+
+ RETURN
+ -1 if impossible select (i.e. certainly no rows will be selected)
+ 0 if can't use quick_select
+ 1 if found usable ranges and quick select has been successfully created.
*/
int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
@@ -613,6 +1534,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
uint idx;
double scan_time;
DBUG_ENTER("test_quick_select");
+ //printf("\nQUERY: %s\n", thd->query);
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
keys_to_use.to_ulonglong(), (ulong) prev_tables,
(ulong) const_tables));
@@ -657,11 +1579,16 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.table=head;
param.keys=0;
param.mem_root= &alloc;
+ param.needed_reg= &needed_reg;
+
+ param.imerge_cost_buff_size= 0;
+
thd->no_errors=1; // Don't warn about NULL
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
if (!(param.key_parts = (KEY_PART*) alloc_root(&alloc,
sizeof(KEY_PART)*
- head->key_parts)))
+ head->key_parts))
+ || fill_used_fields_bitmap(&param))
{
thd->no_errors=0;
free_root(&alloc,MYF(0)); // Return memory & allocator
@@ -671,6 +1598,10 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
my_pthread_setspecific_ptr(THR_MALLOC,&alloc);
+ /*
+ Make an array with description of all key parts of all table keys.
+ This is used in get_mm_parts function.
+ */
key_info= head->key_info;
for (idx=0 ; idx < head->keys ; idx++, key_info++)
{
@@ -698,81 +1629,114 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
param.key_parts_end=key_parts;
+ /* Calculate cost of full index read for the shortest covering index */
+ if (!head->used_keys.is_clear_all())
+ {
+ int key_for_use= find_shortest_key(head, &head->used_keys);
+ double key_read_time= get_index_only_read_time(&param, records,
+ key_for_use);
+ DBUG_PRINT("info", ("'all'+'using index' scan will be using key %d, "
+ "read time %g", key_for_use, key_read_time));
+ if (key_read_time < read_time)
+ read_time= key_read_time;
+ }
+
if ((tree=get_mm_tree(&param,cond)))
{
if (tree->type == SEL_TREE::IMPOSSIBLE)
{
- records=0L; // Return -1 from this function
+ records=0L; // Return -1 from this function
read_time= (double) HA_POS_ERROR;
}
else if (tree->type == SEL_TREE::KEY ||
- tree->type == SEL_TREE::KEY_SMALLER)
+ tree->type == SEL_TREE::KEY_SMALLER)
{
- SEL_ARG **key,**end,**best_key=0;
-
-
- for (idx=0,key=tree->keys, end=key+param.keys ;
- key != end ;
- key++,idx++)
- {
- ha_rows found_records;
- double found_read_time;
- if (*key)
- {
- uint keynr= param.real_keynr[idx];
- if ((*key)->type == SEL_ARG::MAYBE_KEY ||
- (*key)->maybe_flag)
- needed_reg.set_bit(keynr);
-
- found_records=check_quick_select(&param, idx, *key);
- if (found_records != HA_POS_ERROR && found_records > 2 &&
- head->used_keys.is_set(keynr) &&
- (head->file->index_flags(keynr, param.max_key_part, 1) &
- HA_KEYREAD_ONLY))
- {
- /*
- We can resolve this by only reading through this key.
- Assume that we will read trough the whole key range
- and that all key blocks are half full (normally things are
- much better).
- */
- uint keys_per_block= (head->file->block_size/2/
- (head->key_info[keynr].key_length+
- head->file->ref_length) + 1);
- found_read_time=((double) (found_records+keys_per_block-1)/
- (double) keys_per_block);
- }
- else
- found_read_time= (head->file->read_time(keynr,
- param.range_count,
- found_records)+
- (double) found_records / TIME_FOR_COMPARE);
- DBUG_PRINT("info",("read_time: %g found_read_time: %g",
- read_time, found_read_time));
- if (read_time > found_read_time && found_records != HA_POS_ERROR)
- {
- read_time=found_read_time;
- records=found_records;
- best_key=key;
- }
- }
- }
- if (best_key && records)
- {
- if ((quick=get_quick_select(&param,(uint) (best_key-tree->keys),
- *best_key)))
- {
- quick->records=records;
- quick->read_time=read_time;
- }
- }
+ TABLE_READ_PLAN *best_trp;
+ /*
+ It is possible to use a quick select (but maybe it would be slower
+ than 'all' table scan).
+ */
+ if (tree->merges.is_empty())
+ {
+ double best_read_time= read_time;
+ TRP_ROR_INTERSECT *new_trp;
+ bool can_build_covering= FALSE;
+
+ /* Get best 'range' plan and prepare data for making other plans */
+ if ((best_trp= get_key_scans_params(&param, tree, FALSE,
+ best_read_time)))
+ best_read_time= best_trp->read_cost;
+
+ /*
+ Simultaneous key scans and row deletes on several handler
+ objects are not allowed so don't use ROR-intersection for
+ table deletes.
+ */
+ if ((thd->lex->sql_command != SQLCOM_DELETE) )//&&
+// (thd->lex->sql_command != SQLCOM_UPDATE))
+ {
+ /*
+ Get best non-covering ROR-intersection plan and prepare data for
+ building covering ROR-intersection.
+ */
+ if ((new_trp= get_best_ror_intersect(&param, tree, best_read_time,
+ &can_build_covering)))
+ {
+ best_trp= new_trp;
+ best_read_time= best_trp->read_cost;
+ }
+
+ /*
+ Try constructing covering ROR-intersect only if it looks possible
+ and worth doing.
+ */
+ if (new_trp && !new_trp->is_covering && can_build_covering &&
+ (new_trp= get_best_covering_ror_intersect(&param, tree,
+ best_read_time)))
+ best_trp= new_trp;
+ }
+ }
+ else
+ {
+ /* Try creating index_merge/ROR-union scan. */
+ SEL_IMERGE *imerge;
+ TABLE_READ_PLAN *best_conj_trp= NULL, *new_conj_trp;
+ LINT_INIT(new_conj_trp); /* no empty index_merge lists possible */
+
+
+ DBUG_PRINT("info",("No range reads possible,"
+ " trying to construct index_merge"));
+ List_iterator_fast<SEL_IMERGE> it(tree->merges);
+ while ((imerge= it++))
+ {
+ new_conj_trp= get_best_disjunct_quick(&param, imerge, read_time);
+ if (!best_conj_trp || (new_conj_trp && new_conj_trp->read_cost <
+ best_conj_trp->read_cost))
+ best_conj_trp= new_conj_trp;
+ }
+ best_trp= best_conj_trp;
+ }
+ my_pthread_setspecific_ptr(THR_MALLOC, old_root);
+
+ /* If we got a read plan, create a quick select from it. */
+ if (best_trp)
+ {
+ records= best_trp->records;
+ if (!(quick= best_trp->make_quick(&param, TRUE)) || quick->init())
+ {
+ delete quick;
+ quick= NULL;
+ }
+ }
}
}
+ my_pthread_setspecific_ptr(THR_MALLOC, old_root);
free_root(&alloc,MYF(0)); // Return memory & allocator
- my_pthread_setspecific_ptr(THR_MALLOC,old_root);
thd->no_errors=0;
}
- DBUG_EXECUTE("info",print_quick(quick,&needed_reg););
+
+ DBUG_EXECUTE("info", print_quick(quick, &needed_reg););
+
/*
Assume that if the user is using 'limit' we will only need to scan
limit rows if we are using a key
@@ -780,6 +1744,1402 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_RETURN(records ? test(quick) : -1);
}
+
+/*
+ Get cost of 'sweep' full records retrieval.
+ SYNOPSIS
+ get_sweep_read_cost()
+ param Parameter from test_quick_select
+ records # of records to be retrieved
+ RETURN
+ cost of sweep
+*/
+
+double get_sweep_read_cost(const PARAM *param, ha_rows records)
+{
+ double result;
+ if (param->table->file->primary_key_is_clustered())
+ {
+ result= param->table->file->read_time(param->table->primary_key,
+ records, records);
+ }
+ else
+ {
+ double n_blocks=
+ ceil((double)param->table->file->data_file_length / IO_SIZE);
+ double busy_blocks=
+ n_blocks * (1.0 - pow(1.0 - 1.0/n_blocks, rows2double(records)));
+ if (busy_blocks < 1.0)
+ busy_blocks= 1.0;
+ DBUG_PRINT("info",("sweep: nblocks=%g, busy_blocks=%g", n_blocks,
+ busy_blocks));
+ /*
+ Disabled: Bail out if # of blocks to read is bigger than # of blocks in
+ table data file.
+ if (max_cost != DBL_MAX && (busy_blocks+index_reads_cost) >= n_blocks)
+ return 1;
+ */
+ JOIN *join= param->thd->lex->select_lex.join;
+ if (!join || join->tables == 1)
+ {
+ /* No join, assume reading is done in one 'sweep' */
+ result= busy_blocks*(DISK_SEEK_BASE_COST +
+ DISK_SEEK_PROP_COST*n_blocks/busy_blocks);
+ }
+ else
+ {
+ /*
+ Possibly this is a join with source table being non-last table, so
+ assume that disk seeks are random here.
+ */
+ result= busy_blocks;
+ }
+ }
+ DBUG_PRINT("info",("returning cost=%g", result));
+ return result;
+}
+
+
+/*
+ Get best plan for a SEL_IMERGE disjunctive expression.
+ SYNOPSIS
+ get_best_disjunct_quick()
+ param Parameter from check_quick_select function
+ imerge Expression to use
+ read_time Don't create scans with cost > read_time
+
+ NOTES
+ index_merge cost is calculated as follows:
+ index_merge_cost =
+ cost(index_reads) + (see #1)
+ cost(rowid_to_row_scan) + (see #2)
+ cost(unique_use) (see #3)
+
+ 1. cost(index_reads) =SUM_i(cost(index_read_i))
+ For non-CPK scans,
+ cost(index_read_i) = {cost of ordinary 'index only' scan}
+ For CPK scan,
+ cost(index_read_i) = {cost of non-'index only' scan}
+
+ 2. cost(rowid_to_row_scan)
+ If table PK is clustered then
+ cost(rowid_to_row_scan) =
+ {cost of ordinary clustered PK scan with n_ranges=n_rows}
+
+ Otherwise, we use the following model to calculate costs:
+ We need to retrieve n_rows rows from file that occupies n_blocks blocks.
+ We assume that offsets of rows we need are independent variates with
+ uniform distribution in [0..max_file_offset] range.
+
+ We'll denote block as "busy" if it contains row(s) we need to retrieve
+ and "empty" if doesn't contain rows we need.
+
+ Probability that a block is empty is (1 - 1/n_blocks)^n_rows (this
+ applies to any block in file). Let x_i be a variate taking value 1 if
+ block #i is empty and 0 otherwise.
+
+ Then E(x_i) = (1 - 1/n_blocks)^n_rows;
+
+ E(n_empty_blocks) = E(sum(x_i)) = sum(E(x_i)) =
+ = n_blocks * ((1 - 1/n_blocks)^n_rows) =
+ ~= n_blocks * exp(-n_rows/n_blocks).
+
+ E(n_busy_blocks) = n_blocks*(1 - (1 - 1/n_blocks)^n_rows) =
+ ~= n_blocks * (1 - exp(-n_rows/n_blocks)).
+
+ Average size of "hole" between neighbor non-empty blocks is
+ E(hole_size) = n_blocks/E(n_busy_blocks).
+
+ The total cost of reading all needed blocks in one "sweep" is:
+
+ E(n_busy_blocks)*
+ (DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*n_blocks/E(n_busy_blocks)).
+
+ 3. Cost of Unique use is calculated in Unique::get_use_cost function.
+
+ ROR-union cost is calculated in the same way index_merge, but instead of
+ Unique a priority queue is used.
+
+ RETURN
+ Created read plan
+ NULL - Out of memory or no read scan could be built.
+*/
+
+static
+TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
+ double read_time)
+{
+ SEL_TREE **ptree;
+ TRP_INDEX_MERGE *imerge_trp= NULL;
+ uint n_child_scans= imerge->trees_next - imerge->trees;
+ TRP_RANGE **range_scans;
+ TRP_RANGE **cur_child;
+ TRP_RANGE **cpk_scan= NULL;
+ bool imerge_too_expensive= FALSE;
+ double imerge_cost= 0.0;
+ ha_rows cpk_scan_records= 0;
+ ha_rows non_cpk_scan_records= 0;
+ bool pk_is_clustered= param->table->file->primary_key_is_clustered();
+ bool all_scans_ror_able= TRUE;
+ bool all_scans_rors= TRUE;
+ uint unique_calc_buff_size;
+ TABLE_READ_PLAN **roru_read_plans;
+ TABLE_READ_PLAN **cur_roru_plan;
+ double roru_index_costs;
+ double blocks_in_index_read;
+ ha_rows roru_total_records;
+ double roru_intersect_part= 1.0;
+ DBUG_ENTER("get_best_disjunct_quick");
+ DBUG_PRINT("info", ("Full table scan cost =%g", read_time));
+
+ if (!(range_scans= (TRP_RANGE**)alloc_root(param->mem_root,
+ sizeof(TRP_RANGE*)*
+ n_child_scans)))
+ DBUG_RETURN(NULL);
+ /*
+ Collect best 'range' scan for each of disjuncts, and, while doing so,
+ analyze possibility of ROR scans. Also calculate some values needed by
+ other parts of the code.
+ */
+ for (ptree= imerge->trees, cur_child= range_scans;
+ ptree != imerge->trees_next;
+ ptree++, cur_child++)
+ {
+ DBUG_EXECUTE("info", print_sel_tree(param, *ptree, &(*ptree)->keys_map,
+ "tree in SEL_IMERGE"););
+ if (!(*cur_child= get_key_scans_params(param, *ptree, TRUE, read_time)))
+ {
+ /*
+ One of index scans in this index_merge is more expensive than entire
+ table read for another available option. The entire index_merge (and
+ any possible ROR-union) will be more expensive then, too. We continue
+ here only to update SQL_SELECT members.
+ */
+ imerge_too_expensive= TRUE;
+ }
+ if (imerge_too_expensive)
+ continue;
+
+ imerge_cost += (*cur_child)->read_cost;
+ all_scans_ror_able &= ((*ptree)->n_ror_scans > 0);
+ all_scans_rors &= (*cur_child)->is_ror;
+ if (pk_is_clustered &&
+ param->real_keynr[(*cur_child)->key_idx] == param->table->primary_key)
+ {
+ cpk_scan= cur_child;
+ cpk_scan_records= (*cur_child)->records;
+ }
+ else
+ non_cpk_scan_records += (*cur_child)->records;
+ }
+
+ DBUG_PRINT("info", ("index_merge scans cost=%g", imerge_cost));
+ if (imerge_too_expensive || (imerge_cost > read_time) ||
+ (non_cpk_scan_records+cpk_scan_records >= param->table->file->records) &&
+ read_time != DBL_MAX)
+ {
+ /*
+ Bail out if it is obvious that both index_merge and ROR-union will be
+ more expensive
+ */
+ DBUG_PRINT("info", ("Sum of index_merge scans is more expensive than "
+ "full table scan, bailing out"));
+ DBUG_RETURN(NULL);
+ }
+ if (all_scans_rors)
+ {
+ roru_read_plans= (TABLE_READ_PLAN**)range_scans;
+ goto skip_to_ror_scan;
+ }
+ blocks_in_index_read= imerge_cost;
+ if (cpk_scan)
+ {
+ /*
+ Add one ROWID comparison for each row retrieved on non-CPK scan. (it
+ is done in QUICK_RANGE_SELECT::row_in_ranges)
+ */
+ imerge_cost += non_cpk_scan_records / TIME_FOR_COMPARE_ROWID;
+ }
+
+ /* Calculate cost(rowid_to_row_scan) */
+ imerge_cost += get_sweep_read_cost(param, non_cpk_scan_records);
+ DBUG_PRINT("info",("index_merge cost with rowid-to-row scan: %g",
+ imerge_cost));
+ if (imerge_cost > read_time)
+ goto build_ror_index_merge;
+
+ /* Add Unique operations cost */
+ unique_calc_buff_size=
+ Unique::get_cost_calc_buff_size(non_cpk_scan_records,
+ param->table->file->ref_length,
+ param->thd->variables.sortbuff_size);
+ if (param->imerge_cost_buff_size < unique_calc_buff_size)
+ {
+ if (!(param->imerge_cost_buff= (uint*)alloc_root(param->mem_root,
+ unique_calc_buff_size)))
+ DBUG_RETURN(NULL);
+ param->imerge_cost_buff_size= unique_calc_buff_size;
+ }
+
+ imerge_cost +=
+ Unique::get_use_cost(param->imerge_cost_buff, non_cpk_scan_records,
+ param->table->file->ref_length,
+ param->thd->variables.sortbuff_size);
+ DBUG_PRINT("info",("index_merge total cost: %g (wanted: less then %g)",
+ imerge_cost, read_time));
+ if (imerge_cost < read_time)
+ {
+ if ((imerge_trp= new (param->mem_root)TRP_INDEX_MERGE))
+ {
+ imerge_trp->read_cost= imerge_cost;
+ imerge_trp->records= non_cpk_scan_records + cpk_scan_records;
+ imerge_trp->records= min(imerge_trp->records,
+ param->table->file->records);
+ imerge_trp->range_scans= range_scans;
+ imerge_trp->range_scans_end= range_scans + n_child_scans;
+ read_time= imerge_cost;
+ }
+ }
+
+build_ror_index_merge:
+ if (!all_scans_ror_able || param->thd->lex->sql_command == SQLCOM_DELETE)
+ DBUG_RETURN(imerge_trp);
+
+ /* Ok, it is possible to build a ROR-union, try it. */
+ bool dummy;
+ if (!(roru_read_plans=
+ (TABLE_READ_PLAN**)alloc_root(param->mem_root,
+ sizeof(TABLE_READ_PLAN*)*
+ n_child_scans)))
+ DBUG_RETURN(imerge_trp);
+skip_to_ror_scan:
+ roru_index_costs= 0.0;
+ roru_total_records= 0;
+ cur_roru_plan= roru_read_plans;
+
+ /* Find 'best' ROR scan for each of trees in disjunction */
+ for (ptree= imerge->trees, cur_child= range_scans;
+ ptree != imerge->trees_next;
+ ptree++, cur_child++, cur_roru_plan++)
+ {
+ /*
+ Assume the best ROR scan is the one that has cheapest full-row-retrieval
+ scan cost.
+ Also accumulate index_only scan costs as we'll need them to calculate
+ overall index_intersection cost.
+ */
+ double cost;
+ if ((*cur_child)->is_ror)
+ {
+ /* Ok, we have index_only cost, now get full rows scan cost */
+ cost= param->table->file->
+ read_time(param->real_keynr[(*cur_child)->key_idx], 1,
+ (*cur_child)->records) +
+ rows2double((*cur_child)->records) / TIME_FOR_COMPARE;
+ }
+ else
+ cost= read_time;
+
+ TABLE_READ_PLAN *prev_plan= *cur_child;
+ if (!(*cur_roru_plan= get_best_ror_intersect(param, *ptree, cost,
+ &dummy)))
+ {
+ if (prev_plan->is_ror)
+ *cur_roru_plan= prev_plan;
+ else
+ DBUG_RETURN(imerge_trp);
+ roru_index_costs += (*cur_roru_plan)->read_cost;
+ }
+ else
+ roru_index_costs +=
+ ((TRP_ROR_INTERSECT*)(*cur_roru_plan))->index_scan_costs;
+ roru_total_records += (*cur_roru_plan)->records;
+ roru_intersect_part *= (*cur_roru_plan)->records /
+ param->table->file->records;
+ }
+
+ /*
+ rows to retrieve=
+ SUM(rows_in_scan_i) - table_rows * PROD(rows_in_scan_i / table_rows).
+ This is valid because index_merge construction guarantees that conditions
+ in disjunction do not share key parts.
+ */
+ roru_total_records -= (ha_rows)(roru_intersect_part*
+ param->table->file->records);
+ /* ok, got a ROR read plan for each of the disjuncts
+ Calculate cost:
+ cost(index_union_scan(scan_1, ... scan_n)) =
+ SUM_i(cost_of_index_only_scan(scan_i)) +
+ queue_use_cost(rowid_len, n) +
+ cost_of_row_retrieval
+ See get_merge_buffers_cost function for queue_use_cost formula derivation.
+ */
+
+ double roru_total_cost;
+ roru_total_cost= roru_index_costs +
+ rows2double(roru_total_records)*log((double)n_child_scans) /
+ (TIME_FOR_COMPARE_ROWID * M_LN2) +
+ get_sweep_read_cost(param, roru_total_records);
+
+ DBUG_PRINT("info", ("ROR-union: cost %g, %d members", roru_total_cost,
+ n_child_scans));
+ TRP_ROR_UNION* roru;
+ if (roru_total_cost < read_time)
+ {
+ if ((roru= new (param->mem_root) TRP_ROR_UNION))
+ {
+ roru->first_ror= roru_read_plans;
+ roru->last_ror= roru_read_plans + n_child_scans;
+ roru->read_cost= roru_total_cost;
+ roru->records= roru_total_records;
+ DBUG_RETURN(roru);
+ }
+ }
+ DBUG_RETURN(imerge_trp);
+}
+
+
+/*
+ Calculate cost of 'index only' scan for given index and number of records.
+
+ SYNOPSIS
+ get_index_only_read_time()
+ param parameters structure
+ records #of records to read
+ keynr key to read
+
+ NOTES
+ It is assumed that we will read trough the whole key range and that all
+ key blocks are half full (normally things are much better).
+*/
+
+inline double get_index_only_read_time(const PARAM* param, ha_rows records,
+ int keynr)
+{
+ double read_time;
+ uint keys_per_block= (param->table->file->block_size/2/
+ (param->table->key_info[keynr].key_length+
+ param->table->file->ref_length) + 1);
+ read_time=((double) (records+keys_per_block-1)/
+ (double) keys_per_block);
+ return read_time;
+}
+
+typedef struct st_ror_scan_info
+{
+ uint idx; /* # of used key in param->keys */
+ uint keynr; /* # of used key in table */
+ ha_rows records; /* estimate of # records this scan will return */
+
+ /* Set of intervals over key fields that will be used for row retrieval. */
+ SEL_ARG *sel_arg;
+
+ /* Fields used in the query and covered by this ROR scan. */
+ MY_BITMAP covered_fields;
+ uint used_fields_covered; /* # of set bits in covered_fields */
+ int key_rec_length; /* length of key record (including rowid) */
+
+ /*
+ Cost of reading all index records with values in sel_arg intervals set
+ (assuming there is no need to access full table records)
+ */
+ double index_read_cost;
+ uint first_uncovered_field; /* first unused bit in covered_fields */
+ uint key_components; /* # of parts in the key */
+} ROR_SCAN_INFO;
+
+
+/*
+ Create ROR_SCAN_INFO* structure with a single ROR scan on index idx using
+ sel_arg set of intervals.
+
+ SYNOPSIS
+ make_ror_scan()
+ param Parameter from test_quick_select function
+ idx Index of key in param->keys
+ sel_arg Set of intervals for a given key
+ RETURN
+ NULL - out of memory
+ ROR scan structure containing a scan for {idx, sel_arg}
+*/
+
+static
+ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
+{
+ ROR_SCAN_INFO *ror_scan;
+ uchar *bitmap_buf;
+ uint keynr;
+ DBUG_ENTER("make_ror_scan");
+ if (!(ror_scan= (ROR_SCAN_INFO*)alloc_root(param->mem_root,
+ sizeof(ROR_SCAN_INFO))))
+ DBUG_RETURN(NULL);
+
+ ror_scan->idx= idx;
+ ror_scan->keynr= keynr= param->real_keynr[idx];
+ ror_scan->key_rec_length= param->table->key_info[keynr].key_length +
+ param->table->file->ref_length;
+ ror_scan->sel_arg= sel_arg;
+ ror_scan->records= param->table->quick_rows[keynr];
+
+ if (!(bitmap_buf= (uchar*)alloc_root(param->mem_root,
+ param->fields_bitmap_size)))
+ DBUG_RETURN(NULL);
+
+ if (bitmap_init(&ror_scan->covered_fields, bitmap_buf,
+ param->fields_bitmap_size*8, FALSE))
+ DBUG_RETURN(NULL);
+ bitmap_clear_all(&ror_scan->covered_fields);
+
+ KEY_PART_INFO *key_part= param->table->key_info[keynr].key_part;
+ KEY_PART_INFO *key_part_end= key_part +
+ param->table->key_info[keynr].key_parts;
+ uint n_used_covered= 0;
+ for (;key_part != key_part_end; ++key_part)
+ {
+ if (bitmap_is_set(&param->needed_fields, key_part->fieldnr))
+ {
+ n_used_covered++;
+ bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr);
+ }
+ }
+ ror_scan->index_read_cost=
+ get_index_only_read_time(param, param->table->quick_rows[ror_scan->keynr],
+ ror_scan->keynr);
+ DBUG_RETURN(ror_scan);
+}
+
+
+/*
+ Compare two ROR_SCAN_INFO** by E(#records_matched) * key_record_length.
+ SYNOPSIS
+ cmp_ror_scan_info()
+ a ptr to first compared value
+ b ptr to second compared value
+
+ RETURN
+ -1 a < b
+ 0 a = b
+ 1 a > b
+*/
+static int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
+{
+ double val1= rows2double((*a)->records) * (*a)->key_rec_length;
+ double val2= rows2double((*b)->records) * (*b)->key_rec_length;
+ return (val1 < val2)? -1: (val1 == val2)? 0 : 1;
+}
+
+/*
+ Compare two ROR_SCAN_INFO** by
+ (#covered fields in F desc,
+ #components asc,
+ number of first not covered component asc)
+
+ SYNOPSIS
+ cmp_ror_scan_info_covering()
+ a ptr to first compared value
+ b ptr to second compared value
+
+ RETURN
+ -1 a < b
+ 0 a = b
+ 1 a > b
+*/
+static int cmp_ror_scan_info_covering(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
+{
+ if ((*a)->used_fields_covered > (*b)->used_fields_covered)
+ return -1;
+ if ((*a)->used_fields_covered < (*b)->used_fields_covered)
+ return 1;
+ if ((*a)->key_components < (*b)->key_components)
+ return -1;
+ if ((*a)->key_components > (*b)->key_components)
+ return 1;
+ if ((*a)->first_uncovered_field < (*b)->first_uncovered_field)
+ return -1;
+ if ((*a)->first_uncovered_field > (*b)->first_uncovered_field)
+ return 1;
+ return 0;
+}
+
+/* Auxiliary structure for incremental ROR-intersection creation */
+typedef struct
+{
+ const PARAM *param;
+ MY_BITMAP covered_fields; /* union of fields covered by all scans */
+
+ /* TRUE if covered_fields is a superset of needed_fields */
+ bool is_covering;
+
+ double index_scan_costs; /* SUM(cost of 'index-only' scans) */
+ double total_cost;
+ /*
+ Fraction of table records that satisfies conditions of all scans.
+ This is the number of full records that will be retrieved if a
+ non-index_only index intersection will be employed.
+ */
+ double records_fract;
+ ha_rows index_records; /* sum(#records to look in indexes) */
+} ROR_INTERSECT_INFO;
+
+
+/*
+ Re-initialize an allocated intersect info to contain zero scans.
+ SYNOPSIS
+ info Intersection info structure to re-initialize.
+*/
+
+static void ror_intersect_reinit(ROR_INTERSECT_INFO *info)
+{
+ info->is_covering= FALSE;
+ info->index_scan_costs= 0.0f;
+ info->records_fract= 1.0f;
+ bitmap_clear_all(&info->covered_fields);
+}
+
+/*
+ Allocate a ROR_INTERSECT_INFO and initialize it to contain zero scans.
+
+ SYNOPSIS
+ ror_intersect_init()
+ param Parameter from test_quick_select
+ is_index_only If TRUE, set ROR_INTERSECT_INFO to be covering
+
+ RETURN
+ allocated structure
+ NULL on error
+*/
+
+static
+ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only)
+{
+ ROR_INTERSECT_INFO *info;
+ uchar* buf;
+ if (!(info= (ROR_INTERSECT_INFO*)alloc_root(param->mem_root,
+ sizeof(ROR_INTERSECT_INFO))))
+ return NULL;
+ info->param= param;
+ if (!(buf= (uchar*)alloc_root(param->mem_root, param->fields_bitmap_size)))
+ return NULL;
+ if (bitmap_init(&info->covered_fields, buf, param->fields_bitmap_size*8,
+ FALSE))
+ return NULL;
+ ror_intersect_reinit(info);
+ return info;
+}
+
+
+/*
+ Check if adding a ROR scan to a ROR-intersection reduces its cost of
+ ROR-intersection and if yes, update parameters of ROR-intersection,
+ including its cost.
+
+ SYNOPSIS
+ ror_intersect_add()
+ param Parameter from test_quick_select
+ info ROR-intersection structure to add the scan to.
+ ror_scan ROR scan info to add.
+ is_cpk_scan If TRUE, add the scan as CPK scan (this can be inferred
+ from other parameters and is passed separately only to
+ avoid duplicating the inference code)
+
+ NOTES
+ Adding a ROR scan to ROR-intersect "makes sense" iff the cost of ROR-
+ intersection decreases. The cost of ROR-intersection is caclulated as
+ follows:
+
+ cost= SUM_i(key_scan_cost_i) + cost_of_full_rows_retrieval
+
+ if (union of indexes used covers all needed fields)
+ cost_of_full_rows_retrieval= 0;
+ else
+ {
+ cost_of_full_rows_retrieval=
+ cost_of_sweep_read(E(rows_to_retrieve), rows_in_table);
+ }
+
+ E(rows_to_retrieve) is caclulated as follows:
+ Suppose we have a condition on several keys
+ cond=k_11=c_11 AND k_12=c_12 AND ... // parts of first key
+ k_21=c_21 AND k_22=c_22 AND ... // parts of second key
+ ...
+ k_n1=c_n1 AND k_n3=c_n3 AND ... (1)
+
+ where k_ij may be the same as any k_pq (i.e. keys may have common parts).
+
+ A full row is retrieved iff entire cond holds.
+
+ The recursive procedure for finding P(cond) is as follows:
+
+ First step:
+ Pick 1st part of 1st key and break conjunction (1) into two parts:
+ cond= (k_11=c_11 AND R)
+
+ Here R may still contain condition(s) equivalent to k_11=c_11.
+ Nevertheless, the following holds:
+
+ P(k_11=c_11 AND R) = P(k_11=c_11) * P(R|k_11=c_11).
+
+ Mark k_11 as fixed field (and satisfied condition) F, save P(F),
+ save R to be cond and proceed to recursion step.
+
+ Recursion step:
+ We have a set of fixed fields/satisfied conditions) F, probability P(F),
+ and remaining conjunction R
+ Pick next key part on current key and its condition "k_ij=c_ij".
+ We will add "k_ij=c_ij" into F and update P(F).
+ Lets denote k_ij as t, R = t AND R1, where R1 may still contain t. Then
+
+ P((t AND R1)|F) = P(t|F) * P(R1|t|F) = P(t|F) * P(R1|(t AND F)) (2)
+
+ (where '|' mean conditional probability, not "or")
+
+ Consider the first multiplier in (2). One of the following holds:
+ a) F contains condition on field used in t (i.e. t AND F = F).
+ Then P(t|F) = 1
+
+ b) F doesn't contain condition on field used in t. Then F and t are
+ considered independent.
+
+ P(t|F) = P(t|(fields_before_t_in_key AND other_fields)) =
+ = P(t|fields_before_t_in_key).
+
+ P(t|fields_before_t_in_key) = #records(fields_before_t_in_key) /
+ #records(fields_before_t_in_key, t)
+
+ The second multiplier is calculated by applying this step recursively.
+
+ IMPLEMENTATION
+ This function calculates the result of application of the "recursion step"
+ described above for all fixed key members of a single key, accumulating set
+ of covered fields, selectivity, etc.
+
+ The calculation is conducted as follows:
+ Lets denote #records(keypart1, ... keypartK) as n_k. We need to calculate
+
+ n_{k1} n_{k_2}
+ --------- * --------- * .... (3)
+ n_{k1-1} n_{k2_1}
+
+ where k1,k2,... are key parts which fields were not yet marked as fixed
+ ( this is result of application of option b) of the recursion step for
+ parts of a single key).
+ Since it is reasonable to expect that most of the fields are not marked
+ as fixed, we calcualate (3) as
+
+ n_{i1} n_{i_2}
+ (3) = n_{max_key_part} / ( --------- * --------- * .... )
+ n_{i1-1} n_{i2_1}
+
+ where i1,i2, .. are key parts that were already marked as fixed.
+
+ In order to minimize number of expensive records_in_range calls we group
+ and reduce adjacent fractions.
+
+ RETURN
+ TRUE ROR scan added to ROR-intersection, cost updated.
+ FALSE It doesn't make sense to add this ROR scan to this ROR-intersection.
+*/
+
+bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info,
+ ROR_SCAN_INFO* ror_scan, bool is_cpk_scan=FALSE)
+{
+ int i;
+ SEL_ARG *sel_arg;
+ KEY_PART_INFO *key_part=
+ info->param->table->key_info[ror_scan->keynr].key_part;
+ double selectivity_mult= 1.0;
+ byte key_val[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; /* key values tuple */
+
+ DBUG_ENTER("ror_intersect_add");
+ DBUG_PRINT("info", ("Current selectivity= %g", info->records_fract));
+ DBUG_PRINT("info", ("Adding scan on %s",
+ info->param->table->key_info[ror_scan->keynr].name));
+ SEL_ARG *tuple_arg= NULL;
+ char *key_ptr= (char*) key_val;
+ bool cur_covered, prev_covered=
+ bitmap_is_set(&info->covered_fields, key_part->fieldnr);
+
+ ha_rows prev_records= param->table->file->records;
+ key_range min_range;
+ key_range max_range;
+ min_range.key= (byte*) key_val;
+ min_range.flag= HA_READ_KEY_EXACT;
+ max_range.key= (byte*) key_val;
+ max_range.flag= HA_READ_AFTER_KEY;
+
+ for(i= 0, sel_arg= ror_scan->sel_arg; sel_arg;
+ i++, sel_arg= sel_arg->next_key_part)
+ {
+ cur_covered= bitmap_is_set(&info->covered_fields, (key_part + i)->fieldnr);
+ if (cur_covered != prev_covered)
+ {
+ /* create (part1val, ..., part{n-1}val) tuple. */
+ {
+ if (!tuple_arg)
+ {
+ tuple_arg= ror_scan->sel_arg;
+ tuple_arg->store_min(key_part->length, &key_ptr, 0);
+ }
+ while (tuple_arg->next_key_part != sel_arg)
+ {
+ tuple_arg= tuple_arg->next_key_part;
+ tuple_arg->store_min(key_part->length, &key_ptr, 0);
+ }
+ }
+ ha_rows records;
+ min_range.length= max_range.length= ((char*) key_ptr - (char*) key_val);
+ records= param->table->file->
+ records_in_range(ror_scan->keynr,
+ &min_range,
+ &max_range);
+ if (cur_covered)
+ {
+ /* uncovered -> covered */
+ double tmp= rows2double(records)/rows2double(prev_records);
+ DBUG_PRINT("info", ("Selectivity multiplier: %g", tmp));
+ selectivity_mult *= tmp;
+ prev_records= HA_POS_ERROR;
+ }
+ else
+ {
+ /* covered -> uncovered */
+ prev_records= records;
+ }
+ }
+ prev_covered= cur_covered;
+ }
+ if (!prev_covered)
+ {
+ double tmp= rows2double(param->table->quick_rows[ror_scan->keynr]) /
+ rows2double(prev_records);
+ DBUG_PRINT("info", ("Selectivity multiplier: %g", tmp));
+ selectivity_mult *= tmp;
+ }
+
+ if (selectivity_mult == 1.0)
+ {
+ /* Don't add this scan if it doesn't improve selectivity. */
+ DBUG_PRINT("info", ("The scan doesn't improve selectivity."));
+ DBUG_RETURN(FALSE);
+ }
+
+ info->records_fract *= selectivity_mult;
+ ha_rows cur_scan_records= info->param->table->quick_rows[ror_scan->keynr];
+ if (is_cpk_scan)
+ {
+ info->index_scan_costs += rows2double(cur_scan_records)*
+ TIME_FOR_COMPARE_ROWID;
+ }
+ else
+ {
+ info->index_records += cur_scan_records;
+ info->index_scan_costs += ror_scan->index_read_cost;
+ bitmap_union(&info->covered_fields, &ror_scan->covered_fields);
+ }
+
+ if (!info->is_covering && bitmap_is_subset(&info->param->needed_fields,
+ &info->covered_fields))
+ {
+ DBUG_PRINT("info", ("ROR-intersect is covering now"));
+ info->is_covering= TRUE;
+ }
+
+ info->total_cost= info->index_scan_costs;
+ if (!info->is_covering)
+ {
+ ha_rows table_recs= info->param->table->file->records;
+ info->total_cost +=
+ get_sweep_read_cost(info->param,
+ (ha_rows)(info->records_fract*table_recs));
+ }
+ DBUG_PRINT("info", ("New selectivity= %g", info->records_fract));
+ DBUG_PRINT("info", ("New cost= %g, %scovering", info->total_cost,
+ info->is_covering?"" : "non-"));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Get best ROR-intersection plan using non-covering ROR-intersection search
+ algorithm. The returned plan may be covering.
+
+ SYNOPSIS
+ get_best_ror_intersect()
+ param Parameter from test_quick_select function.
+ tree Transformed restriction condition to be used to look
+ for ROR scans.
+ read_time Do not return read plans with cost > read_time.
+ are_all_covering [out] set to TRUE if union of all scans covers all
+ fields needed by the query (and it is possible to build
+ a covering ROR-intersection)
+
+ NOTES
+ get_key_scans_params must be called before for the same SEL_TREE before
+ this function can be called.
+
+ The approximate best non-covering plan search algorithm is as follows:
+
+ find_min_ror_intersection_scan()
+ {
+ R= select all ROR scans;
+ order R by (E(#records_matched) * key_record_length).
+
+ S= first(R); -- set of scans that will be used for ROR-intersection
+ R= R-first(S);
+ min_cost= cost(S);
+ min_scan= make_scan(S);
+ while (R is not empty)
+ {
+ if (!selectivity(S + first(R) < selectivity(S)))
+ continue;
+
+ S= S + first(R);
+ R= R - first(R);
+ if (cost(S) < min_cost)
+ {
+ min_cost= cost(S);
+ min_scan= make_scan(S);
+ }
+ }
+ return min_scan;
+ }
+
+ See ror_intersect_add function for ROR intersection costs.
+
+ Special handling for Clustered PK scans
+ Clustered PK contains all table fields, so using it as a regular scan in
+ index intersection doesn't make sense: a range scan on CPK will be less
+ expensive in this case.
+ Clustered PK scan has special handling in ROR-intersection: it is not used
+ to retrieve rows, instead its condition is used to filter row references
+ we get from scans on other keys.
+
+ RETURN
+ ROR-intersection table read plan
+ NULL if out of memory or no suitable plan found.
+*/
+
+static
+TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
+ double read_time,
+ bool *are_all_covering)
+{
+ uint idx;
+ double min_cost= read_time;
+ DBUG_ENTER("get_best_ror_intersect");
+
+ if (tree->n_ror_scans < 2)
+ DBUG_RETURN(NULL);
+
+ /*
+ Collect ROR-able SEL_ARGs and create ROR_SCAN_INFO for each of them.
+ Also find and save clustered PK scan if there is one.
+ */
+ ROR_SCAN_INFO **cur_ror_scan;
+ ROR_SCAN_INFO *cpk_scan= NULL;
+ bool cpk_scan_used= FALSE;
+ if (!(tree->ror_scans= (ROR_SCAN_INFO**)alloc_root(param->mem_root,
+ sizeof(ROR_SCAN_INFO*)*
+ param->keys)))
+ return NULL;
+ uint cpk_no= (param->table->file->primary_key_is_clustered())?
+ param->table->primary_key : MAX_KEY;
+
+ for (idx= 0, cur_ror_scan= tree->ror_scans; idx < param->keys; idx++)
+ {
+ ROR_SCAN_INFO *scan;
+ if (!tree->ror_scans_map.is_set(idx))
+ continue;
+ if (!(scan= make_ror_scan(param, idx, tree->keys[idx])))
+ return NULL;
+ if (param->real_keynr[idx] == cpk_no)
+ {
+ cpk_scan= scan;
+ tree->n_ror_scans--;
+ }
+ else
+ *(cur_ror_scan++)= scan;
+ }
+
+ tree->ror_scans_end= cur_ror_scan;
+ DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "original",
+ tree->ror_scans,
+ tree->ror_scans_end););
+ /*
+ Ok, [ror_scans, ror_scans_end) is array of ptrs to initialized
+ ROR_SCAN_INFOs.
+ Now, get a minimal key scan using an approximate algorithm.
+ */
+ qsort(tree->ror_scans, tree->n_ror_scans, sizeof(ROR_SCAN_INFO*),
+ (qsort_cmp)cmp_ror_scan_info);
+ DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "ordered",
+ tree->ror_scans,
+ tree->ror_scans_end););
+
+ ROR_SCAN_INFO **intersect_scans; /* ROR scans used in index intersection */
+ ROR_SCAN_INFO **intersect_scans_end;
+ if (!(intersect_scans= (ROR_SCAN_INFO**)alloc_root(param->mem_root,
+ sizeof(ROR_SCAN_INFO*)*
+ tree->n_ror_scans)))
+ return NULL;
+ intersect_scans_end= intersect_scans;
+
+ /* Create and incrementally update ROR intersection. */
+ ROR_INTERSECT_INFO *intersect;
+ if (!(intersect= ror_intersect_init(param, FALSE)))
+ return NULL;
+
+ /* [intersect_scans, intersect_scans_best) will hold the best combination */
+ ROR_SCAN_INFO **intersect_scans_best;
+ ha_rows best_rows;
+ bool is_best_covering;
+ double best_index_scan_costs;
+ LINT_INIT(best_rows); /* protected by intersect_scans_best */
+ LINT_INIT(is_best_covering);
+ LINT_INIT(best_index_scan_costs);
+
+ cur_ror_scan= tree->ror_scans;
+ /* Start with one scan */
+ intersect_scans_best= intersect_scans;
+ while (cur_ror_scan != tree->ror_scans_end && !intersect->is_covering)
+ {
+ /* S= S + first(R); */
+ if (ror_intersect_add(param, intersect, *cur_ror_scan))
+ *(intersect_scans_end++)= *cur_ror_scan;
+ /* R= R - first(R); */
+ cur_ror_scan++;
+
+ if (intersect->total_cost < min_cost)
+ {
+ /* Local minimum found, save it */
+ min_cost= intersect->total_cost;
+ best_rows= (ha_rows)(intersect->records_fract*
+ rows2double(param->table->file->records));
+ is_best_covering= intersect->is_covering;
+ intersect_scans_best= intersect_scans_end;
+ best_index_scan_costs= intersect->index_scan_costs;
+ }
+ }
+
+ DBUG_EXECUTE("info",print_ror_scans_arr(param->table,
+ "best ROR-intersection",
+ intersect_scans,
+ intersect_scans_best););
+
+ *are_all_covering= intersect->is_covering;
+ uint best_num= intersect_scans_best - intersect_scans;
+ /*
+ Ok, found the best ROR-intersection of non-CPK key scans.
+ Check if we should add a CPK scan.
+
+ If the obtained ROR-intersection is covering, it doesn't make sense
+ to add CPK scan - Clustered PK contains all fields and if we're doing
+ CPK scan doing other CPK scans will only add more overhead.
+ */
+ if (cpk_scan && !intersect->is_covering)
+ {
+ /*
+ Handle the special case: ROR-intersect(PRIMARY, key1) is
+ the best, but cost(range(key1)) > cost(best_non_ror_range_scan)
+ */
+ if (best_num == 0)
+ {
+ cur_ror_scan= tree->ror_scans;
+ intersect_scans_end= intersect_scans;
+ ror_intersect_reinit(intersect);
+ if (!ror_intersect_add(param, intersect, *cur_ror_scan))
+ DBUG_RETURN(NULL); /* shouldn't happen actually actually */
+ *(intersect_scans_end++)= *cur_ror_scan;
+ best_num++;
+ }
+
+ if (ror_intersect_add(param, intersect, cpk_scan))
+ {
+ cpk_scan_used= TRUE;
+ min_cost= intersect->total_cost;
+ best_rows= (ha_rows)(intersect->records_fract*
+ rows2double(param->table->file->records));
+ is_best_covering= intersect->is_covering;
+ best_index_scan_costs= intersect->index_scan_costs;
+ }
+ }
+
+ /* Ok, return ROR-intersect plan if we have found one */
+ TRP_ROR_INTERSECT *trp= NULL;
+ if (best_num > 1 || cpk_scan_used)
+ {
+ if (!(trp= new (param->mem_root) TRP_ROR_INTERSECT))
+ DBUG_RETURN(trp);
+ if (!(trp->first_scan=
+ (ROR_SCAN_INFO**)alloc_root(param->mem_root,
+ sizeof(ROR_SCAN_INFO*)*best_num)))
+ DBUG_RETURN(NULL);
+ memcpy(trp->first_scan, intersect_scans, best_num*sizeof(ROR_SCAN_INFO*));
+ trp->last_scan= trp->first_scan + best_num;
+ trp->is_covering= is_best_covering;
+ trp->read_cost= min_cost;
+ trp->records= best_rows? best_rows : 1;
+ trp->index_scan_costs= best_index_scan_costs;
+ trp->cpk_scan= cpk_scan;
+ }
+ DBUG_RETURN(trp);
+}
+
+
+/*
+ Get best covering ROR-intersection.
+ SYNOPSIS
+ get_best_covering_ror_intersect()
+ param Parameter from test_quick_select function.
+ tree SEL_TREE with sets of intervals for different keys.
+ read_time Don't return table read plans with cost > read_time.
+
+ RETURN
+ Best covering ROR-intersection plan
+ NULL if no plan found.
+
+ NOTES
+ get_best_ror_intersect must be called for a tree before calling this
+ function for it.
+ This function invalidates tree->ror_scans member values.
+
+ The following approximate algorithm is used:
+ I=set of all covering indexes
+ F=set of all fields to cover
+ S={}
+
+ do {
+ Order I by (#covered fields in F desc,
+ #components asc,
+ number of first not covered component asc);
+ F=F-covered by first(I);
+ S=S+first(I);
+ I=I-first(I);
+ } while F is not empty.
+*/
+
+static
+TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
+ SEL_TREE *tree,
+ double read_time)
+{
+ ROR_SCAN_INFO **ror_scan_mark;
+ ROR_SCAN_INFO **ror_scans_end= tree->ror_scans_end;
+ DBUG_ENTER("get_best_covering_ror_intersect");
+ uint nbits= param->fields_bitmap_size*8;
+
+ for (ROR_SCAN_INFO **scan= tree->ror_scans; scan != ror_scans_end; ++scan)
+ (*scan)->key_components=
+ param->table->key_info[(*scan)->keynr].key_parts;
+
+ /*
+ Run covering-ROR-search algorithm.
+ Assume set I is [ror_scan .. ror_scans_end)
+ */
+
+ /*I=set of all covering indexes */
+ ror_scan_mark= tree->ror_scans;
+
+ uchar buf[MAX_KEY/8+1];
+ MY_BITMAP covered_fields;
+ if (bitmap_init(&covered_fields, buf, nbits, FALSE))
+ DBUG_RETURN(0);
+ bitmap_clear_all(&covered_fields);
+
+ double total_cost= 0.0f;
+ ha_rows records=0;
+ bool all_covered;
+
+ DBUG_PRINT("info", ("Building covering ROR-intersection"));
+ DBUG_EXECUTE("info", print_ror_scans_arr(param->table,
+ "building covering ROR-I",
+ ror_scan_mark, ror_scans_end););
+ do {
+ /*
+ Update changed sorting info:
+ #covered fields,
+ number of first not covered component
+ Calculate and save these values for each of remaining scans.
+ */
+ for (ROR_SCAN_INFO **scan= ror_scan_mark; scan != ror_scans_end; ++scan)
+ {
+ bitmap_subtract(&(*scan)->covered_fields, &covered_fields);
+ (*scan)->used_fields_covered=
+ bitmap_bits_set(&(*scan)->covered_fields);
+ (*scan)->first_uncovered_field=
+ bitmap_get_first(&(*scan)->covered_fields);
+ }
+
+ qsort(ror_scan_mark, ror_scans_end-ror_scan_mark, sizeof(ROR_SCAN_INFO*),
+ (qsort_cmp)cmp_ror_scan_info_covering);
+
+ DBUG_EXECUTE("info", print_ror_scans_arr(param->table,
+ "remaining scans",
+ ror_scan_mark, ror_scans_end););
+
+ /* I=I-first(I) */
+ total_cost += (*ror_scan_mark)->index_read_cost;
+ records += (*ror_scan_mark)->records;
+ DBUG_PRINT("info", ("Adding scan on %s",
+ param->table->key_info[(*ror_scan_mark)->keynr].name));
+ if (total_cost > read_time)
+ DBUG_RETURN(NULL);
+ /* F=F-covered by first(I) */
+ bitmap_union(&covered_fields, &(*ror_scan_mark)->covered_fields);
+ all_covered= bitmap_is_subset(&param->needed_fields, &covered_fields);
+ } while (!all_covered && (++ror_scan_mark < ror_scans_end));
+
+ if (!all_covered)
+ DBUG_RETURN(NULL); /* should not happen actually */
+
+ /*
+ Ok, [tree->ror_scans .. ror_scan) holds covering index_intersection with
+ cost total_cost.
+ */
+ DBUG_PRINT("info", ("Covering ROR-intersect scans cost: %g", total_cost));
+ DBUG_EXECUTE("info", print_ror_scans_arr(param->table,
+ "creating covering ROR-intersect",
+ tree->ror_scans, ror_scan_mark););
+
+ /* Add priority queue use cost. */
+ total_cost += rows2double(records)*
+ log((double)(ror_scan_mark - tree->ror_scans)) /
+ (TIME_FOR_COMPARE_ROWID * M_LN2);
+ DBUG_PRINT("info", ("Covering ROR-intersect full cost: %g", total_cost));
+
+ if (total_cost > read_time)
+ DBUG_RETURN(NULL);
+
+ TRP_ROR_INTERSECT *trp;
+ if (!(trp= new (param->mem_root) TRP_ROR_INTERSECT))
+ DBUG_RETURN(trp);
+ uint best_num= (ror_scan_mark - tree->ror_scans);
+ if (!(trp->first_scan= (ROR_SCAN_INFO**)alloc_root(param->mem_root,
+ sizeof(ROR_SCAN_INFO*)*
+ best_num)))
+ DBUG_RETURN(NULL);
+ memcpy(trp->first_scan, ror_scan_mark, best_num*sizeof(ROR_SCAN_INFO*));
+ trp->last_scan= trp->first_scan + best_num;
+ trp->is_covering= TRUE;
+ trp->read_cost= total_cost;
+ trp->records= records;
+
+ DBUG_RETURN(trp);
+}
+
+
+/*
+ Get best "range" table read plan for given SEL_TREE.
+ Also update PARAM members and store ROR scans info in the SEL_TREE.
+ SYNOPSIS
+ get_key_scans_params
+ param parameters from test_quick_select
+ tree make range select for this SEL_TREE
+ index_read_must_be_used if TRUE, assume 'index only' option will be set
+ (except for clustered PK indexes)
+ read_time don't create read plans with cost > read_time.
+ RETURN
+ Best range read plan
+ NULL if no plan found or error occurred
+*/
+
+static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
+ bool index_read_must_be_used,
+ double read_time)
+{
+ int idx;
+ SEL_ARG **key,**end, **key_to_read= NULL;
+ ha_rows best_records;
+ TRP_RANGE* read_plan= NULL;
+ bool pk_is_clustered= param->table->file->primary_key_is_clustered();
+ DBUG_ENTER("get_key_scans_params");
+ LINT_INIT(best_records); /* protected by key_to_read */
+ /*
+ Note that there may be trees that have type SEL_TREE::KEY but contain no
+ key reads at all, e.g. tree for expression "key1 is not null" where key1
+ is defined as "not null".
+ */
+ DBUG_EXECUTE("info", print_sel_tree(param, tree, &tree->keys_map,
+ "tree scans"););
+ tree->ror_scans_map.clear_all();
+ tree->n_ror_scans= 0;
+ for (idx= 0,key=tree->keys, end=key+param->keys;
+ key != end ;
+ key++,idx++)
+ {
+ ha_rows found_records;
+ double found_read_time;
+ if (*key)
+ {
+ uint keynr= param->real_keynr[idx];
+ if ((*key)->type == SEL_ARG::MAYBE_KEY ||
+ (*key)->maybe_flag)
+ param->needed_reg->set_bit(keynr);
+
+ bool read_index_only= index_read_must_be_used ? TRUE :
+ (bool) param->table->used_keys.is_set(keynr);
+
+ found_records= check_quick_select(param, idx, *key);
+ if (param->is_ror_scan)
+ {
+ tree->n_ror_scans++;
+ tree->ror_scans_map.set_bit(idx);
+ }
+ if (found_records != HA_POS_ERROR && found_records > 2 &&
+ read_index_only &&
+ (param->table->file->index_flags(keynr, param->max_key_part,1) &
+ HA_KEYREAD_ONLY) &&
+ !(pk_is_clustered && keynr == param->table->primary_key))
+ {
+ /* We can resolve this by only reading through this key. */
+ found_read_time= get_index_only_read_time(param,found_records,keynr);
+ }
+ else
+ {
+ /*
+ cost(read_through_index) = cost(disk_io) + cost(row_in_range_checks)
+ The row_in_range check is in QUICK_RANGE_SELECT::cmp_next function.
+ */
+ found_read_time= (param->table->file->read_time(keynr,
+ param->range_count,
+ found_records)+
+ (double) found_records / TIME_FOR_COMPARE);
+ }
+ DBUG_PRINT("info",("read_time: %g found_read_time: %g",
+ read_time, found_read_time));
+ if (read_time > found_read_time && found_records != HA_POS_ERROR
+ /*|| read_time == DBL_MAX*/ )
+ {
+ read_time= found_read_time;
+ best_records= found_records;
+ key_to_read= key;
+ }
+
+ }
+ }
+
+ DBUG_EXECUTE("info", print_sel_tree(param, tree, &tree->ror_scans_map,
+ "ROR scans"););
+ if (key_to_read)
+ {
+ idx= key_to_read - tree->keys;
+ if ((read_plan= new (param->mem_root) TRP_RANGE(*key_to_read, idx)))
+ {
+ read_plan->records= best_records;
+ read_plan->is_ror= tree->ror_scans_map.is_set(idx);
+ read_plan->read_cost= read_time;
+ DBUG_PRINT("info",("Returning range plan for key %s, cost %g",
+ param->table->key_info[param->real_keynr[idx]].name,
+ read_plan->read_cost));
+ }
+ }
+ else
+ DBUG_PRINT("info", ("No 'range' table read plan found"));
+
+ DBUG_RETURN(read_plan);
+}
+
+
+QUICK_SELECT_I *TRP_INDEX_MERGE::make_quick(PARAM *param,
+ bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc)
+{
+ QUICK_INDEX_MERGE_SELECT *quick_imerge;
+ QUICK_RANGE_SELECT *quick;
+ /* index_merge always retrieves full rows, ignore retrieve_full_rows */
+ if (!(quick_imerge= new QUICK_INDEX_MERGE_SELECT(param->thd, param->table)))
+ return NULL;
+
+ quick_imerge->records= records;
+ quick_imerge->read_time= read_cost;
+ for(TRP_RANGE **range_scan= range_scans; range_scan != range_scans_end;
+ range_scan++)
+ {
+ if (!(quick= (QUICK_RANGE_SELECT*)
+ ((*range_scan)->make_quick(param, FALSE, &quick_imerge->alloc)))||
+ quick_imerge->push_quick_back(quick))
+ {
+ delete quick;
+ delete quick_imerge;
+ return NULL;
+ }
+ }
+ return quick_imerge;
+}
+
+QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param,
+ bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc)
+{
+ QUICK_ROR_INTERSECT_SELECT *quick_intrsect;
+ QUICK_RANGE_SELECT *quick;
+ DBUG_ENTER("TRP_ROR_INTERSECT::make_quick");
+ MEM_ROOT *alloc;
+
+ if ((quick_intrsect=
+ new QUICK_ROR_INTERSECT_SELECT(param->thd, param->table,
+ retrieve_full_rows? (!is_covering):FALSE,
+ parent_alloc)))
+ {
+ DBUG_EXECUTE("info", print_ror_scans_arr(param->table,
+ "creating ROR-intersect",
+ first_scan, last_scan););
+ alloc= parent_alloc? parent_alloc: &quick_intrsect->alloc;
+ for(; first_scan != last_scan;++first_scan)
+ {
+ if (!(quick= get_quick_select(param, (*first_scan)->idx,
+ (*first_scan)->sel_arg, alloc)) ||
+ quick_intrsect->push_quick_back(quick))
+ {
+ delete quick_intrsect;
+ DBUG_RETURN(NULL);
+ }
+ }
+ if (cpk_scan)
+ {
+ if (!(quick= get_quick_select(param, cpk_scan->idx,
+ cpk_scan->sel_arg, alloc)))
+ {
+ delete quick_intrsect;
+ DBUG_RETURN(NULL);
+ }
+ quick->file= NULL;
+ quick_intrsect->cpk_quick= quick;
+ }
+ quick_intrsect->records= records;
+ quick_intrsect->read_time= read_cost;
+ }
+ DBUG_RETURN(quick_intrsect);
+}
+
+
+QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param,
+ bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc)
+{
+ QUICK_ROR_UNION_SELECT *quick_roru;
+ TABLE_READ_PLAN **scan;
+ QUICK_SELECT_I *quick;
+ DBUG_ENTER("TRP_ROR_UNION::make_quick");
+ /*
+ It is impossible to construct a ROR-union that will not retrieve full
+ rows, ignore retrieve_full_rows parameter.
+ */
+ if ((quick_roru= new QUICK_ROR_UNION_SELECT(param->thd, param->table)))
+ {
+ for(scan= first_ror; scan != last_ror; scan++)
+ {
+ if (!(quick= (*scan)->make_quick(param, FALSE, &quick_roru->alloc)) ||
+ quick_roru->push_quick_back(quick))
+ DBUG_RETURN(NULL);
+ }
+ quick_roru->records= records;
+ quick_roru->read_time= read_cost;
+ }
+ DBUG_RETURN(quick_roru);
+}
+
+/****************************************************************************/
/* make a select tree of all keys in condition */
static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
@@ -890,7 +3250,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
param->current_table))
DBUG_RETURN(0); // Can't be calculated yet
if (!(ref_tables & param->current_table))
- DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); // This may be false or true
+ DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); // This may be FALSE or TRUE
/* check field op const */
/* btw, ft_func's arguments()[0] isn't FIELD_ITEM. SerG*/
@@ -924,7 +3284,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
static SEL_TREE *
get_mm_parts(PARAM *param, COND *cond_func, Field *field,
- Item_func::Functype type,
+ Item_func::Functype type,
Item *value, Item_result cmp_type)
{
bool ne_func= FALSE;
@@ -966,11 +3326,12 @@ get_mm_parts(PARAM *param, COND *cond_func, Field *field,
else
{
// This key may be used later
- if (!(sel_arg= new SEL_ARG(SEL_ARG::MAYBE_KEY)))
+ if (!(sel_arg= new SEL_ARG(SEL_ARG::MAYBE_KEY)))
DBUG_RETURN(0); // OOM
}
sel_arg->part=(uchar) key_part->part;
tree->keys[key_part->key]=sel_add(tree->keys[key_part->key],sel_arg);
+ tree->keys_map.set_bit(key_part->key);
}
}
@@ -982,6 +3343,7 @@ get_mm_parts(PARAM *param, COND *cond_func, Field *field,
if (tree2)
tree= tree_or(param,tree,tree2);
}
+
DBUG_RETURN(tree);
}
@@ -1013,8 +3375,8 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
}
/*
- We can't use an index when comparing strings of
- different collations
+ We can't use an index when comparing strings of
+ different collations
*/
if (field->result_type() == STRING_RESULT &&
value->result_type() == STRING_RESULT &&
@@ -1109,11 +3471,11 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
value->result_type() != STRING_RESULT &&
field->cmp_type() != value->result_type())
DBUG_RETURN(0);
-
+
if (value->save_in_field(field, 1) < 0)
{
/* This happens when we try to insert a NULL field in a not null column */
- DBUG_RETURN(&null_element); // cmp with NULL is never true
+ DBUG_RETURN(&null_element); // cmp with NULL is never TRUE
}
/* Get local copy of key */
copies= 1;
@@ -1218,8 +3580,8 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
** If tree is 0 it means that the condition can't be tested. It refers
** to a non existent table or to a field in current table with isn't a key.
** The different tree flags:
-** IMPOSSIBLE: Condition is never true
-** ALWAYS: Condition is always true
+** IMPOSSIBLE: Condition is never TRUE
+** ALWAYS: Condition is always TRUE
** MAYBE: Condition may exists when tables are read
** MAYBE_KEY: Condition refers to a key that may be used in join loop
** KEY_RANGE: Condition uses a key
@@ -1289,6 +3651,8 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
DBUG_RETURN(tree1);
}
+ key_map result_keys;
+ result_keys.clear_all();
/* Join the trees key per key */
SEL_ARG **key1,**key2,**end;
for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ;
@@ -1305,17 +3669,59 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if (*key1 && (*key1)->type == SEL_ARG::IMPOSSIBLE)
{
tree1->type= SEL_TREE::IMPOSSIBLE;
+ DBUG_RETURN(tree1);
+ }
+ result_keys.set_bit(key1 - tree1->keys);
#ifdef EXTRA_DEBUG
- (*key1)->test_use_count(*key1);
+ (*key1)->test_use_count(*key1);
#endif
- break;
- }
}
}
+ tree1->keys_map= result_keys;
+ /* dispose index_merge if there is a "range" option */
+ if (!result_keys.is_clear_all())
+ {
+ tree1->merges.empty();
+ DBUG_RETURN(tree1);
+ }
+
+ /* ok, both trees are index_merge trees */
+ imerge_list_and_list(&tree1->merges, &tree2->merges);
DBUG_RETURN(tree1);
}
+/*
+ Check if two SEL_TREES can be combined into one (i.e. a single key range
+ read can be constructed for "cond_of_tree1 OR cond_of_tree2" ) without
+ using index_merge.
+*/
+
+bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param)
+{
+ key_map common_keys= tree1->keys_map;
+ DBUG_ENTER("sel_trees_can_be_ored");
+ common_keys.intersect(tree2->keys_map);
+
+ if (common_keys.is_clear_all())
+ DBUG_RETURN(FALSE);
+
+ /* trees have a common key, check if they refer to same key part */
+ SEL_ARG **key1,**key2;
+ for (uint key_no=0; key_no < param->keys; key_no++)
+ {
+ if (common_keys.is_set(key_no))
+ {
+ key1= tree1->keys + key_no;
+ key2= tree2->keys + key_no;
+ if ((*key1)->part == (*key2)->part)
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
static SEL_TREE *
tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
@@ -1332,19 +3738,62 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if (tree2->type == SEL_TREE::MAYBE)
DBUG_RETURN(tree2);
- /* Join the trees key per key */
- SEL_ARG **key1,**key2,**end;
- SEL_TREE *result=0;
- for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ;
- key1 != end ; key1++,key2++)
+ SEL_TREE *result= 0;
+ key_map result_keys;
+ result_keys.clear_all();
+ if (sel_trees_can_be_ored(tree1, tree2, param))
{
- *key1=key_or(*key1,*key2);
- if (*key1)
+ /* Join the trees key per key */
+ SEL_ARG **key1,**key2,**end;
+ for (key1= tree1->keys,key2= tree2->keys,end= key1+param->keys ;
+ key1 != end ; key1++,key2++)
{
- result=tree1; // Added to tree1
+ *key1=key_or(*key1,*key2);
+ if (*key1)
+ {
+ result=tree1; // Added to tree1
+ result_keys.set_bit(key1 - tree1->keys);
#ifdef EXTRA_DEBUG
- (*key1)->test_use_count(*key1);
+ (*key1)->test_use_count(*key1);
#endif
+ }
+ }
+ if (result)
+ result->keys_map= result_keys;
+ }
+ else
+ {
+ /* ok, two trees have KEY type but cannot be used without index merge */
+ if (tree1->merges.is_empty() && tree2->merges.is_empty())
+ {
+ SEL_IMERGE *merge;
+ /* both trees are "range" trees, produce new index merge structure */
+ if (!(result= new SEL_TREE()) || !(merge= new SEL_IMERGE()) ||
+ (result->merges.push_back(merge)) ||
+ (merge->or_sel_tree(param, tree1)) ||
+ (merge->or_sel_tree(param, tree2)))
+ result= NULL;
+ else
+ result->type= tree1->type;
+ }
+ else if (!tree1->merges.is_empty() && !tree2->merges.is_empty())
+ {
+ if (imerge_list_or_list(param, &tree1->merges, &tree2->merges))
+ result= new SEL_TREE(SEL_TREE::ALWAYS);
+ else
+ result= tree1;
+ }
+ else
+ {
+ /* one tree is index merge tree and another is range tree */
+ if (tree1->merges.is_empty())
+ swap_variables(SEL_TREE*, tree1, tree2);
+
+ /* add tree2 to tree1->merges, checking if it collapses to ALWAYS */
+ if (imerge_list_or_tree(param, &tree1->merges, tree2))
+ result= new SEL_TREE(SEL_TREE::ALWAYS);
+ else
+ result= tree1;
}
}
DBUG_RETURN(result);
@@ -1393,7 +3842,6 @@ and_all_keys(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
}
-
static SEL_ARG *
key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
{
@@ -1886,7 +4334,7 @@ SEL_ARG::find_range(SEL_ARG *key)
SYNOPSIS
tree_delete()
key Key that is to be deleted from tree (this)
-
+
NOTE
This also frees all sub trees that is used by the element
@@ -2133,7 +4581,7 @@ SEL_ARG *rb_delete_fixup(SEL_ARG *root,SEL_ARG *key,SEL_ARG *par)
}
- /* Test that the proporties for a red-black tree holds */
+ /* Test that the properties for a red-black tree hold */
#ifdef EXTRA_DEBUG
int test_rb_tree(SEL_ARG *element,SEL_ARG *parent)
@@ -2221,38 +4669,116 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
#endif
+/*
+ Calculate estimate of number records that will be retrieved by a range
+ scan on given index using given SEL_ARG intervals tree.
+ SYNOPSIS
+ check_quick_select
+ param Parameter from test_quick_select
+ idx Number of index to use in PARAM::key SEL_TREE::key
+ tree Transformed selection condition, tree->key[idx] holds intervals
+ tree to be used for scanning.
+ NOTES
+ param->is_ror_scan is set to reflect if the key scan is a ROR (see
+ is_key_scan_ror function for more info)
+ param->table->quick_*, param->range_count (and maybe others) are
+ updated with data of given key scan, see check_quick_keys for details.
-/*****************************************************************************
-** Check how many records we will find by using the found tree
-*****************************************************************************/
+ RETURN
+ Estimate # of records to be retrieved.
+ HA_POS_ERROR if estimate calculation failed due to table handler problems.
+
+*/
static ha_rows
check_quick_select(PARAM *param,uint idx,SEL_ARG *tree)
{
ha_rows records;
+ bool cpk_scan;
+ uint key;
DBUG_ENTER("check_quick_select");
+ param->is_ror_scan= FALSE;
+
if (!tree)
DBUG_RETURN(HA_POS_ERROR); // Can't use it
param->max_key_part=0;
param->range_count=0;
+ key= param->real_keynr[idx];
+
if (tree->type == SEL_ARG::IMPOSSIBLE)
DBUG_RETURN(0L); // Impossible select. return
if (tree->type != SEL_ARG::KEY_RANGE || tree->part != 0)
DBUG_RETURN(HA_POS_ERROR); // Don't use tree
+
+ enum ha_key_alg key_alg= param->table->key_info[key].algorithm;
+ if ((key_alg != HA_KEY_ALG_BTREE) && (key_alg!= HA_KEY_ALG_UNDEF))
+ {
+ /* Records are not ordered by rowid for other types of indexes. */
+ cpk_scan= FALSE;
+ }
+ else
+ {
+ /*
+ Clustered PK scan is a special case, check_quick_keys doesn't recognize
+ CPK scans as ROR scans (while actually any CPK scan is a ROR scan).
+ */
+ cpk_scan= (param->table->primary_key == param->real_keynr[idx]) &&
+ param->table->file->primary_key_is_clustered();
+ param->is_ror_scan= !cpk_scan;
+ }
+
records=check_quick_keys(param,idx,tree,param->min_key,0,param->max_key,0);
if (records != HA_POS_ERROR)
{
- uint key=param->real_keynr[idx];
param->table->quick_keys.set_bit(key);
param->table->quick_rows[key]=records;
param->table->quick_key_parts[key]=param->max_key_part+1;
+
+ if (cpk_scan)
+ param->is_ror_scan= TRUE;
}
DBUG_PRINT("exit", ("Records: %lu", (ulong) records));
DBUG_RETURN(records);
}
+/*
+ Recursively calculate estimate of # rows that will be retrieved by
+ key scan on key idx.
+ SYNOPSIS
+ check_quick_keys()
+ param Parameter from test_quick select function.
+ idx Number of key to use in PARAM::keys in list of used keys
+ (param->real_keynr[idx] holds the key number in table)
+ key_tree SEL_ARG tree being examined.
+ min_key Buffer with partial min key value tuple
+ min_key_flag
+ max_key Buffer with partial max key value tuple
+ max_key_flag
+
+ NOTES
+ The function does the recursive descent on the tree via SEL_ARG::left,
+ SEL_ARG::right, and SEL_ARG::next_key_part edges. The #rows estimates
+ are calculated using records_in_range calls at the leaf nodes and then
+ summed.
+
+ param->min_key and param->max_key are used to hold prefixes of key value
+ tuples.
+
+ The side effects are:
+
+ param->max_key_part is updated to hold the maximum number of key parts used
+ in scan minus 1.
+
+ param->range_count is incremented if the function finds a range that
+ wasn't counted by the caller.
+
+ param->is_ror_scan is cleared if the function detects that the key scan is
+ not a Rowid-Ordered Retrieval scan ( see comments for is_key_scan_ror
+ function for description of which key scans are ROR scans)
+*/
+
static ha_rows
check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
char *min_key,uint min_key_flag, char *max_key,
@@ -2263,6 +4789,13 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
param->max_key_part=max(param->max_key_part,key_tree->part);
if (key_tree->left != &null_element)
{
+ /*
+ There are at least two intervals for current key part, i.e. condition
+ was converted to something like
+ (keyXpartY less/equals c1) OR (keyXpartY more/equals c2).
+ This is not a ROR scan if the key is not Clustered Primary Key.
+ */
+ param->is_ror_scan= FALSE;
records=check_quick_keys(param,idx,key_tree->left,min_key,min_key_flag,
max_key,max_key_flag);
if (records == HA_POS_ERROR) // Impossible
@@ -2277,6 +4810,19 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
uint min_key_length= (uint) (tmp_min_key- param->min_key);
uint max_key_length= (uint) (tmp_max_key- param->max_key);
+ if (param->is_ror_scan)
+ {
+ /*
+ If the index doesn't cover entire key, mark the scan as non-ROR scan.
+ Actually we're cutting off some ROR scans here.
+ */
+ uint16 fieldnr= param->table->key_info[param->real_keynr[idx]].
+ key_part[key_tree->part].fieldnr - 1;
+ if (param->table->field[fieldnr]->key_length() !=
+ param->key[idx][key_tree->part].length)
+ param->is_ror_scan= FALSE;
+ }
+
if (key_tree->next_key_part &&
key_tree->next_key_part->part == key_tree->part+1 &&
key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
@@ -2290,6 +4836,12 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
tmp_max_key, max_key_flag | key_tree->max_flag);
goto end; // Ugly, but efficient
}
+ else
+ {
+ /* The interval for current key part is not c1 <= keyXpartY <= c1 */
+ param->is_ror_scan= FALSE;
+ }
+
tmp_min_flag=key_tree->min_flag;
tmp_max_flag=key_tree->max_flag;
if (!tmp_min_flag)
@@ -2318,6 +4870,24 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
tmp=1; // Max one record
else
{
+ if (param->is_ror_scan)
+ {
+ /*
+ If we get here, the condition on the key was converted to form
+ "(keyXpart1 = c1) AND ... AND (keyXpart{key_tree->part - 1} = cN) AND
+ somecond(keyXpart{key_tree->part})"
+ Check if
+ somecond is "keyXpart{key_tree->part} = const" and
+ uncovered "tail" of KeyX parts is either empty or is identical to
+ first members of clustered primary key.
+ */
+ if (!(min_key_length == max_key_length &&
+ !memcmp(min_key,max_key, (uint) (tmp_max_key - max_key)) &&
+ !key_tree->min_flag && !key_tree->max_flag &&
+ is_key_scan_ror(param, keynr, key_tree->part + 1)))
+ param->is_ror_scan= FALSE;
+ }
+
if (tmp_min_flag & GEOM_FLAG)
{
key_range min_range;
@@ -2354,6 +4924,13 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
records+=tmp;
if (key_tree->right != &null_element)
{
+ /*
+ There are at least two intervals for current key part, i.e. condition
+ was converted to something like
+ (keyXpartY less/equals c1) OR (keyXpartY more/equals c2).
+ This is not a ROR scan if the key is not Clustered Primary Key.
+ */
+ param->is_ror_scan= FALSE;
tmp=check_quick_keys(param,idx,key_tree->right,min_key,min_key_flag,
max_key,max_key_flag);
if (tmp == HA_POS_ERROR)
@@ -2364,22 +4941,112 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
}
-/****************************************************************************
-** change a tree to a structure to be used by quick_select
-** This uses it's own malloc tree
-****************************************************************************/
+/*
+ Check if key scan on given index with equality conditions on first n key
+ parts is a ROR scan.
+
+ SYNOPSIS
+ is_key_scan_ror()
+ param Parameter from test_quick_select
+ keynr Number of key in the table. The key must not be a clustered
+ primary key.
+ nparts Number of first key parts for which equality conditions
+ are present.
+
+ NOTES
+ ROR (Rowid Ordered Retrieval) key scan is a key scan that produces
+ ordered sequence of rowids (ha_xxx::cmp_ref is the comparison function)
+
+ An index scan is a ROR scan if it is done using a condition in form
-static QUICK_SELECT *
-get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
+ "key1_1=c_1 AND ... AND key1_n=c_n" (1)
+
+ where the index is defined on (key1_1, ..., key1_N [,a_1, ..., a_n])
+
+ and the table has a clustered Primary Key
+
+ PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) with first key parts being
+ identical to uncovered parts ot the key being scanned (2)
+
+ Scans on HASH indexes are not ROR scans,
+ any range scan on clustered primary key is ROR scan (3)
+
+ Check (1) is made in check_quick_keys()
+ Check (3) is made check_quick_select()
+ Check (2) is made by this function.
+
+ RETURN
+ TRUE If the scan is ROR-scan
+ FALSE otherwise
+*/
+
+static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
+{
+ KEY *table_key= param->table->key_info + keynr;
+ KEY_PART_INFO *key_part= table_key->key_part + nparts;
+ KEY_PART_INFO *key_part_end= table_key->key_part +
+ table_key->key_parts;
+
+ if (key_part == key_part_end)
+ return TRUE;
+ uint pk_number= param->table->primary_key;
+ if (!param->table->file->primary_key_is_clustered() || pk_number == MAX_KEY)
+ return FALSE;
+
+ KEY_PART_INFO *pk_part= param->table->key_info[pk_number].key_part;
+ KEY_PART_INFO *pk_part_end= pk_part +
+ param->table->key_info[pk_number].key_parts;
+ for(;(key_part!=key_part_end) && (pk_part != pk_part_end);
+ ++key_part, ++pk_part)
+ {
+ if ((key_part->field != pk_part->field) ||
+ (key_part->length != pk_part->length))
+ return FALSE;
+ }
+ return (key_part == key_part_end);
+}
+
+
+/*
+ Create a QUICK_RANGE_SELECT from given key and SEL_ARG tree for that key.
+
+ SYNOPSIS
+ get_quick_select()
+ param
+ idx Index of used key in param->key.
+ key_tree SEL_ARG tree for the used key
+ parent_alloc If not NULL, use it to allocate memory for
+ quick select data. Otherwise use quick->alloc.
+
+ RETURN
+ NULL on error
+ otherwise created quick select
+
+ NOTES
+ The caller must call QUICK_SELCT::init for returned quick select
+
+ CAUTION! This function may change THR_MALLOC to a MEM_ROOT which will be
+ deallocated when the returned quick select is deleted.
+*/
+
+QUICK_RANGE_SELECT *
+get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree,
+ MEM_ROOT *parent_alloc)
{
- QUICK_SELECT *quick;
+ QUICK_RANGE_SELECT *quick;
DBUG_ENTER("get_quick_select");
+
+
if (param->table->key_info[param->real_keynr[idx]].flags & HA_SPATIAL)
- quick=new QUICK_SELECT_GEOM(param->thd, param->table, param->real_keynr[idx],
- 0);
+ quick=new QUICK_RANGE_SELECT_GEOM(param->thd, param->table,
+ param->real_keynr[idx],
+ test(parent_alloc),
+ parent_alloc);
else
- quick=new QUICK_SELECT(param->thd, param->table, param->real_keynr[idx]);
+ quick=new QUICK_RANGE_SELECT(param->thd, param->table,
+ param->real_keynr[idx],
+ test(parent_alloc), parent_alloc);
if (quick)
{
@@ -2393,9 +5060,10 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
else
{
quick->key_parts=(KEY_PART*)
- memdup_root(&quick->alloc,(char*) param->key[idx],
- sizeof(KEY_PART)*
- param->table->key_info[param->real_keynr[idx]].key_parts);
+ memdup_root(parent_alloc? parent_alloc : &quick->alloc,
+ (char*) param->key[idx],
+ sizeof(KEY_PART)*
+ param->table->key_info[param->real_keynr[idx]].key_parts);
}
}
DBUG_RETURN(quick);
@@ -2405,9 +5073,8 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
/*
** Fix this to get all possible sub_ranges
*/
-
-static bool
-get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
+bool
+get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
SEL_ARG *key_tree,char *min_key,uint min_key_flag,
char *max_key, uint max_key_flag)
{
@@ -2503,7 +5170,9 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
set_if_bigger(quick->max_used_key_length,range->min_length);
set_if_bigger(quick->max_used_key_length,range->max_length);
set_if_bigger(quick->used_key_parts, (uint) key_tree->part+1);
- quick->ranges.push_back(range);
+ if (insert_dynamic(&quick->ranges, (gptr)&range))
+ return 1;
+
end:
if (key_tree->right != &null_element)
@@ -2517,12 +5186,12 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
Return 1 if there is only one range and this uses the whole primary key
*/
-bool QUICK_SELECT::unique_key_range()
+bool QUICK_RANGE_SELECT::unique_key_range()
{
if (ranges.elements == 1)
{
- QUICK_RANGE *tmp;
- if (((tmp=ranges.head())->flag & (EQ_RANGE | NULL_RANGE)) == EQ_RANGE)
+ QUICK_RANGE *tmp= *((QUICK_RANGE**)ranges.buffer);
+ if ((tmp->flag & (EQ_RANGE | NULL_RANGE)) == EQ_RANGE)
{
KEY *key=head->key_info+index;
return ((key->flags & (HA_NOSAME | HA_END_SPACE_KEY)) == HA_NOSAME &&
@@ -2533,11 +5202,11 @@ bool QUICK_SELECT::unique_key_range()
}
-/* Returns true if any part of the key is NULL */
+/* Returns TRUE if any part of the key is NULL */
static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
{
- for (const char *end=key+length ;
+ for (const char *end=key+length ;
key < end;
key+= key_part++->store_length)
{
@@ -2548,13 +5217,55 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
}
+bool QUICK_SELECT_I::check_if_keys_used(List<Item> *fields)
+{
+ return check_if_key_used(head, index, *fields);
+}
+
+bool QUICK_INDEX_MERGE_SELECT::check_if_keys_used(List<Item> *fields)
+{
+ QUICK_RANGE_SELECT *quick;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ while ((quick= it++))
+ {
+ if (check_if_key_used(head, quick->index, *fields))
+ return 1;
+ }
+ return 0;
+}
+
+bool QUICK_ROR_INTERSECT_SELECT::check_if_keys_used(List<Item> *fields)
+{
+ QUICK_RANGE_SELECT *quick;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ while ((quick= it++))
+ {
+ if (check_if_key_used(head, quick->index, *fields))
+ return 1;
+ }
+ return 0;
+}
+
+bool QUICK_ROR_UNION_SELECT::check_if_keys_used(List<Item> *fields)
+{
+ QUICK_SELECT_I *quick;
+ List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
+ while ((quick= it++))
+ {
+ if (quick->check_if_keys_used(fields))
+ return 1;
+ }
+ return 0;
+}
+
/****************************************************************************
Create a QUICK RANGE based on a key
****************************************************************************/
-QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
+QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
+ TABLE_REF *ref)
{
- QUICK_SELECT *quick=new QUICK_SELECT(thd, table, ref->key, 1);
+ QUICK_RANGE_SELECT *quick=new QUICK_RANGE_SELECT(thd, table, ref->key, 1);
KEY *key_info = &table->key_info[ref->key];
KEY_PART *key_part;
QUICK_RANGE *range;
@@ -2562,11 +5273,16 @@ QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
if (!quick)
return 0; /* no ranges found */
+ if (quick->init())
+ {
+ delete quick;
+ return 0;
+ }
+
if (cp_buffer_from_ref(ref))
{
if (thd->is_fatal_error)
goto err; // out of memory
- return quick; // empty range
}
if (!(range= new QUICK_RANGE()))
@@ -2590,10 +5306,10 @@ QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
key_part->store_length= key_info->key_part[part].store_length;
key_part->null_bit= key_info->key_part[part].null_bit;
}
- if (quick->ranges.push_back(range))
+ if (insert_dynamic(&quick->ranges,(gptr)&range))
goto err;
- /*
+ /*
Add a NULL range if REF_OR_NULL optimization is used.
For example:
if we have "WHERE A=2 OR A IS NULL" we created the (A=2) range above
@@ -2609,7 +5325,7 @@ QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
EQ_RANGE)))
goto err;
*ref->null_ref_key= 0; // Clear null byte
- if (quick->ranges.push_back(null_range))
+ if (insert_dynamic(&quick->ranges,(gptr)&null_range))
goto err;
}
@@ -2620,11 +5336,288 @@ err:
return 0;
}
+
+/*
+ Fetch all row ids into unique.
+
+ If table has a clustered primary key that covers all rows (TRUE for bdb
+ and innodb currently) and one of the index_merge scans is a scan on PK,
+ then
+ primary key scan rowids are not put into Unique and also
+ rows that will be retrieved by PK scan are not put into Unique
+
+ RETURN
+ 0 OK
+ other error
+*/
+
+int QUICK_INDEX_MERGE_SELECT::prepare_unique()
+{
+ int result;
+ DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::prepare_unique");
+
+ /* We're going to just read rowids. */
+ head->file->extra(HA_EXTRA_KEYREAD);
+
+ /*
+ Make innodb retrieve all PK member fields, so
+ * ha_innobase::position (which uses them) call works.
+ * We can filter out rows that will be retrieved by clustered PK.
+ (This also creates a deficiency - it is possible that we will retrieve
+ parts of key that are not used by current query at all.)
+ */
+ head->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
+
+ cur_quick_select->init();
+
+ unique= new Unique(refpos_order_cmp, (void *)head->file,
+ head->file->ref_length,
+ thd->variables.sortbuff_size);
+ if (!unique)
+ DBUG_RETURN(1);
+ for (;;)
+ {
+ while ((result= cur_quick_select->get_next()) == HA_ERR_END_OF_FILE)
+ {
+ cur_quick_select->range_end();
+ cur_quick_select= cur_quick_it++;
+ if (!cur_quick_select)
+ break;
+
+ if (cur_quick_select->init())
+ DBUG_RETURN(1);
+
+ /* QUICK_RANGE_SELECT::reset never fails */
+ cur_quick_select->reset();
+ }
+
+ if (result)
+ {
+ if (result != HA_ERR_END_OF_FILE)
+ DBUG_RETURN(result);
+ break;
+ }
+
+ if (thd->killed)
+ DBUG_RETURN(1);
+
+ /* skip row if it will be retrieved by clustered PK scan */
+ if (pk_quick_select && pk_quick_select->row_in_ranges())
+ continue;
+
+ cur_quick_select->file->position(cur_quick_select->record);
+ result= unique->unique_add((char*)cur_quick_select->file->ref);
+ if (result)
+ DBUG_RETURN(1);
+
+ }
+
+ /* ok, all row ids are in Unique */
+ result= unique->get(head);
+ doing_pk_scan= FALSE;
+ /* start table scan */
+ init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1, 1);
+ /* index_merge currently doesn't support "using index" at all */
+ head->file->extra(HA_EXTRA_NO_KEYREAD);
+
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Get next row for index_merge.
+ NOTES
+ The rows are read from
+ 1. rowids stored in Unique.
+ 2. QUICK_RANGE_SELECT with clustered primary key (if any).
+ The sets of rows retrieved in 1) and 2) are guaranteed to be disjoint.
+*/
+
+int QUICK_INDEX_MERGE_SELECT::get_next()
+{
+ int result;
+ DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::get_next");
+
+ if (doing_pk_scan)
+ DBUG_RETURN(pk_quick_select->get_next());
+
+ result= read_record.read_record(&read_record);
+
+ if (result == -1)
+ {
+ result= HA_ERR_END_OF_FILE;
+ end_read_record(&read_record);
+ /* All rows from Unique have been retrieved, do a clustered PK scan */
+ if (pk_quick_select)
+ {
+ doing_pk_scan= TRUE;
+ if ((result= pk_quick_select->init()))
+ DBUG_RETURN(result);
+ DBUG_RETURN(pk_quick_select->get_next());
+ }
+ }
+
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Retrieve next record.
+ SYNOPSIS
+ QUICK_ROR_INTERSECT_SELECT::get_next()
+
+ NOTES
+ Invariant on enter/exit: all intersected selects have retrieved all index
+ records with rowid <= some_rowid_val and no intersected select has
+ retrieved any index records with rowid > some_rowid_val.
+ We start fresh and loop until we have retrieved the same rowid in each of
+ the key scans or we got an error.
+
+ If a Clustered PK scan is present, it is used only to check if row
+ satisfies its condition (and never used for row retrieval).
+
+ RETURN
+ 0 - Ok
+ other - Error code if any error occurred.
+*/
+
+int QUICK_ROR_INTERSECT_SELECT::get_next()
+{
+ List_iterator_fast<QUICK_RANGE_SELECT> quick_it(quick_selects);
+ QUICK_RANGE_SELECT* quick;
+ int error, cmp;
+ uint last_rowid_count=0;
+ DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::get_next");
+
+ /* Get a rowid for first quick and save it as a 'candidate' */
+ quick= quick_it++;
+ if (cpk_quick)
+ {
+ do {
+ error= quick->get_next();
+ }while (!error && !cpk_quick->row_in_ranges());
+ }
+ else
+ error= quick->get_next();
+
+ if (error)
+ DBUG_RETURN(error);
+
+ quick->file->position(quick->record);
+ memcpy(last_rowid, quick->file->ref, head->file->ref_length);
+ last_rowid_count= 1;
+
+ while (last_rowid_count < quick_selects.elements)
+ {
+ if (!(quick= quick_it++))
+ {
+ quick_it.rewind();
+ quick= quick_it++;
+ }
+
+ do {
+ if ((error= quick->get_next()))
+ DBUG_RETURN(error);
+ quick->file->position(quick->record);
+ cmp= head->file->cmp_ref(quick->file->ref, last_rowid);
+ } while (cmp < 0);
+
+ /* Ok, current select 'caught up' and returned ref >= cur_ref */
+ if (cmp > 0)
+ {
+ /* Found a row with ref > cur_ref. Make it a new 'candidate' */
+ if (cpk_quick)
+ {
+ while (!cpk_quick->row_in_ranges())
+ {
+ if ((error= quick->get_next()))
+ DBUG_RETURN(error);
+ }
+ }
+ memcpy(last_rowid, quick->file->ref, head->file->ref_length);
+ last_rowid_count= 1;
+ }
+ else
+ {
+ /* current 'candidate' row confirmed by this select */
+ last_rowid_count++;
+ }
+ }
+
+ /* We get here iff we got the same row ref in all scans. */
+ if (need_to_fetch_row)
+ error= head->file->rnd_pos(head->record[0], last_rowid);
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Retrieve next record.
+ SYNOPSIS
+ QUICK_ROR_UNION_SELECT::get_next()
+
+ NOTES
+ Enter/exit invariant:
+ For each quick select in the queue a {key,rowid} tuple has been
+ retrieved but the corresponding row hasn't been passed to output.
+
+ RETURN
+ 0 - Ok
+ other - Error code if any error occurred.
+*/
+
+int QUICK_ROR_UNION_SELECT::get_next()
+{
+ int error, dup_row;
+ QUICK_SELECT_I *quick;
+ byte *tmp;
+ DBUG_ENTER("QUICK_ROR_UNION_SELECT::get_next");
+
+ do
+ {
+ if (!queue.elements)
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ /* Ok, we have a queue with >= 1 scans */
+
+ quick= (QUICK_SELECT_I*)queue_top(&queue);
+ memcpy(cur_rowid, quick->last_rowid, rowid_length);
+
+ /* put into queue rowid from the same stream as top element */
+ if ((error= quick->get_next()))
+ {
+ if (error != HA_ERR_END_OF_FILE)
+ DBUG_RETURN(error);
+ queue_remove(&queue, 0);
+ }
+ else
+ {
+ quick->save_last_pos();
+ queue_replaced(&queue);
+ }
+
+ if (!have_prev_rowid)
+ {
+ /* No rows have been returned yet */
+ dup_row= FALSE;
+ have_prev_rowid= TRUE;
+ }
+ else
+ dup_row= !head->file->cmp_ref(cur_rowid, prev_rowid);
+ }while (dup_row);
+
+ tmp= cur_rowid;
+ cur_rowid= prev_rowid;
+ prev_rowid= tmp;
+
+ error= head->file->rnd_pos(quick->record, prev_rowid);
+ DBUG_RETURN(error);
+}
+
/* get next possible record using quick-struct */
-int QUICK_SELECT::get_next()
+int QUICK_RANGE_SELECT::get_next()
{
- DBUG_ENTER("get_next");
+ DBUG_ENTER("QUICK_RANGE_SELECT::get_next");
for (;;)
{
@@ -2638,7 +5631,14 @@ int QUICK_SELECT::get_next()
DBUG_RETURN(result);
}
- if (!(range= it++))
+ if (!cur_range)
+ range= *(cur_range= (QUICK_RANGE**) ranges.buffer);
+ else
+ range=
+ (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ?
+ (QUICK_RANGE*) 0 : *(++cur_range);
+
+ if (!range)
DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used
start_key.key= (const byte*) range->min_key;
@@ -2671,9 +5671,9 @@ int QUICK_SELECT::get_next()
/* Get next for geometrical indexes */
-int QUICK_SELECT_GEOM::get_next()
+int QUICK_RANGE_SELECT_GEOM::get_next()
{
- DBUG_ENTER(" QUICK_SELECT_GEOM::get_next");
+ DBUG_ENTER("QUICK_RANGE_SELECT_GEOM::get_next");
for (;;)
{
@@ -2687,7 +5687,14 @@ int QUICK_SELECT_GEOM::get_next()
DBUG_RETURN(result);
}
- if (!(range= it++))
+ if (!cur_range)
+ range= *(cur_range= (QUICK_RANGE**) ranges.buffer);
+ else
+ range=
+ (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ?
+ (QUICK_RANGE*) 0 : *(++cur_range);
+
+ if (!range)
DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used
result= file->index_read(record,
@@ -2702,6 +5709,46 @@ int QUICK_SELECT_GEOM::get_next()
/*
+ Check if current row will be retrieved by this QUICK_RANGE_SELECT
+
+ NOTES
+ It is assumed that currently a scan is being done on another index
+ which reads all necessary parts of the index that is scanned by this
+ quick select.
+ The implementation does a binary search on sorted array of disjoint
+ ranges, without taking size of range into account.
+
+ This function is used to filter out clustered PK scan rows in
+ index_merge quick select.
+
+ RETURN
+ TRUE if current row will be retrieved by this quick select
+ FALSE if not
+*/
+
+bool QUICK_RANGE_SELECT::row_in_ranges()
+{
+ QUICK_RANGE *range;
+ uint min= 0;
+ uint max= ranges.elements - 1;
+ uint mid= (max + min)/2;
+
+ while (min != max)
+ {
+ if (cmp_next(*(QUICK_RANGE**)dynamic_array_ptr(&ranges, mid)))
+ {
+ /* current row value > mid->max */
+ min= mid + 1;
+ }
+ else
+ max= mid;
+ mid= (min + max) / 2;
+ }
+ range= *(QUICK_RANGE**)dynamic_array_ptr(&ranges, mid);
+ return (!cmp_next(range) && !cmp_prev(range));
+}
+
+/*
This is a hack: we inherit from QUICK_SELECT so that we can use the
get_next() interface, but we have to hold a pointer to the original
QUICK_SELECT because its data are used all over the place. What
@@ -2711,16 +5758,17 @@ int QUICK_SELECT_GEOM::get_next()
for now, this seems to work right at least.
*/
-QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts)
- : QUICK_SELECT(*q), rev_it(rev_ranges)
+QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q,
+ uint used_key_parts)
+ : QUICK_RANGE_SELECT(*q), rev_it(rev_ranges)
{
QUICK_RANGE *r;
- it.rewind();
- for (r = it++; r; r = it++)
- {
- rev_ranges.push_front(r);
- }
+ QUICK_RANGE **pr= (QUICK_RANGE**)ranges.buffer;
+ QUICK_RANGE **last_range= pr + ranges.elements;
+ for (; pr!=last_range; pr++)
+ rev_ranges.push_front(*pr);
+
/* Remove EQ_RANGE flag for keys that are not using the full key */
for (r = rev_it++; r; r = rev_it++)
{
@@ -2813,10 +5861,51 @@ int QUICK_SELECT_DESC::get_next()
/*
+ Compare if found key is over max-value
+ Returns 0 if key <= range->max_key
+*/
+
+int QUICK_RANGE_SELECT::cmp_next(QUICK_RANGE *range_arg)
+{
+ if (range_arg->flag & NO_MAX_RANGE)
+ return 0; /* key can't be to large */
+
+ KEY_PART *key_part=key_parts;
+ uint store_length;
+
+ for (char *key=range_arg->max_key, *end=key+range_arg->max_length;
+ key < end;
+ key+= store_length, key_part++)
+ {
+ int cmp;
+ store_length= key_part->store_length;
+ if (key_part->null_bit)
+ {
+ if (*key)
+ {
+ if (!key_part->field->is_null())
+ return 1;
+ continue;
+ }
+ else if (key_part->field->is_null())
+ return 0;
+ key++; // Skip null byte
+ store_length--;
+ }
+ if ((cmp=key_part->field->key_cmp((byte*) key, key_part->length)) < 0)
+ return 0;
+ if (cmp > 0)
+ return 1;
+ }
+ return (range_arg->flag & NEAR_MAX) ? 1 : 0; // Exact match
+}
+
+
+/*
Returns 0 if found key is inside range (found key >= range->min_key).
*/
-int QUICK_SELECT_DESC::cmp_prev(QUICK_RANGE *range_arg)
+int QUICK_RANGE_SELECT::cmp_prev(QUICK_RANGE *range_arg)
{
int cmp;
if (range_arg->flag & NO_MIN_RANGE)
@@ -2831,7 +5920,7 @@ int QUICK_SELECT_DESC::cmp_prev(QUICK_RANGE *range_arg)
/*
- * True if this range will require using HA_READ_AFTER_KEY
+ * TRUE if this range will require using HA_READ_AFTER_KEY
See comment in get_next() about this
*/
@@ -2843,7 +5932,7 @@ bool QUICK_SELECT_DESC::range_reads_after_key(QUICK_RANGE *range_arg)
}
-/* True if we are reading over a key that may have a NULL value */
+/* TRUE if we are reading over a key that may have a NULL value */
#ifdef NOT_USED
bool QUICK_SELECT_DESC::test_if_null_range(QUICK_RANGE *range_arg,
@@ -2891,6 +5980,232 @@ bool QUICK_SELECT_DESC::test_if_null_range(QUICK_RANGE *range_arg,
#endif
+void QUICK_RANGE_SELECT::add_info_string(String *str)
+{
+ KEY *key_info= head->key_info + index;
+ str->append(key_info->name);
+}
+
+void QUICK_INDEX_MERGE_SELECT::add_info_string(String *str)
+{
+ QUICK_RANGE_SELECT *quick;
+ bool first= TRUE;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ str->append("sort_union(");
+ while ((quick= it++))
+ {
+ if (!first)
+ str->append(',');
+ else
+ first= FALSE;
+ quick->add_info_string(str);
+ }
+ if (pk_quick_select)
+ {
+ str->append(',');
+ pk_quick_select->add_info_string(str);
+ }
+ str->append(')');
+}
+
+void QUICK_ROR_INTERSECT_SELECT::add_info_string(String *str)
+{
+ bool first= TRUE;
+ QUICK_RANGE_SELECT *quick;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ str->append("intersect(");
+ while ((quick= it++))
+ {
+ KEY *key_info= head->key_info + quick->index;
+ if (!first)
+ str->append(',');
+ else
+ first= FALSE;
+ str->append(key_info->name);
+ }
+ if (cpk_quick)
+ {
+ KEY *key_info= head->key_info + cpk_quick->index;
+ str->append(',');
+ str->append(key_info->name);
+ }
+ str->append(')');
+}
+
+void QUICK_ROR_UNION_SELECT::add_info_string(String *str)
+{
+ bool first= TRUE;
+ QUICK_SELECT_I *quick;
+ List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
+ str->append("union(");
+ while ((quick= it++))
+ {
+ if (!first)
+ str->append(',');
+ else
+ first= FALSE;
+ quick->add_info_string(str);
+ }
+ str->append(')');
+}
+
+
+void QUICK_RANGE_SELECT::add_keys_and_lengths(String *key_names,
+ String *used_lengths)
+{
+ char buf[64];
+ uint length;
+ KEY *key_info= head->key_info + index;
+ key_names->append(key_info->name);
+ length= longlong2str(max_used_key_length, buf, 10) - buf;
+ used_lengths->append(buf, length);
+}
+
+void QUICK_INDEX_MERGE_SELECT::add_keys_and_lengths(String *key_names,
+ String *used_lengths)
+{
+ char buf[64];
+ uint length;
+ bool first= TRUE;
+ QUICK_RANGE_SELECT *quick;
+
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ while ((quick= it++))
+ {
+ if (first)
+ first= FALSE;
+ else
+ {
+ key_names->append(',');
+ used_lengths->append(',');
+ }
+
+ KEY *key_info= head->key_info + quick->index;
+ key_names->append(key_info->name);
+ length= longlong2str(quick->max_used_key_length, buf, 10) - buf;
+ used_lengths->append(buf, length);
+ }
+ if (pk_quick_select)
+ {
+ KEY *key_info= head->key_info + pk_quick_select->index;
+ key_names->append(',');
+ key_names->append(key_info->name);
+ length= longlong2str(pk_quick_select->max_used_key_length, buf, 10) - buf;
+ used_lengths->append(',');
+ used_lengths->append(buf, length);
+ }
+}
+
+void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names,
+ String *used_lengths)
+{
+ char buf[64];
+ uint length;
+ bool first= TRUE;
+ QUICK_RANGE_SELECT *quick;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ while ((quick= it++))
+ {
+ KEY *key_info= head->key_info + quick->index;
+ if (first)
+ first= FALSE;
+ else
+ {
+ key_names->append(',');
+ used_lengths->append(',');
+ }
+ key_names->append(key_info->name);
+ length= longlong2str(quick->max_used_key_length, buf, 10) - buf;
+ used_lengths->append(buf, length);
+ }
+
+ if (cpk_quick)
+ {
+ KEY *key_info= head->key_info + cpk_quick->index;
+ key_names->append(',');
+ key_names->append(key_info->name);
+ length= longlong2str(cpk_quick->max_used_key_length, buf, 10) - buf;
+ used_lengths->append(',');
+ used_lengths->append(buf, length);
+ }
+}
+
+void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names,
+ String *used_lengths)
+{
+ bool first= TRUE;
+ QUICK_SELECT_I *quick;
+ List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
+ while ((quick= it++))
+ {
+ if (first)
+ first= FALSE;
+ else
+ {
+ used_lengths->append(',');
+ key_names->append(',');
+ }
+ quick->add_keys_and_lengths(key_names, used_lengths);
+ }
+}
+
+#ifndef DBUG_OFF
+
+static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,
+ const char *msg)
+{
+ SEL_ARG **key,**end;
+ int idx;
+ char buff[1024];
+ DBUG_ENTER("print_sel_tree");
+ if (! _db_on_)
+ DBUG_VOID_RETURN;
+
+ String tmp(buff,sizeof(buff),&my_charset_bin);
+ tmp.length(0);
+ for (idx= 0,key=tree->keys, end=key+param->keys ;
+ key != end ;
+ key++,idx++)
+ {
+ if (tree_map->is_set(idx))
+ {
+ uint keynr= param->real_keynr[idx];
+ if (tmp.length())
+ tmp.append(',');
+ tmp.append(param->table->key_info[keynr].name);
+ }
+ }
+ if (!tmp.length())
+ tmp.append("(empty)");
+
+ DBUG_PRINT("info", ("SEL_TREE %p (%s) scans:%s", tree, msg, tmp.ptr()));
+
+ DBUG_VOID_RETURN;
+}
+
+static void print_ror_scans_arr(TABLE *table, const char *msg,
+ struct st_ror_scan_info **start,
+ struct st_ror_scan_info **end)
+{
+ DBUG_ENTER("print_ror_scans");
+ if (! _db_on_)
+ DBUG_VOID_RETURN;
+
+ char buff[1024];
+ String tmp(buff,sizeof(buff),&my_charset_bin);
+ tmp.length(0);
+ for(;start != end; start++)
+ {
+ if (tmp.length())
+ tmp.append(',');
+ tmp.append(table->key_info[(*start)->keynr].name);
+ }
+ if (!tmp.length())
+ tmp.append("(empty)");
+ DBUG_PRINT("info", ("ROR key scans (%s): %s", msg, tmp.ptr()));
+ DBUG_VOID_RETURN;
+}
+
/*****************************************************************************
** Print a quick range for debugging
** TODO:
@@ -2898,8 +6213,6 @@ bool QUICK_SELECT_DESC::test_if_null_range(QUICK_RANGE *range_arg,
** of locking the DEBUG stream !
*****************************************************************************/
-#ifndef DBUG_OFF
-
static void
print_key(KEY_PART *key_part,const char *key,uint used_length)
{
@@ -2932,48 +6245,122 @@ print_key(KEY_PART *key_part,const char *key,uint used_length)
}
-static void print_quick(QUICK_SELECT *quick,const key_map* needed_reg)
+static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg)
{
- QUICK_RANGE *range;
char buf[MAX_KEY/8+1];
DBUG_ENTER("print_param");
if (! _db_on_ || !quick)
DBUG_VOID_RETURN;
+ DBUG_LOCK_FILE;
+
+ quick->dbug_dump(0, TRUE);
+ fprintf(DBUG_FILE,"other_keys: 0x%s:\n", needed_reg->print(buf));
+
+ DBUG_UNLOCK_FILE;
+ DBUG_VOID_RETURN;
+}
- List_iterator<QUICK_RANGE> li(quick->ranges);
+
+static void print_rowid(byte* val, int len)
+{
+ byte *pb;
DBUG_LOCK_FILE;
- fprintf(DBUG_FILE,"Used quick_range on key: %d (other_keys: 0x%s):\n",
- quick->index, needed_reg->print(buf));
- while ((range=li++))
+ fputc('\"', DBUG_FILE);
+ for (pb= val; pb!= val + len; ++pb)
+ fprintf(DBUG_FILE, "%c", *pb);
+ fprintf(DBUG_FILE, "\", hex: ");
+
+ for (pb= val; pb!= val + len; ++pb)
+ fprintf(DBUG_FILE, "%x ", *pb);
+ fputc('\n', DBUG_FILE);
+ DBUG_UNLOCK_FILE;
+}
+
+void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose)
+{
+ fprintf(DBUG_FILE, "%*squick range select, key %s, length: %d\n",
+ indent, "", head->key_info[index].name, max_used_key_length);
+
+ if (verbose)
{
- if (!(range->flag & NO_MIN_RANGE))
+ QUICK_RANGE *range;
+ QUICK_RANGE **pr= (QUICK_RANGE**)ranges.buffer;
+ QUICK_RANGE **last_range= pr + ranges.elements;
+ for (; pr!=last_range; ++pr)
{
- print_key(quick->key_parts,range->min_key,range->min_length);
- if (range->flag & NEAR_MIN)
- fputs(" < ",DBUG_FILE);
- else
- fputs(" <= ",DBUG_FILE);
- }
- fputs("X",DBUG_FILE);
+ fprintf(DBUG_FILE, "%*s", indent + 2, "");
+ range= *pr;
+ if (!(range->flag & NO_MIN_RANGE))
+ {
+ print_key(key_parts,range->min_key,range->min_length);
+ if (range->flag & NEAR_MIN)
+ fputs(" < ",DBUG_FILE);
+ else
+ fputs(" <= ",DBUG_FILE);
+ }
+ fputs("X",DBUG_FILE);
- if (!(range->flag & NO_MAX_RANGE))
- {
- if (range->flag & NEAR_MAX)
- fputs(" < ",DBUG_FILE);
- else
- fputs(" <= ",DBUG_FILE);
- print_key(quick->key_parts,range->max_key,range->max_length);
+ if (!(range->flag & NO_MAX_RANGE))
+ {
+ if (range->flag & NEAR_MAX)
+ fputs(" < ",DBUG_FILE);
+ else
+ fputs(" <= ",DBUG_FILE);
+ print_key(key_parts,range->max_key,range->max_length);
+ }
+ fputs("\n",DBUG_FILE);
}
- fputs("\n",DBUG_FILE);
}
- DBUG_UNLOCK_FILE;
- DBUG_VOID_RETURN;
+}
+
+void QUICK_INDEX_MERGE_SELECT::dbug_dump(int indent, bool verbose)
+{
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ QUICK_RANGE_SELECT *quick;
+ fprintf(DBUG_FILE, "%*squick index_merge select\n", indent, "");
+ fprintf(DBUG_FILE, "%*smerged scans {\n", indent, "");
+ while ((quick= it++))
+ quick->dbug_dump(indent+2, verbose);
+ if (pk_quick_select)
+ {
+ fprintf(DBUG_FILE, "%*sclustered PK quick:\n", indent, "");
+ pk_quick_select->dbug_dump(indent+2, verbose);
+ }
+ fprintf(DBUG_FILE, "%*s}\n", indent, "");
+}
+
+void QUICK_ROR_INTERSECT_SELECT::dbug_dump(int indent, bool verbose)
+{
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ QUICK_RANGE_SELECT *quick;
+ fprintf(DBUG_FILE, "%*squick ROR-intersect select, %scovering\n",
+ indent, "", need_to_fetch_row? "":"non-");
+ fprintf(DBUG_FILE, "%*smerged scans {\n", indent, "");
+ while ((quick= it++))
+ quick->dbug_dump(indent+2, verbose);
+ if (cpk_quick)
+ {
+ fprintf(DBUG_FILE, "%*sclustered PK quick:\n", indent, "");
+ cpk_quick->dbug_dump(indent+2, verbose);
+ }
+ fprintf(DBUG_FILE, "%*s}\n", indent, "");
+}
+
+void QUICK_ROR_UNION_SELECT::dbug_dump(int indent, bool verbose)
+{
+ List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
+ QUICK_SELECT_I *quick;
+ fprintf(DBUG_FILE, "%*squick ROR-union select\n", indent, "");
+ fprintf(DBUG_FILE, "%*smerged scans {\n", indent, "");
+ while ((quick= it++))
+ quick->dbug_dump(indent+2, verbose);
+ fprintf(DBUG_FILE, "%*s}\n", indent, "");
}
#endif
/*****************************************************************************
-** Instansiate templates
+** Instantiate templates
*****************************************************************************/
#ifdef __GNUC__
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 9b2e9e45bac..974ed409a87 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -66,61 +66,471 @@ class QUICK_RANGE :public Sql_alloc {
};
-class QUICK_SELECT {
+/*
+ Quick select interface.
+ This class is a parent for all QUICK_*_SELECT and FT_SELECT classes.
+*/
+
+class QUICK_SELECT_I
+{
+public:
+ bool sorted;
+ ha_rows records; /* estimate of # of records to be retrieved */
+ double read_time; /* time to perform this retrieval */
+ TABLE *head;
+ /*
+ Index this quick select uses, or MAX_KEY for quick selects
+ that use several indexes
+ */
+ uint index;
+
+ /*
+ Total length of first used_key_parts parts of the key.
+ Applicable if index!= MAX_KEY.
+ */
+ uint max_used_key_length;
+
+ /*
+ Max. number of (first) key parts this quick select uses for retrieval.
+ eg. for "(key1p1=c1 AND key1p2=c2) OR key1p1=c2" used_key_parts == 2.
+ Applicable if index!= MAX_KEY.
+ */
+ uint used_key_parts;
+
+ QUICK_SELECT_I();
+ virtual ~QUICK_SELECT_I(){};
+
+ /*
+ Do post-constructor initialization.
+ SYNOPSIS
+ init()
+
+ init() performs initializations that should have been in constructor if
+ it was possible to return errors from constructors. The join optimizer may
+ create and then delete quick selects without retrieving any rows so init()
+ must not contain any IO or CPU intensive code.
+
+ If init() call fails the only valid action is to delete this quick select,
+ reset() and get_next() must not be called.
+
+ RETURN
+ 0 OK
+ other Error code
+ */
+ virtual int init() = 0;
+
+ /*
+ Initialize quick select for row retrieval.
+ SYNOPSIS
+ reset()
+
+ reset() should be called when it is certain that row retrieval will be
+ necessary. This call may do heavyweight initialization like buffering first
+ N records etc. If reset() call fails get_next() must not be called.
+
+ RETURN
+ 0 OK
+ other Error code
+ */
+ virtual int reset(void) = 0;
+
+ /* Range end should be called when we have looped over the whole index */
+ virtual void range_end() {}
+ virtual int get_next() = 0; /* get next record to retrieve */
+ virtual bool reverse_sorted() = 0;
+ virtual bool unique_key_range() { return false; }
+
+ enum {
+ QS_TYPE_RANGE = 0,
+ QS_TYPE_INDEX_MERGE = 1,
+ QS_TYPE_RANGE_DESC = 2,
+ QS_TYPE_FULLTEXT = 3,
+ QS_TYPE_ROR_INTERSECT = 4,
+ QS_TYPE_ROR_UNION = 5
+ };
+
+ /* Get type of this quick select - one of the QS_TYPE_* values */
+ virtual int get_type() = 0;
+
+ /*
+ Initialize this quick select as a merged scan inside a ROR-union or a ROR-
+ intersection scan. The caller must not additionally call init() if this
+ function is called.
+ SYNOPSIS
+ init_ror_merged_scan()
+ reuse_handler If true, the quick select may use table->handler, otherwise
+ it must create and use a separate handler object.
+ RETURN
+ 0 Ok
+ other Error
+ */
+ virtual int init_ror_merged_scan(bool reuse_handler)
+ { DBUG_ASSERT(0); return 1; }
+
+ /*
+ Save ROWID of last retrieved row in file->ref. This used in ROR-merging.
+ */
+ virtual void save_last_pos(){};
+
+ /*
+ Append comma-separated list of keys this quick select uses to key_names;
+ append comma-separated list of corresponding used lengths to used_lengths.
+ This is used by select_describe.
+ */
+ virtual void add_keys_and_lengths(String *key_names,
+ String *used_lengths)=0;
+
+ /*
+ Append text representation of quick select structure (what and how is
+ merged) to str. The result is added to "Extra" field in EXPLAIN output.
+ This function is implemented only by quick selects that merge other quick
+ selects output and/or can produce output suitable for merging.
+ */
+ virtual void add_info_string(String *str) {};
+ /*
+ Return 1 if any index used by this quick select
+ a) uses field that is listed in passed field list or
+ b) is automatically updated (like a timestamp)
+ */
+ virtual bool check_if_keys_used(List<Item> *fields);
+
+ /*
+ rowid of last row retrieved by this quick select. This is used only when
+ doing ROR-index_merge selects
+ */
+ byte *last_rowid;
+
+ /*
+ Table record buffer used by this quick select.
+ */
+ byte *record;
+#ifndef DBUG_OFF
+ /*
+ Print quick select information to DBUG_FILE. Caller is responsible
+ for locking DBUG_FILE before this call and unlocking it afterwards.
+ */
+ virtual void dbug_dump(int indent, bool verbose)= 0;
+#endif
+};
+
+
+struct st_qsel_param;
+class SEL_ARG;
+
+/*
+ Quick select that does a range scan on a single key. The records are
+ returned in key order.
+*/
+class QUICK_RANGE_SELECT : public QUICK_SELECT_I
+{
+protected:
+ bool next,dont_free;
public:
- bool next,dont_free,sorted;
int error;
- uint index, max_used_key_length, used_key_parts;
- TABLE *head;
+protected:
handler *file;
- byte *record;
- List<QUICK_RANGE> ranges;
- List_iterator<QUICK_RANGE> it;
- QUICK_RANGE *range;
- MEM_ROOT alloc;
+ /*
+ If true, this quick select has its "own" handler object which should be
+ closed no later then this quick select is deleted.
+ */
+ bool free_file;
+protected:
+ friend class TRP_ROR_INTERSECT;
+ friend
+ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
+ struct st_table_ref *ref);
+ friend bool get_quick_keys(struct st_qsel_param *param,
+ QUICK_RANGE_SELECT *quick,KEY_PART *key,
+ SEL_ARG *key_tree,
+ char *min_key, uint min_key_flag,
+ char *max_key, uint max_key_flag);
+ friend QUICK_RANGE_SELECT *get_quick_select(struct st_qsel_param*,uint idx,
+ SEL_ARG *key_tree,
+ MEM_ROOT *alloc);
+ friend class QUICK_SELECT_DESC;
+ friend class QUICK_INDEX_MERGE_SELECT;
+ friend class QUICK_ROR_INTERSECT_SELECT;
+
+ DYNAMIC_ARRAY ranges; /* ordered array of range ptrs */
+ QUICK_RANGE **cur_range; /* current element in ranges */
+
+ QUICK_RANGE *range;
KEY_PART *key_parts;
KEY_PART_INFO *key_part_info;
- ha_rows records;
- double read_time;
+ int cmp_next(QUICK_RANGE *range);
+ int cmp_prev(QUICK_RANGE *range);
+ bool row_in_ranges();
+public:
+ MEM_ROOT alloc;
+
+ QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0,
+ MEM_ROOT *parent_alloc=NULL);
+ ~QUICK_RANGE_SELECT();
- QUICK_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0);
- virtual ~QUICK_SELECT();
- void reset(void) { next=0; it.rewind(); }
- int init()
+ int reset(void)
{
- key_part_info= head->key_info[index].key_part;
- return error=file->ha_index_init(index);
+ next=0;
+ range= NULL;
+ cur_range= NULL;
+ return 0;
}
- virtual int get_next();
- virtual bool reverse_sorted() { return 0; }
+ int init();
+ int get_next();
+ void range_end();
+
+ bool reverse_sorted() { return 0; }
bool unique_key_range();
+ int init_ror_merged_scan(bool reuse_handler);
+ void save_last_pos()
+ {
+ file->position(record);
+ };
+ int get_type() { return QS_TYPE_RANGE; }
+ void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_info_string(String *str);
+#ifndef DBUG_OFF
+ void dbug_dump(int indent, bool verbose);
+#endif
};
-class QUICK_SELECT_GEOM: public QUICK_SELECT
+class QUICK_RANGE_SELECT_GEOM: public QUICK_RANGE_SELECT
{
public:
- QUICK_SELECT_GEOM(THD *thd, TABLE *table, uint index_arg, bool no_alloc)
- :QUICK_SELECT(thd, table, index_arg, no_alloc)
+ QUICK_RANGE_SELECT_GEOM(THD *thd, TABLE *table, uint index_arg,
+ bool no_alloc, MEM_ROOT *parent_alloc)
+ :QUICK_RANGE_SELECT(thd, table, index_arg, no_alloc, parent_alloc)
{};
virtual int get_next();
};
-class QUICK_SELECT_DESC: public QUICK_SELECT
+/*
+ QUICK_INDEX_MERGE_SELECT - index_merge access method quick select.
+
+ QUICK_INDEX_MERGE_SELECT uses
+ * QUICK_RANGE_SELECTs to get rows
+ * Unique class to remove duplicate rows
+
+ INDEX MERGE OPTIMIZER
+ Current implementation doesn't detect all cases where index_merge could
+ be used, in particular:
+ * index_merge will never be used if range scan is possible (even if
+ range scan is more expensive)
+
+ * index_merge+'using index' is not supported (this the consequence of
+ the above restriction)
+
+ * If WHERE part contains complex nested AND and OR conditions, some ways
+ to retrieve rows using index_merge will not be considered. The choice
+ of read plan may depend on the order of conjuncts/disjuncts in WHERE
+ part of the query, see comments near imerge_list_or_list and
+ SEL_IMERGE::or_sel_tree_with_checks functions for details.
+
+ * There is no "index_merge_ref" method (but index_merge on non-first
+ table in join is possible with 'range checked for each record').
+
+ See comments around SEL_IMERGE class and test_quick_select for more
+ details.
+
+ ROW RETRIEVAL ALGORITHM
+
+ index_merge uses Unique class for duplicates removal. index_merge takes
+ advantage of Clustered Primary Key (CPK) if the table has one.
+ The index_merge algorithm consists of two phases:
+
+ Phase 1 (implemented in QUICK_INDEX_MERGE_SELECT::prepare_unique):
+ prepare()
+ {
+ activate 'index only';
+ while(retrieve next row for non-CPK scan)
+ {
+ if (there is a CPK scan and row will be retrieved by it)
+ skip this row;
+ else
+ put its rowid into Unique;
+ }
+ deactivate 'index only';
+ }
+
+ Phase 2 (implemented as sequence of QUICK_INDEX_MERGE_SELECT::get_next
+ calls):
+
+ fetch()
+ {
+ retrieve all rows from row pointers stored in Unique;
+ free Unique;
+ retrieve all rows for CPK scan;
+ }
+*/
+
+class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I
+{
+public:
+ QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table);
+ ~QUICK_INDEX_MERGE_SELECT();
+
+ int init();
+ int reset(void);
+ int get_next();
+ bool reverse_sorted() { return false; }
+ bool unique_key_range() { return false; }
+ int get_type() { return QS_TYPE_INDEX_MERGE; }
+ void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_info_string(String *str);
+ bool check_if_keys_used(List<Item> *fields);
+#ifndef DBUG_OFF
+ void dbug_dump(int indent, bool verbose);
+#endif
+
+ bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
+
+ /* range quick selects this index_merge read consists of */
+ List<QUICK_RANGE_SELECT> quick_selects;
+
+ /* quick select which is currently used for rows retrieval */
+ List_iterator_fast<QUICK_RANGE_SELECT> cur_quick_it;
+ QUICK_RANGE_SELECT* cur_quick_select;
+
+ /* quick select that uses clustered primary key (NULL if none) */
+ QUICK_RANGE_SELECT* pk_quick_select;
+
+ /* true if this select is currently doing a clustered PK scan */
+ bool doing_pk_scan;
+
+ Unique *unique;
+ MEM_ROOT alloc;
+
+ THD *thd;
+ int prepare_unique();
+
+ /* used to get rows collected in Unique */
+ READ_RECORD read_record;
+};
+
+
+/*
+ Rowid-Ordered Retrieval (ROR) index intersection quick select.
+ This quick select produces intersection of row sequences returned
+ by several QUICK_RANGE_SELECTs it "merges".
+
+ All merged QUICK_RANGE_SELECTs must return rowids in rowid order.
+ QUICK_ROR_INTERSECT_SELECT will return rows in rowid order, too.
+
+ All merged quick selects retrieve {rowid, covered_fields} tuples (not full
+ table records).
+ QUICK_ROR_INTERSECT_SELECT retrieves full records if it is not being used
+ by QUICK_ROR_INTERSECT_SELECT and all merged quick selects together don't
+ cover needed all fields.
+
+ If one of the merged quick selects is a Clustered PK range scan, it is
+ used only to filter rowid sequence produced by other merged quick selects.
+*/
+
+class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I
{
public:
- QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts);
+ QUICK_ROR_INTERSECT_SELECT(THD *thd, TABLE *table,
+ bool retrieve_full_rows,
+ MEM_ROOT *parent_alloc);
+ ~QUICK_ROR_INTERSECT_SELECT();
+
+ int init();
+ int reset(void);
+ int get_next();
+ bool reverse_sorted() { return false; }
+ bool unique_key_range() { return false; }
+ int get_type() { return QS_TYPE_ROR_INTERSECT; }
+ void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_info_string(String *str);
+ bool check_if_keys_used(List<Item> *fields);
+#ifndef DBUG_OFF
+ void dbug_dump(int indent, bool verbose);
+#endif
+ int init_ror_merged_scan(bool reuse_handler);
+ bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
+
+ /*
+ Range quick selects this intersection consists of, not including
+ cpk_quick.
+ */
+ List<QUICK_RANGE_SELECT> quick_selects;
+
+ /*
+ Merged quick select that uses Clustered PK, if there is one. This quick
+ select is not used for row retrieval, it is used for row retrieval.
+ */
+ QUICK_RANGE_SELECT *cpk_quick;
+
+ MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */
+ THD *thd; /* current thread */
+ bool need_to_fetch_row; /* if true, do retrieve full table records. */
+};
+
+
+/*
+ Rowid-Ordered Retrieval index union select.
+ This quick select produces union of row sequences returned by several
+ quick select it "merges".
+
+ All merged quick selects must return rowids in rowid order.
+ QUICK_ROR_UNION_SELECT will return rows in rowid order, too.
+
+ All merged quick selects are set not to retrieve full table records.
+ ROR-union quick select always retrieves full records.
+
+*/
+
+class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I
+{
+public:
+ QUICK_ROR_UNION_SELECT(THD *thd, TABLE *table);
+ ~QUICK_ROR_UNION_SELECT();
+
+ int init();
+ int reset(void);
+ int get_next();
+ bool reverse_sorted() { return false; }
+ bool unique_key_range() { return false; }
+ int get_type() { return QS_TYPE_ROR_UNION; }
+ void add_keys_and_lengths(String *key_names, String *used_lengths);
+ void add_info_string(String *str);
+ bool check_if_keys_used(List<Item> *fields);
+#ifndef DBUG_OFF
+ void dbug_dump(int indent, bool verbose);
+#endif
+
+ bool push_quick_back(QUICK_SELECT_I *quick_sel_range);
+
+ List<QUICK_SELECT_I> quick_selects; /* Merged quick selects */
+
+ QUEUE queue; /* Priority queue for merge operation */
+ MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */
+
+ THD *thd; /* current thread */
+ byte *cur_rowid; /* buffer used in get_next() */
+ byte *prev_rowid; /* rowid of last row returned by get_next() */
+ bool have_prev_rowid; /* true if prev_rowid has valid data */
+ uint rowid_length; /* table rowid length */
+private:
+ static int queue_cmp(void *arg, byte *val1, byte *val2);
+};
+
+
+class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
+{
+public:
+ QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts);
int get_next();
bool reverse_sorted() { return 1; }
+ int get_type() { return QS_TYPE_RANGE_DESC; }
private:
- int cmp_prev(QUICK_RANGE *range);
bool range_reads_after_key(QUICK_RANGE *range);
#ifdef NOT_USED
bool test_if_null_range(QUICK_RANGE *range, uint used_key_parts);
#endif
- void reset(void) { next=0; rev_it.rewind(); }
+ int reset(void) { next=0; rev_it.rewind(); return 0; }
List<QUICK_RANGE> rev_ranges;
List_iterator<QUICK_RANGE> rev_it;
};
@@ -128,7 +538,7 @@ private:
class SQL_SELECT :public Sql_alloc {
public:
- QUICK_SELECT *quick; // If quick-select used
+ QUICK_SELECT_I *quick; // If quick-select used
COND *cond; // where condition
TABLE *head;
IO_CACHE file; // Positions to used records
@@ -150,17 +560,16 @@ class SQL_SELECT :public Sql_alloc {
};
-class FT_SELECT: public QUICK_SELECT {
+class FT_SELECT: public QUICK_RANGE_SELECT {
public:
- FT_SELECT(THD *thd, TABLE *table, uint key):
- QUICK_SELECT (thd, table, key, 1) { init(); }
-
- int init() { return error= file->ft_init(); }
- int get_next() { return error= file->ft_read(record); }
+ FT_SELECT(THD *thd, TABLE *table, uint key) :
+ QUICK_RANGE_SELECT (thd, table, key, 1) { init(); }
+ ~FT_SELECT() { file->ft_end(); }
+ int init() { return error=file->ft_init(); }
+ int get_next() { return error=file->ft_read(record); }
+ int get_type() { return QS_TYPE_FULLTEXT; }
};
-
-QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
- struct st_table_ref *ref);
-
+QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
+ struct st_table_ref *ref);
#endif
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index f4c39462d0c..314decb7041 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -89,7 +89,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
where_tables= conds->used_tables();
/* Don't replace expression on a table that is part of an outer join */
- for (TABLE_LIST *tl=tables; tl ; tl= tl->next)
+ for (TABLE_LIST *tl= tables; tl; tl= tl->next_local)
{
if (tl->on_expr)
{
@@ -128,7 +128,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds)
{
longlong count= 1;
TABLE_LIST *table;
- for (table=tables ; table ; table=table->next)
+ for (table= tables; table; table= table->next_local)
{
if (outer_tables || (table->table->file->table_flags() &
HA_NOT_EXACT_COUNT))
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
new file mode 100644
index 00000000000..729f49dcbbe
--- /dev/null
+++ b/sql/parse_file.cc
@@ -0,0 +1,791 @@
+/* Copyright (C) 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+// Text .frm files management routines
+
+#include "mysql_priv.h"
+#include <errno.h>
+#include <m_ctype.h>
+#include <my_sys.h>
+#include <my_dir.h>
+
+
+/*
+ write string with escaping
+
+ SYNOPSIS
+ write_escaped_string()
+ file - IO_CACHE for record
+ val_s - string for writing
+
+ RETURN
+ FALSE - OK
+ TRUE - error
+*/
+
+static my_bool
+write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
+{
+ char *eos= val_s->str + val_s->length;
+ char *ptr= val_s->str;
+
+ for (; ptr < eos; ptr++)
+ {
+ /*
+ Should be in sync with read_escaped_string() and
+ parse_quated_escaped_string()
+ */
+ switch(*ptr) {
+ case '\\': // escape character
+ if (my_b_append(file, (const byte *)"\\\\", 2))
+ return TRUE;
+ break;
+ case '\n': // parameter value delimiter
+ if (my_b_append(file, (const byte *)"\\n", 2))
+ return TRUE;
+ break;
+ case '\0': // problem for some string processing utilites
+ if (my_b_append(file, (const byte *)"\\0", 2))
+ return TRUE;
+ break;
+ case 26: // problem for windows utilites (Ctrl-Z)
+ if (my_b_append(file, (const byte *)"\\z", 2))
+ return TRUE;
+ break;
+ case '\'': // list of string delimiter
+ if (my_b_append(file, (const byte *)"\\\'", 2))
+ return TRUE;
+ break;
+ default:
+ if (my_b_append(file, (const byte *)ptr, 1))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ write parameter value to IO_CACHE
+
+ SYNOPSIS
+ write_parameter()
+ file pointer to IO_CACHE structure for writing
+ base pointer to data structure
+ parameter pointer to parameter descriptor
+ old_version for returning back old version number value
+
+ RETURN
+ FALSE - OK
+ TRUE - error
+*/
+
+static my_bool
+write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
+ ulonglong *old_version)
+{
+ char num_buf[20]; // buffer for numeric operations
+ // string for numeric operations
+ String num(num_buf, sizeof(num_buf), &my_charset_bin);
+ DBUG_ENTER("write_parameter");
+
+ switch (parameter->type) {
+ case FILE_OPTIONS_STRING:
+ {
+ LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
+ if (my_b_append(file, (const byte *)val_s->str, val_s->length))
+ DBUG_RETURN(TRUE);
+ break;
+ }
+ case FILE_OPTIONS_ESTRING:
+ {
+ if (write_escaped_string(file, (LEX_STRING *)(base + parameter->offset)))
+ DBUG_RETURN(TRUE);
+ break;
+ }
+ case FILE_OPTIONS_ULONGLONG:
+ {
+ num.set(*((ulonglong *)(base + parameter->offset)), &my_charset_bin);
+ if (my_b_append(file, (const byte *)num.ptr(), num.length()))
+ DBUG_RETURN(TRUE);
+ break;
+ }
+ case FILE_OPTIONS_REV:
+ {
+ ulonglong *val_i= (ulonglong *)(base + parameter->offset);
+ *old_version= (*val_i)++;
+ num.set(*val_i, &my_charset_bin);
+ if (my_b_append(file, (const byte *)num.ptr(), num.length()))
+ DBUG_RETURN(TRUE);
+ break;
+ }
+ case FILE_OPTIONS_TIMESTAMP:
+ {
+ /* string have to be allocated already */
+ LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
+ time_t tm= time(NULL);
+
+ get_date(val_s->str, GETDATE_DATE_TIME|GETDATE_GMT|GETDATE_FIXEDLENGTH,
+ tm);
+ val_s->length= PARSE_FILE_TIMESTAMPLENGTH;
+ if (my_b_append(file, (const byte *)val_s->str,
+ PARSE_FILE_TIMESTAMPLENGTH))
+ DBUG_RETURN(TRUE);
+ break;
+ }
+ case FILE_OPTIONS_STRLIST:
+ {
+ List_iterator_fast<LEX_STRING> it(*((List<LEX_STRING>*)
+ (base + parameter->offset)));
+ bool first= 1;
+ LEX_STRING *str;
+ while ((str= it++))
+ {
+ num.set((ulonglong)str->length, &my_charset_bin);
+ // ',' after string to detect list continuation
+ if ((!first && my_b_append(file, (const byte *)" ", 1)) ||
+ my_b_append(file, (const byte *)"\'", 1) ||
+ my_b_append(file, (const byte *)str->str, str->length) ||
+ my_b_append(file, (const byte *)"\'", 1))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ first= 0;
+ }
+ break;
+ }
+ default:
+ DBUG_ASSERT(0); // never should happened
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ write new .frm
+
+ SYNOPSIS
+ sql_create_definition_file()
+ dir directory where put .frm
+ file .frm file name
+ type .frm type string (VIEW, TABLE)
+ base base address for parameter reading (structure like
+ TABLE)
+ parameters parameters description
+ max_versions number of versions to save
+
+ RETURN
+ FALSE - OK
+ TRUE - error
+*/
+
+my_bool
+sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
+ const LEX_STRING *type,
+ gptr base, File_option *parameters,
+ uint max_versions)
+{
+ File handler;
+ IO_CACHE file;
+ char path[FN_REFLEN+1]; // +1 to put temporary file name for sure
+ ulonglong old_version= ULONGLONG_MAX;
+ int path_end;
+ File_option *param;
+ DBUG_ENTER("sql_create_definition_file");
+ DBUG_PRINT("enter", ("Dir: %s, file: %s, base 0x%lx",
+ dir->str, file_name->str, (ulong) base));
+
+ fn_format(path, file_name->str, dir->str, 0, MY_UNPACK_FILENAME);
+ path_end= strlen(path);
+
+ // temporary file name
+ path[path_end]='~';
+ path[path_end+1]= '\0';
+ if ((handler= my_create(path, CREATE_MODE, O_RDWR | O_TRUNC,
+ MYF(MY_WME))) <= 0)
+ {
+ DBUG_RETURN(TRUE);
+ }
+
+ if (init_io_cache(&file, handler, 0, SEQ_READ_APPEND, 0L, 0, MYF(MY_WME)))
+ goto err_w_file;
+
+ // write header (file signature)
+ if (my_b_append(&file, (const byte *)"TYPE=", 5) ||
+ my_b_append(&file, (const byte *)type->str, type->length) ||
+ my_b_append(&file, (const byte *)"\n", 1))
+ goto err_w_file;
+
+ // write parameters to temporary file
+ for (param= parameters; param->name.str; param++)
+ {
+ if (my_b_append(&file, (const byte *)param->name.str,
+ param->name.length) ||
+ my_b_append(&file, (const byte *)"=", 1) ||
+ write_parameter(&file, base, param, &old_version) ||
+ my_b_append(&file, (const byte *)"\n", 1))
+ goto err_w_cache;
+ }
+
+ if (end_io_cache(&file))
+ goto err_w_file;
+
+ if (my_close(handler, MYF(MY_WME)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+
+ // archive copies management
+ path[path_end]='\0';
+ if (!access(path, F_OK))
+ {
+ if (old_version != ULONGLONG_MAX && max_versions != 0)
+ {
+ // save buckup
+ char path_arc[FN_REFLEN];
+ // backup old version
+ char path_to[FN_REFLEN];
+
+ // check archive directory existence
+ fn_format(path_arc, "arc", dir->str, "", MY_UNPACK_FILENAME);
+ if (access(path_arc, F_OK))
+ {
+ if (my_mkdir(path_arc, 0777, MYF(MY_WME)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lu",
+ path_arc, file_name->str, (ulong) old_version);
+ if (my_rename(path, path_to, MYF(MY_WME)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+
+ // remove very old version
+ if (old_version > max_versions)
+ {
+ my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lu",
+ path_arc, file_name->str,
+ (ulong)(old_version - max_versions));
+ if (!access(path_arc, F_OK) && my_delete(path_to, MYF(MY_WME)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ else
+ {
+ if (my_delete(path, MYF(MY_WME))) // no backups
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+
+ {
+ // rename temporary file
+ char path_to[FN_REFLEN];
+ memcpy(path_to, path, path_end+1);
+ path[path_end]='~';
+ if (my_rename(path, path_to, MYF(MY_WME)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+err_w_cache:
+ end_io_cache(&file);
+err_w_file:
+ my_close(handler, MYF(MY_WME));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Prepare frm to parse (read to memory)
+
+ SYNOPSIS
+ sql_parse_prepare()
+ file_name - path & filename to .frm file
+ mem_root - MEM_ROOT for buffer allocation
+ bad_format_errors - send errors on bad content
+
+ RETURN
+ 0 - error
+ parser object
+
+ NOTE
+ returned pointer + 1 will be type of .frm
+*/
+
+File_parser *
+sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
+ bool bad_format_errors)
+{
+ MY_STAT stat_info;
+ uint len;
+ char *end, *sign;
+ File_parser *parser;
+ File file;
+ DBUG_ENTER("sql__parse_prepare");
+
+ if (!my_stat(file_name->str, &stat_info, MYF(MY_WME)))
+ {
+ DBUG_RETURN(0);
+ }
+
+ if (stat_info.st_size > INT_MAX-1)
+ {
+ my_error(ER_FPARSER_TOO_BIG_FILE, MYF(0), file_name->str);
+ DBUG_RETURN(0);
+ }
+
+ if (!(parser= new(mem_root) File_parser))
+ {
+ DBUG_RETURN(0);
+ }
+
+ if (!(parser->buff= alloc_root(mem_root, stat_info.st_size+1)))
+ {
+ DBUG_RETURN(0);
+ }
+
+ if ((file= my_open(file_name->str, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
+ {
+ DBUG_RETURN(0);
+ }
+
+ if ((len= my_read(file, (byte *)parser->buff,
+ stat_info.st_size, MYF(MY_WME))) ==
+ MY_FILE_ERROR)
+ {
+ my_close(file, MYF(MY_WME));
+ DBUG_RETURN(0);
+ }
+
+ if (my_close(file, MYF(MY_WME)))
+ {
+ DBUG_RETURN(0);
+ }
+
+ end= parser->end= parser->buff + len;
+ *end= '\0'; // barriaer for more simple parsing
+
+ // 7 = 5 (TYPE=) + 1 (leter at least of type name) + 1 ('\n')
+ if (len < 7 ||
+ parser->buff[0] != 'T' ||
+ parser->buff[1] != 'Y' ||
+ parser->buff[2] != 'P' ||
+ parser->buff[3] != 'E' ||
+ parser->buff[4] != '=')
+ goto frm_error;
+
+ // skip signature;
+ parser->file_type.str= sign= parser->buff + 5;
+ while (*sign >= 'A' && *sign <= 'Z' && sign < end)
+ sign++;
+ if (*sign != '\n')
+ goto frm_error;
+ parser->file_type.length= sign - parser->file_type.str;
+ // EOS for file signature just for safety
+ *sign= '\0';
+
+ parser->start= sign + 1;
+ parser->content_ok= 1;
+
+ DBUG_RETURN(parser);
+
+frm_error:
+ if (bad_format_errors)
+ {
+ my_error(ER_FPARSER_BAD_HEADER, MYF(0), file_name->str);
+ DBUG_RETURN(0);
+ }
+ else
+ DBUG_RETURN(parser); // upper level have to check parser->ok()
+}
+
+
+/*
+ parse LEX_STRING
+
+ SYNOPSIS
+ parse_string()
+ ptr - pointer on string beginning
+ end - pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ mem_root - MEM_ROOT for parameter allocation
+ str - pointer on string, where results should be stored
+
+ RETURN
+ 0 - error
+ # - pointer on symbol after string
+*/
+
+static char *
+parse_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str)
+{
+ // get string length
+ char *eol= strchr(ptr, '\n');
+
+ if (eol >= end)
+ return 0;
+
+ str->length= eol - ptr;
+
+ if (!(str->str= alloc_root(mem_root, str->length+1)))
+ return 0;
+
+ memcpy(str->str, ptr, str->length);
+ str->str[str->length]= '\0'; // just for safety
+ return eol+1;
+}
+
+
+/*
+ read escaped string from ptr to eol in already allocated str
+
+ SYNOPSIS
+ parse_escaped_string()
+ ptr - pointer on string beginning
+ eol - pointer on character after end of string
+ str - target string
+
+ RETURN
+ FALSE - OK
+ TRUE - error
+*/
+
+my_bool
+read_escaped_string(char *ptr, char *eol, LEX_STRING *str)
+{
+ char *write_pos= str->str;
+
+ for(; ptr < eol; ptr++, write_pos++)
+ {
+ char c= *ptr;
+ if (c == '\\')
+ {
+ ptr++;
+ if (ptr >= eol)
+ return TRUE;
+ /*
+ Should be in sync with write_escaped_string() and
+ parse_quated_escaped_string()
+ */
+ switch(*ptr) {
+ case '\\':
+ *write_pos= '\\';
+ break;
+ case 'n':
+ *write_pos= '\n';
+ break;
+ case '0':
+ *write_pos= '\0';
+ break;
+ case 'z':
+ *write_pos= 26;
+ break;
+ case '\'':
+ *write_pos= '\'';
+ default:
+ return TRUE;
+ }
+ }
+ else
+ *write_pos= c;
+ }
+ str->str[str->length= write_pos-str->str]= '\0'; // just for safety
+ return FALSE;
+}
+
+
+/*
+ parse \n delimited escaped string
+
+ SYNOPSIS
+ parse_escaped_string()
+ ptr - pointer on string beginning
+ end - pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ mem_root - MEM_ROOT for parameter allocation
+ str - pointer on string, where results should be stored
+
+ RETURN
+ 0 - error
+ # - pointer on symbol after string
+*/
+
+static char *
+parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str)
+{
+ char *eol= strchr(ptr, '\n');
+
+ if (eol == 0 || eol >= end ||
+ !(str->str= alloc_root(mem_root, (eol - ptr) + 1)) ||
+ read_escaped_string(ptr, eol, str))
+ return 0;
+
+ return eol+1;
+}
+
+
+/*
+ parse '' delimited escaped string
+
+ SYNOPSIS
+ parse_escaped_string()
+ ptr - pointer on string beginning
+ end - pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ mem_root - MEM_ROOT for parameter allocation
+ str - pointer on string, where results should be stored
+
+ RETURN
+ 0 - error
+ # - pointer on symbol after string
+*/
+
+static char *
+parse_quated_escaped_string(char *ptr, char *end,
+ MEM_ROOT *mem_root, LEX_STRING *str)
+{
+ char *eol;
+ uint result_len= 0;
+ bool escaped= 0;
+
+ // starting '
+ if (*(ptr++) != '\'')
+ return 0;
+
+ // find ending '
+ for (eol= ptr; (*eol != '\'' || escaped) && eol < end; eol++)
+ {
+ if (!(escaped= (*eol == '\\' && !escaped)))
+ result_len++;
+ }
+
+ // process string
+ if (eol >= end ||
+ !(str->str= alloc_root(mem_root, result_len + 1)) ||
+ read_escaped_string(ptr, eol, str))
+ return 0;
+
+ return eol+1;
+}
+
+
+/*
+ parse parameters
+
+ SYNOPSIS
+ File_parser::parse()
+ base base address for parameter writing (structure like
+ TABLE)
+ mem_root MEM_ROOT for parameters allocation
+ parameters parameters description
+ required number of required parameters in above list
+
+ RETURN
+ FALSE - OK
+ TRUE - error
+*/
+
+my_bool
+File_parser::parse(gptr base, MEM_ROOT *mem_root,
+ struct File_option *parameters, uint required)
+{
+ uint first_param= 0, found= 0;
+ register char *ptr= start;
+ char *eol;
+ LEX_STRING *str;
+ MEM_ROOT *sql_mem;
+ List<LEX_STRING> *list;
+ bool change_mem;
+ DBUG_ENTER("File_parser::parse");
+
+ while (ptr < end && found < required)
+ {
+ char *line= ptr;
+ if (*ptr == '#')
+ {
+ // it is comment
+ if (!(ptr= strchr(ptr, '\n')))
+ {
+ my_error(ER_FPARSER_EOF_IN_COMMENT, MYF(0), line);
+ DBUG_RETURN(TRUE);
+ }
+ ptr++;
+ }
+ else
+ {
+ File_option *parameter= parameters+first_param,
+ *parameters_end= parameters+required;
+ int len= 0;
+ for(; parameter < parameters_end; parameter++)
+ {
+ len= parameter->name.length;
+ // check length
+ if (len < (end-ptr) && ptr[len] != '=')
+ continue;
+ // check keyword
+ if (memcmp(parameter->name.str, ptr, len) == 0)
+ break;
+ }
+
+ if (parameter < parameters_end)
+ {
+ found++;
+ /*
+ if we found first parameter, start search from next parameter
+ next time.
+ (this small optimisation should work, because they should be
+ written in same order)
+ */
+ if (parameter == parameters+first_param)
+ first_param++;
+
+ // get value
+ ptr+= (len+1);
+ switch (parameter->type) {
+ case FILE_OPTIONS_STRING:
+ {
+ if (!(ptr= parse_string(ptr, end, mem_root,
+ (LEX_STRING *)(base +
+ parameter->offset))))
+ {
+ my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
+ parameter->name.str, line);
+ DBUG_RETURN(TRUE);
+ }
+ break;
+ }
+ case FILE_OPTIONS_ESTRING:
+ {
+ if (!(ptr= parse_escaped_string(ptr, end, mem_root,
+ (LEX_STRING *)
+ (base + parameter->offset))))
+ {
+ my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
+ parameter->name.str, line);
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(TRUE);
+ }
+ break;
+ }
+ case FILE_OPTIONS_ULONGLONG:
+ case FILE_OPTIONS_REV:
+ if (!(eol= strchr(ptr, '\n')))
+ {
+ my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
+ parameter->name.str, line);
+ DBUG_RETURN(TRUE);
+ }
+ {
+ int not_used;
+ *((ulonglong*)(base + parameter->offset))=
+ my_strtoll10(ptr, 0, &not_used);
+ }
+ ptr= eol+1;
+ break;
+ case FILE_OPTIONS_TIMESTAMP:
+ {
+ /* string have to be allocated already */
+ LEX_STRING *val= (LEX_STRING *)(base + parameter->offset);
+ /* yyyy-mm-dd HH:MM:SS = 19(PARSE_FILE_TIMESTAMPLENGTH) characters */
+ if (ptr[PARSE_FILE_TIMESTAMPLENGTH] != '\n')
+ {
+ my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
+ parameter->name.str, line);
+ DBUG_RETURN(TRUE);
+ }
+ memcpy(val->str, ptr, PARSE_FILE_TIMESTAMPLENGTH);
+ val->str[val->length= PARSE_FILE_TIMESTAMPLENGTH]= '\0';
+ ptr+= (PARSE_FILE_TIMESTAMPLENGTH+1);
+ break;
+ }
+ case FILE_OPTIONS_STRLIST:
+ {
+ /*
+ TODO: remove play with mem_root, when List will be able
+ to store MEM_ROOT* pointer for list elements allocation
+ */
+ sql_mem= my_pthread_getspecific_ptr(MEM_ROOT*, THR_MALLOC);
+ list= (List<LEX_STRING>*)(base + parameter->offset);
+ if ((change_mem= (sql_mem != mem_root)))
+ {
+ change_mem= 1;
+ my_pthread_setspecific_ptr(THR_MALLOC, mem_root);
+ }
+
+ list->empty();
+ // list parsing
+ while (ptr < end)
+ {
+ if (!(str= (LEX_STRING*)alloc_root(mem_root,
+ sizeof(LEX_STRING))) ||
+ list->push_back(str))
+ goto list_err;
+ if(!(ptr= parse_quated_escaped_string(ptr, end, mem_root, str)))
+ goto list_err_w_message;
+ switch (*ptr) {
+ case '\n':
+ goto end_of_list;
+ case ' ':
+ // we cant go over buffer bounds, because we have \0 at the end
+ ptr++;
+ break;
+ default:
+ goto list_err_w_message;
+ }
+ }
+ end_of_list:
+ if (*(ptr++) != '\n')
+ goto list_err;
+
+ if (change_mem)
+ my_pthread_setspecific_ptr(THR_MALLOC, sql_mem);
+ break;
+
+ list_err_w_message:
+ my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
+ parameter->name.str, line);
+ list_err:
+ if (change_mem)
+ my_pthread_setspecific_ptr(THR_MALLOC, sql_mem);
+ DBUG_RETURN(TRUE);
+ }
+ default:
+ DBUG_ASSERT(0); // never should happened
+ }
+ }
+ else
+ {
+ // skip unknown parameter
+ if (!(ptr= strchr(ptr, '\n')))
+ {
+ my_error(ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER, MYF(0),
+ line);
+ DBUG_RETURN(TRUE);
+ }
+ ptr++;
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
diff --git a/sql/parse_file.h b/sql/parse_file.h
new file mode 100644
index 00000000000..6a426aa0423
--- /dev/null
+++ b/sql/parse_file.h
@@ -0,0 +1,68 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _PARSE_FILE_H_
+#define _PARSE_FILE_H_
+
+#define PARSE_FILE_TIMESTAMPLENGTH 19
+
+enum file_opt_type {
+ FILE_OPTIONS_STRING, /* String (LEX_STRING) */
+ FILE_OPTIONS_ESTRING, /* Escaped string (LEX_STRING) */
+ FILE_OPTIONS_ULONGLONG, /* ulonglong parapeter (ulonglong) */
+ FILE_OPTIONS_REV, /* Revision version number (ulonglong) */
+ FILE_OPTIONS_TIMESTAMP, /* timestamp (LEX_STRING have to be
+ allocated with length 20 (19+1) */
+ FILE_OPTIONS_STRLIST /* list of strings (List<char*>) */
+};
+
+struct File_option
+{
+ LEX_STRING name; /* Name of the option */
+ int offset; /* offset to base address of value */
+ file_opt_type type; /* Option type */
+};
+
+class File_parser;
+File_parser *sql_parse_prepare(const LEX_STRING *file_name,
+ MEM_ROOT *mem_root, bool bad_format_errors);
+
+my_bool
+sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
+ const LEX_STRING *type,
+ gptr base, File_option *parameters, uint versions);
+
+class File_parser: public Sql_alloc
+{
+ char *buff, *start, *end;
+ LEX_STRING file_type;
+ my_bool content_ok;
+public:
+ File_parser() :buff(0), start(0), end(0), content_ok(0)
+ { file_type.str= 0; file_type.length= 0; }
+
+ my_bool ok() { return content_ok; }
+ LEX_STRING *type() { return &file_type; }
+ my_bool parse(gptr base, MEM_ROOT *mem_root,
+ struct File_option *parameters, uint required);
+
+ friend File_parser *sql_parse_prepare(const LEX_STRING *file_name,
+ MEM_ROOT *mem_root,
+ bool bad_format_errors);
+};
+
+#endif /* _PARSE_FILE_H_ */
diff --git a/sql/protocol.cc b/sql/protocol.cc
index da2a285fffc..bceac780037 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -24,6 +24,7 @@
#endif
#include "mysql_priv.h"
+#include "sp_rcontext.h"
#include <stdarg.h>
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
@@ -64,6 +65,10 @@ void send_error(THD *thd, uint sql_errno, const char *err)
err ? err : net->last_error[0] ?
net->last_error : "NULL"));
+ if (thd->spcont && thd->spcont->find_handler(sql_errno))
+ {
+ DBUG_VOID_RETURN;
+ }
#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/
query_cache_abort(net);
#endif
@@ -147,6 +152,10 @@ void send_error(THD *thd, uint sql_errno, const char *err)
void send_warning(THD *thd, uint sql_errno, const char *err)
{
DBUG_ENTER("send_warning");
+ if (thd->spcont && thd->spcont->find_handler(sql_errno))
+ {
+ DBUG_VOID_RETURN;
+ }
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno,
err ? err : ER(sql_errno));
send_ok(thd);
@@ -177,6 +186,10 @@ net_printf(THD *thd, uint errcode, ...)
DBUG_ENTER("net_printf");
DBUG_PRINT("enter",("message: %u",errcode));
+ if (thd->spcont && thd->spcont->find_handler(errcode))
+ {
+ DBUG_VOID_RETURN;
+ }
thd->query_error= 1; // needed to catch query errors during replication
#ifndef EMBEDDED_LIBRARY
query_cache_abort(net); // Safety
@@ -346,7 +359,7 @@ send_eof(THD *thd, bool no_flush)
{
NET *net= &thd->net;
DBUG_ENTER("send_eof");
- if (net->vio != 0)
+ if (net->vio != 0 && !net->no_send_eof)
{
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
{
@@ -476,6 +489,7 @@ void Protocol::init(THD *thd_arg)
flag Bit mask with the following functions:
1 send number of rows
2 send default values
+ 4 don't write eof packet
DESCRIPTION
Sum fields has table name empty and field_name.
@@ -486,7 +500,7 @@ void Protocol::init(THD *thd_arg)
*/
#ifndef EMBEDDED_LIBRARY
-bool Protocol::send_fields(List<Item> *list, uint flag)
+bool Protocol::send_fields(List<Item> *list, int flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
@@ -497,7 +511,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
DBUG_ENTER("send_fields");
- if (flag & 1)
+ if (flags & SEND_NUM_ROWS)
{ // Packet with number of elements
char *pos=net_store_length(buff, (uint) list->elements);
(void) my_net_write(&thd->net, buff,(uint) (pos-buff));
@@ -583,7 +597,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
}
}
local_packet->length((uint) (pos - local_packet->ptr()));
- if (flag & 2)
+ if (flags & SEND_DEFAULTS)
item->send(&prot, &tmp); // Send default value
if (prot.write())
break; /* purecov: inspected */
@@ -592,7 +606,8 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
#endif
}
- my_net_write(&thd->net, eof_buff, 1);
+ if (flags & SEND_EOF)
+ my_net_write(&thd->net, eof_buff, 1);
DBUG_RETURN(prepare_for_send(list));
err:
@@ -951,12 +966,6 @@ void Protocol_prep::prepare_for_resend()
bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs)
{
CHARSET_INFO *tocs= thd->variables.character_set_results;
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
- (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
- field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
-#endif
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
}
@@ -964,12 +973,6 @@ bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs)
bool Protocol_prep::store(const char *from,uint length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
- (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
- field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
-#endif
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
}
@@ -987,10 +990,6 @@ bool Protocol_prep::store_null()
bool Protocol_prep::store_tiny(longlong from)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_TINY);
-#endif
char buff[1];
field_pos++;
buff[0]= (uchar) from;
@@ -1000,11 +999,6 @@ bool Protocol_prep::store_tiny(longlong from)
bool Protocol_prep::store_short(longlong from)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_SHORT ||
- field_types[field_pos] == MYSQL_TYPE_YEAR);
-#endif
field_pos++;
char *to= packet->prep_append(2, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1016,11 +1010,6 @@ bool Protocol_prep::store_short(longlong from)
bool Protocol_prep::store_long(longlong from)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_INT24 ||
- field_types[field_pos] == MYSQL_TYPE_LONG);
-#endif
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1032,10 +1021,6 @@ bool Protocol_prep::store_long(longlong from)
bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_LONGLONG);
-#endif
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1047,10 +1032,6 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_FLOAT);
-#endif
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1062,10 +1043,6 @@ bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DOUBLE);
-#endif
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1089,12 +1066,6 @@ bool Protocol_prep::store(Field *field)
bool Protocol_prep::store(TIME *tm)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DATETIME ||
- field_types[field_pos] == MYSQL_TYPE_DATE ||
- field_types[field_pos] == MYSQL_TYPE_TIMESTAMP);
-#endif
char buff[12],*pos;
uint length;
field_pos++;
@@ -1129,10 +1100,6 @@ bool Protocol_prep::store_date(TIME *tm)
bool Protocol_prep::store_time(TIME *tm)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_TIME);
-#endif
char buff[13], *pos;
uint length;
field_pos++;
@@ -1152,12 +1119,3 @@ bool Protocol_prep::store_time(TIME *tm)
buff[0]=(char) length; // Length is stored first
return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
}
-
-#ifdef EMBEDDED_LIBRARY
-/* Should be removed when we define the Protocol_cursor's future */
-bool Protocol_cursor::write()
-{
- return Protocol_simple::write();
-}
-#endif
-
diff --git a/sql/protocol.h b/sql/protocol.h
index d7ce5425ad1..1a5896a3ae5 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -50,17 +50,16 @@ public:
Protocol(THD *thd_arg) { init(thd_arg); }
virtual ~Protocol() {}
void init(THD* thd_arg);
- bool send_fields(List<Item> *list, uint flag);
+
+ enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 };
+ virtual bool send_fields(List<Item> *list, int flags);
+
bool send_records_num(List<Item> *list, ulonglong records);
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
String *storage_packet() { return packet; }
inline void free() { packet->free(); }
-#ifndef EMBEDDED_LIBRARY
- bool write();
-#else
virtual bool write();
-#endif
inline bool store(uint32 from)
{ return store_long((longlong) from); }
inline bool store(longlong from)
@@ -161,13 +160,15 @@ public:
Protocol_cursor(THD *thd_arg, MEM_ROOT *ini_alloc) :Protocol_simple(thd_arg), alloc(ini_alloc) {}
bool prepare_for_send(List<Item> *item_list)
{
+ row_count= 0;
fields= NULL;
data= NULL;
prev_record= &data;
return Protocol_simple::prepare_for_send(item_list);
}
- bool send_fields(List<Item> *list, uint flag);
+ bool send_fields(List<Item> *list, int flags);
bool write();
+ uint get_field_count() { return field_count; }
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc
index 5f35552c562..31eaa894045 100644
--- a/sql/protocol_cursor.cc
+++ b/sql/protocol_cursor.cc
@@ -26,7 +26,7 @@
#include "mysql_priv.h"
#include <mysql.h>
-bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
+bool Protocol_cursor::send_fields(List<Item> *list, int flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
@@ -51,6 +51,7 @@ bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
client_field->name= strdup_root(alloc, server_field.col_name);
client_field->org_table= strdup_root(alloc, server_field.org_table_name);
client_field->org_name= strdup_root(alloc, server_field.org_col_name);
+ client_field->catalog= strdup_root(alloc, "");
client_field->length= server_field.length;
client_field->type= server_field.type;
client_field->flags= server_field.flags;
@@ -60,12 +61,13 @@ bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
client_field->name_length= strlen(client_field->name);
client_field->org_name_length= strlen(client_field->org_name);
client_field->org_table_length= strlen(client_field->org_table);
+ client_field->catalog_length= 0;
client_field->charsetnr= server_field.charsetnr;
if (INTERNAL_NUM_FIELD(client_field))
client_field->flags|= NUM_FLAG;
- if (flag & 2)
+ if (flags & (int) Protocol::SEND_DEFAULTS)
{
char buff[80];
String tmp(buff, sizeof(buff), default_charset_info), *res;
@@ -100,17 +102,17 @@ bool Protocol_cursor::write()
byte *to;
new_record= (MYSQL_ROWS *)alloc_root(alloc,
- sizeof(MYSQL_ROWS) + (field_count + 1)*sizeof(char *) + packet->length());
+ sizeof(MYSQL_ROWS) + (field_count + 1)*sizeof(char *) + packet->length());
if (!new_record)
goto err;
data_tmp= (byte **)(new_record + 1);
new_record->data= (char **)data_tmp;
- to= (byte *)(fields + field_count + 1);
+ to= (byte *)data_tmp + (field_count + 1)*sizeof(char *);
for (; cur_field < fields_end; ++cur_field, ++data_tmp)
{
- if ((len=net_field_length((uchar **)&cp)))
+ if ((len= net_field_length((uchar **)&cp)) == 0)
{
*data_tmp= 0;
}
@@ -121,6 +123,7 @@ bool Protocol_cursor::write()
// TODO error signal send_error(thd, CR_MALFORMED_PACKET);
return TRUE;
}
+ *data_tmp= to;
memcpy(to,(char*) cp,len);
to[len]=0;
to+=len+1;
@@ -129,6 +132,7 @@ bool Protocol_cursor::write()
cur_field->max_length=len;
}
}
+ *data_tmp= 0;
*prev_record= new_record;
prev_record= &new_record->next;
@@ -139,5 +143,3 @@ bool Protocol_cursor::write()
// TODO error signal send_error(thd, ER_OUT_OF_RESOURCES);
return TRUE;
}
-
-
diff --git a/sql/records.cc b/sql/records.cc
index 5a969ef9c20..8dd4f548093 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -180,7 +180,7 @@ static int rr_sequential(READ_RECORD *info)
{
if (info->thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ info->thd->send_kill_message();
return 1;
}
if (tmp != HA_ERR_RECORD_DELETED)
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index d7b70fe122c..10ff5fa3596 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -461,7 +461,8 @@ int show_new_master(THD* thd)
field_list.push_back(new Item_empty_string("Log_name", 20));
field_list.push_back(new Item_return_int("Log_pos", 10,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
protocol->prepare_for_resend();
protocol->store(lex_mi->log_file_name, &my_charset_bin);
@@ -652,7 +653,8 @@ int show_slave_hosts(THD* thd)
field_list.push_back(new Item_return_int("Master_id", 10,
MYSQL_TYPE_LONG));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
pthread_mutex_lock(&LOCK_slave_list);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 5351d2d12b2..d92d5eb42b2 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -82,7 +82,7 @@ TYPELIB delay_key_write_typelib=
array_elements(delay_key_write_type_names)-1, "", delay_key_write_type_names
};
-static int sys_check_charset(THD *thd, set_var *var);
+static int sys_check_charset(THD *thd, set_var *var);
static bool sys_update_charset(THD *thd, set_var *var);
static void sys_set_default_charset(THD *thd, enum_var_type type);
static int sys_check_ftb_syntax(THD *thd, set_var *var);
@@ -97,6 +97,7 @@ static bool set_option_autocommit(THD *thd, set_var *var);
static int check_log_update(THD *thd, set_var *var);
static bool set_log_update(THD *thd, set_var *var);
static int check_pseudo_thread_id(THD *thd, set_var *var);
+static bool set_log_bin(THD *thd, set_var *var);
static void fix_low_priority_updates(THD *thd, enum_var_type type);
static void fix_tx_isolation(THD *thd, enum_var_type type);
static void fix_net_read_timeout(THD *thd, enum_var_type type);
@@ -268,6 +269,10 @@ sys_var_thd_ulong sys_net_retry_count("net_retry_count",
0, fix_net_retry_count);
sys_var_thd_bool sys_new_mode("new", &SV::new_mode);
sys_var_thd_bool sys_old_passwords("old_passwords", &SV::old_passwords);
+sys_var_thd_ulong sys_optimizer_prune_level("optimizer_prune_level",
+ &SV::optimizer_prune_level);
+sys_var_thd_ulong sys_optimizer_search_depth("optimizer_search_depth",
+ &SV::optimizer_search_depth);
sys_var_thd_ulong sys_preload_buff_size("preload_buffer_size",
&SV::preload_buff_size);
sys_var_thd_ulong sys_read_buff_size("read_buffer_size",
@@ -327,6 +332,10 @@ sys_var_thd_ulong sys_sort_buffer("sort_buffer_size",
&SV::sortbuff_size);
sys_var_thd_sql_mode sys_sql_mode("sql_mode",
&SV::sql_mode);
+sys_var_thd_enum sys_sql_updatable_view_key("sql_updatable_view_key",
+ &SV::sql_updatable_view_key,
+ &sql_updatable_view_key_typelib);
+
sys_var_thd_table_type sys_table_type("table_type",
&SV::table_type);
sys_var_thd_storage_engine sys_storage_engine("storage_engine",
@@ -391,8 +400,8 @@ static sys_var_thd_bit sys_log_update("sql_log_update",
OPTION_UPDATE_LOG);
static sys_var_thd_bit sys_log_binlog("sql_log_bin",
check_log_update,
- set_log_update,
- OPTION_BIN_LOG);
+ set_log_bin,
+ OPTION_BIN_LOG);
static sys_var_thd_bit sys_sql_warnings("sql_warnings", 0,
set_option_bit,
OPTION_WARNINGS);
@@ -545,6 +554,8 @@ sys_var *sys_variables[]=
&sys_net_write_timeout,
&sys_new_mode,
&sys_old_passwords,
+ &sys_optimizer_prune_level,
+ &sys_optimizer_search_depth,
&sys_preload_buff_size,
&sys_pseudo_thread_id,
&sys_query_alloc_block_size,
@@ -582,6 +593,7 @@ sys_var *sys_variables[]=
&sys_sql_low_priority_updates,
&sys_sql_max_join_size,
&sys_sql_mode,
+ &sys_sql_updatable_view_key,
&sys_sql_warnings,
&sys_storage_engine,
#ifdef HAVE_REPLICATION
@@ -765,6 +777,10 @@ struct show_var_st init_vars[]= {
{sys_new_mode.name, (char*) &sys_new_mode, SHOW_SYS},
{sys_old_passwords.name, (char*) &sys_old_passwords, SHOW_SYS},
{"open_files_limit", (char*) &open_files_limit, SHOW_LONG},
+ {sys_optimizer_prune_level.name, (char*) &sys_optimizer_prune_level,
+ SHOW_SYS},
+ {sys_optimizer_search_depth.name,(char*) &sys_optimizer_search_depth,
+ SHOW_SYS},
{"pid_file", (char*) pidfile_name, SHOW_CHAR},
{"port", (char*) &mysqld_port, SHOW_INT},
{sys_preload_buff_size.name, (char*) &sys_preload_buff_size, SHOW_SYS},
@@ -808,6 +824,8 @@ struct show_var_st init_vars[]= {
#endif
{sys_sort_buffer.name, (char*) &sys_sort_buffer, SHOW_SYS},
{sys_sql_mode.name, (char*) &sys_sql_mode, SHOW_SYS},
+ {sys_sql_updatable_view_key.name,
+ (char*) &sys_sql_updatable_view_key, SHOW_SYS},
{sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS},
#ifdef HAVE_REPLICATION
{sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period, SHOW_SYS},
@@ -1915,7 +1933,7 @@ void sys_var_character_set_server::set_default(THD *thd, enum_var_type type)
}
}
-#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+#if defined(HAVE_REPLICATION)
bool sys_var_character_set_server::check(THD *thd, set_var *var)
{
if ((var->type == OPT_GLOBAL) &&
@@ -2022,7 +2040,7 @@ void sys_var_collation_database::set_default(THD *thd, enum_var_type type)
}
}
-#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+#if defined(HAVE_REPLICATION)
bool sys_var_collation_server::check(THD *thd, set_var *var)
{
if ((var->type == OPT_GLOBAL) &&
@@ -2374,7 +2392,7 @@ bool sys_var_thd_time_zone::check(THD *thd, set_var *var)
String str(buff, sizeof(buff), &my_charset_latin1);
String *res= var->value->val_str(&str);
-#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+#if defined(HAVE_REPLICATION)
if ((var->type == OPT_GLOBAL) &&
(mysql_bin_log.is_open() ||
active_mi->slave_running || active_mi->rli.slave_running))
@@ -2500,6 +2518,30 @@ static int check_log_update(THD *thd, set_var *var)
static bool set_log_update(THD *thd, set_var *var)
{
+ /*
+ The update log is not supported anymore since 5.0.
+ See sql/mysqld.cc/, comments in function init_server_components() for an
+ explaination of the different warnings we send below
+ */
+
+ if (opt_sql_bin_update)
+ {
+ ((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG |
+ OPTION_UPDATE_LOG);
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_UPDATE_LOG_DEPRECATED_TRANSLATED,
+ ER(ER_UPDATE_LOG_DEPRECATED_TRANSLATED));
+ }
+ else
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_UPDATE_LOG_DEPRECATED_IGNORED,
+ ER(ER_UPDATE_LOG_DEPRECATED_IGNORED));
+ set_option_bit(thd, var);
+ return 0;
+}
+
+static bool set_log_bin(THD *thd, set_var *var)
+{
if (opt_sql_bin_update)
((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG |
OPTION_UPDATE_LOG);
diff --git a/sql/set_var.h b/sql/set_var.h
index 4a4e631d88c..c34ebbca4b2 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -49,20 +49,14 @@ public:
const char *name;
sys_after_update_func after_update;
-#if MYSQL_VERSION_ID < 50000
bool no_support_one_shot;
-#endif
sys_var(const char *name_arg)
:name(name_arg), after_update(0)
-#if MYSQL_VERSION_ID < 50000
, no_support_one_shot(1)
-#endif
{}
sys_var(const char *name_arg,sys_after_update_func func)
:name(name_arg), after_update(func)
-#if MYSQL_VERSION_ID < 50000
, no_support_one_shot(1)
-#endif
{}
virtual ~sys_var() {}
virtual bool check(THD *thd, set_var *var);
@@ -507,9 +501,7 @@ class sys_var_collation :public sys_var_thd
public:
sys_var_collation(const char *name_arg) :sys_var_thd(name_arg)
{
-#if MYSQL_VERSION_ID < 50000
no_support_one_shot= 0;
-#endif
}
bool check(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_CHAR; }
@@ -529,13 +521,11 @@ public:
sys_var_thd(name_arg)
{
nullable= 0;
-#if MYSQL_VERSION_ID < 50000
/*
In fact only almost all variables derived from sys_var_character_set
support ONE_SHOT; character_set_results doesn't. But that's good enough.
*/
no_support_one_shot= 0;
-#endif
}
bool check(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_CHAR; }
@@ -574,7 +564,7 @@ class sys_var_character_set_server :public sys_var_character_set
public:
sys_var_character_set_server(const char *name_arg) :
sys_var_character_set(name_arg) {}
-#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+#if defined(HAVE_REPLICATION)
bool check(THD *thd, set_var *var);
#endif
void set_default(THD *thd, enum_var_type type);
@@ -612,7 +602,7 @@ class sys_var_collation_server :public sys_var_collation
{
public:
sys_var_collation_server(const char *name_arg) :sys_var_collation(name_arg) {}
-#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+#if defined(HAVE_REPLICATION)
bool check(THD *thd, set_var *var);
#endif
bool update(THD *thd, set_var *var);
@@ -722,9 +712,7 @@ public:
sys_var_thd_time_zone(const char *name_arg):
sys_var_thd(name_arg)
{
-#if MYSQL_VERSION_ID < 50000
no_support_one_shot= 0;
-#endif
}
bool check(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_CHAR; }
@@ -752,9 +740,7 @@ public:
virtual int update(THD *thd)=0; /* To set the value */
/* light check for PS */
virtual int light_check(THD *thd) { return check(thd); }
-#if MYSQL_VERSION_ID < 50000
virtual bool no_support_one_shot() { return 1; }
-#endif
};
@@ -797,9 +783,7 @@ public:
int check(THD *thd);
int update(THD *thd);
int light_check(THD *thd);
-#if MYSQL_VERSION_ID < 50000
bool no_support_one_shot() { return var->no_support_one_shot; }
-#endif
};
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 022a624c921..587a8be7ac8 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -244,8 +244,8 @@ character-set=latin2
"Deadlock found when trying to get lock; try restarting transaction",
"The used table type doesn't support FULLTEXT indexes",
"Cannot add foreign key constraint",
-"Cannot add a child row: a foreign key constraint fails",
-"Cannot delete a parent row: a foreign key constraint fails",
+"Cannot add or update a child row: a foreign key constraint fails",
+"Cannot delete or update a parent row: a foreign key constraint fails",
"Error connecting to master: %-.128s",
"Error running query on master: %-.128s",
"Error when executing command %s: %-.128s",
@@ -306,21 +306,21 @@ character-set=latin2
"Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format",
"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d",
"Incorrect parameter or combination of parameters for START SLAVE UNTIL",
-"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you are not safe in case of unexpected slave's mysqld restart",
+"It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart",
"SQL thread is not to be started so UNTIL options are ignored",
"Incorrect index name '%-.100s'",
"Incorrect catalog name '%-.100s'",
-"Query cache failed to set size %lu, new query cache size is %lu",
+"Query cache failed to set size %lu; new query cache size is %lu",
"Column '%-.64s' cannot be part of FULLTEXT index",
"Unknown key cache '%-.100s'",
-"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
+"MySQL is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work",
"Unknown table engine '%s'",
-"'%s' is deprecated, use '%s' instead",
+"'%s' is deprecated; use '%s' instead",
"The target table %-.100s of the %s is not updatable",
-"The '%s' feature was disabled; you need MySQL built with '%s' to have it working",
+"The '%s' feature is disabled; you need MySQL built with '%s' to have it working",
"The MySQL server is running with the %s option so it cannot execute this statement",
"Column '%-.100s' has duplicated value '%-.64s' in %s"
-"Truncated wrong %-.32s value: '%-.128s'"
+"Truncated incorrect %-.32s value: '%-.128s'"
"Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
"Invalid ON UPDATE clause for '%-.64s' column",
"This command is not supported in the prepared statement protocol yet",
@@ -331,3 +331,59 @@ character-set=latin2
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index 18ebe5712f8..8d6e23d449e 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -322,3 +322,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 54377b5949a..0c60c63de64 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -331,3 +331,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 8ede3f61a0b..b850c2d36d0 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -319,3 +319,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index 5aab524e0d9..ef958921b5e 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -324,3 +324,59 @@ character-set=latin7
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index 355e784b156..8723919ab47 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -319,3 +319,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index 03e838dd805..74274ebfc8c 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -332,3 +332,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index 06f31a79a73..ea148999fc4 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -319,3 +319,59 @@ character-set=greek
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index af10c33ee2d..ede873f21c0 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -324,3 +324,59 @@ character-set=latin2
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index cd66f15db5f..7898503dc03 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -319,3 +319,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index eaab58d8403..f73eca2a183 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -323,3 +323,59 @@ character-set=ujis
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index 1cff50432e9..6c8a5c2661a 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -319,3 +319,59 @@ character-set=euckr
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index 27f7a18f029..48ade24703b 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -321,3 +321,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index 772f30e5d94..e217155a4df 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -321,3 +321,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index 634a4d93f42..c514581ec1c 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -324,3 +324,59 @@ character-set=latin2
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index c79c346008e..ca6cccb7471 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -321,3 +321,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index 7cb0427dc3f..3c69e33a222 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -324,3 +324,59 @@ character-set=latin2
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index df354d5797f..13c7a410443 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -324,3 +324,59 @@ character-set=koi8r
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"óÌÉÛËÏÍ ÂÏÌØÛÏÊ ËÏÎÆÉÇÕÒÁÃÉÏÎÎÙÊ ÆÁÊÌ '%-.64s'"
+"îÅ×ÅÒÎÙÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÁ ÆÁÊÌÁ '%-.64s'"
+"îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ × ËÏÍÅÎÔÁÒÉÉ '%-.64s'"
+"ïÛÉÂËÁ ÐÒÉ ÒÁÓÐÏÚÎÁ×ÁÎÉÉ ÐÁÒÁÍÅÔÒÁ '%-.64s' (ÓÔÒÏËÁ: '%-.64s')"
+"îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ ÐÒÉ ÐÒÏÐÕÓËÅ ÎÅÉÚ×ÅÓÔÎÏÇÏ ÐÁÒÁÍÅÔÒÁ '%-.64s'"
+"EXPLAIN/SHOW ÎÅ ÍÏÖÅÔ ÂÙÔØ ×ÙÐÏÌÎÅÎÎÏ; ÎÅÄÏÓÔÁÔÏÞÎÏ ÐÒÁ× ÎÁ ÔÁËÂÌÉÃÙ ÚÁÐÒÏÓÁ"
+"æÁÊÌ '%-.64s' ÓÏÄÅÒÖÉÔ ÎÅÉÚ×ÅÓÔÎÙÊ ÔÉÐ '%-.64s' × ÚÁÇÏÌÏ×ËÅ"
+"'%-.64s.%-.64s' - ÎÅ %s"
+"óÔÏÌÂÅà '%-.64s' ÎÅ ÏÂÎÏ×ÌÑÅÍÙÊ"
+"View SELECT ÓÏÄÅÒÖÉÔ ÐÏÄÚÁÐÒÏÓ × ËÏÎÓÔÒÕËÃÉÉ FROM"
+"View SELECT ÓÏÄÅÒÖÉÔ ËÏÎÓÔÒÕËÃÉÀ PROCEDURE"
+"View SELECT ÓÏÄÅÒÖÉÔ ÐÅÒÅÍÅÎÎÕÀ ÉÌÉ ÐÁÒÁÍÅÔÒ"
+"View SELECT ÓÏÄÅÒÖÉÔ ÓÓÙÌËÕ ÎÁ ×ÒÅÍÅÎÎÕÀ ÔÁÂÌÉÃÕ '%-.64s'"
+"View SELECT É ÓÐÉÓÏË ÐÏÌÅÊ view ÉÍÅÀÔ ÒÁÚÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ×"
+"áÌÇÏÒÉÔÍ ÓÌÉÑÎÉÑ view ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ÓÅÊÞÁÓ (ÁÌÇÏÒÉÔÍ ÂÕÄÅÔ ÎÅÏÐÅÒÅÄÅÌÅÎÎÙÍ)"
+"ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÏÊ × ÎÅÍ ÔÁÂÌÉÃ(Ù)"
+"View '%-.64s.%-.64s' ÓÓÙÌÁÅÔÓÑ ÎÁ ÎÅÓÕÝÅÓÔ×ÕÀÝÉÅ ÔÁÂÌÉÃÙ ÉÌÉ ÓÌÏÌÂÃÙ"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index 45b56c8269c..b1fa4d86b54 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -312,3 +312,59 @@ character-set=cp1250
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index 12c3eb2b6af..9570ba1fef6 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -327,3 +327,59 @@ character-set=latin2
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index bd2439c44a6..4058fcc7c4e 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -140,7 +140,7 @@ character-set=latin1
"Muchos campos",
"Tamaño de línea muy grande. Máximo tamaño de línea, no contando blob, es %d. Tu tienes que cambiar algunos campos para blob",
"Sobrecarga de la pila de thread: Usada: %ld de una %ld pila. Use 'mysqld -O thread_stack=#' para especificar una mayor pila si necesario",
-"Dependencia cruzada encontrada en OUTER JOIN; examine su condición ON",
+"Dependencia cruzada encontrada en OUTER JOIN. Examine su condición ON",
"Columna '%-.32s' es usada con UNIQUE o INDEX pero no está definida como NOT NULL",
"No puedo cargar función '%-.64s'",
"No puedo inicializar función '%-.64s'; %-.80s",
@@ -182,7 +182,7 @@ character-set=latin1
"Obtenido timeout leyendo paquetes de comunicación",
"Obtenido un error de escribiendo paquetes de comunicación",
"Obtenido timeout escribiendo paquetes de comunicación",
-"La string resultante es mayor que 'max_allowed_packet'",
+"La string resultante es mayor que max_allowed_packet",
"El tipo de tabla usada no permite soporte para columnas BLOB/TEXT",
"El tipo de tabla usada no permite soporte para columnas AUTO_INCREMENT",
"INSERT DELAYED no puede ser usado con tablas '%-.64s', porque esta bloqueada con LOCK TABLES",
@@ -263,7 +263,7 @@ character-set=latin1
"Referencia de llave y referencia de tabla no coinciden",
"Operando debe tener %d columna(s)",
"Subconsulta retorna mas que 1 línea",
-"Desconocido preparado comando handler (%.*s) dado para %s",
+"Desconocido preparado comando handler (%ld) dado para %s",
"Base de datos Help está corrupto o no existe",
"Cíclica referencia en subconsultas",
"Convirtiendo columna '%s' de %s para %s",
@@ -272,15 +272,15 @@ character-set=latin1
"Select %u fué reducido durante optimización",
"Tabla '%-.64s' de uno de los SELECT no puede ser usada en %-.32s",
"Cliente no soporta protocolo de autenticación solicitado por el servidor; considere actualizar el cliente MySQL",
-"Todas las partes de una SPATIAL index deben ser NOT NULL",
+"Todas las partes de una SPATIAL KEY deben ser NOT NULL",
"COLLATION '%s' no es válido para CHARACTER SET '%s'",
"Slave ya está funcionando",
"Slave ya fué parado",
"Tamaño demasiado grande para datos descomprimidos. El máximo tamaño es %d. (probablemente, extensión de datos descomprimidos fué corrompida)",
-"ZLIB: No suficiente memoria",
-"ZLIB: No suficiente espacio en el búfer de salida (probablemente, extensión de datos descomprimidos fué corrompida)",
-"ZLIB: Dato de entrada fué corrompido",
-"%d línea(s) fueron cortadas por GROUP_CONCAT()",
+"Z_MEM_ERROR: No suficiente memoria para zlib",
+"Z_BUF_ERROR: No suficiente espacio en el búfer de salida para zlib (probablemente, extensión de datos descomprimidos fué corrompida)",
+"Z_DATA_ERROR: Dato de entrada fué corrompido para zlib",
+"%d línea(s) fue(fueron) cortadas por group_concat()",
"Línea %ld no contiene datos para todas las columnas",
"Línea %ld fué truncada; La misma contine mas datos que las que existen en las columnas de entrada",
"Datos truncado, NULL suministrado para NOT NULL columna '%s' en la línea %ld",
@@ -323,3 +323,59 @@ character-set=latin1
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index da75e4fcede..e03af70d03c 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -178,7 +178,7 @@ character-set=latin1
"Fick 'timeout' vid läsning från klienten",
"Fick ett fel vid skrivning till klienten",
"Fick 'timeout' vid skrivning till klienten",
-"Resultatsträngen är längre än 'max_allowed_packet'",
+"Resultatsträngen är längre än max_allowed_packet",
"Den använda tabelltypen kan inte hantera BLOB/TEXT-kolumner",
"Den använda tabelltypen kan inte hantera AUTO_INCREMENT-kolumner",
"INSERT DELAYED kan inte användas med tabell '%-.64s', emedan den är låst med LOCK TABLES",
@@ -259,7 +259,7 @@ character-set=latin1
"Nyckelreferensen och tabellreferensen stämmer inte överens",
"Operand should contain %d column(s)",
"Subquery returnerade mer än 1 rad",
-"Okänd PREPARED STATEMENT id (%.*s) var given till %s",
+"Okänd PREPARED STATEMENT id (%ld) var given till %s",
"Hjälpdatabasen finns inte eller är skadad",
"Cyklisk referens i subqueries",
"Konvertar kolumn '%s' från %s till %s",
@@ -268,19 +268,19 @@ character-set=latin1
"Select %u reducerades vid optimiering",
"Tabell '%-.64s' från en SELECT kan inte användas i %-.32s",
"Klienten stöder inte autentiseringsprotokollet som begärts av servern; överväg uppgradering av klientprogrammet.",
-"Alla delar av en SPATIAL index måste vara NOT NULL",
+"Alla delar av en SPATIAL KEY måste vara NOT NULL",
"COLLATION '%s' är inte tillåtet för CHARACTER SET '%s'",
"Slaven har redan startat",
"Slaven har redan stoppat",
-"Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)",
-"ZLIB: Not enough memory",
-"ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted)",
-"ZLIB: Input data corrupted",
-"%d rad(er) kapades av GROUP_CONCAT()",
+"Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)",
+"Z_MEM_ERROR: Not enough memory available for zlib",
+"Z_BUF_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)",
+"Z_DATA_ERROR: Input data was corrupted for zlib",
+"%d rad(er) kapades av group_concat()",
"Row %ld doesn't contain data for all columns",
-"Row %ld was truncated; it contained more data than there were input columns",
-"Data truncated; NULL supplied to NOT NULL column '%s' at row %ld",
-"Data truncated; out of range for column '%s' at row %ld",
+"Row %ld was truncated; It contained more data than there were input columns",
+"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld",
+"Data truncated, out of range for column '%s' at row %ld",
"Data truncated for column '%s' at row %ld",
"Använder handler %s för tabell '%s'",
"Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'",
@@ -288,13 +288,13 @@ character-set=latin1
"Can't revoke all privileges, grant for one or more of the requested users",
"Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
"Illegal mix of collations for operation '%s'",
-"Variable '%-.64s' is not a variable component (can't be used as XXXX.variable_name)",
+"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
-"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later if MySQL slave with SSL is started",
+"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be started",
"Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format",
"Field or reference '%-.64s%s%-.64s%s%-.64s' of SELECT #%d was resolved in SELECT #%d",
-"Incorrect parameter or combination of parameters for START SLAVE UNTIL",
-"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you are not safe in case of unexpected slave's mysqld restart",
+"Wrong parameter or combination of parameters for START SLAVE UNTIL",
+"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart",
"SQL thread is not to be started so UNTIL options are ignored",
"Felaktigt index namn '%-.100s'",
"Felaktigt katalog namn '%-.100s'",
@@ -309,13 +309,69 @@ character-set=latin1
"MySQL är startad med --skip-grant-tables. Pga av detta kan du inte använda detta kommando",
"Column '%-.100s' has duplicated value '%-.64s' in %s"
"Truncated wrong %-.32s value: '%-.128s'"
-"Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
-"Invalid ON UPDATE clause for '%-.64s' column",
+"Incorrect table definition; There can only be one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
+"Invalid ON UPDATE clause for '%-.64s' field",
"This command is not supported in the prepared statement protocol yet",
-"Fick felkod %d '%-.100s' från %s",
-"Fick tilfällig felkod %d '%-.100s' från %s",
+"Got error %d '%-.100s' from %s",
+"Got temporary error %d '%-.100s' from %s",
"Unknown or incorrect time zone: '%-.64s'",
"Invalid TIMESTAMP value in column '%s' at row %ld",
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"Configuration file '%-.64s' is too big"
+"Malformed file type header in file '%-.64s'"
+"Unexpected end of file while parsing comment '%-.64s'"
+"Error while parsing parameter '%-.64s' (line: '%-.64s')"
+"Unexpected end of file while skipping unknown parameter '%-.64s'"
+"EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
+"File '%-.64s' has unknown type '%-.64s' in its header"
+"'%-.64s.%-.64s' is not %s"
+"Column '%-.64s' is not updatable"
+"View's SELECT contains a subquery in the FROM clause"
+"View's SELECT contains a PROCEDURE clause"
+"View's SELECT contains a variable or parameter"
+"View's SELECT contains a temporary table '%-.64s'"
+"View's SELECT and view's field list have different column counts"
+"View merge algorithm can't be used here for now (assumed undefined algorithm)"
+"View being updated does not have complete key of underlying table in it"
+"View '%-.64s.%-.64s' references invalid table(s) or column(s)"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index a19cf946cb1..a919422a6cf 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -175,7 +175,7 @@ character-set=koi8u
"ç¦ÌËÁ ÄÌÑ INSERT DELAYED ÎÅ ÍÏÖÅ ÏÔÒÉÍÁÔÉ ÂÌÏËÕ×ÁÎÎÑ ÄÌÑ ÔÁÂÌÉæ %-.64s",
"úÁÂÁÇÁÔÏ ÚÁÔÒÉÍÁÎÉÈ Ç¦ÌÏË ×ÉËÏÒÉÓÔÏ×Õ¤ÔØÓÑ",
"ðÅÒÅÒ×ÁÎÏ Ú'¤ÄÎÁÎÎÑ %ld ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ: '%-.64s' ËÏÒÉÓÔÕ×ÁÞÁ: '%-.32s' (%-.64s)",
-"ïÔÒÉÍÁÎÏ ÐÁËÅÔ Â¦ÌØÛÉÊ Î¦Ö 'max_allowed_packet'",
+"ïÔÒÉÍÁÎÏ ÐÁËÅÔ Â¦ÌØÛÉÊ Î¦Ö max_allowed_packet",
"ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÞÉÔÁÎÎÑ Ú ËÏÍÕΦËÁæÊÎÏÇÏ ËÁÎÁÌÕ",
"ïÔÒÉÍÁÎÏ ÐÏÍÉÌËËÕ ×¦Ä fcntl()",
"ïÔÒÉÍÁÎÏ ÐÁËÅÔÉ Õ ÎÅÎÁÌÅÖÎÏÍÕ ÐÏÒÑÄËÕ",
@@ -184,7 +184,7 @@ character-set=koi8u
"ïÔÒÉÍÁÎÏ ÚÁÔÒÉÍËÕ ÞÉÔÁÎÎÑ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×",
"ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÚÁÐÉÓÕ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×",
"ïÔÒÉÍÁÎÏ ÚÁÔÒÉÍËÕ ÚÁÐÉÓÕ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×",
-"óÔÒÏËÁ ÒÅÚÕÌØÔÁÔÕ ÄÏ×ÛÁ Î¦Ö 'max_allowed_packet'",
+"óÔÒÏËÁ ÒÅÚÕÌØÔÁÔÕ ÄÏ×ÛÁ Î¦Ö max_allowed_packet",
"÷ÉËÏÒÉÓÔÁÎÉÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ BLOB/TEXT ÓÔÏ×Âæ",
"÷ÉËÏÒÉÓÔÁÎÉÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ AUTO_INCREMENT ÓÔÏ×Âæ",
"INSERT DELAYED ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÏ Ú ÔÁÂÌÉÃÅÀ '%-.64s', ÔÏÍÕ ÝÏ §§ ÚÁÂÌÏËÏ×ÁÎÏ Ú LOCK TABLES",
@@ -235,7 +235,7 @@ character-set=koi8u
"èÉÂÎÉÊ ÁÒÇÕÍÅÎÔ ÄÌÑ %s",
"ëÏÒÉÓÔÕ×ÁÞÕ '%-.32s'@'%-.64s' ÎÅ ÄÏÚ×ÏÌÅÎÏ ÓÔ×ÏÒÀ×ÁÔÉ ÎÏ×ÉÈ ËÏÒÉÓÔÕ×ÁÞ¦×",
"Incorrect table definition; all MERGE tables must be in the same database",
-"Deadlock found when trying to get lock; try restarting transaction",
+"Deadlock found when trying to get lock; Try restarting transaction",
"÷ÉËÏÒÉÓÔÁÎÉÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ FULLTEXT ¦ÎÄÅËÓ¦×",
"Cannot add foreign key constraint",
"Cannot add a child row: a foreign key constraint fails",
@@ -243,50 +243,50 @@ character-set=koi8u
"Error connecting to master: %-.128s",
"Error running query on master: %-.128s",
"Error when executing command %s: %-.128s",
-"Incorrect usage of %s and %s",
+"Wrong usage of %s and %s",
"The used SELECT statements have a different number of columns",
"Can't execute the query because you have a conflicting read lock",
"Mixing of transactional and non-transactional tables is disabled",
"Option '%s' used twice in statement",
"User '%-.64s' has exceeded the '%s' resource (current value: %ld)",
-"Access denied; you need the %-.128s privilege for this operation",
+"Access denied. You need the %-.128s privilege for this operation",
"Variable '%-.64s' is a SESSION variable and can't be used with SET GLOBAL",
"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL",
"Variable '%-.64s' doesn't have a default value",
"Variable '%-.64s' can't be set to the value of '%-.64s'",
-"Incorrect argument type to variable '%-.64s'",
+"Wrong argument type to variable '%-.64s'",
"Variable '%-.64s' can only be set, not read",
-"Incorrect usage/placement of '%s'",
+"Wrong usage/placement of '%s'",
"This version of MySQL doesn't yet support '%s'",
"Got fatal error %d: '%-.128s' from master when reading data from binary log",
"Slave SQL thread ignored the query because of replicate-*-table rules",
"Variable '%-.64s' is a %s variable",
-"Incorrect foreign key definition for '%-.64s': %s",
-"Key reference and table reference don't match",
+"Wrong foreign key definition for '%-.64s': %s",
+"Key reference and table reference doesn't match",
"ïÐÅÒÁÎÄ ÍÁ¤ ÓËÌÁÄÁÔÉÓÑ Ú %d ÓÔÏ×Âæ×",
"ð¦ÄÚÁÐÉÔ ÐÏ×ÅÒÔÁ¤ Â¦ÌØÛ ÎiÖ 1 ÚÁÐÉÓ",
-"Unknown prepared statement handler (%.*s) given to %s",
+"Unknown prepared statement handler (%ld) given to %s",
"Help database is corrupt or does not exist",
"ãÉË̦ÞÎÅ ÐÏÓÉÌÁÎÎÑ ÎÁ ЦÄÚÁÐÉÔ",
"ðÅÒÅÔ×ÏÒÅÎÎÑ ÓÔÏ×ÂÃÁ '%s' Ú %s Õ %s",
"ðÏÓÉÌÁÎÎÑ '%-.64s' ÎÅ ÐiÄÔÒÉÍÕÅÔÓÑ (%s)",
-"Every derived table must have its own alias",
+"Every derived table must have it's own alias",
"Select %u was ÓËÁÓÏ×ÁÎÏ ÐÒÉ ÏÐÔÉÍiÚÁÃii",
-"Table '%-.64s' from one of the SELECTs cannot be used in %-.32s",
+"Table '%-.64s' from one of SELECT's can not be used in %-.32s",
"Client does not support authentication protocol requested by server; consider upgrading MySQL client",
-"All parts of a SPATIAL index must be NOT NULL",
+"All parts of a SPATIAL KEY must be NOT NULL",
"COLLATION '%s' is not valid for CHARACTER SET '%s'",
"Slave is already running",
"Slave has already been stopped",
-"Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)",
-"ZLIB: Not enough memory",
-"ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted)",
-"ZLIB: Input data corrupted",
-"%d line(s) were cut by GROUP_CONCAT()",
+"Too big size of uncompressed data. The maximum size is %d. (probably, length of uncompressed data was corrupted)",
+"Z_MEM_ERROR: Not enough memory available for zlib",
+"Z_BUF_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)",
+"Z_DATA_ERROR: Input data was corrupted for zlib",
+"%d line(s) was(were) cut by group_concat()",
"Row %ld doesn't contain data for all columns",
-"Row %ld was truncated; it contained more data than there were input columns",
-"Data truncated; NULL supplied to NOT NULL column '%s' at row %ld",
-"Data truncated; out of range for column '%s' at row %ld",
+"Row %ld was truncated; It contained more data than there were input columns",
+"Data truncated, NULL supplied to NOT NULL column '%s' at row %ld",
+"Data truncated, out of range for column '%s' at row %ld",
"Data truncated for column '%s' at row %ld",
"Using storage engine %s for table '%s'",
"Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'",
@@ -294,13 +294,13 @@ character-set=koi8u
"Can't revoke all privileges, grant for one or more of the requested users",
"Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
"Illegal mix of collations for operation '%s'",
-"Variable '%-.64s' is not a variable component (can't be used as XXXX.variable_name)",
+"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
-"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later if MySQL slave with SSL is started",
+"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support; they can be used later when MySQL slave with SSL will be started",
"Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format",
"óÔÏ×ÂÅÃØ ÁÂÏ ÐÏÓÉÌÁÎÎÑ '%-.64s%s%-.64s%s%-.64s' ¦Ú SELECTÕ #%d ÂÕÌÏ ÚÎÁÊÄÅÎÅ Õ SELECT¦ #%d",
-"Incorrect parameter or combination of parameters for START SLAVE UNTIL",
-"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you are not safe in case of unexpected slave's mysqld restart",
+"Wrong parameter or combination of parameters for START SLAVE UNTIL",
+"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart",
"SQL thread is not to be started so UNTIL options are ignored",
"Incorrect index name '%-.100s'",
"Incorrect catalog name '%-.100s'",
@@ -315,8 +315,8 @@ character-set=koi8u
"The MySQL server is running with the %s option so it cannot execute this statement",
"Column '%-.100s' has duplicated value '%-.64s' in %s"
"Truncated wrong %-.32s value: '%-.128s'"
-"Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
-"Invalid ON UPDATE clause for '%-.64s' column",
+"Incorrect table definition; There can only be one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
+"Invalid ON UPDATE clause for '%-.64s' field",
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
@@ -325,3 +325,59 @@ character-set=koi8u
"Invalid %s character string: '%.64s'",
"Result of %s() was larger than max_allowed_packet (%ld) - truncated"
"Conflicting declarations: '%s%s' and '%s%s'"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been ignored"
+"The update log is deprecated and replaced by the binary log; SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN"
+"Query execution was interrupted"
+"Incorrect number of arguments for %s %s; expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Incorrect number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
+"USE is not allowed in a stored procedure"
+"Variable or condition declaration after cursor or handler declaration"
+"Cursor declaration after handler declaration"
+"Case not found for CASE statement"
+"úÁÎÁÄÔÏ ×ÅÌÉËÉÊ ËÏÎÆ¦ÇÕÒÁæÊÎÉÊ ÆÁÊÌ '%-.64s'"
+"îÅצÒÎÉÊ ÚÁÇÏÌÏ×ÏË ÔÉÐÕ Õ ÆÁÊ̦ '%-.64s'"
+"îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ËÏÍÅÎÔÁÒ¦ '%-.64s'"
+"ðÏÍÉÌËÁ × ÒÏÓЦÚÎÁ×ÁÎΦ ÐÁÒÁÍÅÔÒÕ '%-.64s' (ÒÑÄÏË: '%-.64s')"
+"îÅÓÐÏĦ×ÁÎÎÉÊ Ë¦ÎÅÃØ ÆÁÊÌÕ Õ ÓÐÒϦ ÐÒÏÍÉÎÕÔÉ ÎÅצÄÏÍÉÊ ÐÁÒÁÍÅÔÒ '%-.64s'"
+"EXPLAIN/SHOW ÎÅ ÍÏÖÅ ÂÕÔÉ ×¦ËÏÎÁÎÏ; ÎÅÍÁ¤ ÐÒÁ× ÎÁ ÔÉÂÌÉæ ÚÁÐÉÔÕ"
+"æÁÊÌ '%-.64s' ÍÁ¤ ÎÅצÄÏÍÉÊ ÔÉÐ '%-.64s' Õ ÚÁÇÏÌÏ×ËÕ"
+"'%-.64s.%-.64s' ÎÅ ¤ %s"
+"óÔÏ×ÂÅÃØ '%-.64s' ÎÅ ÍÏÖÅ ÂÕÔÉ ÚÍÉÎÅÎÉÊ"
+"View SELECT ÍÁ¤ ЦÄÚÁÐÉÔ Õ ËÏÎÓÔÒÕËæ§ FROM"
+"View SELECT ÍÁ¤ ËÏÎÓÔÒÕËæÀ PROCEDURE"
+"View SELECT ÍÁ¤ ÚÍÉÎÎÕ ÁÂÏ ÐÁÒÁÍÅÔÅÒ"
+"View SELECT ×ÉËÏÒÉÓÔÏ×Õ¤ ÔÉÍÞÁÓÏ×Õ ÔÁÂÌÉÃÀ '%-.64s'"
+"View SELECT ¦ ÐÅÒÅÌ¦Ë ÓÔÏ×ÂÃ¦× view ÍÁÀÔØ Ò¦ÚÎÕ Ë¦ÌØË¦ÓÔØ ÓËÏ×Âæ×"
+"áÌÇÏÒÉÔÍ ÚÌÉ×ÁÎÎÑ view ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÉÊ ÚÁÒÁÚ (ÁÌÇÏÒÉÔÍ ÂÕÄÅ ÎÅ×ÉÚÎÁÞÅÎÉÊ)"
+"View, ÝÏ ÏÎÏ×ÌÀÅÔØÓÑ, ΊͦÓÔÉÔØ ÐÏ×ÎÏÇÏ ËÌÀÞÁ ÔÁÂÌÉæ(Ø), ÝÏ ×ÉËÏÒ¦ÓÔÁÎÁ × ÎØÀÏÍÕ"
+"View '%-.64s.%-.64s' ÐÏÓÉÌÁ¤ÔÓÑ ÎÁ ÎŦÓÎÕÀÞ¦ ÔÁÂÌÉæ ÁÂÏ ÓÔÏ×Âæ"
+"Can't drop a %s from within another stored routine"
+"GOTO is not allowed in a stored procedure handler"
diff --git a/sql/slave.cc b/sql/slave.cc
index cb37a798037..d7b60107096 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -74,7 +74,6 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
const char* table_name, bool overwrite);
static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi);
-
/*
Find out which replications threads are running
@@ -218,6 +217,12 @@ static byte* get_table_key(TABLE_RULE_ENT* e, uint* len,
pos Position in relay log file
need_data_lock Set to 1 if this functions should do mutex locks
errmsg Store pointer to error message here
+ look_for_description_event
+ 1 if we should look for such an event. We only need
+ this when the SQL thread starts and opens an existing
+ relay log and has to execute it (possibly from an offset
+ >4); then we need to read the first event of the relay
+ log to be able to parse the events we have to execute.
DESCRIPTION
- Close old open relay log files.
@@ -235,15 +240,35 @@ static byte* get_table_key(TABLE_RULE_ENT* e, uint* len,
int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
ulonglong pos, bool need_data_lock,
- const char** errmsg)
+ const char** errmsg,
+ bool look_for_description_event)
{
DBUG_ENTER("init_relay_log_pos");
+ DBUG_PRINT("info", ("pos=%lu", pos));
*errmsg=0;
pthread_mutex_t *log_lock=rli->relay_log.get_log_lock();
if (need_data_lock)
pthread_mutex_lock(&rli->data_lock);
+
+ /*
+ Slave threads are not the only users of init_relay_log_pos(). CHANGE MASTER
+ is, too, and init_slave() too; these 2 functions allocate a description
+ event in init_relay_log_pos, which is not freed by the terminating SQL slave
+ thread as that thread is not started by these functions. So we have to free
+ the description_event here, in case, so that there is no memory leak in
+ running, say, CHANGE MASTER.
+ */
+ delete rli->relay_log.description_event_for_exec;
+ /*
+ By default the relay log is in binlog format 3 (4.0).
+ Even if format is 4, this will work enough to read the first event
+ (Format_desc) (remember that format 4 is just lenghtened compared to format
+ 3; format 3 is a prefix of format 4).
+ */
+ rli->relay_log.description_event_for_exec= new
+ Format_description_log_event(3);
pthread_mutex_lock(log_lock);
@@ -283,9 +308,8 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
In this case, we will use the same IO_CACHE pointer to
read data as the IO thread is using to write data.
*/
- rli->cur_log= rli->relay_log.get_log_file();
- if (my_b_tell(rli->cur_log) == 0 &&
- check_binlog_magic(rli->cur_log, errmsg))
+ my_b_seek((rli->cur_log=rli->relay_log.get_log_file()), (off_t)0);
+ if (check_binlog_magic(rli->cur_log,errmsg))
goto err;
rli->cur_log_old_open_count=rli->relay_log.get_open_count();
}
@@ -299,8 +323,85 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,
goto err;
rli->cur_log = &rli->cache_buf;
}
- if (pos >= BIN_LOG_HEADER_SIZE)
+ /*
+ In all cases, check_binlog_magic() has been called so we're at offset 4 for
+ sure.
+ */
+ if (pos > BIN_LOG_HEADER_SIZE) /* If pos<=4, we stay at 4 */
+ {
+ Log_event* ev;
+ while (look_for_description_event)
+ {
+ /*
+ Read the possible Format_description_log_event; if position was 4, no need, it will
+ be read naturally.
+ */
+ DBUG_PRINT("info",("looking for a Format_description_log_event"));
+
+ if (my_b_tell(rli->cur_log) >= pos)
+ break;
+
+ /*
+ Because of we have rli->data_lock and log_lock, we can safely read an
+ event
+ */
+ if (!(ev=Log_event::read_log_event(rli->cur_log,0,
+ rli->relay_log.description_event_for_exec)))
+ {
+ DBUG_PRINT("info",("could not read event, rli->cur_log->error=%d",
+ rli->cur_log->error));
+ if (rli->cur_log->error) /* not EOF */
+ {
+ *errmsg= "I/O error reading event at position 4";
+ goto err;
+ }
+ break;
+ }
+ else if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
+ {
+ DBUG_PRINT("info",("found Format_description_log_event"));
+ delete rli->relay_log.description_event_for_exec;
+ rli->relay_log.description_event_for_exec= (Format_description_log_event*) ev;
+ /*
+ As ev was returned by read_log_event, it has passed is_valid(), so
+ my_malloc() in ctor worked, no need to check again.
+ */
+ /*
+ Ok, we found a Format_description event. But it is not sure that this
+ describes the whole relay log; indeed, one can have this sequence
+ (starting from position 4):
+ Format_desc (of slave)
+ Rotate (of master)
+ Format_desc (of master)
+ So the Format_desc which really describes the rest of the relay log is
+ the 3rd event (it can't be further than that, because we rotate the
+ relay log when we queue a Rotate event from the master).
+ But what describes the Rotate is the first Format_desc.
+ So what we do is:
+ go on searching for Format_description events, until you exceed the
+ position (argument 'pos') or until you find another event than Rotate
+ or Format_desc.
+ */
+ }
+ else
+ {
+ DBUG_PRINT("info",("found event of another type=%d",
+ ev->get_type_code()));
+ look_for_description_event= (ev->get_type_code() == ROTATE_EVENT);
+ delete ev;
+ }
+ }
my_b_seek(rli->cur_log,(off_t)pos);
+#ifndef DBUG_OFF
+ {
+ char llbuf1[22], llbuf2[22];
+ DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
+ llstr(my_b_tell(rli->cur_log),llbuf1),
+ llstr(rli->event_relay_log_pos,llbuf2)));
+ }
+#endif
+
+ }
err:
/*
@@ -315,6 +416,8 @@ err:
if (need_data_lock)
pthread_mutex_unlock(&rli->data_lock);
+ if (!rli->relay_log.description_event_for_exec->is_valid() && !*errmsg)
+ *errmsg= "Invalid Format_description log event; could be out of memory";
DBUG_RETURN ((*errmsg) ? 1 : 0);
}
@@ -360,16 +463,15 @@ void init_slave_skip_errors(const char* arg)
}
-void st_relay_log_info::inc_group_relay_log_pos(ulonglong val,
- ulonglong log_pos,
- bool skip_lock)
+void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
+ bool skip_lock)
{
if (!skip_lock)
pthread_mutex_lock(&data_lock);
- inc_event_relay_log_pos(val);
+ inc_event_relay_log_pos();
group_relay_log_pos= event_relay_log_pos;
strmake(group_relay_log_name,event_relay_log_name,
- sizeof(group_relay_log_name)-1);
+ sizeof(group_relay_log_name)-1);
notify_group_relay_log_name_update();
@@ -383,6 +485,28 @@ void st_relay_log_info::inc_group_relay_log_pos(ulonglong val,
not advance as it should on the non-transactional slave (it advances by
big leaps, whereas it should advance by small leaps).
*/
+ /*
+ In 4.x we used the event's len to compute the positions here. This is
+ wrong if the event was 3.23/4.0 and has been converted to 5.0, because
+ then the event's len is not what is was in the master's binlog, so this
+ will make a wrong group_master_log_pos (yes it's a bug in 3.23->4.0
+ replication: Exec_master_log_pos is wrong). Only way to solve this is to
+ have the original offset of the end of the event the relay log. This is
+ what we do in 5.0: log_pos has become "end_log_pos" (because the real use
+ of log_pos in 4.0 was to compute the end_log_pos; so better to store
+ end_log_pos instead of begin_log_pos.
+ If we had not done this fix here, the problem would also have appeared
+ when the slave and master are 5.0 but with different event length (for
+ example the slave is more recent than the master and features the event
+ UID). It would give false MASTER_POS_WAIT, false Exec_master_log_pos in
+ SHOW SLAVE STATUS, and so the user would do some CHANGE MASTER using this
+ value which would lead to badly broken replication.
+ Even the relay_log_pos will be corrupted in this case, because the len is
+ the relay log is not "val".
+ With the end_log_pos solution, we avoid computations involving lengthes.
+ */
+ DBUG_PRINT("info", ("log_pos=%lld group_master_log_pos=%lld",
+ log_pos,group_master_log_pos));
if (log_pos) // 3.23 binlogs don't have log_posx
{
#if MYSQL_VERSION_ID < 50000
@@ -396,10 +520,10 @@ void st_relay_log_info::inc_group_relay_log_pos(ulonglong val,
Yes this is a hack but it's just to make 3.23->4.x replication work;
3.23->5.0 replication is working much better.
*/
- group_master_log_pos= log_pos + val -
+ group_master_log_pos= log_pos -
(mi->old_format ? (LOG_EVENT_HEADER_LEN - OLD_HEADER_LEN) : 0);
#else
- group_master_log_pos= log_pos+ val;
+ group_master_log_pos= log_pos;
#endif /* MYSQL_VERSION_ID < 5000 */
}
pthread_cond_broadcast(&data_cond);
@@ -481,13 +605,15 @@ int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset,
sizeof(rli->group_relay_log_name)-1);
strmake(rli->event_relay_log_name, rli->relay_log.get_log_fname(),
sizeof(rli->event_relay_log_name)-1);
- // Just first log with magic number and nothing else
- rli->log_space_total= BIN_LOG_HEADER_SIZE;
rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
- rli->relay_log.reset_bytes_written();
+ if (count_relay_log_space(rli))
+ {
+ *errmsg= "Error counting relay log space";
+ goto err;
+ }
if (!just_reset)
error= init_relay_log_pos(rli, rli->group_relay_log_name, rli->group_relay_log_pos,
- 0 /* do not need data lock */, errmsg);
+ 0 /* do not need data lock */, errmsg, 0);
err:
#ifndef DBUG_OFF
@@ -635,7 +761,7 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
thd->exit_cond(old_msg);
pthread_mutex_lock(cond_lock); // re-acquire it as exit_cond() released
if (thd->killed)
- DBUG_RETURN(ER_SERVER_SHUTDOWN);
+ DBUG_RETURN(thd->killed_errno());
}
}
if (start_lock)
@@ -754,6 +880,11 @@ static TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len)
second call will make the decision (because
all_tables_not_ok() = !tables_ok(1st_list) && !tables_ok(2nd_list)).
+ Thought which arose from a question of a big customer "I want to include all
+ tables like "abc.%" except the "%.EFG"". This can't be done now. If we
+ supported Perl regexps we could do it with this pattern: /^abc\.(?!EFG)/
+ (I could not find an equivalent in the regex library MySQL uses).
+
RETURN VALUES
0 should not be logged/replicated
1 should be logged/replicated
@@ -764,7 +895,7 @@ int tables_ok(THD* thd, TABLE_LIST* tables)
bool some_tables_updating= 0;
DBUG_ENTER("tables_ok");
- for (; tables; tables = tables->next)
+ for (; tables; tables= tables->next_global)
{
char hash_key[2*NAME_LEN+2];
char *end;
@@ -1160,29 +1291,86 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val)
return 1;
}
+/*
+ Note that we rely on the master's version (3.23, 4.0.14 etc) instead of
+ relying on the binlog's version. This is not perfect: imagine an upgrade
+ of the master without waiting that all slaves are in sync with the master;
+ then a slave could be fooled about the binlog's format. This is what happens
+ when people upgrade a 3.23 master to 4.0 without doing RESET MASTER: 4.0
+ slaves are fooled. So we do this only to distinguish between 3.23 and more
+ recent masters (it's too late to change things for 3.23).
+
+ RETURNS
+ 0 ok
+ 1 error
+*/
static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi)
{
const char* errmsg= 0;
-
+
/*
- Note the following switch will bug when we have MySQL branch 30 ;)
+ Free old description_event_for_queue (that is needed if we are in
+ a reconnection).
*/
- switch (*mysql->server_version) {
- case '3':
- mi->old_format =
- (strncmp(mysql->server_version, "3.23.57", 7) < 0) /* < .57 */ ?
- BINLOG_FORMAT_323_LESS_57 :
- BINLOG_FORMAT_323_GEQ_57 ;
- break;
- case '4':
- mi->old_format = BINLOG_FORMAT_CURRENT;
- break;
- default:
- /* 5.0 is not supported */
- errmsg = "Master reported an unrecognized MySQL version. Note that 4.1 \
-slaves can't replicate a 5.0 or newer master.";
- break;
+ delete mi->rli.relay_log.description_event_for_queue;
+ mi->rli.relay_log.description_event_for_queue= 0;
+
+ if (!my_isdigit(&my_charset_bin,*mysql->server_version))
+ errmsg = "Master reported unrecognized MySQL version";
+ else
+ {
+ /*
+ Note the following switch will bug when we have MySQL branch 30 ;)
+ */
+ switch (*mysql->server_version)
+ {
+ case '0':
+ case '1':
+ case '2':
+ errmsg = "Master reported unrecognized MySQL version";
+ break;
+ case '3':
+ mi->rli.relay_log.description_event_for_queue= new
+ Format_description_log_event(1, mysql->server_version);
+ break;
+ case '4':
+ mi->rli.relay_log.description_event_for_queue= new
+ Format_description_log_event(3, mysql->server_version);
+ break;
+ default:
+ /*
+ Master is MySQL >=5.0. Give a default Format_desc event, so that we can
+ take the early steps (like tests for "is this a 3.23 master") which we
+ have to take before we receive the real master's Format_desc which will
+ override this one. Note that the Format_desc we create below is garbage
+ (it has the format of the *slave*); it's only good to help know if the
+ master is 3.23, 4.0, etc.
+ */
+ mi->rli.relay_log.description_event_for_queue= new
+ Format_description_log_event(4, mysql->server_version);
+ break;
+ }
+ }
+
+ /*
+ This does not mean that a 5.0 slave will be able to read a 6.0 master; but
+ as we don't know yet, we don't want to forbid this for now. If a 5.0 slave
+ can't read a 6.0 master, this will show up when the slave can't read some
+ events sent by the master, and there will be error messages.
+ */
+
+ if (errmsg)
+ {
+ sql_print_error(errmsg);
+ return 1;
+ }
+
+ /* as we are here, we tried to allocate the event */
+ if (!mi->rli.relay_log.description_event_for_queue)
+ {
+ sql_print_error("Slave I/O thread failed to create a default Format_description_log_event");
+ return 1;
}
/*
@@ -1526,7 +1714,7 @@ int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname)
if (open_log(&rli->relay_log, glob_hostname, opt_relay_logname,
"-relay-bin", opt_relaylog_index_name,
LOG_BIN, 1 /* read_append cache */,
- 1 /* no auto events */,
+ 0 /* starting from 5.0 we want relay logs to have auto events */,
max_relay_log_size ? max_relay_log_size : max_binlog_size))
{
pthread_mutex_unlock(&rli->data_lock);
@@ -1561,7 +1749,7 @@ file '%s', errno %d)", fname, my_errno);
/* Init relay log with first entry in the relay index file */
if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */,
- &msg))
+ &msg, 0))
{
sql_print_error("Failed to open the relay log 'FIRST' (relay_log_pos 4)");
goto err;
@@ -1626,7 +1814,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
rli->group_relay_log_name,
rli->group_relay_log_pos,
0 /* no data lock*/,
- &msg))
+ &msg, 0))
{
char llbuf[22];
sql_print_error("Failed to open the relay log '%s' (relay_log_pos %s)",
@@ -1635,8 +1823,18 @@ Failed to open the existing relay log info file '%s' (errno %d)",
goto err;
}
}
- DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
- DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
+
+#ifndef DBUG_OFF
+ {
+ char llbuf1[22], llbuf2[22];
+ DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
+ llstr(my_b_tell(rli->cur_log),llbuf1),
+ llstr(rli->event_relay_log_pos,llbuf2)));
+ DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
+ DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
+ }
+#endif
+
/*
Now change the cache from READ to WRITE - must do this
before flush_relay_log_info
@@ -2140,7 +2338,8 @@ int show_master_info(THD* thd, MASTER_INFO* mi)
field_list.push_back(new Item_return_int("Seconds_Behind_Master", 10,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
if (mi->host[0])
@@ -2518,7 +2717,7 @@ err:
thd->exit_cond(msg);
DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \
improper_arguments: %d timed_out: %d",
- (int) thd->killed,
+ thd->killed_errno(),
(int) (init_abort_pos_wait != abort_pos_wait),
(int) slave_running,
(int) (error == -2),
@@ -2531,6 +2730,11 @@ improper_arguments: %d timed_out: %d",
DBUG_RETURN( error ? error : event_count );
}
+void set_slave_thread_options(THD* thd)
+{
+ thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) |
+ OPTION_AUTO_IS_NULL;
+}
/*
init_slave_thread()
@@ -2548,6 +2752,7 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
thd->master_access= ~0;
thd->priv_user = 0;
thd->slave_thread = 1;
+ set_slave_thread_options(thd);
/*
It's nonsense to constrain the slave threads with max_join_size; if a
query succeeded on master, we HAVE to execute it. So set
@@ -2809,13 +3014,14 @@ bool st_relay_log_info::is_until_satisfied()
if (until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_UNKNOWN)
{
- /*
- We have no cached comaprison results so we should compare log names
- and cache result
+ /*
+ We have no cached comparison results so we should compare log names
+ and cache result.
+ If we are after RESET SLAVE, and the SQL slave thread has not processed
+ any event yet, it could be that group_master_log_name is "". In that case,
+ just wait for more events (as there is no sensible comparison to do).
*/
- DBUG_ASSERT(*log_name || log_pos == 0);
-
if (*log_name)
{
const char *basename= log_name + dirname_length(log_name);
@@ -2890,28 +3096,63 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
int exec_res;
/*
- Skip queries originating from this server or number of
- queries specified by the user in slave_skip_counter
- We can't however skip event's that has something to do with the
+ Queries originating from this server must be skipped.
+ Low-level events (Format_desc, Rotate, Stop) from this server
+ must also be skipped. But for those we don't want to modify
+ group_master_log_pos, because these events did not exist on the master.
+ Format_desc is not completely skipped.
+ Skip queries specified by the user in slave_skip_counter.
+ We can't however skip events that has something to do with the
log files themselves.
+ Filtering on own server id is extremely important, to ignore execution of
+ events created by the creation/rotation of the relay log (remember that
+ now the relay log starts with its Format_desc, has a Rotate etc).
*/
-
- if ((ev->server_id == (uint32) ::server_id && !replicate_same_server_id) ||
- (rli->slave_skip_counter && type_code != ROTATE_EVENT))
+
+ DBUG_PRINT("info",("type_code=%d, server_id=%d",type_code,ev->server_id));
+
+ if ((ev->server_id == (uint32) ::server_id &&
+ !replicate_same_server_id &&
+ type_code != FORMAT_DESCRIPTION_EVENT) ||
+ (rli->slave_skip_counter &&
+ type_code != ROTATE_EVENT && type_code != STOP_EVENT &&
+ type_code != START_EVENT_V3 && type_code!= FORMAT_DESCRIPTION_EVENT))
{
- /* TODO: I/O thread should not even log events with the same server id */
- rli->inc_group_relay_log_pos(ev->get_event_len(),
- type_code != STOP_EVENT ? ev->log_pos : LL(0),
- 1/* skip lock*/);
- flush_relay_log_info(rli);
-
+ DBUG_PRINT("info", ("event skipped"));
+ if (thd->options & OPTION_BEGIN)
+ rli->inc_event_relay_log_pos();
+ else
+ {
+ rli->inc_group_relay_log_pos((type_code == ROTATE_EVENT ||
+ type_code == STOP_EVENT ||
+ type_code == FORMAT_DESCRIPTION_EVENT) ?
+ LL(0) : ev->log_pos,
+ 1/* skip lock*/);
+ flush_relay_log_info(rli);
+ }
+
/*
- Protect against common user error of setting the counter to 1
- instead of 2 while recovering from an failed auto-increment insert
+ Protect against common user error of setting the counter to 1
+ instead of 2 while recovering from an insert which used auto_increment,
+ rand or user var.
*/
if (rli->slave_skip_counter &&
- !((type_code == INTVAR_EVENT || type_code == STOP_EVENT) &&
- rli->slave_skip_counter == 1))
+ !((type_code == INTVAR_EVENT ||
+ type_code == RAND_EVENT ||
+ type_code == USER_VAR_EVENT) &&
+ rli->slave_skip_counter == 1) &&
+ /*
+ The events from ourselves which have something to do with the relay
+ log itself must be skipped, true, but they mustn't decrement
+ rli->slave_skip_counter, because the user is supposed to not see
+ these events (they are not in the master's binlog) and if we
+ decremented, START SLAVE would for example decrement when it sees
+ the Rotate, so the event which the user probably wanted to skip
+ would not be skipped.
+ */
+ !(ev->server_id == (uint32) ::server_id &&
+ (type_code == ROTATE_EVENT || type_code == STOP_EVENT ||
+ type_code == START_EVENT_V3 || type_code == FORMAT_DESCRIPTION_EVENT)))
--rli->slave_skip_counter;
pthread_mutex_unlock(&rli->data_lock);
delete ev;
@@ -2927,7 +3168,16 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
ev->thd = thd;
exec_res = ev->exec_event(rli);
DBUG_ASSERT(rli->sql_thd==thd);
- delete ev;
+ /*
+ Format_description_log_event should not be deleted because it will be
+ used to read info about the relay log's format; it will be deleted when
+ the SQL thread does not need it, i.e. when this thread terminates.
+ */
+ if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ {
+ DBUG_PRINT("info", ("Deleting the event after it has been executed"));
+ delete ev;
+ }
return exec_res;
}
else
@@ -3028,7 +3278,8 @@ connected:
thd->proc_info = "Checking master version";
if (get_master_version_and_clock(mysql, mi))
goto err;
- if (!mi->old_format)
+
+ if (mi->rli.relay_log.description_event_for_queue->binlog_version > 1)
{
/*
Register ourselves with the master.
@@ -3236,6 +3487,9 @@ err:
pthread_mutex_lock(&mi->run_lock);
mi->slave_running = 0;
mi->io_thd = 0;
+ /* Forget the relay log's format */
+ delete mi->rli.relay_log.description_event_for_queue;
+ mi->rli.relay_log.description_event_for_queue= 0;
// TODO: make rpl_status part of MASTER_INFO
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
mi->abort_slave = 0; // TODO: check if this is needed
@@ -3331,15 +3585,38 @@ slave_begin:
if (init_relay_log_pos(rli,
rli->group_relay_log_name,
rli->group_relay_log_pos,
- 1 /*need data lock*/, &errmsg))
+ 1 /*need data lock*/, &errmsg,
+ 1 /*look for a description_event*/))
{
sql_print_error("Error initializing relay log position: %s",
errmsg);
goto err;
}
THD_CHECK_SENTRY(thd);
- DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
- DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
+#ifndef DBUG_OFF
+ {
+ char llbuf1[22], llbuf2[22];
+ DBUG_PRINT("info", ("my_b_tell(rli->cur_log)=%s rli->event_relay_log_pos=%s",
+ llstr(my_b_tell(rli->cur_log),llbuf1),
+ llstr(rli->event_relay_log_pos,llbuf2)));
+ DBUG_ASSERT(rli->event_relay_log_pos >= BIN_LOG_HEADER_SIZE);
+ /*
+ Wonder if this is correct. I (Guilhem) wonder if my_b_tell() returns the
+ correct position when it's called just after my_b_seek() (the questionable
+ stuff is those "seek is done on next read" comments in the my_b_seek()
+ source code).
+ The crude reality is that this assertion randomly fails whereas
+ replication seems to work fine. And there is no easy explanation why it
+ fails (as we my_b_seek(rli->event_relay_log_pos) at the very end of
+ init_relay_log_pos() called above). Maybe the assertion would be
+ meaningful if we held rli->data_lock between the my_b_seek() and the
+ DBUG_ASSERT().
+ */
+#ifdef SHOULD_BE_CHECKED
+ DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->event_relay_log_pos);
+#endif
+ }
+#endif
DBUG_ASSERT(rli->sql_thd == thd);
DBUG_PRINT("master_info",("log_file_name: %s position: %s",
@@ -3390,7 +3667,12 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
err:
VOID(pthread_mutex_lock(&LOCK_thread_count));
- thd->query = thd->db = 0; // extra safety
+ /*
+ Some extra safety, which should not been needed (normally, event deletion
+ should already have done these assignments (each event which sets these
+ variables is supposed to set them to 0 before terminating)).
+ */
+ thd->query= thd->db= thd->catalog= 0;
thd->query_length = 0;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->proc_info = "Waiting for slave mutex on exit";
@@ -3400,11 +3682,9 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun
/* When master_pos_wait() wakes up it will check this and terminate */
rli->slave_running= 0;
- /*
- Going out of the transaction. Necessary to mark it, in case the user
- restarts replication from a non-transactional statement (with CHANGE
- MASTER).
- */
+ /* Forget the relay log's format */
+ delete rli->relay_log.description_event_for_exec;
+ rli->relay_log.description_event_for_exec= 0;
/* Wake up master_pos_wait() */
pthread_mutex_unlock(&rli->data_lock);
DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions"));
@@ -3502,7 +3782,7 @@ static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev)
if (unlikely(cev_not_written))
break;
Execute_load_log_event xev(thd,0,0);
- xev.log_pos = mi->master_log_pos;
+ xev.log_pos = cev->log_pos;
if (unlikely(mi->rli.relay_log.append(&xev)))
{
sql_print_error("Slave I/O: error writing Exec_load event to \
@@ -3516,7 +3796,6 @@ relay log");
{
cev->block = (char*)net->read_pos;
cev->block_len = num_bytes;
- cev->log_pos = mi->master_log_pos;
if (unlikely(mi->rli.relay_log.append(cev)))
{
sql_print_error("Slave I/O: error writing Create_file event to \
@@ -3530,7 +3809,7 @@ relay log");
{
aev.block = (char*)net->read_pos;
aev.block_len = num_bytes;
- aev.log_pos = mi->master_log_pos;
+ aev.log_pos = cev->log_pos;
if (unlikely(mi->rli.relay_log.append(&aev)))
{
sql_print_error("Slave I/O: error writing Append_block event to \
@@ -3558,6 +3837,7 @@ err:
DESCRIPTION
Updates the master info with the place in the next binary
log where we should start reading.
+ Rotate the relay log to avoid mixed-format relay logs.
NOTES
We assume we already locked mi->data_lock
@@ -3588,21 +3868,33 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev)
if (disconnect_slave_event_count)
events_till_disconnect++;
#endif
+ /*
+ If description_event_for_queue is format <4, there is conversion in the
+ relay log to the slave's format (4). And Rotate can mean upgrade or
+ nothing. If upgrade, it's to 5.0 or newer, so we will get a Format_desc, so
+ no need to reset description_event_for_queue now. And if it's nothing (same
+ master version as before), no need (still using the slave's format).
+ */
+ if (mi->rli.relay_log.description_event_for_queue->binlog_version >= 4)
+ {
+ delete mi->rli.relay_log.description_event_for_queue;
+ /* start from format 3 (MySQL 4.0) again */
+ mi->rli.relay_log.description_event_for_queue= new
+ Format_description_log_event(3);
+ }
+ /*
+ Rotate the relay log makes binlog format detection easier (at next slave
+ start or mysqlbinlog)
+ */
+ rotate_relay_log(mi); /* will take the right mutexes */
DBUG_RETURN(0);
}
-
/*
- queue_old_event()
-
- Writes a 3.23 event to the relay log.
-
- TODO:
- Test this code before release - it has to be tested on a separate
- setup with 3.23 master
+ Reads a 3.23 event and converts it to the slave's format. This code was copied
+ from MySQL 4.0.
*/
-
-static int queue_old_event(MASTER_INFO *mi, const char *buf,
+static int queue_binlog_ver_1_event(MASTER_INFO *mi, const char *buf,
ulong event_len)
{
const char *errmsg = 0;
@@ -3610,7 +3902,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
bool ignore_event= 0;
char *tmp_buf = 0;
RELAY_LOG_INFO *rli= &mi->rli;
- DBUG_ENTER("queue_old_event");
+ DBUG_ENTER("queue_binlog_ver_1_event");
/*
If we get Load event, we need to pass a non-reusable buffer
@@ -3642,7 +3934,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
connected to the master).
*/
Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg,
- 1 /*old format*/ );
+ mi->rli.relay_log.description_event_for_queue);
if (unlikely(!ev))
{
sql_print_error("Read invalid event from master: '%s',\
@@ -3652,7 +3944,7 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
DBUG_RETURN(1);
}
pthread_mutex_lock(&mi->data_lock);
- ev->log_pos = mi->master_log_pos;
+ ev->log_pos= mi->master_log_pos; /* 3.23 events don't contain log_pos */
switch (ev->get_type_code()) {
case STOP_EVENT:
ignore_event= 1;
@@ -3677,13 +3969,11 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
{
/* We come here when and only when tmp_buf != 0 */
DBUG_ASSERT(tmp_buf);
+ inc_pos=event_len;
+ ev->log_pos+= inc_pos;
int error = process_io_create_file(mi,(Create_file_log_event*)ev);
delete ev;
- /*
- We had incremented event_len, but now when it is used to calculate the
- position in the master's log, we must use the original value.
- */
- mi->master_log_pos += --event_len;
+ mi->master_log_pos += inc_pos;
DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
pthread_mutex_unlock(&mi->data_lock);
my_free((char*)tmp_buf, MYF(0));
@@ -3695,6 +3985,12 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
}
if (likely(!ignore_event))
{
+ if (ev->log_pos)
+ /*
+ Don't do it for fake Rotate events (see comment in
+ Log_event::Log_event(const char* buf...) in log_event.cc).
+ */
+ ev->log_pos+= event_len; /* make log_pos be the pos of the end of the event */
if (unlikely(rli->relay_log.append(ev)))
{
delete ev;
@@ -3710,10 +4006,98 @@ static int queue_old_event(MASTER_INFO *mi, const char *buf,
DBUG_RETURN(0);
}
+/*
+ Reads a 4.0 event and converts it to the slave's format. This code was copied
+ from queue_binlog_ver_1_event(), with some affordable simplifications.
+*/
+static int queue_binlog_ver_3_event(MASTER_INFO *mi, const char *buf,
+ ulong event_len)
+{
+ const char *errmsg = 0;
+ ulong inc_pos;
+ char *tmp_buf = 0;
+ RELAY_LOG_INFO *rli= &mi->rli;
+ DBUG_ENTER("queue_binlog_ver_3_event");
+
+ /* read_log_event() will adjust log_pos to be end_log_pos */
+ Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg,
+ mi->rli.relay_log.description_event_for_queue);
+ if (unlikely(!ev))
+ {
+ sql_print_error("Read invalid event from master: '%s',\
+ master could be corrupt but a more likely cause of this is a bug",
+ errmsg);
+ my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR));
+ DBUG_RETURN(1);
+ }
+ pthread_mutex_lock(&mi->data_lock);
+ switch (ev->get_type_code()) {
+ case STOP_EVENT:
+ goto err;
+ case ROTATE_EVENT:
+ if (unlikely(process_io_rotate(mi,(Rotate_log_event*)ev)))
+ {
+ delete ev;
+ pthread_mutex_unlock(&mi->data_lock);
+ DBUG_RETURN(1);
+ }
+ inc_pos= 0;
+ break;
+ default:
+ inc_pos= event_len;
+ break;
+ }
+ if (unlikely(rli->relay_log.append(ev)))
+ {
+ delete ev;
+ pthread_mutex_unlock(&mi->data_lock);
+ DBUG_RETURN(1);
+ }
+ rli->relay_log.harvest_bytes_written(&rli->log_space_total);
+ delete ev;
+ mi->master_log_pos+= inc_pos;
+err:
+ DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
+ pthread_mutex_unlock(&mi->data_lock);
+ DBUG_RETURN(0);
+}
+
+/*
+ queue_old_event()
+
+ Writes a 3.23 or 4.0 event to the relay log, after converting it to the 5.0
+ (exactly, slave's) format. To do the conversion, we create a 5.0 event from
+ the 3.23/4.0 bytes, then write this event to the relay log.
+
+ TODO:
+ Test this code before release - it has to be tested on a separate
+ setup with 3.23 master or 4.0 master
+*/
+
+static int queue_old_event(MASTER_INFO *mi, const char *buf,
+ ulong event_len)
+{
+ switch (mi->rli.relay_log.description_event_for_queue->binlog_version)
+ {
+ case 1:
+ return queue_binlog_ver_1_event(mi,buf,event_len);
+ case 3:
+ return queue_binlog_ver_3_event(mi,buf,event_len);
+ default: /* unsupported format; eg version 2 */
+ DBUG_PRINT("info",("unsupported binlog format %d in queue_old_event()",
+ mi->rli.relay_log.description_event_for_queue->binlog_version));
+ return 1;
+ }
+}
/*
queue_event()
+ If the event is 3.23/4.0, passes it to queue_old_event() which will convert
+ it. Otherwise, writes a 5.0 (or newer) event to the relay log. Then there is
+ no format conversion, it's pure read/write of bytes.
+ So a 5.0.0 slave's relay log can contain events in the slave's format or in
+ any >=5.0.0 format.
*/
int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
@@ -3723,7 +4107,8 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
RELAY_LOG_INFO *rli= &mi->rli;
DBUG_ENTER("queue_event");
- if (mi->old_format)
+ if (mi->rli.relay_log.description_event_for_queue->binlog_version<4 &&
+ buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT /* a way to escape */)
DBUG_RETURN(queue_old_event(mi,buf,event_len));
pthread_mutex_lock(&mi->data_lock);
@@ -3750,7 +4135,7 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
goto err;
case ROTATE_EVENT:
{
- Rotate_log_event rev(buf,event_len,0);
+ Rotate_log_event rev(buf,event_len,mi->rli.relay_log.description_event_for_queue);
if (unlikely(process_io_rotate(mi,&rev)))
{
error= 1;
@@ -3763,6 +4148,42 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
inc_pos= 0;
break;
}
+ case FORMAT_DESCRIPTION_EVENT:
+ {
+ /*
+ Create an event, and save it (when we rotate the relay log, we will have
+ to write this event again).
+ */
+ /*
+ We are the only thread which reads/writes description_event_for_queue. The
+ relay_log struct does not move (though some members of it can change), so
+ we needn't any lock (no rli->data_lock, no log lock).
+ */
+ Format_description_log_event* tmp;
+ const char* errmsg;
+ if (!(tmp= (Format_description_log_event*)
+ Log_event::read_log_event(buf, event_len, &errmsg,
+ mi->rli.relay_log.description_event_for_queue)))
+ {
+ error= 2;
+ goto err;
+ }
+ delete mi->rli.relay_log.description_event_for_queue;
+ mi->rli.relay_log.description_event_for_queue= tmp;
+ /*
+ Though this does some conversion to the slave's format, this will
+ preserve the master's binlog format version, and number of event types.
+ */
+ /*
+ If the event was not requested by the slave (the slave did not ask for
+ it), i.e. has end_log_pos=0, we do not increment mi->master_log_pos
+ */
+ inc_pos= uint4korr(buf+LOG_POS_OFFSET) ? event_len : 0;
+ DBUG_PRINT("info",("binlog format is now %d",
+ mi->rli.relay_log.description_event_for_queue->binlog_version));
+
+ }
+ break;
default:
inc_pos= event_len;
break;
@@ -3789,23 +4210,32 @@ int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len)
We still want to increment, so that we won't re-read this event from the
master if the slave IO thread is now stopped/restarted (more efficient if
the events we are ignoring are big LOAD DATA INFILE).
+ But events which were generated by this slave and which do not exist in
+ the master's binlog (i.e. Format_desc, Rotate & Stop) should not increment
+ mi->master_log_pos.
*/
- mi->master_log_pos+= inc_pos;
+ if (buf[EVENT_TYPE_OFFSET]!=FORMAT_DESCRIPTION_EVENT &&
+ buf[EVENT_TYPE_OFFSET]!=ROTATE_EVENT &&
+ buf[EVENT_TYPE_OFFSET]!=STOP_EVENT)
+ mi->master_log_pos+= inc_pos;
DBUG_PRINT("info", ("master_log_pos: %d, event originating from the same server, ignored", (ulong) mi->master_log_pos));
}
else
{
/* write the event to the relay log */
- if (likely(!(error= rli->relay_log.appendv(buf,event_len,0))))
+ if (likely(!(rli->relay_log.appendv(buf,event_len,0))))
{
mi->master_log_pos+= inc_pos;
DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos));
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
}
+ else
+ error=3;
}
err:
pthread_mutex_unlock(&mi->data_lock);
+ DBUG_PRINT("info", ("error=%d", error));
DBUG_RETURN(error);
}
@@ -3830,6 +4260,7 @@ void end_relay_log_info(RELAY_LOG_INFO* rli)
}
rli->inited = 0;
rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT);
+ rli->relay_log.harvest_bytes_written(&rli->log_space_total);
/*
Delete the slave's temporary tables from memory.
In the future there will be other actions than this, to ensure persistance
@@ -4051,6 +4482,7 @@ static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg)
relay_log_pos Current log pos
pending Number of bytes already processed from the event
*/
+ rli->event_relay_log_pos= max(rli->event_relay_log_pos, BIN_LOG_HEADER_SIZE);
my_b_seek(cur_log,rli->event_relay_log_pos);
DBUG_RETURN(cur_log);
}
@@ -4109,28 +4541,40 @@ Log_event* next_event(RELAY_LOG_INFO* rli)
hot_log=0; // Using old binary log
}
}
+
#ifndef DBUG_OFF
{
+ /* This is an assertion which sometimes fails, let's try to track it */
char llbuf1[22], llbuf2[22];
- DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE);
- /*
- The next assertion sometimes (very rarely) fails, let's try to track
- it
- */
- DBUG_PRINT("info", ("\
-Before assert, my_b_tell(cur_log)=%s rli->event_relay_log_pos=%s",
+ DBUG_PRINT("info", ("my_b_tell(cur_log)=%s rli->event_relay_log_pos=%s",
llstr(my_b_tell(cur_log),llbuf1),
- llstr(rli->group_relay_log_pos,llbuf2)));
- DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos);
+ llstr(rli->event_relay_log_pos,llbuf2)));
+ DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE);
+ DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos);
}
#endif
/*
Relay log is always in new format - if the master is 3.23, the
- I/O thread will convert the format for us
+ I/O thread will convert the format for us.
+ A problem: the description event may be in a previous relay log. So if the
+ slave has been shutdown meanwhile, we would have to look in old relay
+ logs, which may even have been deleted. So we need to write this
+ description event at the beginning of the relay log.
+ When the relay log is created when the I/O thread starts, easy: the master
+ will send the description event and we will queue it.
+ But if the relay log is created by new_file(): then the solution is:
+ MYSQL_LOG::open() will write the buffered description event.
*/
- if ((ev=Log_event::read_log_event(cur_log,0,(bool)0 /* new format */)))
+ if ((ev=Log_event::read_log_event(cur_log,0,
+ rli->relay_log.description_event_for_exec)))
+
{
DBUG_ASSERT(thd==rli->sql_thd);
+ /*
+ read it while we have a lock, to avoid a mutex lock in
+ inc_event_relay_log_pos()
+ */
+ rli->future_event_relay_log_pos= my_b_tell(cur_log);
if (hot_log)
pthread_mutex_unlock(log_lock);
DBUG_RETURN(ev);
@@ -4348,8 +4792,9 @@ void rotate_relay_log(MASTER_INFO* mi)
DBUG_ENTER("rotate_relay_log");
RELAY_LOG_INFO* rli= &mi->rli;
- lock_slave_threads(mi);
- pthread_mutex_lock(&rli->data_lock);
+ /* We don't lock rli->run_lock. This would lead to deadlocks. */
+ pthread_mutex_lock(&mi->run_lock);
+
/*
We need to test inited because otherwise, new_file() will attempt to lock
LOCK_log, which may not be inited (if we're not a slave).
@@ -4378,8 +4823,7 @@ void rotate_relay_log(MASTER_INFO* mi)
*/
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
end:
- pthread_mutex_unlock(&rli->data_lock);
- unlock_slave_threads(mi);
+ pthread_mutex_unlock(&mi->run_lock);
DBUG_VOID_RETURN;
}
diff --git a/sql/slave.h b/sql/slave.h
index 384436fdfcc..46fe58e1976 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -77,11 +77,6 @@ extern my_bool opt_log_slave_updates;
extern ulonglong relay_log_space_limit;
struct st_master_info;
-enum enum_binlog_formats {
- BINLOG_FORMAT_CURRENT=0, /* 0 is important for easy 'if (mi->old_format)' */
- BINLOG_FORMAT_323_LESS_57,
- BINLOG_FORMAT_323_GEQ_57 };
-
/****************************************************************************
Replication SQL Thread
@@ -183,6 +178,8 @@ typedef struct st_relay_log_info
ulonglong group_relay_log_pos;
char event_relay_log_name[FN_REFLEN];
ulonglong event_relay_log_pos;
+ ulonglong future_event_relay_log_pos;
+
/*
Original log name and position of the group we're currently executing
(whose coordinates are group_relay_log_name/pos in the relay log)
@@ -286,12 +283,14 @@ typedef struct st_relay_log_info
until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN;
}
- inline void inc_event_relay_log_pos(ulonglong val)
+ inline void inc_event_relay_log_pos()
{
- event_relay_log_pos+= val;
+ event_relay_log_pos= future_event_relay_log_pos;
}
- void inc_group_relay_log_pos(ulonglong val, ulonglong log_pos, bool skip_lock=0);
+ void inc_group_relay_log_pos(ulonglong log_pos,
+ bool skip_lock=0);
+
int wait_for_pos(THD* thd, String* log_name, longlong log_pos,
longlong timeout);
void close_temporary_tables();
@@ -368,7 +367,6 @@ typedef struct st_master_info
int events_till_abort;
#endif
bool inited;
- enum enum_binlog_formats old_format;
volatile bool abort_slave, slave_running;
volatile ulong slave_run_id;
/*
@@ -383,7 +381,7 @@ typedef struct st_master_info
long clock_diff_with_master;
st_master_info()
- :ssl(0), fd(-1), io_thd(0), inited(0), old_format(BINLOG_FORMAT_CURRENT),
+ :ssl(0), fd(-1), io_thd(0), inited(0),
abort_slave(0),slave_running(0), slave_run_id(0)
{
host[0] = 0; user[0] = 0; password[0] = 0;
@@ -514,10 +512,12 @@ void lock_slave_threads(MASTER_INFO* mi);
void unlock_slave_threads(MASTER_INFO* mi);
void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse);
int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,ulonglong pos,
- bool need_data_lock, const char** errmsg);
+ bool need_data_lock, const char** errmsg,
+ bool look_for_description_event);
int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset,
const char** errmsg);
+void set_slave_thread_options(THD* thd);
void rotate_relay_log(MASTER_INFO* mi);
extern "C" pthread_handler_decl(handle_slave_io,arg);
diff --git a/sql/sp.cc b/sql/sp.cc
new file mode 100644
index 00000000000..dda6e0fad60
--- /dev/null
+++ b/sql/sp.cc
@@ -0,0 +1,1183 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include "sp.h"
+#include "sp_head.h"
+#include "sp_cache.h"
+
+static bool
+create_string(THD *thd, String *buf,
+ int sp_type,
+ sp_name *name,
+ const char *params, ulong paramslen,
+ const char *returns, ulong returnslen,
+ const char *body, ulong bodylen,
+ st_sp_chistics *chistics);
+
+/*
+ *
+ * DB storage of Stored PROCEDUREs and FUNCTIONs
+ *
+ */
+
+enum
+{
+ MYSQL_PROC_FIELD_DB = 0,
+ MYSQL_PROC_FIELD_NAME,
+ MYSQL_PROC_FIELD_TYPE,
+ MYSQL_PROC_FIELD_SPECIFIC_NAME,
+ MYSQL_PROC_FIELD_LANGUAGE,
+ MYSQL_PROC_FIELD_ACCESS,
+ MYSQL_PROC_FIELD_DETERMINISTIC,
+ MYSQL_PROC_FIELD_SECURITY_TYPE,
+ MYSQL_PROC_FIELD_PARAM_LIST,
+ MYSQL_PROC_FIELD_RETURNS,
+ MYSQL_PROC_FIELD_BODY,
+ MYSQL_PROC_FIELD_DEFINER,
+ MYSQL_PROC_FIELD_CREATED,
+ MYSQL_PROC_FIELD_MODIFIED,
+ MYSQL_PROC_FIELD_SQL_MODE,
+ MYSQL_PROC_FIELD_COMMENT,
+ MYSQL_PROC_FIELD_COUNT
+};
+
+bool mysql_proc_table_exists= 1;
+
+/* *opened=true means we opened ourselves */
+static int
+db_find_routine_aux(THD *thd, int type, sp_name *name,
+ enum thr_lock_type ltype, TABLE **tablep, bool *opened)
+{
+ TABLE *table;
+ byte key[64+64+1]; // db, name, type
+ uint keylen;
+ DBUG_ENTER("db_find_routine_aux");
+ DBUG_PRINT("enter", ("type: %d name: %*s",
+ type, name->m_name.length, name->m_name.str));
+
+ /*
+ Speed up things if mysql.proc doesn't exists
+ mysql_proc_table_exists is set when on creates a stored procedure
+ or on flush privileges
+ */
+ if (!mysql_proc_table_exists && ltype == TL_READ)
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+
+ // Put the key used to read the row together
+ keylen= name->m_db.length;
+ if (keylen > 64)
+ keylen= 64;
+ memcpy(key, name->m_db.str, keylen);
+ memset(key+keylen, (int)' ', 64-keylen); // Pad with space
+ keylen= name->m_name.length;
+ if (keylen > 64)
+ keylen= 64;
+ memcpy(key+64, name->m_name.str, keylen);
+ memset(key+64+keylen, (int)' ', 64-keylen); // Pad with space
+ key[128]= type;
+ keylen= sizeof(key);
+
+ for (table= thd->open_tables ; table ; table= table->next)
+ if (strcmp(table->table_cache_key, "mysql") == 0 &&
+ strcmp(table->real_name, "proc") == 0)
+ break;
+ if (table)
+ *opened= FALSE;
+ else
+ {
+ TABLE_LIST tables;
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+ if (! (table= open_ltable(thd, &tables, ltype)))
+ {
+ *tablep= NULL;
+ mysql_proc_table_exists= 0;
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ }
+ *opened= TRUE;
+ }
+ mysql_proc_table_exists= 1;
+
+ if (table->file->index_read_idx(table->record[0], 0,
+ key, keylen,
+ HA_READ_KEY_EXACT))
+ {
+ *tablep= NULL;
+ DBUG_RETURN(SP_KEY_NOT_FOUND);
+ }
+ *tablep= table;
+
+ DBUG_RETURN(SP_OK);
+}
+
+
+static int
+db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
+{
+ extern int yyparse(void *thd);
+ TABLE *table;
+ const char *params, *returns, *body;
+ int ret;
+ bool opened;
+ const char *definer;
+ longlong created;
+ longlong modified;
+ st_sp_chistics chistics;
+ char *ptr;
+ uint length;
+ char buff[65];
+ String str(buff, sizeof(buff), &my_charset_bin);
+ ulong sql_mode;
+ DBUG_ENTER("db_find_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s",
+ type, name->m_name.length, name->m_name.str));
+
+ ret= db_find_routine_aux(thd, type, name, TL_READ, &table, &opened);
+ if (ret != SP_OK)
+ goto done;
+
+ if (table->fields != MYSQL_PROC_FIELD_COUNT)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ if ((ptr= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_DETERMINISTIC])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+ bzero((char *)&chistics, sizeof(chistics));
+ chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE);
+
+ if ((ptr= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+ chistics.suid= (ptr[0] == 'I' ? IS_NOT_SUID : IS_SUID);
+
+ if ((params= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL)
+ {
+ params= "";
+ }
+
+ if (type == TYPE_ENUM_PROCEDURE)
+ returns= "";
+ else if ((returns= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_RETURNS])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ if ((body= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_BODY])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ // Get additional information
+ if ((definer= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ modified= table->field[MYSQL_PROC_FIELD_MODIFIED]->val_int();
+ created= table->field[MYSQL_PROC_FIELD_CREATED]->val_int();
+
+ sql_mode= (ulong) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
+
+ table->field[MYSQL_PROC_FIELD_COMMENT]->val_str(&str, &str);
+
+ ptr= 0;
+ if ((length= str.length()))
+ ptr= thd->strmake(str.ptr(), length);
+ chistics.comment.str= ptr;
+ chistics.comment.length= length;
+
+ if (opened)
+ {
+ opened= FALSE;
+ close_thread_tables(thd, 0, 1);
+ }
+
+ {
+ String defstr;
+ LEX *oldlex= thd->lex;
+ char olddb[128];
+ bool dbchanged;
+ enum enum_sql_command oldcmd= thd->lex->sql_command;
+ ulong old_sql_mode= thd->variables.sql_mode;
+ ha_rows select_limit= thd->variables.select_limit;
+
+ thd->variables.sql_mode= sql_mode;
+ thd->variables.select_limit= HA_POS_ERROR;
+
+ defstr.set_charset(system_charset_info);
+ if (!create_string(thd, &defstr,
+ type,
+ name,
+ params, strlen(params),
+ returns, strlen(returns),
+ body, strlen(body),
+ &chistics))
+ {
+ ret= SP_INTERNAL_ERROR;
+ goto done;
+ }
+
+ dbchanged= FALSE;
+ if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb),
+ 1, &dbchanged)))
+ goto done;
+
+ {
+ /* This is something of a kludge. We need to initialize some fields
+ * in thd->lex (the unit and master stuff), and the easiest way to
+ * do it is, is to call mysql_init_query(), but this unfortunately
+ * resets teh value_list where we keep the CALL parameters. So we
+ * copy the list and then restore it.
+ */
+ List<Item> vals= thd->lex->value_list;
+
+ mysql_init_query(thd, (uchar*)defstr.c_ptr(), defstr.length(), TRUE);
+ thd->lex->value_list= vals;
+ }
+
+ if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
+ {
+ LEX *newlex= thd->lex;
+ sp_head *sp= newlex->sphead;
+
+ if (dbchanged && (ret= sp_change_db(thd, olddb, 1)))
+ goto done;
+ if (sp)
+ {
+ if (oldlex != newlex)
+ sp->restore_lex(thd);
+ delete sp;
+ newlex->sphead= NULL;
+ }
+ ret= SP_PARSE_ERROR;
+ }
+ else
+ {
+ if (dbchanged && (ret= sp_change_db(thd, olddb, 1)))
+ goto done;
+ *sphp= thd->lex->sphead;
+ (*sphp)->set_info((char *)definer, (uint)strlen(definer),
+ created, modified, &chistics, sql_mode);
+ (*sphp)->optimize();
+ }
+ thd->lex->sql_command= oldcmd;
+ thd->variables.sql_mode= old_sql_mode;
+ thd->variables.select_limit= select_limit;
+ }
+
+ done:
+
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+
+static int
+db_create_routine(THD *thd, int type, sp_head *sp)
+{
+ int ret;
+ TABLE *table;
+ TABLE_LIST tables;
+ char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
+ char olddb[128];
+ bool dbchanged;
+ DBUG_ENTER("db_create_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s",type,sp->m_name.length,sp->m_name.str));
+
+ dbchanged= FALSE;
+ if ((ret= sp_use_new_db(thd, sp->m_db.str, olddb, sizeof(olddb),
+ 0, &dbchanged)))
+ {
+ ret= SP_NO_DB_ERROR;
+ goto done;
+ }
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+
+ if (! (table= open_ltable(thd, &tables, TL_WRITE)))
+ ret= SP_OPEN_TABLE_FAILED;
+ else
+ {
+ restore_record(table, default_values); // Get default values for fields
+ strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS);
+
+ if (table->fields != MYSQL_PROC_FIELD_COUNT)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+ table->field[MYSQL_PROC_FIELD_DB]->
+ store(sp->m_db.str, sp->m_db.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_NAME]->
+ store(sp->m_name.str, sp->m_name.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_TYPE]->
+ store((longlong)type);
+ table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]->
+ store(sp->m_name.str, sp->m_name.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->
+ store((longlong)(sp->m_chistics->detistic ? 1 : 2));
+ if (sp->m_chistics->suid != IS_DEFAULT_SUID)
+ table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
+ store((longlong)sp->m_chistics->suid);
+ table->field[MYSQL_PROC_FIELD_PARAM_LIST]->
+ store(sp->m_params.str, sp->m_params.length, system_charset_info);
+ if (sp->m_retstr.str)
+ table->field[MYSQL_PROC_FIELD_RETURNS]->
+ store(sp->m_retstr.str, sp->m_retstr.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_BODY]->
+ store(sp->m_body.str, sp->m_body.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_DEFINER]->
+ store(definer, (uint)strlen(definer), system_charset_info);
+ ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
+ table->field[MYSQL_PROC_FIELD_SQL_MODE]->
+ store((longlong)thd->variables.sql_mode);
+ if (sp->m_chistics->comment.str)
+ table->field[MYSQL_PROC_FIELD_COMMENT]->
+ store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
+ system_charset_info);
+
+ ret= SP_OK;
+ if (table->file->write_row(table->record[0]))
+ ret= SP_WRITE_ROW_FAILED;
+ }
+
+done:
+ close_thread_tables(thd);
+ if (dbchanged)
+ (void)sp_change_db(thd, olddb, 1);
+ DBUG_RETURN(ret);
+}
+
+
+static int
+db_drop_routine(THD *thd, int type, sp_name *name)
+{
+ TABLE *table;
+ int ret;
+ bool opened;
+ DBUG_ENTER("db_drop_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s",
+ type, name->m_name.length, name->m_name.str));
+
+ ret= db_find_routine_aux(thd, type, name, TL_WRITE, &table, &opened);
+ if (ret == SP_OK)
+ {
+ if (table->file->delete_row(table->record[0]))
+ ret= SP_DELETE_ROW_FAILED;
+ }
+
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+
+static int
+db_update_routine(THD *thd, int type, sp_name *name,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics)
+{
+ TABLE *table;
+ int ret;
+ bool opened;
+ DBUG_ENTER("db_update_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s",
+ type, name->m_name.length, name->m_name.str));
+
+ ret= db_find_routine_aux(thd, type, name, TL_WRITE, &table, &opened);
+ if (ret == SP_OK)
+ {
+ store_record(table,record[1]);
+ table->timestamp_on_update_now = 0; // Don't update create time now.
+ ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
+ if (chistics->suid != IS_DEFAULT_SUID)
+ table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->store((longlong)chistics->suid);
+ if (newname)
+ table->field[MYSQL_PROC_FIELD_NAME]->store(newname,
+ newnamelen,
+ system_charset_info);
+ if (chistics->comment.str)
+ table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str,
+ chistics->comment.length,
+ system_charset_info);
+ if ((table->file->update_row(table->record[1],table->record[0])))
+ ret= SP_WRITE_ROW_FAILED;
+ }
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+
+struct st_used_field
+{
+ const char *field_name;
+ uint field_length;
+ enum enum_field_types field_type;
+ Field *field;
+};
+
+static struct st_used_field init_fields[]=
+{
+ { "Db", NAME_LEN, MYSQL_TYPE_STRING, 0},
+ { "Name", NAME_LEN, MYSQL_TYPE_STRING, 0},
+ { "Type", 9, MYSQL_TYPE_STRING, 0},
+ { "Definer", 77, MYSQL_TYPE_STRING, 0},
+ { "Modified", 0, MYSQL_TYPE_TIMESTAMP, 0},
+ { "Created", 0, MYSQL_TYPE_TIMESTAMP, 0},
+ { "Security_type", 1, MYSQL_TYPE_STRING, 0},
+ { "Comment", NAME_LEN, MYSQL_TYPE_STRING, 0},
+ { 0, 0, MYSQL_TYPE_STRING, 0}
+};
+
+
+static int
+print_field_values(THD *thd, TABLE *table,
+ struct st_used_field *used_fields,
+ int type, const char *wild)
+{
+ Protocol *protocol= thd->protocol;
+
+ if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type)
+ {
+ String db_string;
+ String name_string;
+ struct st_used_field *used_field= used_fields;
+
+ if (get_field(&thd->mem_root, used_field->field, &db_string))
+ db_string.set_ascii("", 0);
+ used_field+= 1;
+ get_field(&thd->mem_root, used_field->field, &name_string);
+
+ if (!wild || !wild[0] || !wild_compare(name_string.ptr(), wild, 0))
+ {
+ protocol->prepare_for_resend();
+ protocol->store(&db_string);
+ protocol->store(&name_string);
+ for (used_field++;
+ used_field->field_name;
+ used_field++)
+ {
+ switch (used_field->field_type) {
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ TIME tmp_time;
+
+ bzero((char *)&tmp_time, sizeof(tmp_time));
+ ((Field_timestamp *) used_field->field)->get_time(&tmp_time);
+ protocol->store(&tmp_time);
+ }
+ break;
+ default:
+ {
+ String tmp_string;
+
+ get_field(&thd->mem_root, used_field->field, &tmp_string);
+ protocol->store(&tmp_string);
+ }
+ break;
+ }
+ }
+ if (protocol->write())
+ return SP_INTERNAL_ERROR;
+ }
+ }
+ return SP_OK;
+}
+
+
+static int
+db_show_routine_status(THD *thd, int type, const char *wild)
+{
+ TABLE *table;
+ TABLE_LIST tables;
+ int res;
+ DBUG_ENTER("db_show_routine_status");
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+
+ if (! (table= open_ltable(thd, &tables, TL_READ)))
+ {
+ res= SP_OPEN_TABLE_FAILED;
+ goto done;
+ }
+ else
+ {
+ Item *item;
+ List<Item> field_list;
+ struct st_used_field *used_field;
+ st_used_field used_fields[array_elements(init_fields)];
+
+ memcpy((char*) used_fields, (char*) init_fields, sizeof(used_fields));
+ /* Init header */
+ for (used_field= &used_fields[0];
+ used_field->field_name;
+ used_field++)
+ {
+ switch (used_field->field_type) {
+ case MYSQL_TYPE_TIMESTAMP:
+ field_list.push_back(item=new Item_datetime(used_field->field_name));
+ break;
+ default:
+ field_list.push_back(item=new Item_empty_string(used_field->field_name,
+ used_field->
+ field_length));
+ break;
+ }
+ }
+ /* Print header */
+ if (thd->protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ {
+ res= SP_INTERNAL_ERROR;
+ goto err_case;
+ }
+
+ /*
+ Init fields
+
+ tables is not VIEW for sure => we can pass 0 as condition
+ */
+ setup_tables(thd, &tables, 0);
+ for (used_field= &used_fields[0];
+ used_field->field_name;
+ used_field++)
+ {
+ Item_field *field= new Item_field("mysql", "proc",
+ used_field->field_name);
+ if (!(used_field->field= find_field_in_tables(thd, field, &tables,
+ 0, TRUE, 1)))
+ {
+ res= SP_INTERNAL_ERROR;
+ goto err_case1;
+ }
+ }
+
+ table->file->ha_index_init(0);
+ if ((res= table->file->index_first(table->record[0])))
+ {
+ res= (res == HA_ERR_END_OF_FILE) ? 0 : SP_INTERNAL_ERROR;
+ goto err_case1;
+ }
+ if ((res= print_field_values(thd, table, used_fields, type, wild)))
+ goto err_case1;
+ while (!table->file->index_next(table->record[0]))
+ {
+ if ((res= print_field_values(thd, table, used_fields, type, wild)))
+ goto err_case1;
+ }
+ res= SP_OK;
+ }
+
+err_case1:
+ send_eof(thd);
+err_case:
+ table->file->ha_index_end();
+ close_thread_tables(thd);
+done:
+ DBUG_RETURN(res);
+}
+
+
+/* Drop all routines in database 'db' */
+int
+sp_drop_db_routines(THD *thd, char *db)
+{
+ TABLE *table;
+ byte key[64]; // db
+ uint keylen;
+ int ret;
+ DBUG_ENTER("sp_drop_db_routines");
+ DBUG_PRINT("enter", ("db: %s", db));
+
+ // Put the key used to read the row together
+ keylen= strlen(db);
+ if (keylen > 64)
+ keylen= 64;
+ memcpy(key, db, keylen);
+ memset(key+keylen, (int)' ', 64-keylen); // Pad with space
+ keylen= sizeof(key);
+
+ for (table= thd->open_tables ; table ; table= table->next)
+ if (strcmp(table->table_cache_key, "mysql") == 0 &&
+ strcmp(table->real_name, "proc") == 0)
+ break;
+ if (! table)
+ {
+ TABLE_LIST tables;
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+ if (! (table= open_ltable(thd, &tables, TL_WRITE)))
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ }
+
+ ret= SP_OK;
+ table->file->ha_index_init(0);
+ if (! table->file->index_read(table->record[0],
+ key, keylen, HA_READ_KEY_EXACT))
+ {
+ int nxtres;
+ bool deleted= FALSE;
+
+ do {
+ if (! table->file->delete_row(table->record[0]))
+ deleted= TRUE; /* We deleted something */
+ else
+ {
+ ret= SP_DELETE_ROW_FAILED;
+ nxtres= 0;
+ break;
+ }
+ } while (! (nxtres= table->file->index_next_same(table->record[0],
+ key, keylen)));
+ if (nxtres != HA_ERR_END_OF_FILE)
+ ret= SP_KEY_NOT_FOUND;
+ if (deleted)
+ sp_cache_invalidate();
+ }
+ table->file->ha_index_end();
+
+ close_thread_tables(thd);
+
+ DBUG_RETURN(ret);
+}
+
+
+/*****************************************************************************
+ PROCEDURE
+******************************************************************************/
+
+sp_head *
+sp_find_procedure(THD *thd, sp_name *name)
+{
+ sp_head *sp;
+ DBUG_ENTER("sp_find_procedure");
+ DBUG_PRINT("enter", ("name: %*s.%*s",
+ name->m_db.length, name->m_db.str,
+ name->m_name.length, name->m_name.str));
+
+ if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name)))
+ {
+ if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK)
+ sp_cache_insert(&thd->sp_proc_cache, sp);
+ }
+
+ DBUG_RETURN(sp);
+}
+
+
+int
+sp_create_procedure(THD *thd, sp_head *sp)
+{
+ int ret;
+ DBUG_ENTER("sp_create_procedure");
+ DBUG_PRINT("enter", ("name: %*s", sp->m_name.length, sp->m_name.str));
+
+ ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, sp);
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_drop_procedure(THD *thd, sp_name *name)
+{
+ int ret;
+ bool found;
+ DBUG_ENTER("sp_drop_procedure");
+ DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+
+ found= sp_cache_remove(&thd->sp_proc_cache, name);
+ ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name);
+ if (!found && !ret)
+ sp_cache_invalidate();
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_update_procedure(THD *thd, sp_name *name,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics)
+{
+ int ret;
+ bool found;
+ DBUG_ENTER("sp_update_procedure");
+ DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+
+ found= sp_cache_remove(&thd->sp_proc_cache, name);
+ ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name,
+ newname, newnamelen, chistics);
+ if (!found && !ret)
+ sp_cache_invalidate();
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_show_create_procedure(THD *thd, sp_name *name)
+{
+ sp_head *sp;
+ DBUG_ENTER("sp_show_create_procedure");
+ DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+
+ if ((sp= sp_find_procedure(thd, name)))
+ {
+ int ret= sp->show_create_procedure(thd);
+
+ DBUG_RETURN(ret);
+ }
+
+ DBUG_RETURN(SP_KEY_NOT_FOUND);
+}
+
+
+int
+sp_show_status_procedure(THD *thd, const char *wild)
+{
+ int ret;
+ DBUG_ENTER("sp_show_status_procedure");
+
+ ret= db_show_routine_status(thd, TYPE_ENUM_PROCEDURE, wild);
+ DBUG_RETURN(ret);
+}
+
+
+/*****************************************************************************
+ FUNCTION
+******************************************************************************/
+
+sp_head *
+sp_find_function(THD *thd, sp_name *name)
+{
+ sp_head *sp;
+ DBUG_ENTER("sp_find_function");
+ DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+
+ if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name)))
+ {
+ if (db_find_routine(thd, TYPE_ENUM_FUNCTION, name, &sp) != SP_OK)
+ sp= NULL;
+ else
+ sp_cache_insert(&thd->sp_func_cache, sp);
+ }
+ DBUG_RETURN(sp);
+}
+
+
+int
+sp_create_function(THD *thd, sp_head *sp)
+{
+ int ret;
+ DBUG_ENTER("sp_create_function");
+ DBUG_PRINT("enter", ("name: %*s", sp->m_name.length, sp->m_name.str));
+
+ ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, sp);
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_drop_function(THD *thd, sp_name *name)
+{
+ int ret;
+ bool found;
+ DBUG_ENTER("sp_drop_function");
+ DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+
+ found= sp_cache_remove(&thd->sp_func_cache, name);
+ ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name);
+ if (!found && !ret)
+ sp_cache_invalidate();
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_update_function(THD *thd, sp_name *name,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics)
+{
+ int ret;
+ bool found;
+ DBUG_ENTER("sp_update_procedure");
+ DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+
+ found= sp_cache_remove(&thd->sp_func_cache, name);
+ ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name,
+ newname, newnamelen, chistics);
+ if (!found && !ret)
+ sp_cache_invalidate();
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_show_create_function(THD *thd, sp_name *name)
+{
+ sp_head *sp;
+ DBUG_ENTER("sp_show_create_function");
+ DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
+
+ if ((sp= sp_find_function(thd, name)))
+ {
+ int ret= sp->show_create_function(thd);
+
+ DBUG_RETURN(ret);
+ }
+ DBUG_RETURN(SP_KEY_NOT_FOUND);
+}
+
+
+int
+sp_show_status_function(THD *thd, const char *wild)
+{
+ int ret;
+ DBUG_ENTER("sp_show_status_function");
+ ret= db_show_routine_status(thd, TYPE_ENUM_FUNCTION, wild);
+ DBUG_RETURN(ret);
+}
+
+
+bool
+sp_function_exists(THD *thd, sp_name *name)
+{
+ TABLE *table;
+ bool ret= FALSE;
+ bool opened= FALSE;
+ DBUG_ENTER("sp_function_exists");
+
+ if (sp_cache_lookup(&thd->sp_func_cache, name) ||
+ db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
+ name, TL_READ,
+ &table, &opened) == SP_OK)
+ ret= TRUE;
+ if (opened)
+ close_thread_tables(thd, 0, 1);
+ thd->clear_error();
+ DBUG_RETURN(ret);
+}
+
+
+byte *
+sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first)
+{
+ LEX_STRING *lsp= (LEX_STRING *)ptr;
+ *plen= lsp->length;
+ return (byte *)lsp->str;
+}
+
+
+void
+sp_add_fun_to_lex(LEX *lex, sp_name *fun)
+{
+ if (! hash_search(&lex->spfuns,
+ (byte *)fun->m_qname.str, fun->m_qname.length))
+ {
+ LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
+ ls->str= sql_strmake(fun->m_qname.str, fun->m_qname.length);
+ ls->length= fun->m_qname.length;
+
+ my_hash_insert(&lex->spfuns, (byte *)ls);
+ }
+}
+
+
+void
+sp_merge_funs(LEX *dst, LEX *src)
+{
+ for (uint i=0 ; i < src->spfuns.records ; i++)
+ {
+ LEX_STRING *ls= (LEX_STRING *)hash_element(&src->spfuns, i);
+
+ if (! hash_search(&dst->spfuns, (byte *)ls->str, ls->length))
+ my_hash_insert(&dst->spfuns, (byte *)ls);
+ }
+}
+
+
+int
+sp_cache_functions(THD *thd, LEX *lex)
+{
+ HASH *h= &lex->spfuns;
+ int ret= 0;
+
+ for (uint i=0 ; i < h->records ; i++)
+ {
+ LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
+ sp_name name(*ls);
+
+ name.m_qname= *ls;
+ if (! sp_cache_lookup(&thd->sp_func_cache, &name))
+ {
+ sp_head *sp;
+ LEX *oldlex= thd->lex;
+ LEX *newlex= new st_lex;
+
+ thd->lex= newlex;
+ name.m_name.str= strchr(name.m_qname.str, '.');
+ name.m_db.length= name.m_name.str - name.m_qname.str;
+ name.m_db.str= strmake_root(&thd->mem_root,
+ name.m_qname.str, name.m_db.length);
+ name.m_name.str+= 1;
+ name.m_name.length= name.m_qname.length - name.m_db.length - 1;
+
+ if (db_find_routine(thd, TYPE_ENUM_FUNCTION, &name, &sp)
+ == SP_OK)
+ {
+ ret= sp_cache_functions(thd, newlex);
+ delete newlex;
+ thd->lex= oldlex;
+ if (ret)
+ break;
+ sp_cache_insert(&thd->sp_func_cache, sp);
+ }
+ else
+ {
+ delete newlex;
+ thd->lex= oldlex;
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, "FUNCTION", ls->str);
+ ret= 1;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * Generates the CREATE... string from the table information.
+ * Returns TRUE on success, FALSE on (alloc) failure.
+ */
+static bool
+create_string(THD *thd, String *buf,
+ int type,
+ sp_name *name,
+ const char *params, ulong paramslen,
+ const char *returns, ulong returnslen,
+ const char *body, ulong bodylen,
+ st_sp_chistics *chistics)
+{
+ /* Make some room to begin with */
+ if (buf->alloc(100 + name->m_qname.length + paramslen + returnslen + bodylen +
+ chistics->comment.length))
+ return FALSE;
+
+ buf->append("CREATE ", 7);
+ if (type == TYPE_ENUM_FUNCTION)
+ buf->append("FUNCTION ", 9);
+ else
+ buf->append("PROCEDURE ", 10);
+ append_identifier(thd, buf, name->m_db.str, name->m_db.length);
+ buf->append('.');
+ append_identifier(thd, buf, name->m_name.str, name->m_name.length);
+ buf->append('(');
+ buf->append(params, paramslen);
+ buf->append(')');
+ if (type == TYPE_ENUM_FUNCTION)
+ {
+ buf->append(" RETURNS ", 9);
+ buf->append(returns, returnslen);
+ }
+ buf->append('\n');
+ if (chistics->detistic)
+ buf->append( " DETERMINISTIC\n", 18);
+ if (chistics->suid == IS_NOT_SUID)
+ buf->append(" SQL SECURITY INVOKER\n", 25);
+ if (chistics->comment.length)
+ {
+ buf->append(" COMMENT ");
+ append_unescaped(buf, chistics->comment.str, chistics->comment.length);
+ buf->append('\n');
+ }
+ buf->append(body, bodylen);
+ return TRUE;
+}
+
+
+//
+// Utilities...
+//
+
+int
+sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen,
+ bool no_access_check, bool *dbchangedp)
+{
+ bool changeit;
+ DBUG_ENTER("sp_use_new_db");
+ DBUG_PRINT("enter", ("newdb: %s", newdb));
+
+ if (thd->db && thd->db[0])
+ {
+ if (my_strcasecmp(system_charset_info, thd->db, newdb) == 0)
+ changeit= 0;
+ else
+ {
+ changeit= 1;
+ strnmov(olddb, thd->db, olddblen);
+ }
+ }
+ else
+ { // thd->db empty
+ if (newdb[0])
+ changeit= 1;
+ else
+ changeit= 0;
+ olddb[0] = '\0';
+ }
+ if (!changeit)
+ {
+ *dbchangedp= FALSE;
+ DBUG_RETURN(0);
+ }
+ else
+ {
+ int ret= sp_change_db(thd, newdb, no_access_check);
+
+ if (! ret)
+ *dbchangedp= TRUE;
+ DBUG_RETURN(ret);
+ }
+}
+
+/*
+ Change database.
+
+ SYNOPSIS
+ sp_change_db()
+ thd Thread handler
+ name Database name
+ empty_is_ok True= it's ok with "" as name
+ no_access_check True= don't do access check
+
+ DESCRIPTION
+ This is the same as mysql_change_db(), but with some extra
+ arguments for Stored Procedure usage; doing implicit "use"
+ when executing an SP in a different database.
+ We also use different error routines, since this might be
+ invoked from a function when executing a query or statement.
+ Note: We would have prefered to reuse mysql_change_db(), but
+ the error handling in particular made that too awkward, so
+ we (reluctantly) have a "copy" here.
+
+ RETURN VALUES
+ 0 ok
+ 1 error
+*/
+
+int
+sp_change_db(THD *thd, char *name, bool no_access_check)
+{
+ int length, db_length;
+ char *dbname=my_strdup((char*) name,MYF(MY_WME));
+ char path[FN_REFLEN];
+ ulong db_access;
+ HA_CREATE_INFO create;
+ DBUG_ENTER("sp_change_db");
+ DBUG_PRINT("enter", ("db: %s, no_access_check: %d", name, no_access_check));
+
+ db_length= (!dbname ? 0 : strip_sp(dbname));
+ if (dbname && db_length)
+ {
+ if ((db_length > NAME_LEN) || check_db_name(dbname))
+ {
+ my_printf_error(ER_WRONG_DB_NAME, ER(ER_WRONG_DB_NAME), MYF(0), dbname);
+ x_free(dbname);
+ DBUG_RETURN(1);
+ }
+ }
+
+ if (dbname && db_length)
+ {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (! no_access_check)
+ {
+ if (test_all_bits(thd->master_access,DB_ACLS))
+ db_access=DB_ACLS;
+ else
+ db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
+ thd->master_access);
+ if (!(db_access & DB_ACLS) &&
+ (!grant_option || check_grant_db(thd,dbname)))
+ {
+ my_printf_error(ER_DBACCESS_DENIED_ERROR, ER(ER_DBACCESS_DENIED_ERROR),
+ MYF(0),
+ thd->priv_user,
+ thd->priv_host,
+ dbname);
+ mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
+ thd->priv_user,
+ thd->priv_host,
+ dbname);
+ my_free(dbname,MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+#endif
+ (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
+ length=unpack_dirname(path,path); // Convert if not unix
+ if (length && path[length-1] == FN_LIBCHAR)
+ path[length-1]=0; // remove ending '\'
+ if (access(path,F_OK))
+ {
+ my_printf_error(ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), MYF(0), dbname);
+ my_free(dbname,MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+
+ x_free(thd->db);
+ thd->db=dbname; // THD::~THD will free this
+ thd->db_length=db_length;
+
+ if (dbname && db_length)
+ {
+ strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
+ load_db_opt(thd, path, &create);
+ thd->db_charset= create.default_table_charset ?
+ create.default_table_charset :
+ thd->variables.collation_server;
+ thd->variables.collation_database= thd->db_charset;
+ }
+ DBUG_RETURN(0);
+}
diff --git a/sql/sp.h b/sql/sp.h
new file mode 100644
index 00000000000..783de2fe7ee
--- /dev/null
+++ b/sql/sp.h
@@ -0,0 +1,106 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_H_
+#define _SP_H_
+
+// Return codes from sp_create_*, sp_drop_*, and sp_show_*:
+#define SP_OK 0
+#define SP_KEY_NOT_FOUND -1
+#define SP_OPEN_TABLE_FAILED -2
+#define SP_WRITE_ROW_FAILED -3
+#define SP_DELETE_ROW_FAILED -4
+#define SP_GET_FIELD_FAILED -5
+#define SP_PARSE_ERROR -6
+#define SP_INTERNAL_ERROR -7
+#define SP_NO_DB_ERROR -8
+
+/* Drop all routines in database 'db' */
+int
+sp_drop_db_routines(THD *thd, char *db);
+
+sp_head *
+sp_find_procedure(THD *thd, sp_name *name);
+
+int
+sp_create_procedure(THD *thd, sp_head *sp);
+
+int
+sp_drop_procedure(THD *thd, sp_name *name);
+
+
+int
+sp_update_procedure(THD *thd, sp_name *name,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics);
+
+int
+sp_show_create_procedure(THD *thd, sp_name *name);
+
+int
+sp_show_status_procedure(THD *thd, const char *wild);
+
+sp_head *
+sp_find_function(THD *thd, sp_name *name);
+
+int
+sp_create_function(THD *thd, sp_head *sp);
+
+int
+sp_drop_function(THD *thd, sp_name *name);
+
+int
+sp_update_function(THD *thd, sp_name *name,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics);
+
+int
+sp_show_create_function(THD *thd, sp_name *name);
+
+int
+sp_show_status_function(THD *thd, const char *wild);
+
+bool
+sp_function_exists(THD *thd, sp_name *name);
+
+
+// This is needed since we have to read the functions before we
+// do anything else.
+void
+sp_add_fun_to_lex(LEX *lex, sp_name *fun);
+void
+sp_merge_funs(LEX *dst, LEX *src);
+int
+sp_cache_functions(THD *thd, LEX *lex);
+
+
+//
+// Utilities...
+//
+
+// Do a "use newdb". The current db is stored at olddb.
+// If newdb is the same as the current one, nothing is changed.
+// dbchangedp is set to true if the db was actually changed.
+int
+sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddbmax,
+ bool no_access_check, bool *dbchangedp);
+
+// Like mysql_change_db() but handles empty db name and the send_ok() problem.
+int
+sp_change_db(THD *thd, char *db, bool no_access_check);
+
+#endif /* _SP_H_ */
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
new file mode 100644
index 00000000000..056ac6d7e96
--- /dev/null
+++ b/sql/sp_cache.cc
@@ -0,0 +1,166 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sp_cache.h"
+#include "sp_head.h"
+
+static pthread_mutex_t Cversion_lock;
+static ulong Cversion = 0;
+
+void
+sp_cache_init()
+{
+ pthread_mutex_init(&Cversion_lock, MY_MUTEX_INIT_FAST);
+}
+
+void
+sp_cache_clear(sp_cache **cp)
+{
+ sp_cache *c= *cp;
+
+ if (c)
+ {
+ delete c;
+ *cp= NULL;
+ }
+}
+
+void
+sp_cache_insert(sp_cache **cp, sp_head *sp)
+{
+ sp_cache *c= *cp;
+
+ if (! c)
+ c= new sp_cache();
+ if (c)
+ {
+ ulong v;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ {
+ if (*cp)
+ c->remove_all();
+ c->version= v;
+ }
+ c->insert(sp);
+ if (*cp == NULL)
+ *cp= c;
+ }
+}
+
+sp_head *
+sp_cache_lookup(sp_cache **cp, sp_name *name)
+{
+ ulong v;
+ sp_cache *c= *cp;
+
+ if (! c)
+ return NULL;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ {
+ c->remove_all();
+ c->version= v;
+ return NULL;
+ }
+ return c->lookup(name->m_qname.str, name->m_qname.length);
+}
+
+bool
+sp_cache_remove(sp_cache **cp, sp_name *name)
+{
+ sp_cache *c= *cp;
+ bool found= FALSE;
+
+ if (c)
+ {
+ ulong v;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion++;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ c->remove_all();
+ else
+ found= c->remove(name->m_qname.str, name->m_qname.length);
+ c->version= v+1;
+ }
+ return found;
+}
+
+void
+sp_cache_invalidate()
+{
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ Cversion++;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+}
+
+static byte *
+hash_get_key_for_sp_head(const byte *ptr, uint *plen,
+ my_bool first)
+{
+ sp_head *sp= (sp_head *)ptr;
+
+ *plen= sp->m_qname.length;
+ return (byte*) sp->m_qname.str;
+}
+
+static void
+hash_free_sp_head(void *p)
+{
+ sp_head *sp= (sp_head *)p;
+
+ delete sp;
+}
+
+sp_cache::sp_cache()
+{
+ init();
+}
+
+sp_cache::~sp_cache()
+{
+ hash_free(&m_hashtable);
+}
+
+void
+sp_cache::init()
+{
+ hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
+ hash_get_key_for_sp_head, hash_free_sp_head, 0);
+ version= 0;
+}
+
+void
+sp_cache::cleanup()
+{
+ hash_free(&m_hashtable);
+}
diff --git a/sql/sp_cache.h b/sql/sp_cache.h
new file mode 100644
index 00000000000..754a987090e
--- /dev/null
+++ b/sql/sp_cache.h
@@ -0,0 +1,107 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_CACHE_H_
+#define _SP_CACHE_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class sp_head;
+class sp_cache;
+
+/* Initialize the SP caching once at startup */
+void sp_cache_init();
+
+/* Clear the cache *cp and set *cp to NULL */
+void sp_cache_clear(sp_cache **cp);
+
+/* Insert an SP to cache. If 'cp' points to NULL, it's set to a new cache */
+void sp_cache_insert(sp_cache **cp, sp_head *sp);
+
+/* Lookup an SP in cache */
+sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name);
+
+/* Remove an SP from cache. Returns true if something was removed */
+bool sp_cache_remove(sp_cache **cp, sp_name *name);
+
+/* Invalidate a cache */
+void sp_cache_invalidate();
+
+
+/*
+ *
+ * The cache class. Don't use this directly, use the C API above
+ *
+ */
+
+class sp_cache
+{
+public:
+
+ ulong version;
+
+ sp_cache();
+
+ ~sp_cache();
+
+ void
+ init();
+
+ void
+ cleanup();
+
+ inline void
+ insert(sp_head *sp)
+ {
+ my_hash_insert(&m_hashtable, (const byte *)sp);
+ }
+
+ inline sp_head *
+ lookup(char *name, uint namelen)
+ {
+ return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen);
+ }
+
+ inline bool
+ remove(char *name, uint namelen)
+ {
+ sp_head *sp= lookup(name, namelen);
+
+ if (sp)
+ {
+ hash_delete(&m_hashtable, (byte *)sp);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ inline void
+ remove_all()
+ {
+ cleanup();
+ init();
+ }
+
+private:
+
+ HASH m_hashtable;
+
+}; // class sp_cache
+
+#endif /* _SP_CACHE_H_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
new file mode 100644
index 00000000000..16d13154263
--- /dev/null
+++ b/sql/sp_head.cc
@@ -0,0 +1,1761 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include "sp_head.h"
+#include "sp.h"
+#include "sp_pcontext.h"
+#include "sp_rcontext.h"
+
+Item_result
+sp_map_result_type(enum enum_field_types type)
+{
+ switch (type)
+ {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ return INT_RESULT;
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ return REAL_RESULT;
+ default:
+ return STRING_RESULT;
+ }
+}
+
+/*
+ * Returns TRUE if the 'cmd' is a command that might result in
+ * multiple result sets being sent back.
+ * Note: This does not include SQLCOM_SELECT which is treated
+ * separately in sql_yacc.yy.
+ */
+bool
+sp_multi_results_command(enum enum_sql_command cmd)
+{
+ switch (cmd) {
+ case SQLCOM_ANALYZE:
+ case SQLCOM_CHECKSUM:
+ case SQLCOM_HA_READ:
+ case SQLCOM_SHOW_BINLOGS:
+ case SQLCOM_SHOW_BINLOG_EVENTS:
+ case SQLCOM_SHOW_CHARSETS:
+ case SQLCOM_SHOW_COLLATIONS:
+ case SQLCOM_SHOW_COLUMN_TYPES:
+ case SQLCOM_SHOW_CREATE:
+ case SQLCOM_SHOW_CREATE_DB:
+ case SQLCOM_SHOW_CREATE_FUNC:
+ case SQLCOM_SHOW_CREATE_PROC:
+ case SQLCOM_SHOW_DATABASES:
+ case SQLCOM_SHOW_ERRORS:
+ case SQLCOM_SHOW_FIELDS:
+ case SQLCOM_SHOW_GRANTS:
+ case SQLCOM_SHOW_INNODB_STATUS:
+ case SQLCOM_SHOW_KEYS:
+ case SQLCOM_SHOW_LOGS:
+ case SQLCOM_SHOW_MASTER_STAT:
+ case SQLCOM_SHOW_NEW_MASTER:
+ case SQLCOM_SHOW_OPEN_TABLES:
+ case SQLCOM_SHOW_PRIVILEGES:
+ case SQLCOM_SHOW_PROCESSLIST:
+ case SQLCOM_SHOW_SLAVE_HOSTS:
+ case SQLCOM_SHOW_SLAVE_STAT:
+ case SQLCOM_SHOW_STATUS:
+ case SQLCOM_SHOW_STATUS_FUNC:
+ case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STORAGE_ENGINES:
+ case SQLCOM_SHOW_TABLES:
+ case SQLCOM_SHOW_VARIABLES:
+ case SQLCOM_SHOW_WARNS:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/* Evaluate a (presumed) func item. Always returns an item, the parameter
+** if nothing else.
+*/
+Item *
+sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type)
+{
+ DBUG_ENTER("sp_eval_func_item");
+ it= it->this_item();
+ DBUG_PRINT("info", ("type: %d", type));
+
+ if (!it->fixed && it->fix_fields(thd, 0, &it))
+ {
+ DBUG_PRINT("info", ("fix_fields() failed"));
+ DBUG_RETURN(NULL);
+ }
+
+ /* QQ How do we do this? Is there some better way? */
+ if (type == MYSQL_TYPE_NULL)
+ it= new Item_null();
+ else
+ {
+ switch (sp_map_result_type(type)) {
+ case INT_RESULT:
+ {
+ longlong i= it->val_int();
+
+ if (it->null_value)
+ {
+ DBUG_PRINT("info", ("INT_RESULT: null"));
+ it= new Item_null();
+ }
+ else
+ {
+ DBUG_PRINT("info", ("INT_RESULT: %d", i));
+ it= new Item_int(it->val_int());
+ }
+ break;
+ }
+ case REAL_RESULT:
+ {
+ double d= it->val();
+
+ if (it->null_value)
+ {
+ DBUG_PRINT("info", ("REAL_RESULT: null"));
+ it= new Item_null();
+ }
+ else
+ {
+ /* There's some difference between Item::new_item() and the
+ * constructor; the former crashes, the latter works... weird. */
+ uint8 decimals= it->decimals;
+ uint32 max_length= it->max_length;
+ DBUG_PRINT("info", ("REAL_RESULT: %g", d));
+ it= new Item_real(it->val());
+ it->decimals= decimals;
+ it->max_length= max_length;
+ }
+ break;
+ }
+ default:
+ {
+ char buffer[MAX_FIELD_WIDTH];
+ String tmp(buffer, sizeof(buffer), it->collation.collation);
+ String *s= it->val_str(&tmp);
+
+ if (it->null_value)
+ {
+ DBUG_PRINT("info", ("default result: null"));
+ it= new Item_null();
+ }
+ else
+ {
+ DBUG_PRINT("info",("default result: %*s",s->length(),s->c_ptr_quick()));
+ it= new Item_string(thd->strmake(s->c_ptr_quick(), s->length()),
+ s->length(), it->collation.collation);
+ }
+ break;
+ }
+ }
+ }
+
+ DBUG_RETURN(it);
+}
+
+
+/*
+ *
+ * sp_name
+ *
+ */
+
+void
+sp_name::init_qname(THD *thd)
+{
+ m_qname.length= m_db.length+m_name.length+1;
+ m_qname.str= alloc_root(&thd->mem_root, m_qname.length+1);
+ sprintf(m_qname.str, "%*s.%*s",
+ m_db.length, (m_db.length ? m_db.str : ""),
+ m_name.length, m_name.str);
+}
+
+sp_name *
+sp_name_current_db_new(THD *thd, LEX_STRING name)
+{
+ sp_name *qname;
+
+ if (! thd->db)
+ qname= new sp_name(name);
+ else
+ {
+ LEX_STRING db;
+
+ db.length= strlen(thd->db);
+ db.str= thd->strmake(thd->db, db.length);
+ qname= new sp_name(db, name);
+ }
+ qname->init_qname(thd);
+ return qname;
+}
+
+
+/* ------------------------------------------------------------------ */
+
+
+/*
+ *
+ * sp_head
+ *
+ */
+
+void *
+sp_head::operator new(size_t size)
+{
+ DBUG_ENTER("sp_head::operator new");
+ MEM_ROOT own_root;
+ sp_head *sp;
+
+ bzero((char *)&own_root, sizeof(own_root));
+ init_alloc_root(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ sp= (sp_head *)alloc_root(&own_root, size);
+ sp->mem_root= own_root;
+ DBUG_PRINT("info", ("mem_root 0x%lx", (ulong) &sp->mem_root));
+ DBUG_RETURN(sp);
+}
+
+void
+sp_head::operator delete(void *ptr, size_t size)
+{
+ DBUG_ENTER("sp_head::operator delete");
+ MEM_ROOT own_root;
+ sp_head *sp= (sp_head *)ptr;
+
+ memcpy(&own_root, (const void *)&sp->mem_root, sizeof(MEM_ROOT));
+ DBUG_PRINT("info", ("mem_root 0x%lx moved to 0x%lx",
+ (ulong) &sp->mem_root, (ulong) &own_root));
+ free_root(&own_root, MYF(0));
+
+ DBUG_VOID_RETURN;
+}
+
+
+sp_head::sp_head()
+ :Item_arena((bool)FALSE), m_returns_cs(NULL), m_has_return(FALSE),
+ m_simple_case(FALSE), m_multi_results(FALSE), m_in_handler(FALSE)
+{
+ DBUG_ENTER("sp_head::sp_head");
+
+ m_backpatch.empty();
+ m_lex.empty();
+ DBUG_VOID_RETURN;
+}
+
+
+void
+sp_head::init(LEX *lex)
+{
+ DBUG_ENTER("sp_head::init");
+
+ lex->spcont= m_pcont= new sp_pcontext(NULL);
+ my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
+ m_param_begin= m_param_end= m_returns_begin= m_returns_end= m_body_begin= 0;
+ m_qname.str= m_db.str= m_name.str= m_params.str= m_retstr.str=
+ m_body.str= m_defstr.str= 0;
+ m_qname.length= m_db.length= m_name.length= m_params.length=
+ m_retstr.length= m_body.length= m_defstr.length= 0;
+ m_returns_cs= NULL;
+ DBUG_VOID_RETURN;
+}
+
+void
+sp_head::init_strings(THD *thd, LEX *lex, sp_name *name)
+{
+ DBUG_ENTER("sp_head::init_strings");
+ uint n; /* Counter for nul trimming */
+ /* During parsing, we must use thd->mem_root */
+ MEM_ROOT *root= &thd->mem_root;
+
+ DBUG_PRINT("info", ("name: %*.s%*s",
+ name->m_db.length, name->m_db.str,
+ name->m_name.length, name->m_name.str));
+ /* We have to copy strings to get them into the right memroot */
+ if (name->m_db.length == 0)
+ {
+ m_db.length= (thd->db ? strlen(thd->db) : 0);
+ m_db.str= strmake_root(root, (thd->db ? thd->db : ""), m_db.length);
+ }
+ else
+ {
+ m_db.length= name->m_db.length;
+ m_db.str= strmake_root(root, name->m_db.str, name->m_db.length);
+ }
+ m_name.length= name->m_name.length;
+ m_name.str= strmake_root(root, name->m_name.str, name->m_name.length);
+
+ if (name->m_qname.length == 0)
+ name->init_qname(thd);
+ m_qname.length= name->m_qname.length;
+ m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length);
+
+ m_params.length= m_param_end- m_param_begin;
+ m_params.str= strmake_root(root,
+ (char *)m_param_begin, m_params.length);
+ if (m_returns_begin && m_returns_end)
+ {
+ /* QQ KLUDGE: We can't seem to cut out just the type in the parser
+ (without the RETURNS), so we'll have to do it here. :-(
+ Furthermore, if there's a character type as well, it's not include
+ (beyond the m_returns_end pointer), in which case we need
+ m_returns_cs. */
+ char *p= (char *)m_returns_begin+strspn((char *)m_returns_begin,"\t\n\r ");
+ p+= strcspn(p, "\t\n\r ");
+ p+= strspn(p, "\t\n\r ");
+ if (p < (char *)m_returns_end)
+ m_returns_begin= (uchar *)p;
+ /* While we're at it, trim the end too. */
+ p= (char *)m_returns_end-1;
+ while (p > (char *)m_returns_begin &&
+ (*p == '\t' || *p == '\n' || *p == '\r' || *p == ' '))
+ p-= 1;
+ m_returns_end= (uchar *)p+1;
+ if (m_returns_cs)
+ {
+ String s((char *)m_returns_begin, m_returns_end - m_returns_begin,
+ system_charset_info);
+
+ s.append(' ');
+ s.append(m_returns_cs->csname);
+ m_retstr.length= s.length();
+ m_retstr.str= strmake_root(root, s.ptr(), m_retstr.length);
+ }
+ else
+ {
+ m_retstr.length= m_returns_end - m_returns_begin;
+ m_retstr.str= strmake_root(root,
+ (char *)m_returns_begin, m_retstr.length);
+ }
+ }
+ m_body.length= lex->ptr - m_body_begin;
+ /* Trim nuls at the end */
+ n= 0;
+ while (m_body.length && m_body_begin[m_body.length-1] == '\0')
+ {
+ m_body.length-= 1;
+ n+= 1;
+ }
+ m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length);
+ m_defstr.length= lex->ptr - lex->buf;
+ m_defstr.length-= n;
+ m_defstr.str= strmake_root(root, (char *)lex->buf, m_defstr.length);
+ DBUG_VOID_RETURN;
+}
+
+int
+sp_head::create(THD *thd)
+{
+ DBUG_ENTER("sp_head::create");
+ int ret;
+
+ DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s",
+ m_type, m_name.str, m_params.str, m_body.str));
+
+#ifndef DBUG_OFF
+ optimize();
+ {
+ String s;
+ sp_instr *i;
+ uint ip= 0;
+ while ((i = get_instr(ip)))
+ {
+ char buf[8];
+
+ sprintf(buf, "%4u: ", ip);
+ s.append(buf);
+ i->print(&s);
+ s.append('\n');
+ ip+= 1;
+ }
+ s.append('\0');
+ DBUG_PRINT("info", ("Code %s\n%s", m_qname.str, s.ptr()));
+ }
+#endif
+
+ if (m_type == TYPE_ENUM_FUNCTION)
+ ret= sp_create_function(thd, this);
+ else
+ ret= sp_create_procedure(thd, this);
+
+ DBUG_RETURN(ret);
+}
+
+sp_head::~sp_head()
+{
+ destroy();
+ if (m_thd)
+ restore_thd_mem_root(m_thd);
+}
+
+void
+sp_head::destroy()
+{
+ DBUG_ENTER("sp_head::destroy");
+ DBUG_PRINT("info", ("name: %s", m_name.str));
+ sp_instr *i;
+ LEX *lex;
+
+ for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
+ delete i;
+ delete_dynamic(&m_instr);
+ m_pcont->destroy();
+ free_items(free_list);
+ while ((lex= (LEX *)m_lex.pop()))
+ {
+ if (lex != &m_thd->main_lex) // We got interrupted and have lex'es left
+ delete lex;
+ }
+ DBUG_VOID_RETURN;
+}
+
+int
+sp_head::execute(THD *thd)
+{
+ DBUG_ENTER("sp_head::execute");
+ char olddb[128];
+ bool dbchanged;
+ sp_rcontext *ctx;
+ int ret= 0;
+ uint ip= 0;
+ Item_arena *old_arena;
+
+
+#ifndef EMBEDDED_LIBRARY
+ if (check_stack_overrun(thd, olddb))
+ {
+ DBUG_RETURN(-1);
+ }
+#endif
+
+ dbchanged= FALSE;
+ if ((ret= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0, &dbchanged)))
+ goto done;
+
+ if ((ctx= thd->spcont))
+ ctx->clear_handler();
+ thd->query_error= 0;
+ old_arena= thd->current_arena;
+ thd->current_arena= this;
+
+ do
+ {
+ sp_instr *i;
+ uint hip; // Handler ip
+
+ i = get_instr(ip); // Returns NULL when we're done.
+ if (i == NULL)
+ break;
+ DBUG_PRINT("execute", ("Instruction %u", ip));
+ ret= i->execute(thd, &ip);
+ if (i->free_list)
+ cleanup_items(i->free_list);
+ // Check if an exception has occurred and a handler has been found
+ // Note: We havo to check even if ret==0, since warnings (and some
+ // errors don't return a non-zero value.
+ if (!thd->killed && ctx)
+ {
+ uint hf;
+
+ switch (ctx->found_handler(&hip, &hf))
+ {
+ case SP_HANDLER_NONE:
+ break;
+ case SP_HANDLER_CONTINUE:
+ ctx->save_variables(hf);
+ ctx->push_hstack(ip);
+ // Fall through
+ default:
+ ip= hip;
+ ret= 0;
+ ctx->clear_handler();
+ continue;
+ }
+ }
+ } while (ret == 0 && !thd->killed && !thd->query_error);
+
+ if (thd->current_arena)
+ cleanup_items(thd->current_arena->free_list);
+ thd->current_arena= old_arena;
+
+ done:
+ DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d",
+ ret, thd->killed, thd->query_error));
+
+ if (thd->killed || thd->query_error)
+ ret= -1;
+ /* If the DB has changed, the pointer has changed too, but the
+ original thd->db will then have been freed */
+ if (dbchanged)
+ {
+ if (! thd->killed)
+ ret= sp_change_db(thd, olddb, 0);
+ }
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
+{
+ DBUG_ENTER("sp_head::execute_function");
+ DBUG_PRINT("info", ("function %s", m_name.str));
+ uint csize = m_pcont->max_pvars();
+ uint params = m_pcont->current_pvars();
+ uint hmax = m_pcont->max_handlers();
+ uint cmax = m_pcont->max_cursors();
+ sp_rcontext *octx = thd->spcont;
+ sp_rcontext *nctx = NULL;
+ uint i;
+ int ret;
+
+ if (argcount != params)
+ {
+ // Need to use my_printf_error here, or it will not terminate the
+ // invoking query properly.
+ my_printf_error(ER_SP_WRONG_NO_OF_ARGS, ER(ER_SP_WRONG_NO_OF_ARGS), MYF(0),
+ "FUNCTION", m_name.str, params, argcount);
+ DBUG_RETURN(-1);
+ }
+
+ // QQ Should have some error checking here? (types, etc...)
+ nctx= new sp_rcontext(csize, hmax, cmax);
+ for (i= 0 ; i < params && i < argcount ; i++)
+ {
+ sp_pvar_t *pvar = m_pcont->find_pvar(i);
+ Item *it= sp_eval_func_item(thd, *argp++, pvar->type);
+
+ if (it)
+ nctx->push_item(it);
+ else
+ {
+ DBUG_RETURN(-1);
+ }
+ }
+#ifdef NOT_WORKING
+ /*
+ Close tables opened for subselect in argument list
+ This can't be done as this will close all other tables used
+ by the query.
+ */
+ close_thread_tables(thd);
+#endif
+ // The rest of the frame are local variables which are all IN.
+ // Default all variables to null (those with default clauses will
+ // be set by an set instruction).
+ {
+ Item_null *nit= NULL; // Re-use this, and only create if needed
+ for (; i < csize ; i++)
+ {
+ if (! nit)
+ nit= new Item_null();
+ nctx->push_item(nit);
+ }
+ }
+ thd->spcont= nctx;
+
+ ret= execute(thd);
+ if (ret == 0)
+ {
+ Item *it= nctx->get_result();
+
+ if (it)
+ *resp= it;
+ else
+ {
+ my_printf_error(ER_SP_NORETURNEND, ER(ER_SP_NORETURNEND), MYF(0),
+ m_name.str);
+ ret= -1;
+ }
+ }
+
+ nctx->pop_all_cursors(); // To avoid memory leaks after an error
+ thd->spcont= octx;
+ DBUG_RETURN(ret);
+}
+
+int
+sp_head::execute_procedure(THD *thd, List<Item> *args)
+{
+ DBUG_ENTER("sp_head::execute_procedure");
+ DBUG_PRINT("info", ("procedure %s", m_name.str));
+ int ret= 0;
+ uint csize = m_pcont->max_pvars();
+ uint params = m_pcont->current_pvars();
+ uint hmax = m_pcont->max_handlers();
+ uint cmax = m_pcont->max_cursors();
+ sp_rcontext *octx = thd->spcont;
+ sp_rcontext *nctx = NULL;
+ my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx
+
+ if (args->elements != params)
+ {
+ net_printf(thd, ER_SP_WRONG_NO_OF_ARGS, "PROCEDURE", m_name.str,
+ params, args->elements);
+ DBUG_RETURN(-1);
+ }
+
+ if (csize > 0 || hmax > 0 || cmax > 0)
+ {
+ Item_null *nit= NULL; // Re-use this, and only create if needed
+ uint i;
+ List_iterator_fast<Item> li(*args);
+ Item *it;
+
+ nctx= new sp_rcontext(csize, hmax, cmax);
+ if (! octx)
+ { // Create a temporary old context
+ octx= new sp_rcontext(csize, hmax, cmax);
+ tmp_octx= TRUE;
+ }
+ // QQ: Should do type checking?
+ for (i = 0 ; (it= li++) && i < params ; i++)
+ {
+ sp_pvar_t *pvar = m_pcont->find_pvar(i);
+
+ if (! pvar)
+ nctx->set_oindex(i, -1); // Shouldn't happen
+ else
+ {
+ if (pvar->mode == sp_param_out)
+ {
+ if (! nit)
+ nit= new Item_null();
+ nctx->push_item(nit); // OUT
+ }
+ else
+ {
+ Item *it2= sp_eval_func_item(thd, it,pvar->type);
+
+ if (it2)
+ nctx->push_item(it2); // IN or INOUT
+ else
+ {
+ ret= -1; // Eval failed
+ break;
+ }
+ }
+ // Note: If it's OUT or INOUT, it must be a variable.
+ // QQ: We can check for global variables here, or should we do it
+ // while parsing?
+ if (pvar->mode == sp_param_in)
+ nctx->set_oindex(i, -1); // IN
+ else // OUT or INOUT
+ nctx->set_oindex(i, static_cast<Item_splocal *>(it)->get_offset());
+ }
+ }
+ // Close tables opened for subselect in argument list
+ close_thread_tables(thd);
+
+ // The rest of the frame are local variables which are all IN.
+ // Default all variables to null (those with default clauses will
+ // be set by an set instruction).
+ for (; i < csize ; i++)
+ {
+ if (! nit)
+ nit= new Item_null();
+ nctx->push_item(nit);
+ }
+ thd->spcont= nctx;
+ }
+
+ if (! ret)
+ ret= execute(thd);
+
+ // Don't copy back OUT values if we got an error
+ if (ret)
+ {
+ if (thd->net.report_error)
+ send_error(thd, 0, NullS);
+ }
+ else if (csize > 0)
+ {
+ List_iterator_fast<Item> li(*args);
+ Item *it;
+
+ // Copy back all OUT or INOUT values to the previous frame, or
+ // set global user variables
+ for (uint i = 0 ; (it= li++) && i < params ; i++)
+ {
+ int oi = nctx->get_oindex(i);
+
+ if (oi >= 0)
+ {
+ if (! tmp_octx)
+ octx->set_item(nctx->get_oindex(i), nctx->get_item(i));
+ else
+ {
+ // QQ Currently we just silently ignore non-user-variable arguments.
+ // We should check this during parsing, when setting up the call
+ // above
+ if (it->type() == Item::FUNC_ITEM)
+ {
+ Item_func *fi= static_cast<Item_func*>(it);
+
+ if (fi->functype() == Item_func::GUSERVAR_FUNC)
+ { // A global user variable
+ Item *item= nctx->get_item(i);
+ Item_func_set_user_var *suv;
+ Item_func_get_user_var *guv=
+ static_cast<Item_func_get_user_var*>(fi);
+
+ suv= new Item_func_set_user_var(guv->get_name(), item);
+ suv->fix_fields(thd, NULL, &item);
+ suv->fix_length_and_dec();
+ suv->check();
+ suv->update();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (tmp_octx)
+ octx= NULL;
+ if (nctx)
+ nctx->pop_all_cursors(); // To avoid memory leaks after an error
+ thd->spcont= octx;
+
+ DBUG_RETURN(ret);
+}
+
+
+// Reset lex during parsing, before we parse a sub statement.
+void
+sp_head::reset_lex(THD *thd)
+{
+ DBUG_ENTER("sp_head::reset_lex");
+ LEX *sublex;
+ LEX *oldlex= thd->lex;
+
+ (void)m_lex.push_front(oldlex);
+ thd->lex= sublex= new st_lex;
+
+ /* Reset most stuff. The length arguments doesn't matter here. */
+ mysql_init_query(thd,oldlex->buf, oldlex->end_of_query - oldlex->ptr, TRUE);
+
+ /* We must reset ptr and end_of_query again */
+ sublex->ptr= oldlex->ptr;
+ sublex->end_of_query= oldlex->end_of_query;
+ sublex->tok_start= oldlex->tok_start;
+ sublex->yylineno= oldlex->yylineno;
+ /* And keep the SP stuff too */
+ sublex->sphead= oldlex->sphead;
+ sublex->spcont= oldlex->spcont;
+ sublex->sp_lex_in_use= FALSE;
+ DBUG_VOID_RETURN;
+}
+
+// Restore lex during parsing, after we have parsed a sub statement.
+void
+sp_head::restore_lex(THD *thd)
+{
+ DBUG_ENTER("sp_head::restore_lex");
+ LEX *sublex= thd->lex;
+ LEX *oldlex= (LEX *)m_lex.pop();
+
+ if (! oldlex)
+ return; // Nothing to restore
+
+ // Update some state in the old one first
+ oldlex->ptr= sublex->ptr;
+ oldlex->next_state= sublex->next_state;
+
+ // Collect some data from the sub statement lex.
+ sp_merge_funs(oldlex, sublex);
+#ifdef NOT_USED_NOW
+ // QQ We're not using this at the moment.
+ if (sublex.sql_command == SQLCOM_CALL)
+ {
+ // It would be slightly faster to keep the list sorted, but we need
+ // an "insert before" method to do that.
+ char *proc= sublex.udf.name.str;
+
+ List_iterator_fast<char *> li(m_calls);
+ char **it;
+
+ while ((it= li++))
+ if (my_strcasecmp(system_charset_info, proc, *it) == 0)
+ break;
+ if (! it)
+ m_calls.push_back(&proc);
+
+ }
+ // Merge used tables
+ // QQ ...or just open tables in thd->open_tables?
+ // This is not entirerly clear at the moment, but for now, we collect
+ // tables here.
+ for (sl= sublex.all_selects_list ;
+ sl ;
+ sl= sl->next_select())
+ {
+ for (TABLE_LIST *tables= sl->get_table_list() ;
+ tables ;
+ tables= tables->next)
+ {
+ List_iterator_fast<char *> li(m_tables);
+ char **tb;
+
+ while ((tb= li++))
+ if (my_strcasecmp(system_charset_info, tables->real_name, *tb) == 0)
+ break;
+ if (! tb)
+ m_tables.push_back(&tables->real_name);
+ }
+ }
+#endif
+ if (! sublex->sp_lex_in_use)
+ delete sublex;
+ thd->lex= oldlex;
+ DBUG_VOID_RETURN;
+}
+
+void
+sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
+{
+ bp_t *bp= (bp_t *)sql_alloc(sizeof(bp_t));
+
+ if (bp)
+ {
+ bp->lab= lab;
+ bp->instr= i;
+ (void)m_backpatch.push_front(bp);
+ }
+}
+
+void
+sp_head::backpatch(sp_label_t *lab)
+{
+ bp_t *bp;
+ uint dest= instructions();
+ List_iterator_fast<bp_t> li(m_backpatch);
+
+ while ((bp= li++))
+ {
+ if (bp->lab == lab ||
+ (bp->lab->type == SP_LAB_REF &&
+ my_strcasecmp(system_charset_info, bp->lab->name, lab->name) == 0))
+ {
+ if (bp->lab->type != SP_LAB_REF)
+ bp->instr->backpatch(dest, lab->ctx);
+ else
+ {
+ sp_label_t *dstlab= bp->lab->ctx->find_label(lab->name);
+
+ if (dstlab)
+ {
+ bp->lab= lab;
+ bp->instr->backpatch(dest, dstlab->ctx);
+ }
+ }
+ }
+ }
+}
+
+int
+sp_head::check_backpatch(THD *thd)
+{
+ bp_t *bp;
+ List_iterator_fast<bp_t> li(m_backpatch);
+
+ while ((bp= li++))
+ {
+ if (bp->lab->type == SP_LAB_REF)
+ {
+ net_printf(thd, ER_SP_LILABEL_MISMATCH, "GOTO", bp->lab->name);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+sp_head::set_info(char *definer, uint definerlen,
+ longlong created, longlong modified,
+ st_sp_chistics *chistics, ulong sql_mode)
+{
+ char *p= strchr(definer, '@');
+ uint len;
+
+ if (! p)
+ p= definer; // Weird...
+ len= p-definer;
+ m_definer_user.str= strmake_root(&mem_root, definer, len);
+ m_definer_user.length= len;
+ len= definerlen-len-1;
+ m_definer_host.str= strmake_root(&mem_root, p+1, len);
+ m_definer_host.length= len;
+ m_created= created;
+ m_modified= modified;
+ m_chistics= (st_sp_chistics *)alloc_root(&mem_root, sizeof(st_sp_chistics));
+ memcpy(m_chistics, chistics, sizeof(st_sp_chistics));
+ if (m_chistics->comment.length == 0)
+ m_chistics->comment.str= 0;
+ else
+ m_chistics->comment.str= strmake_root(&mem_root,
+ m_chistics->comment.str,
+ m_chistics->comment.length);
+ m_sql_mode= sql_mode;
+}
+
+void
+sp_head::reset_thd_mem_root(THD *thd)
+{
+ DBUG_ENTER("sp_head::reset_thd_mem_root");
+ m_thd_root= thd->mem_root;
+ thd->mem_root= mem_root;
+ DBUG_PRINT("info", ("mem_root 0x%lx moved to thd mem root 0x%lx",
+ (ulong) &mem_root, (ulong) &thd->mem_root));
+ free_list= thd->free_list; // Keep the old list
+ thd->free_list= NULL; // Start a new one
+ /* Copy the db, since substatements will point to it */
+ m_thd_db= thd->db;
+ thd->db= strmake_root(&thd->mem_root, thd->db, thd->db_length);
+ m_thd= thd;
+ DBUG_VOID_RETURN;
+}
+
+void
+sp_head::restore_thd_mem_root(THD *thd)
+{
+ DBUG_ENTER("sp_head::restore_thd_mem_root");
+ Item *flist= free_list; // The old list
+ set_item_arena(thd); // Get new fre_list and mem_root
+ DBUG_PRINT("info", ("mem_root 0x%lx returned from thd mem root 0x%lx",
+ (ulong) &mem_root, (ulong) &thd->mem_root));
+ thd->free_list= flist; // Restore the old one
+ thd->db= m_thd_db; // Restore the original db pointer
+ thd->mem_root= m_thd_root;
+ m_thd= NULL;
+ DBUG_VOID_RETURN;
+}
+
+
+int
+sp_head::show_create_procedure(THD *thd)
+{
+ Protocol *protocol= thd->protocol;
+ char buff[2048];
+ String buffer(buff, sizeof(buff), system_charset_info);
+ int res;
+ List<Item> field_list;
+ ulong old_sql_mode;
+ sys_var *sql_mode_var;
+ byte *sql_mode_str;
+ ulong sql_mode_len;
+
+ DBUG_ENTER("sp_head::show_create_procedure");
+ DBUG_PRINT("info", ("procedure %s", m_name.str));
+ LINT_INIT(sql_mode_str);
+ LINT_INIT(sql_mode_len);
+
+ old_sql_mode= thd->variables.sql_mode;
+ thd->variables.sql_mode= m_sql_mode;
+ sql_mode_var= find_sys_var("SQL_MODE", 8);
+ if (sql_mode_var)
+ {
+ sql_mode_str= sql_mode_var->value_ptr(thd, OPT_SESSION, 0);
+ sql_mode_len= strlen((char*) sql_mode_str);
+ }
+
+ field_list.push_back(new Item_empty_string("Procedure", NAME_LEN));
+ if (sql_mode_var)
+ field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+ // 1024 is for not to confuse old clients
+ field_list.push_back(new Item_empty_string("Create Procedure",
+ max(buffer.length(), 1024)));
+ if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ {
+ res= 1;
+ goto done;
+ }
+ protocol->prepare_for_resend();
+ protocol->store(m_name.str, m_name.length, system_charset_info);
+ if (sql_mode_var)
+ protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+ protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
+ res= protocol->write();
+ send_eof(thd);
+
+ done:
+ thd->variables.sql_mode= old_sql_mode;
+ DBUG_RETURN(res);
+}
+
+
+/*
+ Add instruction to SP
+
+ SYNOPSIS
+ sp_head::add_instr()
+ instr Instruction
+*/
+
+void sp_head::add_instr(sp_instr *instr)
+{
+ instr->free_list= m_thd->free_list;
+ m_thd->free_list= 0;
+ insert_dynamic(&m_instr, (gptr)&instr);
+}
+
+
+int
+sp_head::show_create_function(THD *thd)
+{
+ Protocol *protocol= thd->protocol;
+ char buff[2048];
+ String buffer(buff, sizeof(buff), system_charset_info);
+ int res;
+ List<Item> field_list;
+ ulong old_sql_mode;
+ sys_var *sql_mode_var;
+ byte *sql_mode_str;
+ ulong sql_mode_len;
+ DBUG_ENTER("sp_head::show_create_function");
+ DBUG_PRINT("info", ("procedure %s", m_name.str));
+ LINT_INIT(sql_mode_str);
+ LINT_INIT(sql_mode_len);
+
+ old_sql_mode= thd->variables.sql_mode;
+ thd->variables.sql_mode= m_sql_mode;
+ sql_mode_var= find_sys_var("SQL_MODE", 8);
+ if (sql_mode_var)
+ {
+ sql_mode_str= sql_mode_var->value_ptr(thd, OPT_SESSION, 0);
+ sql_mode_len= strlen((char*) sql_mode_str);
+ }
+
+ field_list.push_back(new Item_empty_string("Function",NAME_LEN));
+ if (sql_mode_var)
+ field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
+ field_list.push_back(new Item_empty_string("Create Function",
+ max(buffer.length(),1024)));
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ {
+ res= 1;
+ goto done;
+ }
+ protocol->prepare_for_resend();
+ protocol->store(m_name.str, m_name.length, system_charset_info);
+ if (sql_mode_var)
+ protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info);
+ protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
+ res= protocol->write();
+ send_eof(thd);
+
+ done:
+ thd->variables.sql_mode= old_sql_mode;
+ DBUG_RETURN(res);
+}
+
+void
+sp_head::optimize()
+{
+ List<sp_instr> bp;
+ sp_instr *i;
+ uint src, dst;
+
+ opt_mark(0);
+
+ bp.empty();
+ src= dst= 0;
+ while ((i= get_instr(src)))
+ {
+ if (! i->marked)
+ {
+ delete i;
+ src+= 1;
+ }
+ else
+ {
+ if (src != dst)
+ {
+ sp_instr *ibp;
+ List_iterator_fast<sp_instr> li(bp);
+
+ set_dynamic(&m_instr, (gptr)&i, dst);
+ while ((ibp= li++))
+ {
+ sp_instr_jump *ji= static_cast<sp_instr_jump *>(ibp);
+ if (ji->m_dest == src)
+ ji->m_dest= dst;
+ }
+ }
+ i->opt_move(dst, &bp);
+ src+= 1;
+ dst+= 1;
+ }
+ }
+ m_instr.elements= dst;
+ bp.empty();
+}
+
+void
+sp_head::opt_mark(uint ip)
+{
+ sp_instr *i;
+
+ while ((i= get_instr(ip)) && !i->marked)
+ ip= i->opt_mark(this);
+}
+
+// ------------------------------------------------------------------
+
+//
+// sp_instr_stmt
+//
+sp_instr_stmt::~sp_instr_stmt()
+{
+ if (m_lex)
+ delete m_lex;
+}
+
+int
+sp_instr_stmt::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_stmt::execute");
+ DBUG_PRINT("info", ("command: %d", m_lex->sql_command));
+ int res= exec_stmt(thd, m_lex);
+ *nextp = m_ip+1;
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_stmt::print(String *str)
+{
+ str->reserve(12);
+ str->append("stmt ");
+ str->qs_append((uint)m_lex->sql_command);
+}
+
+
+int
+sp_instr_stmt::exec_stmt(THD *thd, LEX *lex)
+{
+ LEX *olex; // The other lex
+ int res;
+
+ olex= thd->lex; // Save the other lex
+ thd->lex= lex; // Use my own lex
+ thd->lex->thd = thd; // QQ Not reentrant!
+ thd->lex->unit.thd= thd; // QQ Not reentrant
+ thd->free_list= NULL;
+
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ thd->query_id= query_id++;
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+
+ reset_stmt_for_execute(thd, lex);
+
+ res= mysql_execute_command(thd);
+
+ lex->unit.cleanup();
+ if (thd->lock || thd->open_tables || thd->derived_tables)
+ {
+ thd->proc_info="closing tables";
+ close_thread_tables(thd); /* Free tables */
+ }
+
+ thd->lex= olex; // Restore the other lex
+
+ return res;
+}
+
+//
+// sp_instr_set
+//
+int
+sp_instr_set::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_set::execute");
+ DBUG_PRINT("info", ("offset: %u", m_offset));
+ Item *it;
+ int res;
+
+ if (tables &&
+ ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
+ (res= open_and_lock_tables(thd, tables))))
+ DBUG_RETURN(-1);
+
+ it= sp_eval_func_item(thd, m_value, m_type);
+ if (! it)
+ res= -1;
+ else
+ {
+ res= 0;
+ thd->spcont->set_item(m_offset, it);
+ }
+ *nextp = m_ip+1;
+ if (thd->lock || thd->open_tables || thd->derived_tables)
+ close_thread_tables(thd);
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_set::print(String *str)
+{
+ str->reserve(12);
+ str->append("set ");
+ str->qs_append(m_offset);
+ str->append(' ');
+ m_value->print(str);
+}
+
+//
+// sp_instr_jump
+//
+int
+sp_instr_jump::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+
+ *nextp= m_dest;
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_jump::print(String *str)
+{
+ str->reserve(12);
+ str->append("jump ");
+ str->qs_append(m_dest);
+}
+
+uint
+sp_instr_jump::opt_mark(sp_head *sp)
+{
+ m_dest= opt_shortcut_jump(sp, this);
+ if (m_dest != m_ip+1) /* Jumping to following instruction? */
+ marked= 1;
+ m_optdest= sp->get_instr(m_dest);
+ return m_dest;
+}
+
+uint
+sp_instr_jump::opt_shortcut_jump(sp_head *sp, sp_instr *start)
+{
+ uint dest= m_dest;
+ sp_instr *i;
+
+ while ((i= sp->get_instr(dest)))
+ {
+ uint ndest;
+
+ if (start == i)
+ break;
+ ndest= i->opt_shortcut_jump(sp, start);
+ if (ndest == dest)
+ break;
+ dest= ndest;
+ }
+ return dest;
+}
+
+void
+sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp)
+{
+ if (m_dest > m_ip)
+ bp->push_back(this); // Forward
+ else if (m_optdest)
+ m_dest= m_optdest->m_ip; // Backward
+ m_ip= dst;
+}
+
+//
+// sp_instr_jump_if
+//
+int
+sp_instr_jump_if::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump_if::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+ Item *it;
+ int res;
+
+ if (tables &&
+ ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
+ (res= open_and_lock_tables(thd, tables))))
+ DBUG_RETURN(-1);
+
+ it= sp_eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
+ if (!it)
+ res= -1;
+ else
+ {
+ res= 0;
+ if (it->val_int())
+ *nextp = m_dest;
+ else
+ *nextp = m_ip+1;
+ }
+ if (thd->lock || thd->open_tables || thd->derived_tables)
+ close_thread_tables(thd);
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_jump_if::print(String *str)
+{
+ str->reserve(12);
+ str->append("jump_if ");
+ str->qs_append(m_dest);
+ str->append(' ');
+ m_expr->print(str);
+}
+
+uint
+sp_instr_jump_if::opt_mark(sp_head *sp)
+{
+ sp_instr *i;
+
+ marked= 1;
+ if ((i= sp->get_instr(m_dest)))
+ {
+ m_dest= i->opt_shortcut_jump(sp, this);
+ m_optdest= sp->get_instr(m_dest);
+ }
+ sp->opt_mark(m_dest);
+ return m_ip+1;
+}
+
+//
+// sp_instr_jump_if_not
+//
+int
+sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump_if_not::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+ Item *it;
+ int res;
+
+ if (tables &&
+ ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
+ (res= open_and_lock_tables(thd, tables))))
+ DBUG_RETURN(-1);
+
+ it= sp_eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
+ if (! it)
+ res= -1;
+ else
+ {
+ res= 0;
+ if (! it->val_int())
+ *nextp = m_dest;
+ else
+ *nextp = m_ip+1;
+ }
+ if (thd->lock || thd->open_tables || thd->derived_tables)
+ close_thread_tables(thd);
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_jump_if_not::print(String *str)
+{
+ str->reserve(16);
+ str->append("jump_if_not ");
+ str->qs_append(m_dest);
+ str->append(' ');
+ m_expr->print(str);
+}
+
+uint
+sp_instr_jump_if_not::opt_mark(sp_head *sp)
+{
+ sp_instr *i;
+
+ marked= 1;
+ if ((i= sp->get_instr(m_dest)))
+ {
+ m_dest= i->opt_shortcut_jump(sp, this);
+ m_optdest= sp->get_instr(m_dest);
+ }
+ sp->opt_mark(m_dest);
+ return m_ip+1;
+}
+
+//
+// sp_instr_freturn
+//
+int
+sp_instr_freturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_freturn::execute");
+ Item *it;
+ int res;
+
+ if (tables &&
+ ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
+ (res= open_and_lock_tables(thd, tables))))
+ DBUG_RETURN(-1);
+
+ it= sp_eval_func_item(thd, m_value, m_type);
+ if (! it)
+ res= -1;
+ else
+ {
+ res= 0;
+ thd->spcont->set_result(it);
+ }
+ *nextp= UINT_MAX;
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_freturn::print(String *str)
+{
+ str->reserve(12);
+ str->append("freturn ");
+ str->qs_append((uint)m_type);
+ str->append(' ');
+ m_value->print(str);
+}
+
+//
+// sp_instr_hpush_jump
+//
+int
+sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hpush_jump::execute");
+ List_iterator_fast<sp_cond_type_t> li(m_cond);
+ sp_cond_type_t *p;
+
+ while ((p= li++))
+ thd->spcont->push_handler(p, m_handler, m_type, m_frame);
+
+ *nextp= m_dest;
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_hpush_jump::print(String *str)
+{
+ str->reserve(32);
+ str->append("hpush_jump ");
+ str->qs_append(m_dest);
+ str->append(" t=");
+ str->qs_append(m_type);
+ str->append(" f=");
+ str->qs_append(m_frame);
+ str->append(" h=");
+ str->qs_append(m_handler);
+}
+
+uint
+sp_instr_hpush_jump::opt_mark(sp_head *sp)
+{
+ sp_instr *i;
+
+ marked= 1;
+ if ((i= sp->get_instr(m_dest)))
+ {
+ m_dest= i->opt_shortcut_jump(sp, this);
+ m_optdest= sp->get_instr(m_dest);
+ }
+ sp->opt_mark(m_dest);
+ return m_ip+1;
+}
+
+//
+// sp_instr_hpop
+//
+int
+sp_instr_hpop::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hpop::execute");
+ thd->spcont->pop_handlers(m_count);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_hpop::print(String *str)
+{
+ str->reserve(12);
+ str->append("hpop ");
+ str->qs_append(m_count);
+}
+
+void
+sp_instr_hpop::backpatch(uint dest, sp_pcontext *dst_ctx)
+{
+ m_count= m_ctx->diff_handlers(dst_ctx);
+}
+
+
+//
+// sp_instr_hreturn
+//
+int
+sp_instr_hreturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hreturn::execute");
+ thd->spcont->restore_variables(m_frame);
+ *nextp= thd->spcont->pop_hstack();
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_hreturn::print(String *str)
+{
+ str->reserve(12);
+ str->append("hreturn ");
+ str->qs_append(m_frame);
+}
+
+//
+// sp_instr_cpush
+//
+int
+sp_instr_cpush::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cpush::execute");
+ thd->spcont->push_cursor(m_lex);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+sp_instr_cpush::~sp_instr_cpush()
+{
+ if (m_lex)
+ delete m_lex;
+}
+
+void
+sp_instr_cpush::print(String *str)
+{
+ str->append("cpush");
+}
+
+//
+// sp_instr_cpop
+//
+int
+sp_instr_cpop::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cpop::execute");
+ thd->spcont->pop_cursors(m_count);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+void
+sp_instr_cpop::print(String *str)
+{
+ str->reserve(12);
+ str->append("cpop ");
+ str->qs_append(m_count);
+}
+
+void
+sp_instr_cpop::backpatch(uint dest, sp_pcontext *dst_ctx)
+{
+ m_count= m_ctx->diff_cursors(dst_ctx);
+}
+
+//
+// sp_instr_copen
+//
+int
+sp_instr_copen::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_copen::execute");
+
+ if (! c)
+ res= -1;
+ else
+ {
+ LEX *lex= c->pre_open(thd);
+
+ if (! lex)
+ res= -1;
+ else
+ res= exec_stmt(thd, lex);
+ c->post_open(thd, (lex ? TRUE : FALSE));
+ }
+
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_copen::print(String *str)
+{
+ str->reserve(12);
+ str->append("copen ");
+ str->qs_append(m_cursor);
+}
+
+//
+// sp_instr_cclose
+//
+int
+sp_instr_cclose::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_cclose::execute");
+
+ if (! c)
+ res= -1;
+ else
+ res= c->close(thd);
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_cclose::print(String *str)
+{
+ str->reserve(12);
+ str->append("cclose ");
+ str->qs_append(m_cursor);
+}
+
+//
+// sp_instr_cfetch
+//
+int
+sp_instr_cfetch::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_cfetch::execute");
+
+ if (! c)
+ res= -1;
+ else
+ res= c->fetch(thd, &m_varlist);
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+void
+sp_instr_cfetch::print(String *str)
+{
+ List_iterator_fast<struct sp_pvar> li(m_varlist);
+ sp_pvar_t *pv;
+
+ str->reserve(12);
+ str->append("cfetch ");
+ str->qs_append(m_cursor);
+ while ((pv= li++))
+ {
+ str->reserve(8);
+ str->append(' ');
+ str->qs_append(pv->offset);
+ }
+}
+
+//
+// sp_instr_error
+//
+int
+sp_instr_error::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_error::execute");
+
+ my_error(m_errcode, MYF(0));
+ *nextp= m_ip+1;
+ DBUG_RETURN(-1);
+}
+
+void
+sp_instr_error::print(String *str)
+{
+ str->reserve(12);
+ str->append("error ");
+ str->qs_append(m_errcode);
+}
+
+/* ------------------------------------------------------------------ */
+
+
+//
+// Security context swapping
+//
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+void
+sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
+{
+ ctxp->changed= (sp->m_chistics->suid != IS_NOT_SUID &&
+ (strcmp(sp->m_definer_user.str, thd->priv_user) ||
+ strcmp(sp->m_definer_host.str, thd->priv_host)));
+
+ if (ctxp->changed)
+ {
+ ctxp->master_access= thd->master_access;
+ ctxp->db_access= thd->db_access;
+ ctxp->priv_user= thd->priv_user;
+ strncpy(ctxp->priv_host, thd->priv_host, sizeof(ctxp->priv_host));
+ ctxp->user= thd->user;
+ ctxp->host= thd->host;
+ ctxp->ip= thd->ip;
+
+ /* Change thise just to do the acl_getroot_no_password */
+ thd->user= sp->m_definer_user.str;
+ thd->host= thd->ip = sp->m_definer_host.str;
+
+ if (acl_getroot_no_password(thd))
+ { // Failed, run as invoker for now
+ ctxp->changed= FALSE;
+ thd->master_access= ctxp->master_access;
+ thd->db_access= ctxp->db_access;
+ thd->priv_user= ctxp->priv_user;
+ strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host));
+ }
+
+ /* Restore these immiediately */
+ thd->user= ctxp->user;
+ thd->host= ctxp->host;
+ thd->ip= ctxp->ip;
+ }
+}
+
+void
+sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
+{
+ if (ctxp->changed)
+ {
+ ctxp->changed= FALSE;
+ thd->master_access= ctxp->master_access;
+ thd->db_access= ctxp->db_access;
+ thd->priv_user= ctxp->priv_user;
+ strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host));
+ }
+}
+
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
diff --git a/sql/sp_head.h b/sql/sp_head.h
new file mode 100644
index 00000000000..c0881661ad1
--- /dev/null
+++ b/sql/sp_head.h
@@ -0,0 +1,835 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_HEAD_H_
+#define _SP_HEAD_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#include <stddef.h>
+
+// Values for the type enum. This reflects the order of the enum declaration
+// in the CREATE TABLE command.
+#define TYPE_ENUM_FUNCTION 1
+#define TYPE_ENUM_PROCEDURE 2
+
+Item_result
+sp_map_result_type(enum enum_field_types type);
+
+bool
+sp_multi_results_command(enum enum_sql_command cmd);
+
+struct sp_label;
+class sp_instr;
+struct sp_cond_type;
+struct sp_pvar;
+
+class sp_name : public Sql_alloc
+{
+public:
+
+ LEX_STRING m_db;
+ LEX_STRING m_name;
+ LEX_STRING m_qname;
+
+ sp_name(LEX_STRING name)
+ : m_name(name)
+ {
+ m_db.str= m_qname.str= 0;
+ m_db.length= m_qname.length= 0;
+ }
+
+ sp_name(LEX_STRING db, LEX_STRING name)
+ : m_db(db), m_name(name)
+ {
+ m_qname.str= 0;
+ m_qname.length= 0;
+ }
+
+ // Init. the qualified name from the db and name.
+ void init_qname(THD *thd); // thd for memroot allocation
+
+ ~sp_name()
+ {}
+};
+
+sp_name *
+sp_name_current_db_new(THD *thd, LEX_STRING name);
+
+
+class sp_head :private Item_arena
+{
+ sp_head(const sp_head &); /* Prevent use of these */
+ void operator=(sp_head &);
+
+public:
+
+ int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
+ enum enum_field_types m_returns; // For FUNCTIONs only
+ CHARSET_INFO *m_returns_cs; // For FUNCTIONs only
+ my_bool m_has_return; // For FUNCTIONs only
+ my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise
+ my_bool m_multi_results; // TRUE if a procedure with SELECT(s)
+ my_bool m_in_handler; // TRUE if parser in a handler body
+ uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
+ st_sp_chistics *m_chistics;
+ ulong m_sql_mode; // For SHOW CREATE
+#if NOT_USED_NOW
+ // QQ We're not using this at the moment.
+ List<char *> m_calls; // Called procedures.
+ List<char *> m_tables; // Used tables.
+#endif
+ LEX_STRING m_qname; // db.name
+ LEX_STRING m_db;
+ LEX_STRING m_name;
+ LEX_STRING m_params;
+ LEX_STRING m_retstr; // For FUNCTIONs only
+ LEX_STRING m_body;
+ LEX_STRING m_defstr;
+ LEX_STRING m_definer_user;
+ LEX_STRING m_definer_host;
+ longlong m_created;
+ longlong m_modified;
+ // Pointers set during parsing
+ uchar *m_param_begin, *m_param_end, *m_returns_begin, *m_returns_end,
+ *m_body_begin;
+
+ static void *
+ operator new(size_t size);
+
+ static void
+ operator delete(void *ptr, size_t size);
+
+ sp_head();
+
+ // Initialize after we have reset mem_root
+ void
+ init(LEX *lex);
+
+ // Initialize strings after parsing header
+ void
+ init_strings(THD *thd, LEX *lex, sp_name *name);
+
+ int
+ create(THD *thd);
+
+ virtual ~sp_head();
+
+ // Free memory
+ void
+ destroy();
+
+ int
+ execute_function(THD *thd, Item **args, uint argcount, Item **resp);
+
+ int
+ execute_procedure(THD *thd, List<Item> *args);
+
+ int
+ show_create_procedure(THD *thd);
+
+ int
+ show_create_function(THD *thd);
+
+ void
+ add_instr(sp_instr *instr);
+
+ inline uint
+ instructions()
+ {
+ return m_instr.elements;
+ }
+
+ inline sp_instr *
+ last_instruction()
+ {
+ sp_instr *i;
+
+ get_dynamic(&m_instr, (gptr)&i, m_instr.elements-1);
+ return i;
+ }
+
+ // Resets lex in 'thd' and keeps a copy of the old one.
+ void
+ reset_lex(THD *thd);
+
+ // Restores lex in 'thd' from our copy, but keeps some status from the
+ // one in 'thd', like ptr, tables, fields, etc.
+ void
+ restore_lex(THD *thd);
+
+ // Put the instruction on the backpatch list, associated with the label.
+ void
+ push_backpatch(sp_instr *, struct sp_label *);
+
+ // Update all instruction with this label in the backpatch list to
+ // the current position.
+ void
+ backpatch(struct sp_label *);
+
+ // Check that no unresolved references exist.
+ // If none found, 0 is returned, otherwise errors have been issued
+ // and -1 is returned.
+ // This is called by the parser at the end of a create procedure/function.
+ int
+ check_backpatch(THD *thd);
+
+ char *name(uint *lenp = 0) const
+ {
+ if (lenp)
+ *lenp= m_name.length;
+ return m_name.str;
+ }
+
+ char *create_string(THD *thd, ulong *lenp);
+
+ inline Item_result result()
+ {
+ return sp_map_result_type(m_returns);
+ }
+
+ void set_info(char *definer, uint definerlen,
+ longlong created, longlong modified,
+ st_sp_chistics *chistics, ulong sql_mode);
+
+ void reset_thd_mem_root(THD *thd);
+
+ void restore_thd_mem_root(THD *thd);
+
+ void optimize();
+ void opt_mark(uint ip);
+
+ inline sp_instr *
+ get_instr(uint i)
+ {
+ sp_instr *ip;
+
+ if (i < m_instr.elements)
+ get_dynamic(&m_instr, (gptr)&ip, i);
+ else
+ ip= NULL;
+ return ip;
+ }
+
+private:
+
+ MEM_ROOT m_thd_root; // Temp. store for thd's mem_root
+ THD *m_thd; // Set if we have reset mem_root
+ char *m_thd_db; // Original thd->db pointer
+
+ sp_pcontext *m_pcont; // Parse context
+ List<LEX> m_lex; // Temp. store for the other lex
+ DYNAMIC_ARRAY m_instr; // The "instructions"
+ typedef struct
+ {
+ struct sp_label *lab;
+ sp_instr *instr;
+ } bp_t;
+ List<bp_t> m_backpatch; // Instructions needing backpatching
+
+ int
+ execute(THD *thd);
+
+}; // class sp_head : public Sql_alloc
+
+
+//
+// "Instructions"...
+//
+
+class sp_instr : public Sql_alloc
+{
+ sp_instr(const sp_instr &); /* Prevent use of these */
+ void operator=(sp_instr &);
+
+public:
+
+ uint marked;
+ Item *free_list; // My Items
+ uint m_ip; // My index
+ sp_pcontext *m_ctx; // My parse context
+
+ // Should give each a name or type code for debugging purposes?
+ sp_instr(uint ip, sp_pcontext *ctx)
+ :Sql_alloc(), marked(0), free_list(0), m_ip(ip), m_ctx(ctx)
+ {}
+
+ virtual ~sp_instr()
+ { free_items(free_list); }
+
+ // Execute this instrution. '*nextp' will be set to the index of the next
+ // instruction to execute. (For most instruction this will be the
+ // instruction following this one.)
+ // Returns 0 on success, non-zero if some error occured.
+ virtual int execute(THD *thd, uint *nextp) = 0;
+
+ virtual void print(String *str) = 0;
+
+ virtual void backpatch(uint dest, sp_pcontext *dst_ctx)
+ {}
+
+ virtual uint opt_mark(sp_head *sp)
+ {
+ marked= 1;
+ return m_ip+1;
+ }
+
+ virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
+ {
+ return m_ip;
+ }
+
+ virtual void opt_move(uint dst, List<sp_instr> *ibp)
+ {
+ m_ip= dst;
+ }
+
+}; // class sp_instr : public Sql_alloc
+
+
+//
+// Call out to some prepared SQL statement.
+//
+class sp_instr_stmt : public sp_instr
+{
+ sp_instr_stmt(const sp_instr_stmt &); /* Prevent use of these */
+ void operator=(sp_instr_stmt &);
+
+public:
+
+ sp_instr_stmt(uint ip, sp_pcontext *ctx)
+ : sp_instr(ip, ctx), m_lex(NULL)
+ {}
+
+ virtual ~sp_instr_stmt();
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ inline void
+ set_lex(LEX *lex)
+ {
+ m_lex= lex;
+ }
+
+ inline LEX *
+ get_lex()
+ {
+ return m_lex;
+ }
+
+protected:
+
+ int exec_stmt(THD *thd, LEX *lex); // Execute a statement
+
+private:
+
+ LEX *m_lex; // My own lex
+
+}; // class sp_instr_stmt : public sp_instr
+
+
+class sp_instr_set : public sp_instr
+{
+ sp_instr_set(const sp_instr_set &); /* Prevent use of these */
+ void operator=(sp_instr_set &);
+
+public:
+
+ TABLE_LIST *tables;
+
+ sp_instr_set(uint ip, sp_pcontext *ctx,
+ uint offset, Item *val, enum enum_field_types type)
+ : sp_instr(ip, ctx),
+ tables(NULL), m_offset(offset), m_value(val), m_type(type)
+ {}
+
+ virtual ~sp_instr_set()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+private:
+
+ uint m_offset; // Frame offset
+ Item *m_value;
+ enum enum_field_types m_type; // The declared type
+
+}; // class sp_instr_set : public sp_instr
+
+
+class sp_instr_jump : public sp_instr
+{
+ sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */
+ void operator=(sp_instr_jump &);
+
+public:
+
+ uint m_dest; // Where we will go
+
+ sp_instr_jump(uint ip, sp_pcontext *ctx)
+ : sp_instr(ip, ctx), m_dest(0), m_optdest(0)
+ {}
+
+ sp_instr_jump(uint ip, sp_pcontext *ctx, uint dest)
+ : sp_instr(ip, ctx), m_dest(dest), m_optdest(0)
+ {}
+
+ virtual ~sp_instr_jump()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual uint opt_mark(sp_head *sp);
+
+ virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start);
+
+ virtual void opt_move(uint dst, List<sp_instr> *ibp);
+
+ virtual void backpatch(uint dest, sp_pcontext *dst_ctx)
+ {
+ if (m_dest == 0) // Don't reset
+ m_dest= dest;
+ }
+
+protected:
+
+ sp_instr *m_optdest; // Used during optimization
+
+}; // class sp_instr_jump : public sp_instr
+
+
+class sp_instr_jump_if : public sp_instr_jump
+{
+ sp_instr_jump_if(const sp_instr_jump_if &); /* Prevent use of these */
+ void operator=(sp_instr_jump_if &);
+
+public:
+
+ TABLE_LIST *tables;
+
+ sp_instr_jump_if(uint ip, sp_pcontext *ctx, Item *i)
+ : sp_instr_jump(ip, ctx), tables(NULL), m_expr(i)
+ {}
+
+ sp_instr_jump_if(uint ip, sp_pcontext *ctx, Item *i, uint dest)
+ : sp_instr_jump(ip, ctx, dest), tables(NULL), m_expr(i)
+ {}
+
+ virtual ~sp_instr_jump_if()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual uint opt_mark(sp_head *sp);
+
+ virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
+ {
+ return m_ip;
+ }
+
+private:
+
+ Item *m_expr; // The condition
+
+}; // class sp_instr_jump_if : public sp_instr_jump
+
+
+class sp_instr_jump_if_not : public sp_instr_jump
+{
+ sp_instr_jump_if_not(const sp_instr_jump_if_not &); /* Prevent use of these */
+ void operator=(sp_instr_jump_if_not &);
+
+public:
+
+ TABLE_LIST *tables;
+
+ sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i)
+ : sp_instr_jump(ip, ctx), tables(NULL), m_expr(i)
+ {}
+
+ sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, uint dest)
+ : sp_instr_jump(ip, ctx, dest), tables(NULL), m_expr(i)
+ {}
+
+ virtual ~sp_instr_jump_if_not()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual uint opt_mark(sp_head *sp);
+
+ virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
+ {
+ return m_ip;
+ }
+
+private:
+
+ Item *m_expr; // The condition
+
+}; // class sp_instr_jump_if_not : public sp_instr_jump
+
+
+class sp_instr_freturn : public sp_instr
+{
+ sp_instr_freturn(const sp_instr_freturn &); /* Prevent use of these */
+ void operator=(sp_instr_freturn &);
+
+public:
+
+ TABLE_LIST *tables;
+
+ sp_instr_freturn(uint ip, sp_pcontext *ctx,
+ Item *val, enum enum_field_types type)
+ : sp_instr(ip, ctx), tables(NULL), m_value(val), m_type(type)
+ {}
+
+ virtual ~sp_instr_freturn()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual uint opt_mark(sp_head *sp)
+ {
+ marked= 1;
+ return UINT_MAX;
+ }
+
+protected:
+
+ Item *m_value;
+ enum enum_field_types m_type;
+
+}; // class sp_instr_freturn : public sp_instr
+
+
+class sp_instr_hpush_jump : public sp_instr_jump
+{
+ sp_instr_hpush_jump(const sp_instr_hpush_jump &); /* Prevent use of these */
+ void operator=(sp_instr_hpush_jump &);
+
+public:
+
+ sp_instr_hpush_jump(uint ip, sp_pcontext *ctx, int htype, uint fp)
+ : sp_instr_jump(ip, ctx), m_type(htype), m_frame(fp)
+ {
+ m_handler= ip+1;
+ m_cond.empty();
+ }
+
+ virtual ~sp_instr_hpush_jump()
+ {
+ m_cond.empty();
+ }
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual uint opt_mark(sp_head *sp);
+
+ virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
+ {
+ return m_ip;
+ }
+
+ inline void add_condition(struct sp_cond_type *cond)
+ {
+ m_cond.push_front(cond);
+ }
+
+private:
+
+ int m_type; // Handler type
+ uint m_frame;
+ uint m_handler; // Location of handler
+ List<struct sp_cond_type> m_cond;
+
+}; // class sp_instr_hpush_jump : public sp_instr_jump
+
+
+class sp_instr_hpop : public sp_instr
+{
+ sp_instr_hpop(const sp_instr_hpop &); /* Prevent use of these */
+ void operator=(sp_instr_hpop &);
+
+public:
+
+ sp_instr_hpop(uint ip, sp_pcontext *ctx, uint count)
+ : sp_instr(ip, ctx), m_count(count)
+ {}
+
+ virtual ~sp_instr_hpop()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual void backpatch(uint dest, sp_pcontext *dst_ctx);
+
+ virtual uint opt_mark(sp_head *sp)
+ {
+ if (m_count)
+ marked= 1;
+ return m_ip+1;
+ }
+
+private:
+
+ uint m_count;
+
+}; // class sp_instr_hpop : public sp_instr
+
+
+class sp_instr_hreturn : public sp_instr
+{
+ sp_instr_hreturn(const sp_instr_hreturn &); /* Prevent use of these */
+ void operator=(sp_instr_hreturn &);
+
+public:
+
+ sp_instr_hreturn(uint ip, sp_pcontext *ctx, uint fp)
+ : sp_instr(ip, ctx), m_frame(fp)
+ {}
+
+ virtual ~sp_instr_hreturn()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual uint opt_mark(sp_head *sp)
+ {
+ marked= 1;
+ return UINT_MAX;
+ }
+
+private:
+
+ uint m_frame;
+
+}; // class sp_instr_hreturn : public sp_instr
+
+
+class sp_instr_cpush : public sp_instr
+{
+ sp_instr_cpush(const sp_instr_cpush &); /* Prevent use of these */
+ void operator=(sp_instr_cpush &);
+
+public:
+
+ sp_instr_cpush(uint ip, sp_pcontext *ctx, LEX *lex)
+ : sp_instr(ip, ctx), m_lex(lex)
+ {}
+
+ virtual ~sp_instr_cpush();
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+private:
+
+ LEX *m_lex;
+
+}; // class sp_instr_cpush : public sp_instr
+
+
+class sp_instr_cpop : public sp_instr
+{
+ sp_instr_cpop(const sp_instr_cpop &); /* Prevent use of these */
+ void operator=(sp_instr_cpop &);
+
+public:
+
+ sp_instr_cpop(uint ip, sp_pcontext *ctx, uint count)
+ : sp_instr(ip, ctx), m_count(count)
+ {}
+
+ virtual ~sp_instr_cpop()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual void backpatch(uint dest, sp_pcontext *dst_ctx);
+
+ virtual uint opt_mark(sp_head *sp)
+ {
+ if (m_count)
+ marked= 1;
+ return m_ip+1;
+ }
+
+private:
+
+ uint m_count;
+
+}; // class sp_instr_cpop : public sp_instr
+
+
+class sp_instr_copen : public sp_instr_stmt
+{
+ sp_instr_copen(const sp_instr_copen &); /* Prevent use of these */
+ void operator=(sp_instr_copen &);
+
+public:
+
+ sp_instr_copen(uint ip, sp_pcontext *ctx, uint c)
+ : sp_instr_stmt(ip, ctx), m_cursor(c)
+ {}
+
+ virtual ~sp_instr_copen()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+private:
+
+ uint m_cursor; // Stack index
+
+}; // class sp_instr_copen : public sp_instr_stmt
+
+
+class sp_instr_cclose : public sp_instr
+{
+ sp_instr_cclose(const sp_instr_cclose &); /* Prevent use of these */
+ void operator=(sp_instr_cclose &);
+
+public:
+
+ sp_instr_cclose(uint ip, sp_pcontext *ctx, uint c)
+ : sp_instr(ip, ctx), m_cursor(c)
+ {}
+
+ virtual ~sp_instr_cclose()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+private:
+
+ uint m_cursor;
+
+}; // class sp_instr_cclose : public sp_instr
+
+
+class sp_instr_cfetch : public sp_instr
+{
+ sp_instr_cfetch(const sp_instr_cfetch &); /* Prevent use of these */
+ void operator=(sp_instr_cfetch &);
+
+public:
+
+ sp_instr_cfetch(uint ip, sp_pcontext *ctx, uint c)
+ : sp_instr(ip, ctx), m_cursor(c)
+ {
+ m_varlist.empty();
+ }
+
+ virtual ~sp_instr_cfetch()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ void add_to_varlist(struct sp_pvar *var)
+ {
+ m_varlist.push_back(var);
+ }
+
+private:
+
+ uint m_cursor;
+ List<struct sp_pvar> m_varlist;
+
+}; // class sp_instr_cfetch : public sp_instr
+
+
+class sp_instr_error : public sp_instr
+{
+ sp_instr_error(const sp_instr_error &); /* Prevent use of these */
+ void operator=(sp_instr_error &);
+
+public:
+
+ sp_instr_error(uint ip, sp_pcontext *ctx, int errcode)
+ : sp_instr(ip, ctx), m_errcode(errcode)
+ {}
+
+ virtual ~sp_instr_error()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void print(String *str);
+
+ virtual uint opt_mark(sp_head *sp)
+ {
+ marked= 1;
+ return UINT_MAX;
+ }
+
+private:
+
+ int m_errcode;
+
+}; // class sp_instr_error : public sp_instr
+
+
+struct st_sp_security_context
+{
+ bool changed;
+ uint master_access;
+ uint db_access;
+ char *priv_user;
+ char priv_host[MAX_HOSTNAME];
+ char *user;
+ char *host;
+ char *ip;
+};
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+void
+sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp);
+void
+sp_restore_security_context(THD *thd, sp_head *sp,st_sp_security_context *ctxp);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+#endif /* _SP_HEAD_H_ */
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
new file mode 100644
index 00000000000..3b60f0936f9
--- /dev/null
+++ b/sql/sp_pcontext.cc
@@ -0,0 +1,274 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#if defined(WIN32) || defined(__WIN__)
+#undef SAFEMALLOC /* Problems with threads */
+#endif
+
+#include "mysql_priv.h"
+#include "sp_pcontext.h"
+#include "sp_head.h"
+
+sp_pcontext::sp_pcontext(sp_pcontext *prev)
+ : Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0),
+ m_handlers(0), m_parent(prev)
+{
+ VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8));
+ VOID(my_init_dynamic_array(&m_cond, sizeof(sp_cond_type_t *), 16, 8));
+ VOID(my_init_dynamic_array(&m_cursor, sizeof(LEX_STRING), 16, 8));
+ m_label.empty();
+ m_children.empty();
+ if (!prev)
+ m_poffset= m_coffset= 0;
+ else
+ {
+ m_poffset= prev->current_pvars();
+ m_coffset= prev->current_cursors();
+ }
+}
+
+void
+sp_pcontext::destroy()
+{
+ List_iterator_fast<sp_pcontext> li(m_children);
+ sp_pcontext *child;
+
+ while ((child= li++))
+ child->destroy();
+
+ m_children.empty();
+ m_label.empty();
+ delete_dynamic(&m_pvar);
+ delete_dynamic(&m_cond);
+ delete_dynamic(&m_cursor);
+}
+
+sp_pcontext *
+sp_pcontext::push_context()
+{
+ sp_pcontext *child= new sp_pcontext(this);
+
+ if (child)
+ m_children.push_back(child);
+ return child;
+}
+
+sp_pcontext *
+sp_pcontext::pop_context()
+{
+ uint submax= max_pvars();
+
+ if (submax > m_parent->m_psubsize)
+ m_parent->m_psubsize= submax;
+ submax= max_handlers();
+ if (submax > m_parent->m_hsubsize)
+ m_parent->m_hsubsize= submax;
+ submax= max_cursors();
+ if (submax > m_parent->m_csubsize)
+ m_parent->m_csubsize= submax;
+ return m_parent;
+}
+
+uint
+sp_pcontext::diff_handlers(sp_pcontext *ctx)
+{
+ uint n= 0;
+ sp_pcontext *pctx= this;
+
+ while (pctx && pctx != ctx)
+ {
+ n+= pctx->m_handlers;
+ pctx= pctx->parent_context();
+ }
+ if (pctx)
+ return n;
+ return 0; // Didn't find ctx
+}
+
+uint
+sp_pcontext::diff_cursors(sp_pcontext *ctx)
+{
+ uint n= 0;
+ sp_pcontext *pctx= this;
+
+ while (pctx && pctx != ctx)
+ pctx= pctx->parent_context();
+ if (pctx)
+ return ctx->current_cursors() - pctx->current_cursors();
+ return 0; // Didn't find ctx
+}
+
+/* This does a linear search (from newer to older variables, in case
+** we have shadowed names).
+** It's possible to have a more efficient allocation and search method,
+** but it might not be worth it. The typical number of parameters and
+** variables will in most cases be low (a handfull).
+** ...and, this is only called during parsing.
+*/
+sp_pvar_t *
+sp_pcontext::find_pvar(LEX_STRING *name, my_bool scoped)
+{
+ uint i= m_pvar.elements;
+
+ while (i--)
+ {
+ sp_pvar_t *p;
+
+ get_dynamic(&m_pvar, (gptr)&p, i);
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)p->name.str, p->name.length) == 0)
+ {
+ return p;
+ }
+ }
+ if (!scoped && m_parent)
+ return m_parent->find_pvar(name, scoped);
+ return NULL;
+}
+
+void
+sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type,
+ sp_param_mode_t mode)
+{
+ sp_pvar_t *p= (sp_pvar_t *)sql_alloc(sizeof(sp_pvar_t));
+
+ if (p)
+ {
+ if (m_pvar.elements == m_psubsize)
+ m_psubsize+= 1;
+ p->name.str= name->str;
+ p->name.length= name->length;
+ p->type= type;
+ p->mode= mode;
+ p->offset= current_pvars();
+ p->isset= (mode == sp_param_out ? FALSE : TRUE);
+ p->dflt= NULL;
+ insert_dynamic(&m_pvar, (gptr)&p);
+ }
+}
+
+sp_label_t *
+sp_pcontext::push_label(char *name, uint ip)
+{
+ sp_label_t *lab = (sp_label_t *)sql_alloc(sizeof(sp_label_t));
+
+ if (lab)
+ {
+ lab->name= name;
+ lab->ip= ip;
+ lab->type= SP_LAB_GOTO;
+ lab->ctx= this;
+ m_label.push_front(lab);
+ }
+ return lab;
+}
+
+sp_label_t *
+sp_pcontext::find_label(char *name)
+{
+ List_iterator_fast<sp_label_t> li(m_label);
+ sp_label_t *lab;
+
+ while ((lab= li++))
+ if (my_strcasecmp(system_charset_info, name, lab->name) == 0)
+ return lab;
+
+ if (m_parent)
+ return m_parent->find_label(name);
+ return NULL;
+}
+
+void
+sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val)
+{
+ sp_cond_t *p= (sp_cond_t *)sql_alloc(sizeof(sp_cond_t));
+
+ if (p)
+ {
+ p->name.str= name->str;
+ p->name.length= name->length;
+ p->val= val;
+ insert_dynamic(&m_cond, (gptr)&p);
+ }
+}
+
+/*
+ * See comment for find_pvar() above
+ */
+sp_cond_type_t *
+sp_pcontext::find_cond(LEX_STRING *name, my_bool scoped)
+{
+ uint i= m_cond.elements;
+
+ while (i--)
+ {
+ sp_cond_t *p;
+
+ get_dynamic(&m_cond, (gptr)&p, i);
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)p->name.str, p->name.length) == 0)
+ {
+ return p->val;
+ }
+ }
+ if (!scoped && m_parent)
+ return m_parent->find_cond(name, scoped);
+ return NULL;
+}
+
+void
+sp_pcontext::push_cursor(LEX_STRING *name)
+{
+ LEX_STRING n;
+
+ if (m_cursor.elements == m_csubsize)
+ m_csubsize+= 1;
+ n.str= name->str;
+ n.length= name->length;
+ insert_dynamic(&m_cursor, (gptr)&n);
+}
+
+/*
+ * See comment for find_pvar() above
+ */
+my_bool
+sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
+{
+ uint i= m_cursor.elements;
+
+ while (i--)
+ {
+ LEX_STRING n;
+
+ get_dynamic(&m_cursor, (gptr)&n, i);
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)n.str, n.length) == 0)
+ {
+ *poff= i;
+ return TRUE;
+ }
+ }
+ if (!scoped && m_parent)
+ return m_parent->find_cursor(name, poff, scoped);
+ return FALSE;
+}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
new file mode 100644
index 00000000000..36e4ed06aa7
--- /dev/null
+++ b/sql/sp_pcontext.h
@@ -0,0 +1,298 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_PCONTEXT_H_
+#define _SP_PCONTEXT_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+typedef enum
+{
+ sp_param_in,
+ sp_param_out,
+ sp_param_inout
+} sp_param_mode_t;
+
+typedef struct sp_pvar
+{
+ LEX_STRING name;
+ enum enum_field_types type;
+ sp_param_mode_t mode;
+ uint offset; // Offset in current frame
+ my_bool isset;
+ Item *dflt;
+} sp_pvar_t;
+
+
+#define SP_LAB_REF 0 // Unresolved reference (for goto)
+#define SP_LAB_GOTO 1 // Free goto label
+#define SP_LAB_BEGIN 2 // Label at BEGIN
+#define SP_LAB_ITER 3 // Label at iteration control
+
+typedef struct sp_label
+{
+ char *name;
+ uint ip; // Instruction index
+ int type; // begin/iter or ref/free
+ struct sp_pcontext *ctx; // The label's context
+} sp_label_t;
+
+typedef struct sp_cond_type
+{
+ enum { number, state, warning, notfound, exception } type;
+ char sqlstate[6];
+ uint mysqlerr;
+} sp_cond_type_t;
+
+typedef struct sp_cond
+{
+ LEX_STRING name;
+ sp_cond_type_t *val;
+} sp_cond_t;
+
+class sp_pcontext : public Sql_alloc
+{
+ sp_pcontext(const sp_pcontext &); /* Prevent use of these */
+ void operator=(sp_pcontext &);
+
+ public:
+
+ sp_pcontext(sp_pcontext *prev);
+
+ // Free memory
+ void
+ destroy();
+
+ sp_pcontext *
+ push_context();
+
+ // Returns the previous context, not the one we pop
+ sp_pcontext *
+ pop_context();
+
+ sp_pcontext *
+ parent_context()
+ {
+ return m_parent;
+ }
+
+ uint
+ diff_handlers(sp_pcontext *ctx);
+
+ uint
+ diff_cursors(sp_pcontext *ctx);
+
+
+ //
+ // Parameters and variables
+ //
+
+ inline uint
+ max_pvars()
+ {
+ return m_psubsize + m_pvar.elements;
+ }
+
+ inline uint
+ current_pvars()
+ {
+ return m_poffset + m_pvar.elements;
+ }
+
+ inline uint
+ context_pvars()
+ {
+ return m_pvar.elements;
+ }
+
+ inline uint
+ pvar_context2index(uint i)
+ {
+ return m_poffset + i;
+ }
+
+ inline void
+ set_type(uint i, enum enum_field_types type)
+ {
+ sp_pvar_t *p= find_pvar(i);
+
+ if (p)
+ p->type= type;
+ }
+
+ inline void
+ set_isset(uint i, my_bool val)
+ {
+ sp_pvar_t *p= find_pvar(i);
+
+ if (p)
+ p->isset= val;
+ }
+
+ inline void
+ set_default(uint i, Item *it)
+ {
+ sp_pvar_t *p= find_pvar(i);
+
+ if (p)
+ p->dflt= it;
+ }
+
+ void
+ push_pvar(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode);
+
+ // Pop the last 'num' slots of the frame
+ inline void
+ pop_pvar(uint num = 1)
+ {
+ while (num--)
+ pop_dynamic(&m_pvar);
+ }
+
+ // Find by name
+ sp_pvar_t *
+ find_pvar(LEX_STRING *name, my_bool scoped=0);
+
+ // Find by index
+ sp_pvar_t *
+ find_pvar(uint i)
+ {
+ sp_pvar_t *p;
+
+ if (i < m_pvar.elements)
+ get_dynamic(&m_pvar, (gptr)&p, i);
+ else
+ p= NULL;
+ return p;
+ }
+
+ //
+ // Labels
+ //
+
+ sp_label_t *
+ push_label(char *name, uint ip);
+
+ sp_label_t *
+ find_label(char *name);
+
+ inline sp_label_t *
+ last_label()
+ {
+ sp_label_t *lab= m_label.head();
+
+ if (!lab && m_parent)
+ lab= m_parent->last_label();
+ return lab;
+ }
+
+ inline sp_label_t *
+ pop_label()
+ {
+ return m_label.pop();
+ }
+
+ //
+ // Conditions
+ //
+
+ void
+ push_cond(LEX_STRING *name, sp_cond_type_t *val);
+
+ inline void
+ pop_cond(uint num)
+ {
+ while (num--)
+ pop_dynamic(&m_cond);
+ }
+
+ sp_cond_type_t *
+ find_cond(LEX_STRING *name, my_bool scoped=0);
+
+ //
+ // Handlers
+ //
+
+ inline void
+ add_handler()
+ {
+ m_handlers+= 1;
+ }
+
+ inline uint
+ max_handlers()
+ {
+ return m_hsubsize + m_handlers;
+ }
+
+ inline void
+ push_handlers(uint n)
+ {
+ m_handlers+= n;
+ }
+
+ //
+ // Cursors
+ //
+
+ void
+ push_cursor(LEX_STRING *name);
+
+ my_bool
+ find_cursor(LEX_STRING *name, uint *poff, my_bool scoped=0);
+
+ inline uint
+ max_cursors()
+ {
+ return m_csubsize + m_cursor.elements;
+ }
+
+ inline uint
+ current_cursors()
+ {
+ return m_coffset + m_cursor.elements;
+ }
+
+protected:
+
+ // The maximum sub context's framesizes
+ uint m_psubsize;
+ uint m_csubsize;
+ uint m_hsubsize;
+ uint m_handlers; // No. of handlers in this context
+
+private:
+
+ sp_pcontext *m_parent; // Parent context
+
+ uint m_poffset; // Variable offset for this context
+ uint m_coffset; // Cursor offset for this context
+
+ DYNAMIC_ARRAY m_pvar; // Parameters/variables
+ DYNAMIC_ARRAY m_cond; // Conditions
+ DYNAMIC_ARRAY m_cursor; // Cursors
+
+ List<sp_label_t> m_label; // The label list
+
+ List<sp_pcontext> m_children; // Children contexts, used for destruction
+
+}; // class sp_pcontext : public Sql_alloc
+
+
+#endif /* _SP_PCONTEXT_H_ */
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
new file mode 100644
index 00000000000..7fa44f217f6
--- /dev/null
+++ b/sql/sp_rcontext.cc
@@ -0,0 +1,254 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#if defined(WIN32) || defined(__WIN__)
+#undef SAFEMALLOC /* Problems with threads */
+#endif
+
+#include "mysql_priv.h"
+#include "mysql.h"
+#include "sp_head.h"
+#include "sp_rcontext.h"
+#include "sp_pcontext.h"
+
+sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax)
+ : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0),
+ m_hfound(-1), m_ccount(0)
+{
+ m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
+ m_outs= (int *)sql_alloc(fsize * sizeof(int));
+ m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
+ m_hstack= (uint *)sql_alloc(hmax * sizeof(uint));
+ m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *));
+ m_saved.empty();
+}
+
+int
+sp_rcontext::set_item_eval(uint idx, Item *i, enum_field_types type)
+{
+ extern Item *sp_eval_func_item(THD *thd, Item *it, enum_field_types type);
+ Item *it= sp_eval_func_item(current_thd, i, type);
+
+ if (! it)
+ return -1;
+ else
+ {
+ set_item(idx, it);
+ return 0;
+ }
+}
+
+int
+sp_rcontext::find_handler(uint sql_errno)
+{
+ if (m_hfound >= 0)
+ return 1; // Already got one
+
+ const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
+ int i= m_hcount, found= 0;
+
+ while (!found && i--)
+ {
+ sp_cond_type_t *cond= m_handler[i].cond;
+
+ switch (cond->type)
+ {
+ case sp_cond_type_t::number:
+ if (sql_errno == cond->mysqlerr)
+ found= 1;
+ break;
+ case sp_cond_type_t::state:
+ if (strcmp(sqlstate, cond->sqlstate) == 0)
+ found= 1;
+ break;
+ case sp_cond_type_t::warning:
+ if (sqlstate[0] == '0' && sqlstate[1] == '1')
+ found= 1;
+ break;
+ case sp_cond_type_t::notfound:
+ if (sqlstate[0] == '0' && sqlstate[1] == '2')
+ found= 1;
+ break;
+ case sp_cond_type_t::exception:
+ if (sqlstate[0] != '0' || sqlstate[1] > '2')
+ found= 1;
+ break;
+ }
+ }
+ if (found)
+ m_hfound= i;
+ return found;
+}
+
+void
+sp_rcontext::save_variables(uint fp)
+{
+ while (fp < m_count)
+ m_saved.push_front(m_frame[fp++]);
+}
+
+void
+sp_rcontext::restore_variables(uint fp)
+{
+ uint i= m_count;
+
+ while (i-- > fp)
+ m_frame[i]= m_saved.pop();
+}
+
+void
+sp_rcontext::push_cursor(LEX *lex)
+{
+ m_cstack[m_ccount++]= new sp_cursor(lex);
+}
+
+void
+sp_rcontext::pop_cursors(uint count)
+{
+ while (count--)
+ {
+ delete m_cstack[--m_ccount];
+ }
+}
+
+
+/*
+ *
+ * sp_cursor
+ *
+ */
+
+// We have split this in two to make it easy for sp_instr_copen
+// to reuse the sp_instr::exec_stmt() code.
+LEX *
+sp_cursor::pre_open(THD *thd)
+{
+ if (m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_ALREADY_OPEN);
+ return NULL;
+ }
+
+ bzero((char *)&m_mem_root, sizeof(m_mem_root));
+ init_alloc_root(&m_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ if ((m_prot= new Protocol_cursor(thd, &m_mem_root)) == NULL)
+ return NULL;
+
+ m_oprot= thd->protocol; // Save the original protocol
+ thd->protocol= m_prot;
+
+ m_nseof= thd->net.no_send_eof;
+ thd->net.no_send_eof= TRUE;
+ return m_lex;
+}
+
+void
+sp_cursor::post_open(THD *thd, my_bool was_opened)
+{
+ thd->net.no_send_eof= m_nseof; // Restore the originals
+ thd->protocol= m_oprot;
+ if (was_opened)
+ {
+ m_isopen= was_opened;
+ m_current_row= m_prot->data;
+ }
+}
+
+int
+sp_cursor::close(THD *thd)
+{
+ if (! m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_NOT_OPEN);
+ return -1;
+ }
+ destroy();
+ return 0;
+}
+
+void
+sp_cursor::destroy()
+{
+ if (m_prot)
+ {
+ delete m_prot;
+ m_prot= NULL;
+ free_root(&m_mem_root, MYF(0));
+ bzero((char *)&m_mem_root, sizeof(m_mem_root));
+ }
+ m_isopen= FALSE;
+}
+
+int
+sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars)
+{
+ List_iterator_fast<struct sp_pvar> li(*vars);
+ sp_pvar_t *pv;
+ MYSQL_ROW row;
+ uint fldcount;
+
+ if (! m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_NOT_OPEN);
+ return -1;
+ }
+ if (m_current_row == NULL)
+ {
+ send_error(thd, ER_SP_FETCH_NO_DATA);
+ return -1;
+ }
+
+ row= m_current_row->data;
+ for (fldcount= 0 ; (pv= li++) ; fldcount++)
+ {
+ Item *it;
+ const char *s;
+
+ if (fldcount >= m_prot->get_field_count())
+ {
+ send_error(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS);
+ return -1;
+ }
+ s= row[fldcount];
+ switch (sp_map_result_type(pv->type))
+ {
+ case INT_RESULT:
+ it= new Item_int(s);
+ break;
+ case REAL_RESULT:
+ it= new Item_real(s, strlen(s));
+ break;
+ default:
+ {
+ uint len= strlen(s);
+ it= new Item_string(thd->strmake(s, len), len, thd->db_charset);
+ break;
+ }
+ }
+ thd->spcont->set_item(pv->offset, it);
+ }
+ if (fldcount < m_prot->get_field_count())
+ {
+ send_error(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS);
+ return -1;
+ }
+ m_current_row= m_current_row->next;
+ return 0;
+}
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
new file mode 100644
index 00000000000..15a2fe62138
--- /dev/null
+++ b/sql/sp_rcontext.h
@@ -0,0 +1,253 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_RCONTEXT_H_
+#define _SP_RCONTEXT_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+struct sp_cond_type;
+class sp_cursor;
+struct sp_pvar;
+
+#define SP_HANDLER_NONE 0
+#define SP_HANDLER_EXIT 1
+#define SP_HANDLER_CONTINUE 2
+#define SP_HANDLER_UNDO 3
+
+typedef struct
+{
+ struct sp_cond_type *cond;
+ uint handler; // Location of handler
+ int type;
+ uint foffset; // Frame offset for the handlers declare level
+} sp_handler_t;
+
+class sp_rcontext : public Sql_alloc
+{
+ sp_rcontext(const sp_rcontext &); /* Prevent use of these */
+ void operator=(sp_rcontext &);
+
+ public:
+
+ sp_rcontext(uint fsize, uint hmax, uint cmax);
+
+ ~sp_rcontext()
+ {
+ // Not needed?
+ //sql_element_free(m_frame);
+ //m_saved.empty();
+ }
+
+ inline void
+ push_item(Item *i)
+ {
+ if (m_count < m_fsize)
+ m_frame[m_count++] = i;
+ }
+
+ inline void
+ set_item(uint idx, Item *i)
+ {
+ if (idx < m_count)
+ m_frame[idx] = i;
+ }
+
+ /* Returns 0 on success, -1 on (eval) failure */
+ int
+ set_item_eval(uint idx, Item *i, enum_field_types type);
+
+ inline Item *
+ get_item(uint idx)
+ {
+ return m_frame[idx];
+ }
+
+ inline void
+ set_oindex(uint idx, int oidx)
+ {
+ m_outs[idx] = oidx;
+ }
+
+ inline int
+ get_oindex(uint idx)
+ {
+ return m_outs[idx];
+ }
+
+ inline void
+ set_result(Item *it)
+ {
+ m_result= it;
+ }
+
+ inline Item *
+ get_result()
+ {
+ return m_result;
+ }
+
+ inline void
+ push_handler(struct sp_cond_type *cond, uint h, int type, uint f)
+ {
+ m_handler[m_hcount].cond= cond;
+ m_handler[m_hcount].handler= h;
+ m_handler[m_hcount].type= type;
+ m_handler[m_hcount].foffset= f;
+ m_hcount+= 1;
+ }
+
+ inline void
+ pop_handlers(uint count)
+ {
+ m_hcount-= count;
+ }
+
+ // Returns 1 if a handler was found, 0 otherwise.
+ int
+ find_handler(uint sql_errno);
+
+ // Returns handler type and sets *ip to location if one was found
+ inline int
+ found_handler(uint *ip, uint *fp)
+ {
+ if (m_hfound < 0)
+ return SP_HANDLER_NONE;
+ *ip= m_handler[m_hfound].handler;
+ *fp= m_handler[m_hfound].foffset;
+ return m_handler[m_hfound].type;
+ }
+
+ // Clears the handler find state
+ inline void
+ clear_handler()
+ {
+ m_hfound= -1;
+ }
+
+ inline void
+ push_hstack(uint h)
+ {
+ m_hstack[m_hsp++]= h;
+ }
+
+ inline uint
+ pop_hstack()
+ {
+ return m_hstack[--m_hsp];
+ }
+
+ // Save variables starting at fp and up
+ void
+ save_variables(uint fp);
+
+ // Restore variables down to fp
+ void
+ restore_variables(uint fp);
+
+ void
+ push_cursor(LEX *lex);
+
+ void
+ pop_cursors(uint count);
+
+ void
+ pop_all_cursors()
+ {
+ pop_cursors(m_ccount);
+ }
+
+ inline sp_cursor *
+ get_cursor(uint i)
+ {
+ return m_cstack[i];
+ }
+
+private:
+
+ uint m_count;
+ uint m_fsize;
+ Item **m_frame;
+ int *m_outs;
+
+ Item *m_result; // For FUNCTIONs
+
+ sp_handler_t *m_handler;
+ uint m_hcount;
+ uint *m_hstack;
+ uint m_hsp;
+ int m_hfound; // Set by find_handler; -1 if not found
+ List<Item> m_saved; // Saved variables
+
+ sp_cursor **m_cstack;
+ uint m_ccount;
+
+}; // class sp_rcontext : public Sql_alloc
+
+
+class sp_cursor : public Sql_alloc
+{
+public:
+
+ sp_cursor(LEX *lex)
+ : m_lex(lex), m_prot(NULL), m_isopen(0), m_current_row(NULL)
+ {
+ /* Empty */
+ }
+
+ virtual ~sp_cursor()
+ {
+ destroy();
+ }
+
+ // We have split this in two to make it easy for sp_instr_copen
+ // to reuse the sp_instr::exec_stmt() code.
+ LEX *
+ pre_open(THD *thd);
+ void
+ post_open(THD *thd, my_bool was_opened);
+
+ int
+ close(THD *thd);
+
+ inline my_bool
+ is_open()
+ {
+ return m_isopen;
+ }
+
+ int
+ fetch(THD *, List<struct sp_pvar> *vars);
+
+private:
+
+ MEM_ROOT m_mem_root; // My own mem_root
+ LEX *m_lex;
+ Protocol_cursor *m_prot;
+ my_bool m_isopen;
+ my_bool m_nseof; // Original no_send_eof
+ Protocol *m_oprot; // Original protcol
+ MYSQL_ROWS *m_current_row;
+
+ void
+ destroy();
+
+}; // class sp_cursor : public Sql_alloc
+
+#endif /* _SP_RCONTEXT_H_ */
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 9c6853187f6..e7b4772c3ab 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -140,7 +140,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
MYSQL_LOCK *lock;
my_bool return_val=1;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
-
DBUG_ENTER("acl_init");
if (!acl_cache)
@@ -153,6 +152,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
}
priv_version++; /* Privileges updated */
+ mysql_proc_table_exists= 1; // Assume mysql.proc exists
/*
To be able to run this from boot, we allocate a temporary THD
@@ -168,8 +168,8 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
tables[0].alias=tables[0].real_name=(char*) "host";
tables[1].alias=tables[1].real_name=(char*) "user";
tables[2].alias=tables[2].real_name=(char*) "db";
- tables[0].next=tables+1;
- tables[1].next=tables+2;
+ tables[0].next_local= tables[0].next_global= tables+1;
+ tables[1].next_local= tables[1].next_global= tables+2;
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
tables[0].db=tables[1].db=tables[2].db=thd->db;
@@ -304,6 +304,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
user.sort= get_sort(2,user.host.hostname,user.user);
user.hostname_length= (user.host.hostname ?
(uint) strlen(user.host.hostname) : 0);
+
if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */
{
char *ssl_type=get_field(&mem, table->field[next_field++]);
@@ -492,8 +493,8 @@ void acl_reload(THD *thd)
Get all access bits from table after fieldnr
IMPLEMENTATION
- We know that the access privileges ends when there is no more fields
- or the field is not an enum with two elements.
+ We know that the access privileges ends when there is no more fields
+ or the field is not an enum with two elements.
SYNOPSIS
get_access()
@@ -806,6 +807,86 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
}
+/*
+ * This is like acl_getroot() above, but it doesn't check password,
+ * and we don't care about the user resources.
+ * Used to get access rights for SQL SECURITY DEFINER invokation of
+ * stored procedures.
+ */
+int acl_getroot_no_password(THD *thd)
+{
+ ulong user_access= NO_ACCESS;
+ int res= 1;
+ uint i;
+ ACL_USER *acl_user= 0;
+ DBUG_ENTER("acl_getroot_no_password");
+
+ if (!initialized)
+ {
+ /*
+ here if mysqld's been started with --skip-grant-tables option.
+ */
+ thd->priv_user= (char *) ""; // privileges for
+ *thd->priv_host= '\0'; // the user are unknown
+ thd->master_access= ~NO_ACCESS; // everything is allowed
+ DBUG_RETURN(0);
+ }
+
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ thd->master_access= 0;
+ thd->db_access= 0;
+
+ /*
+ Find acl entry in user database.
+ This is specially tailored to suit the check we do for CALL of
+ a stored procedure; thd->user is set to what is actually a
+ priv_user, which can be ''.
+ */
+ for (i=0 ; i < acl_users.elements ; i++)
+ {
+ acl_user= dynamic_element(&acl_users,i,ACL_USER*);
+ if ((!acl_user->user && (!thd->user || !thd->user[0])) ||
+ (acl_user->user && strcmp(thd->user, acl_user->user) == 0))
+ {
+ if (compare_hostname(&acl_user->host, thd->host, thd->ip))
+ {
+ res= 0;
+ break;
+ }
+ }
+ }
+
+ if (acl_user)
+ {
+ for (i=0 ; i < acl_dbs.elements ; i++)
+ {
+ ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
+ if (!acl_db->user ||
+ (thd->user && thd->user[0] && !strcmp(thd->user, acl_db->user)))
+ {
+ if (compare_hostname(&acl_db->host, thd->host, thd->ip))
+ {
+ if (!acl_db->db || (thd->db && !strcmp(acl_db->db, thd->db)))
+ {
+ thd->db_access= acl_db->access;
+ break;
+ }
+ }
+ }
+ }
+ thd->master_access= acl_user->access;
+ thd->priv_user= acl_user->user ? thd->user : (char *) "";
+
+ if (acl_user->host.hostname)
+ strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ else
+ *thd->priv_host= 0;
+ }
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ DBUG_RETURN(res);
+}
+
static byte* check_get_key(ACL_USER *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
@@ -1226,7 +1307,6 @@ bool change_password(THD *thd, const char *host, const char *user,
acl_user->host.hostname ? acl_user->host.hostname : "",
new_password));
thd->clear_error();
- mysql_update_log.write(thd, buff, query_length);
Query_log_event qinfo(thd, buff, query_length, 0);
mysql_bin_log.write(&qinfo);
DBUG_RETURN(0);
@@ -2218,6 +2298,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
LEX_USER *Str;
TABLE_LIST tables[3];
bool create_new_users=0;
+ char *db_name, *real_name;
DBUG_ENTER("mysql_table_grant");
if (!initialized)
@@ -2234,17 +2315,19 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
if (columns.elements && !revoke_grant)
{
- TABLE *table;
class LEX_COLUMN *column;
List_iterator <LEX_COLUMN> column_iter(columns);
+ int res;
- if (!(table=open_ltable(thd,table_list,TL_READ)))
- DBUG_RETURN(-1);
+ if ((res= open_and_lock_tables(thd, table_list)))
+ DBUG_RETURN(res);
+
while ((column = column_iter++))
{
uint unused_field_idx= NO_CACHED_FIELD_INDEX;
- if (!find_field_in_table(thd,table,column->column.ptr(),
- column->column.length(),0,0,
+ if (!find_field_in_table(thd, table_list, column->column.ptr(),
+ column->column.ptr(),
+ column->column.length(), 0, 0, 0, 0,
&unused_field_idx))
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
@@ -2274,11 +2357,13 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
tables[0].alias=tables[0].real_name= (char*) "user";
tables[1].alias=tables[1].real_name= (char*) "tables_priv";
tables[2].alias=tables[2].real_name= (char*) "columns_priv";
- tables[0].next=tables+1;
+ tables[0].next_local= tables[0].next_global= tables+1;
/* Don't open column table if we don't need it ! */
- tables[1].next=((column_priv ||
- (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
- ? tables+2 : 0);
+ tables[1].next_local=
+ tables[1].next_global= ((column_priv ||
+ (revoke_grant &&
+ ((rights & COL_ACLS) || columns.elements)))
+ ? tables+2 : 0);
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
@@ -2334,10 +2419,16 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
continue; // Add next user
}
+ db_name= (table_list->view_db.length ?
+ table_list->view_db.str :
+ table_list->db);
+ real_name= (table_list->view_name.length ?
+ table_list->view_name.str :
+ table_list->real_name);
+
/* Find/create cached table grant */
- grant_table= table_hash_search(Str->host.str,NullS,table_list->db,
- Str->user.str,
- table_list->real_name,1);
+ grant_table= table_hash_search(Str->host.str, NullS, db_name,
+ Str->user.str, real_name, 1);
if (!grant_table)
{
if (revoke_grant)
@@ -2347,9 +2438,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
result= -1;
continue;
}
- grant_table = new GRANT_TABLE (Str->host.str,table_list->db,
- Str->user.str,
- table_list->real_name,
+ grant_table = new GRANT_TABLE (Str->host.str, db_name,
+ Str->user.str, real_name,
rights,
column_priv);
if (!grant_table) // end of memory
@@ -2394,19 +2484,17 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* update table and columns */
- if (replace_table_table(thd,grant_table,tables[1].table,*Str,
- table_list->db,
- table_list->real_name,
+ if (replace_table_table(thd, grant_table, tables[1].table, *Str,
+ db_name, real_name,
rights, column_priv, revoke_grant))
{ // Crashend table ??
result= -1; /* purecov: deadcode */
}
else if (tables[2].table)
{
- if ((replace_column_table(grant_table,tables[2].table, *Str,
+ if ((replace_column_table(grant_table, tables[2].table, *Str,
columns,
- table_list->db,
- table_list->real_name,
+ db_name, real_name,
rights, revoke_grant)))
{
result= -1;
@@ -2450,11 +2538,9 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
bzero((char*) &tables,sizeof(tables));
tables[0].alias=tables[0].real_name=(char*) "user";
tables[1].alias=tables[1].real_name=(char*) "db";
- tables[0].next=tables+1;
- tables[1].next=0;
+ tables[0].next_local= tables[0].next_global= tables+1;
tables[0].lock_type=tables[1].lock_type=TL_WRITE;
tables[0].db=tables[1].db=(char*) "mysql";
- tables[0].table=tables[1].table=0;
#ifdef HAVE_REPLICATION
/*
@@ -2571,7 +2657,7 @@ my_bool grant_init(THD *org_thd)
bzero((char*) &tables, sizeof(tables));
tables[0].alias=tables[0].real_name= (char*) "tables_priv";
tables[1].alias=tables[1].real_name= (char*) "columns_priv";
- tables[0].next=tables+1;
+ tables[0].next_local= tables[0].next_global= tables+1;
tables[0].lock_type=tables[1].lock_type=TL_READ;
tables[0].db=tables[1].db=thd->db;
@@ -2691,7 +2777,22 @@ void grant_reload(THD *thd)
/****************************************************************************
Check table level grants
- All errors are written directly to the client if no_errors is given !
+
+ SYNPOSIS
+ bool check_grant()
+ thd Thread handler
+ want_access Bits of privileges user needs to have
+ tables List of tables to check. The user should have 'want_access'
+ to all tables in list.
+ show_table <> 0 if we are in show table. In this case it's enough to have
+ any privilege for the table
+ number Check at most this number of tables.
+ no_errors If 0 then we write an error. The error is sent directly to
+ the client
+
+ RETURN
+ 0 ok
+ 1 Error: User did not have the requested privielges
****************************************************************************/
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
@@ -2699,23 +2800,28 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
{
TABLE_LIST *table;
char *user = thd->priv_user;
+ DBUG_ENTER("check_grant");
+ DBUG_ASSERT(number > 0);
- want_access &= ~thd->master_access;
+ want_access&= ~thd->master_access;
if (!want_access)
- return 0; // ok
+ DBUG_RETURN(0); // ok
rw_rdlock(&LOCK_grant);
- for (table= tables; table && number--; table= table->next)
+ for (table= tables; table && number--; table= table->next_global)
{
+ GRANT_TABLE *grant_table;
if (!(~table->grant.privilege & want_access) || table->derived)
{
- table->grant.want_privilege=0;
+ /*
+ It is subquery in the FROM clause. VIEW set table->derived after
+ table opening, but this function always called before table opening.
+ */
+ table->grant.want_privilege= 0;
continue; // Already checked
}
- GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,
- table->db,user,
- table->real_name,0);
- if (!grant_table)
+ if (!(grant_table= table_hash_search(thd->host,thd->ip,
+ table->db,user, table->real_name,0)))
{
want_access &= ~table->grant.privilege;
goto err; // No grants
@@ -2739,7 +2845,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
}
}
rw_unlock(&LOCK_grant);
- return 0;
+ DBUG_RETURN(0);
err:
rw_unlock(&LOCK_grant);
@@ -2764,23 +2870,28 @@ err:
command= "index";
else if (want_access & GRANT_ACL)
command= "grant";
+ else if (want_access & CREATE_VIEW_ACL)
+ command= "create view";
+ else if (want_access & SHOW_VIEW_ACL)
+ command= "show view";
net_printf(thd,ER_TABLEACCESS_DENIED_ERROR,
command,
thd->priv_user,
thd->host_or_ip,
table ? table->real_name : "unknown");
}
- return 1;
+ DBUG_RETURN(1);
}
-bool check_grant_column(THD *thd,TABLE *table, const char *name,
- uint length, uint show_tables)
+bool check_grant_column(THD *thd, GRANT_INFO *grant,
+ char*db_name, char *table_name,
+ const char *name, uint length, uint show_tables)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
- ulong want_access=table->grant.want_privilege;
+ ulong want_access= grant->want_privilege & ~grant->privilege;
if (!want_access)
return 0; // Already checked
@@ -2788,15 +2899,15 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name,
/* reload table if someone has modified any grants */
- if (table->grant.version != grant_version)
+ if (grant->version != grant_version)
{
- table->grant.grant_table=
- table_hash_search(thd->host, thd->ip, table->table_cache_key,
+ grant->grant_table=
+ table_hash_search(thd->host, thd->ip, db_name,
thd->priv_user,
- table->real_name, 0); /* purecov: inspected */
- table->grant.version=grant_version; /* purecov: inspected */
+ table_name, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
}
- if (!(grant_table=table->grant.grant_table))
+ if (!(grant_table= grant->grant_table))
goto err; /* purecov: deadcode */
grant_column=column_hash_search(grant_table, name, length);
@@ -2806,7 +2917,7 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name,
return 0;
}
#ifdef NOT_USED
- if (show_tables && (grant_column || table->grant.privilege & COL_ACLS))
+ if (show_tables && (grant_column || grant->privilege & COL_ACLS))
{
rw_unlock(&LOCK_grant); /* purecov: deadcode */
return 0; /* purecov: deadcode */
@@ -2827,47 +2938,47 @@ err:
thd->priv_user,
thd->host_or_ip,
name,
- table ? table->real_name : "unknown");
+ table_name);
}
return 1;
}
-bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table)
+bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
+ char* db_name, char *table_name,
+ Field_iterator *fields)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
- Field *field=0,**ptr;
+ Field *field=0;
- want_access &= ~table->grant.privilege;
+ want_access &= ~grant->privilege;
if (!want_access)
return 0; // Already checked
if (!grant_option)
- {
- field= table->field[0]; // To give a meaningful error message
goto err2;
- }
rw_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
- if (table->grant.version != grant_version)
+ if (grant->version != grant_version)
{
- table->grant.grant_table=
- table_hash_search(thd->host, thd->ip, table->table_cache_key,
+ grant->grant_table=
+ table_hash_search(thd->host, thd->ip, db_name,
thd->priv_user,
- table->real_name,0); /* purecov: inspected */
- table->grant.version=grant_version; /* purecov: inspected */
+ table_name, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
}
/* The following should always be true */
- if (!(grant_table=table->grant.grant_table))
+ if (!(grant_table= grant->grant_table))
goto err; /* purecov: inspected */
- for (ptr=table->field; (field= *ptr) ; ptr++)
+ for (; !fields->end_of_fields(); fields->next())
{
- grant_column=column_hash_search(grant_table, field->field_name,
- (uint) strlen(field->field_name));
+ const char *field_name= fields->name();
+ grant_column= column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
if (!grant_column || (~grant_column->rights & want_access))
goto err;
}
@@ -2889,8 +3000,8 @@ err2:
command,
thd->priv_user,
thd->host_or_ip,
- field ? field->field_name : "unknown",
- table->real_name);
+ fields->name(),
+ table_name);
return 1;
}
@@ -2957,7 +3068,9 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
}
-ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
+ulong get_column_grant(THD *thd, GRANT_INFO *grant,
+ const char *db_name, const char *table_name,
+ const char *field_name)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
@@ -2965,30 +3078,31 @@ ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
rw_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
- if (table->grant.version != grant_version)
+ if (grant->version != grant_version)
{
- table->grant.grant_table=
- table_hash_search(thd->host, thd->ip, table->db,
+ grant->grant_table=
+ table_hash_search(thd->host, thd->ip, db_name,
thd->priv_user,
- table->real_name,0); /* purecov: inspected */
- table->grant.version=grant_version; /* purecov: inspected */
+ table_name, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
}
- if (!(grant_table=table->grant.grant_table))
- priv=table->grant.privilege;
+ if (!(grant_table= grant->grant_table))
+ priv= grant->privilege;
else
{
- grant_column=column_hash_search(grant_table, field->field_name,
- (uint) strlen(field->field_name));
+ grant_column= column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
if (!grant_column)
- priv=table->grant.privilege;
+ priv= grant->privilege;
else
- priv=table->grant.privilege | grant_column->rights;
+ priv= grant->privilege | grant_column->rights;
}
rw_unlock(&LOCK_grant);
return priv;
}
+
/* Help function for mysql_show_grants */
static void add_user_option(String *grant, ulong value, const char *name)
@@ -3006,15 +3120,16 @@ static void add_user_option(String *grant, ulong value, const char *name)
static const char *command_array[]=
{
- "SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP", "RELOAD","SHUTDOWN",
- "PROCESS","FILE","GRANT","REFERENCES","INDEX", "ALTER", "SHOW DATABASES",
- "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE",
- "REPLICATION SLAVE", "REPLICATION CLIENT",
+ "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
+ "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
+ "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
+ "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
+ "CREATE VIEW", "SHOW VIEW"
};
static uint command_lengths[]=
{
- 6,6,6,6,6,4,6,8,7,4,5,10,5,5,14,5,23,11,7,17,18
+ 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9
};
@@ -3081,7 +3196,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
strxmov(buff,"Grants for ",lex_user->user.str,"@",
lex_user->host.str,NullS);
field_list.push_back(field);
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
rw_wrlock(&LOCK_grant);
@@ -3413,10 +3529,9 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
(tables+1)->alias= (tables+1)->real_name= (char*) "db";
(tables+2)->alias= (tables+2)->real_name= (char*) "tables_priv";
(tables+3)->alias= (tables+3)->real_name= (char*) "columns_priv";
- tables->next= tables+1;
- (tables+1)->next= tables+2;
- (tables+2)->next= tables+3;
- (tables+3)->next= 0;
+ tables->next_local= tables->next_global= tables+1;
+ (tables+1)->next_local= (tables+1)->next_global= tables+2;
+ (tables+2)->next_local= (tables+2)->next_global= tables+3;
tables->lock_type= (tables+1)->lock_type=
(tables+2)->lock_type= (tables+3)->lock_type= TL_WRITE;
tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db=(char*) "mysql";
@@ -3750,3 +3865,44 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
DBUG_RETURN (*str != '\0');
}
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/*
+ fill effective privileges for table
+
+ SYNOPSIS
+ get_effectlige_privileges()
+ thd thread handleg
+ grant grants table descriptor
+ db db name
+ table table name
+*/
+
+void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
+ const char *db, const char *table)
+{
+ /* global privileges */
+ grant->privilege= thd->master_access;
+
+ /* if privileges ignored (--skip-grant-tables) above is enough */
+ if (!grant_option)
+ return;
+
+ /* db privileges */
+ grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0);
+ /* table privileges */
+ if (grant->version != grant_version)
+ {
+ rw_rdlock(&LOCK_grant);
+ grant->grant_table=
+ table_hash_search(thd->host, thd->ip, db,
+ thd->priv_user,
+ table, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
+ rw_unlock(&LOCK_grant);
+ }
+ if (grant->grant_table != 0)
+ {
+ grant->privilege|= grant->grant_table->privs;
+ }
+}
+#endif
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 68cb1476eb5..390106c1546 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -35,7 +35,8 @@
#define EXECUTE_ACL (1L << 18)
#define REPL_SLAVE_ACL (1L << 19)
#define REPL_CLIENT_ACL (1L << 20)
-
+#define CREATE_VIEW_ACL (1L << 21)
+#define SHOW_VIEW_ACL (1L << 22)
/*
don't forget to update
static struct show_privileges_st sys_privileges[]
@@ -45,11 +46,13 @@
#define DB_ACLS \
(UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
- GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | LOCK_TABLES_ACL)
+ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \
+ LOCK_TABLES_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL)
#define TABLE_ACLS \
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
- GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL)
+ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_VIEW_ACL | \
+ SHOW_VIEW_ACL)
#define COL_ACLS \
(SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL)
@@ -59,7 +62,7 @@
RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL | GRANT_ACL | \
REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \
CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \
- EXECUTE_ACL)
+ EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL)
#define EXTRA_ACL (1L << 29)
#define NO_ACCESS (1L << 30)
@@ -72,13 +75,21 @@
/* Continius bit-segments that needs to be shifted */
#define DB_REL1 (RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL)
#define DB_REL2 (GRANT_ACL | REFERENCES_ACL)
+#define DB_REL3 (INDEX_ACL | ALTER_ACL)
/* Privileges that needs to be reallocated (in continous chunks) */
#define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL)
#define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL)
-
-#define fix_rights_for_db(A) (((A) & 63) | (((A) & DB_REL1) << 4) | (((A) & DB_REL2) << 6))
-#define get_rights_for_db(A) (((A) & 63) | (((A) & DB_CHUNK1) >> 4) | (((A) & DB_CHUNK2) >> 6))
+#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+
+#define fix_rights_for_db(A) (((A) & 63) | \
+ (((A) & DB_REL1) << 4) | \
+ (((A) & DB_REL2) << 6) | \
+ (((A) & DB_REL3) << 9))
+#define get_rights_for_db(A) (((A) & 63) | \
+ (((A) & DB_CHUNK1) >> 4) | \
+ (((A) & DB_CHUNK2) >> 6) | \
+ (((A) & DB_CHUNK3) >> 9))
#define fix_rights_for_table(A) (((A) & 63) | (((A) & ~63) << 4))
#define get_rights_for_table(A) (((A) & 63) | (((A) & ~63) >> 4))
#define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8))
@@ -141,6 +152,7 @@ ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern);
int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
uint passwd_len);
+int acl_getroot_no_password(THD *thd);
bool acl_check_host(const char *host, const char *ip);
bool check_change_password(THD *thd, const char *host, const char *user,
char *password);
@@ -156,17 +168,24 @@ void grant_free(void);
void grant_reload(THD *thd);
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
uint show_command, uint number, bool dont_print_error);
-bool check_grant_column (THD *thd,TABLE *table, const char *name, uint length,
- uint show_command=0);
-bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table);
+bool check_grant_column (THD *thd, GRANT_INFO *grant,
+ char *db_name, char *table_name,
+ const char *name, uint length, uint show_command=0);
+bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
+ char* db_name, char *table_name,
+ Field_iterator *fields);
bool check_grant_db(THD *thd,const char *db);
ulong get_table_grant(THD *thd, TABLE_LIST *table);
-ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field);
+ulong get_column_grant(THD *thd, GRANT_INFO *grant,
+ const char *db_name, const char *table_name,
+ const char *field_name);
int mysql_show_grants(THD *thd, LEX_USER *user);
void get_privilege_desc(char *to, uint max_length, ulong access);
void get_mqh(const char *user, const char *host, USER_CONN *uc);
int mysql_drop_user(THD *thd, List <LEX_USER> &list);
int mysql_revoke_all(THD *thd, List <LEX_USER> &list);
+void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
+ const char *db, const char *table);
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define check_grant(A,B,C,D,E,F) 0
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index ac5008717e6..0aee91af0da 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -32,11 +32,20 @@ TABLE *unused_tables; /* Used by mysql_test */
HASH open_cache; /* Used by mysql_test */
HASH assign_cache;
-static int open_unireg_entry(THD *thd,TABLE *entry,const char *db,
- const char *name, const char *alias);
+static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
+ const char *name, const char *alias,
+ TABLE_LIST *table_list, MEM_ROOT *mem_root);
static void free_cache_entry(TABLE *entry);
static void mysql_rm_tmp_tables(void);
-
+static my_bool open_new_frm(const char *path, const char *alias,
+ const char *db, const char *table_name,
+ uint db_stat, uint prgflag,
+ uint ha_open_flags, TABLE *outparam,
+ TABLE_LIST *table_desc, MEM_ROOT *mem_root);
+static Field *find_field_in_real_table(THD *thd, TABLE *table,
+ const char *name, uint length,
+ bool check_grants, bool allow_rowid,
+ uint *cached_field_index_ptr);
extern "C" byte *table_cache_key(const byte *record,uint *length,
my_bool not_used __attribute__((unused)))
@@ -277,7 +286,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
else
{
bool found=0;
- for (TABLE_LIST *table=tables ; table ; table=table->next)
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
{
if (remove_table_from_cache(thd, table->db, table->real_name, 1))
found=1;
@@ -328,7 +337,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
result=reopen_tables(thd,1,1);
thd->in_lock_tables=0;
/* Set version for table */
- for (TABLE *table=thd->open_tables; table ; table=table->next)
+ for (TABLE *table=thd->open_tables; table ; table= table->next)
table->version=refresh_version;
}
VOID(pthread_mutex_unlock(&LOCK_open));
@@ -363,6 +372,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
{
+ bool found_old_table=0;
DBUG_ENTER("close_thread_tables");
if (thd->derived_tables && !skip_derived)
@@ -385,8 +395,6 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
DBUG_VOID_RETURN; // LOCK TABLES in use
}
- bool found_old_table=0;
-
if (thd->lock)
{
mysql_unlock_tables(thd, thd->lock);
@@ -397,7 +405,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
VOID(pthread_mutex_lock(&LOCK_open));
safe_mutex_assert_owner(&LOCK_open);
- DBUG_PRINT("info", ("thd->open_tables=%p", thd->open_tables));
+ DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables));
while (thd->open_tables)
found_old_table|=close_thread_table(thd, &thd->open_tables);
@@ -427,6 +435,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
bool found_old_table= 0;
TABLE *table= *table_ptr;
DBUG_ASSERT(table->key_read == 0);
+ DBUG_ASSERT(table->file->inited == handler::NONE);
*table_ptr=table->next;
if (table->version != refresh_version ||
@@ -540,56 +549,41 @@ void close_temporary_tables(THD *thd)
thd->temporary_tables=0;
}
+
/*
- Find first suitable table by alias in given list.
+ Find table in list.
SYNOPSIS
find_table_in_list()
- table - pointer to table list
- db_name - data base name or 0 for any
- table_name - table name or 0 for any
-
- RETURN VALUES
- NULL Table not found
- # Pointer to found table.
-*/
-
-TABLE_LIST * find_table_in_list(TABLE_LIST *table,
- const char *db_name, const char *table_name)
-{
- for (; table; table= table->next)
- if ((!db_name || !strcmp(table->db, db_name)) &&
- (!table_name || !my_strcasecmp(table_alias_charset,
- table->alias, table_name)))
- break;
- return table;
-}
-
-/*
- Find real table in given list.
+ table Pointer to table list
+ offset Offset to which list in table structure to use
+ db_name Data base name
+ table_name Table name
- SYNOPSIS
- find_real_table_in_list()
- table - pointer to table list
- db_name - data base name
- table_name - table name
+ NOTES:
+ This is called by find_table_in_local_list() and
+ find_table_in_global_list().
RETURN VALUES
NULL Table not found
# Pointer to found table.
*/
-TABLE_LIST * find_real_table_in_list(TABLE_LIST *table,
- const char *db_name,
- const char *table_name)
+TABLE_LIST *find_table_in_list(TABLE_LIST *table,
+ uint offset,
+ const char *db_name,
+ const char *table_name)
{
- for (; table; table= table->next)
+ for (; table; table= *(TABLE_LIST **) ((char*) table + offset))
+ {
if (!strcmp(table->db, db_name) &&
!strcmp(table->real_name, table_name))
break;
+ }
return table;
}
+
TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name)
{
char key[MAX_DBKEY_LENGTH];
@@ -749,7 +743,7 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
pthread_mutex_lock(&LOCK_open);
- if (open_unireg_entry(thd, table, db, table_name, table_name) ||
+ if (open_unireg_entry(thd, table, db, table_name, table_name, 0, 0) ||
!(table->table_cache_key =memdup_root(&table->mem_root,(char*) key,
key_length)))
{
@@ -787,12 +781,13 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
******************************************************************************/
-TABLE *open_table(THD *thd,const char *db,const char *table_name,
- const char *alias,bool *refresh)
+TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
+ bool *refresh)
{
reg1 TABLE *table;
char key[MAX_DBKEY_LENGTH];
uint key_length;
+ char *alias= table_list->alias;
DBUG_ENTER("open_table");
/* find a unused table in the open table cache */
@@ -800,25 +795,29 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
*refresh=0;
if (thd->killed)
DBUG_RETURN(0);
- key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
+ key_length= (uint) (strmov(strmov(key, table_list->db)+1,
+ table_list->real_name)-key)+1;
int4store(key + key_length, thd->server_id);
int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
- for (table=thd->temporary_tables; table ; table=table->next)
+ if (!table_list->skip_temporary)
{
- if (table->key_length == key_length+8 &&
- !memcmp(table->table_cache_key,key,key_length+8))
+ for (table= thd->temporary_tables; table ; table=table->next)
{
- if (table->query_id == thd->query_id)
+ if (table->key_length == key_length + 8 &&
+ !memcmp(table->table_cache_key, key, key_length + 8))
{
- my_printf_error(ER_CANT_REOPEN_TABLE,
- ER(ER_CANT_REOPEN_TABLE),MYF(0),table->table_name);
- DBUG_RETURN(0);
+ if (table->query_id == thd->query_id)
+ {
+ my_printf_error(ER_CANT_REOPEN_TABLE,
+ ER(ER_CANT_REOPEN_TABLE), MYF(0), table->table_name);
+ DBUG_RETURN(0);
+ }
+ table->query_id= thd->query_id;
+ table->clear_query_id= 1;
+ thd->tmp_table_used= 1;
+ goto reset;
}
- table->query_id=thd->query_id;
- table->clear_query_id=1;
- thd->tmp_table_used= 1;
- goto reset;
}
}
@@ -868,7 +867,9 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
if (table->in_use != thd)
wait_for_refresh(thd);
else
+ {
VOID(pthread_mutex_unlock(&LOCK_open));
+ }
if (refresh)
*refresh=1;
DBUG_RETURN(0);
@@ -898,15 +899,23 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(NULL);
}
- if (open_unireg_entry(thd, table,db,table_name,alias) ||
- !(table->table_cache_key=memdup_root(&table->mem_root,(char*) key,
- key_length)))
+ if (open_unireg_entry(thd, table, table_list->db, table_list->real_name,
+ alias, table_list, mem_root) ||
+ (!table_list->view &&
+ !(table->table_cache_key= memdup_root(&table->mem_root, (char*) key,
+ key_length))))
{
table->next=table->prev=table;
free_cache_entry(table);
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(NULL);
}
+ if (table_list->view)
+ {
+ my_free((gptr)table, MYF(0));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(0); // VIEW
+ }
table->key_length=key_length;
table->version=refresh_version;
table->flush_version=flush_version;
@@ -916,7 +925,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
table->in_use=thd;
check_unused(); // Debugging call
-
+
VOID(pthread_mutex_unlock(&LOCK_open));
if (refresh)
{
@@ -946,6 +955,7 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name,
table->used_keys= table->keys_for_keyread;
if (table->timestamp_field)
table->timestamp_field->set_timestamp_offsets();
+ table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
DBUG_ASSERT(table->key_read == 0);
DBUG_RETURN(table);
}
@@ -992,7 +1002,8 @@ bool reopen_table(TABLE *table,bool locked)
VOID(pthread_mutex_lock(&LOCK_open));
safe_mutex_assert_owner(&LOCK_open);
- if (open_unireg_entry(current_thd,&tmp,db,table_name,table->table_name))
+ if (open_unireg_entry(current_thd, &tmp, db, table_name,
+ table->table_name, 0, 0))
goto end;
free_io_cache(table);
@@ -1309,6 +1320,8 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name)
db Database name
name Table name
alias Alias name
+ table_desc TABLE_LIST descriptor (used with views)
+ mem_root temporary mem_root for parsing
NOTES
Extra argument for open is taken from thd->open_options
@@ -1317,9 +1330,9 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name)
0 ok
# Error
*/
-
static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
- const char *name, const char *alias)
+ const char *name, const char *alias,
+ TABLE_LIST *table_desc, MEM_ROOT *mem_root)
{
char path[FN_REFLEN];
int error;
@@ -1327,19 +1340,28 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
DBUG_ENTER("open_unireg_entry");
strxmov(path, mysql_data_home, "/", db, "/", name, NullS);
- while (openfrm(path,alias,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX |
- HA_TRY_READ_ONLY),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
- thd->open_options, entry))
+ while ((error= openfrm(path, alias,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX | HA_TRY_READ_ONLY |
+ NO_ERR_ON_NEW_FRM),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ thd->open_options, entry)) &&
+ (error != 5 ||
+ fn_format(path, path, 0, reg_ext, MY_UNPACK_FILENAME),
+ open_new_frm(path, alias, db, name,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX | HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ thd->open_options, entry, table_desc, mem_root)))
+
{
if (!entry->crashed)
{
/*
- Frm file could not be found on disk
- Since it does not exist, no one can be using it
- LOCK_open has been locked to protect from someone else
- trying to discover the table at the same time.
+ Frm file could not be found on disk
+ Since it does not exist, no one can be using it
+ LOCK_open has been locked to protect from someone else
+ trying to discover the table at the same time.
*/
if (discover_retry_count++ != 0)
goto err;
@@ -1398,6 +1420,10 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
goto err;
break;
}
+
+ if (error == 5)
+ DBUG_RETURN(0); // we have just opened VIEW
+
/*
If we are here, there was no fatal error (but error may be still
unitialized).
@@ -1434,6 +1460,14 @@ memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name);
}
DBUG_RETURN(0);
err:
+ /* Hide "Table doesn't exist" errors if table belong to view */
+ if (thd->net.last_errno == ER_NO_SUCH_TABLE &&
+ table_desc && table_desc->belong_to_view)
+ {
+ TABLE_LIST * view= table_desc->belong_to_view;
+ thd->clear_error();
+ my_error(ER_VIEW_INVALID, MYF(0), view->view_db.str, view->view_name.str);
+ }
DBUG_RETURN(1);
}
@@ -1457,12 +1491,18 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
bool refresh;
int result=0;
DBUG_ENTER("open_tables");
+ MEM_ROOT new_frm_mem;
+ /*
+ temporary mem_root for new .frm parsing.
+ TODO: variables for size
+ */
+ init_alloc_root(&new_frm_mem, 8024, 8024);
thd->current_tablenr= 0;
restart:
*counter= 0;
thd->proc_info="Opening tables";
- for (tables=start ; tables ; tables=tables->next)
+ for (tables= start; tables ;tables= tables->next_global)
{
/*
Ignore placeholders for derived tables. After derived tables
@@ -1472,11 +1512,15 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
continue;
(*counter)++;
if (!tables->table &&
- !(tables->table= open_table(thd,
- tables->db,
- tables->real_name,
- tables->alias, &refresh)))
+ !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh)))
{
+ free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
+ if (tables->view)
+ {
+ (*counter)--;
+ continue; //VIEW placeholder
+ }
+
if (refresh) // Refresh in progress
{
/* close all 'old' tables used by this thread */
@@ -1486,7 +1530,7 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
thd->version=refresh_version;
TABLE **prev_table= &thd->open_tables;
bool found=0;
- for (TABLE_LIST *tmp=start ; tmp ; tmp=tmp->next)
+ for (TABLE_LIST *tmp= start; tmp; tmp= tmp->next_global)
{
/* Close normal (not temporary) changed tables */
if (tmp->table && ! tmp->table->tmp_table)
@@ -1514,11 +1558,15 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
result= -1; // Fatal error
break;
}
+ else
+ free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
+
if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables)
tables->table->reginfo.lock_type=tables->lock_type;
tables->table->grant= tables->grant;
}
thd->proc_info=0;
+ free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block
DBUG_RETURN(result);
}
@@ -1586,9 +1634,10 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
thd->proc_info="Opening table";
thd->current_tablenr= 0;
- while (!(table=open_table(thd,table_list->db,
- table_list->real_name,table_list->alias,
- &refresh)) && refresh) ;
+ /* open_ltable can be used only for BASIC TABLEs */
+ table_list->required_type= FRMTYPE_TABLE;
+ while (!(table= open_table(thd, table_list, 0, &refresh)) && refresh)
+ ;
if (table)
{
@@ -1669,7 +1718,7 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
DBUG_ENTER("open_and_lock_tables");
uint counter;
if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter))
- DBUG_RETURN(-1); /* purecov: inspected */
+ DBUG_RETURN(thd->net.report_error ? -1 : 1); /* purecov: inspected */
/*
Let us propagate pointers to open tables from global table list
to table lists in particular selects if needed.
@@ -1680,11 +1729,15 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
for (SELECT_LEX *sl= thd->lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
+ {
for (TABLE_LIST *cursor= (TABLE_LIST *) sl->table_list.first;
cursor;
cursor=cursor->next)
+ {
if (cursor->table_list)
cursor->table= cursor->table_list->table;
+ }
+ }
}
DBUG_RETURN(mysql_handle_derived(thd->lex));
}
@@ -1721,9 +1774,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
TABLE **start,**ptr;
if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*)*count)))
return -1;
- for (table = tables ; table ; table=table->next)
+ for (table= tables; table; table= table->next_global)
{
- if (!table->derived)
+ if (!table->placeholder())
*(ptr++)= table->table;
}
if (!(thd->lock=mysql_lock_tables(thd,start,count)))
@@ -1731,9 +1784,9 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
}
else
{
- for (table = tables ; table ; table=table->next)
+ for (table= tables; table; table= table->next_global)
{
- if (!table->derived &&
+ if (!table->placeholder() &&
check_lock_and_start_stmt(thd, table->table, table->lock_type))
{
ha_rollback_stmt(thd);
@@ -1833,11 +1886,128 @@ bool rm_temporary_table(enum db_type base, char *path)
** return unique field
******************************************************************************/
+/* Special Field pointers for find_field_in_tables returning */
+const Field *not_found_field= (Field*) 0x1;
+const Field *view_ref_found= (Field*) 0x2;
+
#define WRONG_GRANT (Field*) -1
-Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
- bool check_grants, bool allow_rowid,
- uint *cached_field_index_ptr)
+
+/*
+ Find field in table or view
+
+ SYNOPSIS
+ find_field_in_table()
+ thd thread handler
+ table_list table where to find
+ name name of field
+ item_name name of item if it will be created (VIEW)
+ length length of name
+ ref expression substituted in VIEW should be
+ passed using this reference (return
+ view_ref_found)
+ check_grants_table do check columns grants for table?
+ check_grants_view do check columns grants for view?
+ allow_rowid do allow finding of "_rowid" field?
+ cached_field_index_ptr cached position in field list (used to
+ speedup prepared tables field finding)
+
+ RETURN
+ 0 field is not found
+ view_ref_found found value in VIEW (real result is in *ref)
+ # pointer to field
+*/
+
+Field *
+find_field_in_table(THD *thd, TABLE_LIST *table_list,
+ const char *name, const char *item_name,
+ uint length, Item **ref,
+ bool check_grants_table, bool check_grants_view,
+ bool allow_rowid,
+ uint *cached_field_index_ptr)
+{
+ Field *fld;
+ if (table_list->field_translation)
+ {
+ DBUG_ASSERT(ref != 0 && table_list->view != 0);
+ uint num= table_list->view->select_lex.item_list.elements;
+ Item **trans= table_list->field_translation;
+ for (uint i= 0; i < num; i ++)
+ {
+ if (strcmp(trans[i]->name, name) == 0)
+ {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (check_grants_view &&
+ check_grant_column(thd, &table_list->grant,
+ table_list->view_db.str,
+ table_list->view_name.str,
+ name, length))
+ return WRONG_GRANT;
+#endif
+ if (thd->lex->current_select->no_wrap_view_item)
+ *ref= trans[i];
+ else
+ {
+ Item_arena *arena= thd->current_arena, backup;
+ if (!arena->is_stmt_prepare())
+ arena= 0;
+ else
+ thd->set_n_backup_item_arena(arena, &backup);
+ *ref= new Item_ref(trans + i, 0, table_list->view_name.str,
+ item_name);
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ /* as far as Item_ref have defined refernce it do not need tables */
+ if (*ref)
+ (*ref)->fix_fields(thd, 0, ref);
+ }
+ return (Field*) view_ref_found;
+ }
+ }
+ return 0;
+ }
+ fld= find_field_in_real_table(thd, table_list->table, name, length,
+ check_grants_table, allow_rowid,
+ cached_field_index_ptr);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /* check for views with temporary table algorithm */
+ if (check_grants_view && table_list->view &&
+ fld && fld != WRONG_GRANT &&
+ check_grant_column(thd, &table_list->grant,
+ table_list->view_db.str,
+ table_list->view_name.str,
+ name, length))
+ {
+ return WRONG_GRANT;
+ }
+#endif
+ return fld;
+}
+
+
+/*
+ Find field in table
+
+ SYNOPSIS
+ find_field_in_real_table()
+ thd thread handler
+ table_list table where to find
+ name name of field
+ length length of name
+ check_grants do check columns grants?
+ allow_rowid do allow finding of "_rowid" field?
+ cached_field_index_ptr cached position in field list (used to
+ speedup prepared tables field finding)
+
+ RETURN
+ 0 field is not found
+ # pointer to field
+*/
+
+static Field *find_field_in_real_table(THD *thd, TABLE *table,
+ const char *name, uint length,
+ bool check_grants, bool allow_rowid,
+ uint *cached_field_index_ptr)
{
Field **field_ptr, *field;
uint cached_field_index= *cached_field_index_ptr;
@@ -1884,7 +2054,9 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
thd->dupp_field=field;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (check_grants && check_grant_column(thd,table,name,length))
+ if (check_grants && check_grant_column(thd, &table->grant,
+ table->table_cache_key,
+ table->real_name, name, length))
return WRONG_GRANT;
#endif
return field;
@@ -1899,25 +2071,25 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
thd Pointer to current thread structure
item Field item that should be found
tables Tables for scanning
- where Table where field found will be returned via
- this parameter
+ ref if view field is found, pointer to view item will
+ be returned via this parameter
report_error If FALSE then do not report error if item not found
and return not_found_field
+ check_privileges need to check privileges
RETURN VALUES
0 Field is not found or field is not unique- error
message is reported
not_found_field Function was called with report_error == FALSE and
field was not found. no error message reported.
+ view_ref_found view field is found, item passed through ref parameter
found field
*/
-// Special Field pointer for find_field_in_tables returning
-const Field *not_found_field= (Field*) 0x1;
-
Field *
find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
- TABLE_LIST **where, bool report_error)
+ Item **ref, bool report_error,
+ bool check_privileges)
{
Field *found=0;
const char *db=item->db_name;
@@ -1938,14 +2110,15 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
field makes some prepared query ambiguous and so erronous, but we
accept this trade off.
*/
- found= find_field_in_table(thd, item->cached_table->table, name, length,
- test(item->cached_table->
- table->grant.want_privilege),
- 1, &(item->cached_field_index));
+ found= find_field_in_real_table(thd, item->cached_table->table,
+ name, length,
+ test(item->cached_table->
+ table->grant.want_privilege) &&
+ check_privileges,
+ 1, &(item->cached_field_index));
if (found)
{
- (*where)= tables;
if (found == WRONG_GRANT)
return (Field*) 0;
return found;
@@ -1967,19 +2140,23 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
if (table_name && table_name[0])
{ /* Qualified field */
bool found_table=0;
- for (; tables ; tables=tables->next)
+ for (; tables; tables= tables->next_local)
{
if (!my_strcasecmp(table_alias_charset, tables->alias, table_name) &&
(!db || !tables->db || !tables->db[0] || !strcmp(db,tables->db)))
{
found_table=1;
- Field *find=find_field_in_table(thd,tables->table,name,length,
- test(tables->table->grant.
- want_privilege),
- 1, &(item->cached_field_index));
+ Field *find= find_field_in_table(thd, tables, name, item->name,
+ length, ref,
+ (test(tables->table->grant.
+ want_privilege) &&
+ check_privileges),
+ (test(tables->grant.want_privilege) &&
+ check_privileges),
+ 1, &(item->cached_field_index));
if (find)
{
- (*where)= item->cached_table= tables;
+ item->cached_table= tables;
if (!tables->cacheable_table)
item->cached_table= 0;
if (find == WRONG_GRANT)
@@ -2022,8 +2199,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
return (Field*) not_found_field;
return (Field*) 0;
}
- bool allow_rowid= tables && !tables->next; // Only one table
- for (; tables ; tables=tables->next)
+ bool allow_rowid= tables && !tables->next_local; // Only one table
+ for (; tables ; tables= tables->next_local)
{
if (!tables->table)
{
@@ -2033,14 +2210,19 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
return (Field*) not_found_field;
}
- Field *field=find_field_in_table(thd,tables->table,name,length,
- test(tables->table->grant.want_privilege),
- allow_rowid, &(item->cached_field_index));
+ Field *field= find_field_in_table(thd, tables, name, item->name,
+ length, ref,
+ (test(tables->table->grant.
+ want_privilege) &&
+ check_privileges),
+ (test(tables->grant.want_privilege) &&
+ check_privileges),
+ allow_rowid, &(item->cached_field_index));
if (field)
{
if (field == WRONG_GRANT)
return (Field*) 0;
- (*where)= item->cached_table= tables;
+ item->cached_table= tables;
if (!tables->cacheable_table)
item->cached_table= 0;
if (found)
@@ -2254,26 +2436,29 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
List<Item> *sum_func_list,
uint wild_num)
{
+ Item *item;
if (!wild_num)
return 0;
Item_arena *arena= thd->current_arena, backup;
+ if (!arena->is_stmt_prepare())
+ arena= 0; // For easier test
/*
If we are in preparing prepared statement phase then we have change
temporary mem_root to statement mem root to save changes of SELECT list
*/
- if (arena->is_stmt_prepare())
+ if (arena)
thd->set_n_backup_item_arena(arena, &backup);
- reg2 Item *item;
List_iterator<Item> it(fields);
- while ( wild_num && (item= it++))
- {
+ while (wild_num && (item= it++))
+ {
if (item->type() == Item::FIELD_ITEM && ((Item_field*) item)->field_name &&
((Item_field*) item)->field_name[0] == '*' &&
!((Item_field*) item)->field)
{
uint elem= fields.elements;
+ bool any_privileges= ((Item_field *) item)->any_privileges;
Item_subselect *subsel= thd->lex->current_select->master_unit()->item;
if (subsel &&
subsel->substype() == Item_subselect::EXISTS_SUBS)
@@ -2286,9 +2471,10 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
it.replace(new Item_int("Not_used", (longlong) 1, 21));
}
else if (insert_fields(thd,tables,((Item_field*) item)->db_name,
- ((Item_field*) item)->table_name, &it))
+ ((Item_field*) item)->table_name, &it,
+ any_privileges))
{
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
return (-1);
}
@@ -2304,8 +2490,15 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
wild_num--;
}
}
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
+ if (arena)
+ {
+ /* make * substituting permanent */
+ SELECT_LEX *select_lex= thd->lex->current_select;
+ select_lex->with_wild= 0;
+ select_lex->item_list= fields;
+
+ thd->restore_backup_item_arena(arena, &backup);
+ }
return 0;
}
@@ -2319,6 +2512,7 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
{
reg2 Item *item;
List_iterator<Item> it(fields);
+ SELECT_LEX *select_lex= thd->lex->current_select;
DBUG_ENTER("setup_fields");
thd->set_query_id=set_query_id;
@@ -2330,7 +2524,10 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
{
if (!item->fixed && item->fix_fields(thd, tables, it.ref()) ||
(item= *(it.ref()))->check_cols(1))
+ {
+ select_lex->no_wrap_view_item= 0;
DBUG_RETURN(-1); /* purecov: inspected */
+ }
if (ref)
*(ref++)= item;
if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM &&
@@ -2347,7 +2544,9 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
SYNOPSIS
setup_tables()
+ thd - thread handler
tables - tables list
+ conds - condition of current SELECT (can be changed by VIEW)
RETURN
0 ok; In this case *map will includes the choosed index
@@ -2360,14 +2559,20 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
This has to be called for all tables that are used by items, as otherwise
table->map is not set and all Item_field will be regarded as const items.
+
+ if tables do not contain VIEWs it is OK to pass 0 as conds
*/
-bool setup_tables(TABLE_LIST *tables)
+bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds)
{
DBUG_ENTER("setup_tables");
+ if (!tables || tables->setup_is_done)
+ DBUG_RETURN(0);
+ tables->setup_is_done= 1;
uint tablenr=0;
- for (TABLE_LIST *table_list=tables ; table_list ;
- table_list=table_list->next,tablenr++)
+ for (TABLE_LIST *table_list= tables;
+ table_list;
+ table_list= table_list->next_local, tablenr++)
{
TABLE *table= table_list->table;
setup_table_map(table, table_list, tablenr);
@@ -2389,6 +2594,8 @@ bool setup_tables(TABLE_LIST *tables)
table->keys_in_use_for_query.subtract(map);
}
table->used_keys.intersect(table->keys_in_use_for_query);
+ if (table_list->ancestor && table_list->setup_ancestor(thd, conds))
+ DBUG_RETURN(1);
}
if (tablenr > MAX_TABLES)
{
@@ -2423,8 +2630,9 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
map->clear_all();
while ((name=it++))
{
- if ((pos= find_type(&table->keynames, name->ptr(), name->length(), 1)) <=
- 0)
+ if (table->keynames.type_names == 0 ||
+ (pos= find_type(&table->keynames, name->ptr(), name->length(), 1)) <=
+ 0)
{
my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(),
table->real_name);
@@ -2437,17 +2645,35 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
}
-/****************************************************************************
- This just drops in all fields instead of current '*' field
- Returns pointer to last inserted field if ok
-****************************************************************************/
+/*
+ Drops in all fields instead of current '*' field
+
+ SYNOPSIS
+ insert_fields()
+ thd Thread handler
+ tables List of tables
+ db_name Database name in case of 'database_name.table_name.*'
+ table_name Table name in case of 'table_name.*'
+ it Pointer to '*'
+ any_privileges 0 If we should ensure that we have SELECT privileges
+ for all columns
+ 1 If any privilege is ok
+ RETURN
+ 0 ok
+ 'it' is updated to point at last inserted
+ 1 error. The error message is sent to client
+*/
bool
-insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
- const char *table_name, List_iterator<Item> *it)
+insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name,
+ const char *table_name, List_iterator<Item> *it,
+ bool any_privileges)
{
- char name_buff[NAME_LEN+1];
+ /* allocate variables on stack to avoid pool alloaction */
+ Field_iterator_table table_iter;
+ Field_iterator_view view_iter;
uint found;
+ char name_buff[NAME_LEN+1];
DBUG_ENTER("insert_fields");
if (db_name && lower_case_table_names)
@@ -2462,66 +2688,174 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
db_name= name_buff;
}
-
- found=0;
- for (; tables ; tables=tables->next)
+ found= 0;
+ for (; tables; tables= tables->next_local)
{
- TABLE *table=tables->table;
+ Field_iterator *iterator;
+ TABLE_LIST *natural_join_table;
+ Field *field;
+ TABLE_LIST *embedded;
+ TABLE_LIST *last;
+ TABLE_LIST *embedding;
+ TABLE *table= tables->table;
+
if (!table_name || (!my_strcasecmp(table_alias_charset, table_name,
tables->alias) &&
(!db_name || !strcmp(tables->db,db_name))))
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Ensure that we have access right to all columns */
- if (!(table->grant.privilege & SELECT_ACL) &&
- check_grant_all_columns(thd,SELECT_ACL,table))
- DBUG_RETURN(-1);
+ if (!(table->grant.privilege & SELECT_ACL) && !any_privileges)
+ {
+ if (tables->view)
+ {
+ view_iter.set(tables);
+ if (check_grant_all_columns(thd, SELECT_ACL, &tables->grant,
+ tables->view_db.str,
+ tables->view_name.str,
+ &view_iter))
+ goto err;
+ }
+ else
+ {
+ table_iter.set(tables);
+ if (check_grant_all_columns(thd, SELECT_ACL, &table->grant,
+ table->table_cache_key, table->real_name,
+ &table_iter))
+ goto err;
+ }
+ }
#endif
- Field **ptr=table->field,*field;
- TABLE *natural_join_table= 0;
+ natural_join_table= 0;
+ thd->used_tables|= table->map;
+ last= embedded= tables;
- thd->used_tables|=table->map;
- if (!table->outer_join &&
- tables->natural_join &&
- !tables->natural_join->table->outer_join)
- natural_join_table= tables->natural_join->table;
+ while ((embedding= embedded->embedding) &&
+ embedding->join_list->elements != 1)
+ {
+ TABLE_LIST *next;
+ List_iterator_fast<TABLE_LIST> it(embedding->nested_join->join_list);
+ last= it++;
+ while ((next= it++))
+ last= next;
+ if (last != tables)
+ break;
+ embedded= embedding;
+ }
- while ((field = *ptr++))
+ if (tables == last &&
+ !embedded->outer_join &&
+ embedded->natural_join &&
+ !embedded->natural_join->outer_join)
{
+ embedding= embedded->natural_join;
+ while (embedding->nested_join)
+ embedding= embedding->nested_join->join_list.head();
+ natural_join_table= embedding;
+ }
+ if (tables->field_translation)
+ iterator= &view_iter;
+ else
+ iterator= &table_iter;
+ iterator->set(tables);
+
+ for (; !iterator->end_of_fields(); iterator->next())
+ {
+ Item *not_used_item;
uint not_used_field_index= NO_CACHED_FIELD_INDEX;
+ const char *field_name= iterator->name();
/* Skip duplicate field names if NATURAL JOIN is used */
if (!natural_join_table ||
- !find_field_in_table(thd, natural_join_table, field->field_name,
- strlen(field->field_name), 0, 0,
+ !find_field_in_table(thd, natural_join_table, field_name,
+ field_name,
+ strlen(field_name), &not_used_item, 0, 0, 0,
&not_used_field_index))
{
- Item_field *item= new Item_field(thd, field);
+ Item *item= iterator->item(thd);
if (!found++)
(void) it->replace(item); // Replace '*'
else
it->after(item);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (any_privileges)
+ {
+ /*
+ In time of view creation MEGRGE algorithm for underlying
+ VIEWs can't be used => it should be Item_field
+ */
+ DBUG_ASSERT(item->type() == Item::FIELD_ITEM);
+ Item_field *fld= (Item_field*)item;
+ char *db, *tab;
+ if (tables->view)
+ {
+ db= tables->view_db.str;
+ tab= tables->view_name.str;
+ }
+ else
+ {
+ db= tables->db;
+ tab= tables->real_name;
+ }
+ if (!(fld->have_privileges= (get_column_grant(thd,
+ &table->grant,
+ db,
+ tab,
+ fld->field_name) &
+ VIEW_ANY_ACL)))
+ {
+ my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
+ ER(ER_COLUMNACCESS_DENIED_ERROR),
+ MYF(0),
+ "ANY",
+ thd->priv_user,
+ thd->host_or_ip,
+ fld->field_name,
+ tab);
+ goto err;
+ }
+ }
+#endif
}
- /*
- Mark if field used before in this select.
- Used by 'insert' to verify if a field name is used twice
- */
- if (field->query_id == thd->query_id)
- thd->dupp_field=field;
- field->query_id=thd->query_id;
- table->used_keys.intersect(field->part_of_key);
+ if ((field= iterator->field()))
+ {
+ /*
+ Mark if field used before in this select.
+ Used by 'insert' to verify if a field name is used twice
+ */
+ if (field->query_id == thd->query_id)
+ thd->dupp_field=field;
+ field->query_id=thd->query_id;
+ table->used_keys.intersect(field->part_of_key);
+ }
+ else if (thd->current_arena->is_stmt_prepare() &&
+ thd->lex->current_select->first_execution)
+ {
+ Item_field *item= new Item_field(thd->strdup(tables->view_db.str),
+ thd->strdup(tables->view_name.str),
+ thd->strdup(field_name));
+ /*
+ during cleunup() this item will be put in list to replace
+ expression from VIEW
+ */
+ item->changed_during_fix_field= it->ref();
+ }
+
}
/* All fields are used */
table->used_fields=table->fields;
}
}
- if (!found)
- {
- if (!table_name)
- my_error(ER_NO_TABLES_USED,MYF(0));
- else
- my_error(ER_BAD_TABLE_ERROR,MYF(0),table_name);
- }
- DBUG_RETURN(!found);
+ if (found)
+ DBUG_RETURN(0);
+
+ if (!table_name)
+ my_error(ER_NO_TABLES_USED, MYF(0));
+ else
+ my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name);
+
+err:
+ send_error(thd);
+ DBUG_RETURN(1);
}
@@ -2532,138 +2866,197 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
{
table_map not_null_tables= 0;
- Item_arena *arena= thd->current_arena, backup;
-
+ SELECT_LEX *select_lex= thd->lex->current_select;
+ Item_arena *arena= thd->current_arena;
+ Item_arena backup;
DBUG_ENTER("setup_conds");
+
+ if (select_lex->conds_processed_with_permanent_arena ||
+ !arena->is_stmt_prepare())
+ arena= 0; // For easier test
+
thd->set_query_id=1;
-
- thd->lex->current_select->cond_count= 0;
+
+ select_lex->cond_count= 0;
if (*conds)
{
thd->where="where clause";
if (!(*conds)->fixed && (*conds)->fix_fields(thd, tables, conds) ||
(*conds)->check_cols(1))
DBUG_RETURN(1);
- not_null_tables= (*conds)->not_null_tables();
}
-
/* Check if we are using outer joins */
- for (TABLE_LIST *table=tables ; table ; table=table->next)
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
{
- if (table->on_expr)
+ TABLE_LIST *embedded;
+ TABLE_LIST *embedding= table;
+ do
{
- /* Make a join an a expression */
- thd->where="on clause";
-
- if (!table->on_expr->fixed &&
- table->on_expr->fix_fields(thd, tables, &table->on_expr) ||
- table->on_expr->check_cols(1))
- DBUG_RETURN(1);
- thd->lex->current_select->cond_count++;
-
- /*
- If it's a normal join or a LEFT JOIN which can be optimized away
- add the ON/USING expression to the WHERE
- */
- if (!table->outer_join ||
- ((table->table->map & not_null_tables) &&
- !(specialflag & SPECIAL_NO_NEW_FUNC)))
+ embedded= embedding;
+ if (embedded->on_expr)
{
- table->outer_join= 0;
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
- *conds= and_conds(*conds, table->on_expr);
- table->on_expr=0;
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
- if ((*conds) && !(*conds)->fixed &&
- (*conds)->fix_fields(thd, tables, conds))
+ /* Make a join an a expression */
+ thd->where="on clause";
+ if (!embedded->on_expr->fixed &&
+ embedded->on_expr->fix_fields(thd, tables, &embedded->on_expr) ||
+ embedded->on_expr->check_cols(1))
DBUG_RETURN(1);
+ select_lex->cond_count++;
}
- }
- if (table->natural_join)
- {
- if (arena->is_stmt_prepare())
- thd->set_n_backup_item_arena(arena, &backup);
- /* Make a join of all fields with have the same name */
- TABLE *t1= table->table;
- TABLE *t2= table->natural_join->table;
- Item_cond_and *cond_and= new Item_cond_and();
- if (!cond_and) // If not out of memory
- goto err;
- cond_and->top_level_item();
-
- Field **t1_field, *t2_field;
- for (t1_field= t1->field; (*t1_field); t1_field++)
+ if (embedded->natural_join)
{
- const char *t1_field_name= (*t1_field)->field_name;
- uint not_used_field_index= NO_CACHED_FIELD_INDEX;
-
- if ((t2_field= find_field_in_table(thd, t2, t1_field_name,
- strlen(t1_field_name), 0, 0,
- &not_used_field_index)))
+ /* Make a join of all fields wich have the same name */
+ TABLE_LIST *tab1= embedded;
+ TABLE_LIST *tab2= embedded->natural_join;
+ if (!(embedded->outer_join & JOIN_TYPE_RIGHT))
{
- Item_func_eq *tmp=new Item_func_eq(new Item_field(*t1_field),
- new Item_field(t2_field));
- if (!tmp)
- goto err;
- /* Mark field used for table cache */
- (*t1_field)->query_id= t2_field->query_id= thd->query_id;
- cond_and->list.push_back(tmp);
- t1->used_keys.intersect((*t1_field)->part_of_key);
- t2->used_keys.intersect(t2_field->part_of_key);
+ while (tab1->nested_join)
+ {
+ TABLE_LIST *next;
+ List_iterator_fast<TABLE_LIST> it(tab1->nested_join->join_list);
+ tab1= it++;
+ while ((next= it++))
+ tab1= next;
+ }
+ }
+ else
+ {
+ while (tab1->nested_join)
+ tab1= tab1->nested_join->join_list.head();
+ }
+ if (embedded->outer_join & JOIN_TYPE_RIGHT)
+ {
+ while (tab2->nested_join)
+ {
+ TABLE_LIST *next;
+ List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list);
+ tab2= it++;
+ while ((next= it++))
+ tab2= next;
+ }
+ }
+ else
+ {
+ while (tab2->nested_join)
+ tab2= tab2->nested_join->join_list.head();
}
- }
- thd->lex->current_select->cond_count+= cond_and->list.elements;
- // to prevent natural join processing during PS re-execution
- table->natural_join= 0;
+ if (arena)
+ thd->set_n_backup_item_arena(arena, &backup);
- if (cond_and->list.elements)
- {
- if (!table->outer_join) // Not left join
+ TABLE *t1=tab1->table;
+ TABLE *t2=tab2->table;
+ Field_iterator_table table_iter;
+ Field_iterator_view view_iter;
+ Field_iterator *iterator;
+ Field *t1_field, *t2_field;
+ Item *item_t2;
+ Item_cond_and *cond_and=new Item_cond_and();
+
+ if (!cond_and) // If not out of memory
+ DBUG_RETURN(1);
+ cond_and->top_level_item();
+
+ if (table->field_translation)
{
- *conds= and_conds(*conds, cond_and);
- // fix_fields() should be made with temporary memory pool
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
- if (*conds && !(*conds)->fixed)
+ iterator= &view_iter;
+ view_iter.set(tab1);
+ }
+ else
+ {
+ iterator= &table_iter;
+ table_iter.set(tab1);
+ }
+
+ for (; !iterator->end_of_fields(); iterator->next())
+ {
+ const char *t1_field_name= iterator->name();
+ uint not_used_field_index= NO_CACHED_FIELD_INDEX;
+
+ if ((t2_field= find_field_in_table(thd, tab2, t1_field_name,
+ t1_field_name,
+ strlen(t1_field_name), &item_t2,
+ 0, 0, 0,
+ &not_used_field_index)))
{
- if ((*conds)->fix_fields(thd, tables, conds))
- DBUG_RETURN(1);
+ if (t2_field != view_ref_found)
+ {
+ if (!(item_t2= new Item_field(t2_field)))
+ goto err;
+ /* Mark field used for table cache */
+ t2_field->query_id= thd->query_id;
+ t2->used_keys.intersect(t2_field->part_of_key);
+ }
+ if ((t1_field= iterator->field()))
+ {
+ /* Mark field used for table cache */
+ t1_field->query_id= thd->query_id;
+ t1->used_keys.intersect(t1_field->part_of_key);
+ }
+ Item_func_eq *tmp= new Item_func_eq(iterator->item(thd),
+ item_t2);
+ if (!tmp)
+ goto err;
+ cond_and->list.push_back(tmp);
}
}
- else
+ select_lex->cond_count+= cond_and->list.elements;
+
+ // to prevent natural join processing during PS re-execution
+ embedding->natural_join= 0;
+
+ if (cond_and->list.elements)
{
- table->on_expr= and_conds(table->on_expr, cond_and);
- // fix_fields() should be made with temporary memory pool
- if (arena->is_stmt_prepare())
- thd->restore_backup_item_arena(arena, &backup);
- if (table->on_expr && !table->on_expr->fixed)
+ COND *on_expr= cond_and;
+ on_expr->fix_fields(thd, 0, &on_expr);
+ if (!embedded->outer_join) // Not left join
+ {
+ *conds= and_conds(*conds, cond_and);
+ // fix_fields() should be made with temporary memory pool
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ if (*conds && !(*conds)->fixed)
+ {
+ if ((*conds)->fix_fields(thd, tables, conds))
+ DBUG_RETURN(1);
+ }
+ }
+ else
{
- if (table->on_expr->fix_fields(thd, tables, &table->on_expr))
- DBUG_RETURN(1);
+ embedded->on_expr= and_conds(embedded->on_expr, cond_and);
+ // fix_fields() should be made with temporary memory pool
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ if (embedded->on_expr && !embedded->on_expr->fixed)
+ {
+ if (embedded->on_expr->fix_fields(thd, tables, &table->on_expr))
+ DBUG_RETURN(1);
+ }
}
}
}
+ embedding= embedded->embedding;
}
+ while (embedding &&
+ embedding->nested_join->join_list.head() == embedded);
}
- if (arena->is_stmt_prepare())
+ if (arena)
{
/*
We are in prepared statement preparation code => we should store
WHERE clause changing for next executions.
- We do this ON -> WHERE transformation only once per PS statement.
+ We do this ON -> WHERE transformation only once per PS/SP statement.
*/
- thd->lex->current_select->where= *conds;
+ select_lex->where= *conds;
+ select_lex->conds_processed_with_permanent_arena= 1;
}
DBUG_RETURN(test(thd->net.report_error));
err:
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(1);
}
@@ -2831,7 +3224,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
! in_use->killed)
{
- in_use->killed=1;
+ in_use->killed= THD::KILL_CONNECTION;
pthread_mutex_lock(&in_use->mysys_var->mutex);
if (in_use->mysys_var->current_cond)
{
@@ -2897,3 +3290,65 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
}
return 0;
}
+
+
+/*
+ open new .frm format table
+
+ SYNOPSIS
+ open_new_frm()
+ path path to .frm
+ alias alias for table
+ db database
+ table_name name of table
+ db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..)
+ can be 0 (example in ha_example_table)
+ prgflag READ_ALL etc..
+ ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc..
+ outparam result table
+ table_desc TABLE_LIST descriptor
+ mem_root temporary MEM_ROOT for parsing
+*/
+
+static my_bool
+open_new_frm(const char *path, const char *alias,
+ const char *db, const char *table_name,
+ uint db_stat, uint prgflag,
+ uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc,
+ MEM_ROOT *mem_root)
+{
+ LEX_STRING pathstr;
+ File_parser *parser;
+ DBUG_ENTER("open_new_frm");
+
+ pathstr.str= (char*) path;
+ pathstr.length= strlen(path);
+
+ if (!mem_root)
+ mem_root= &current_thd->mem_root;
+
+ if ((parser= sql_parse_prepare(&pathstr, mem_root, 1)))
+ {
+ if (!strncmp("VIEW", parser->type()->str, parser->type()->length))
+ {
+ if (table_desc == 0 || table_desc->required_type == FRMTYPE_TABLE)
+ {
+ my_error(ER_WRONG_OBJECT, MYF(0), db, table_name, "BASE TABLE");
+ goto err;
+ }
+ if (mysql_make_view(parser, table_desc))
+ goto err;
+ }
+ else
+ {
+ /* only VIEWs are supported now */
+ my_error(ER_FRM_UNKNOWN_TYPE, MYF(0), path, parser->type()->str);
+ goto err;
+ }
+ DBUG_RETURN(0);
+ }
+
+err:
+ bzero(outparam, sizeof(TABLE)); // do not run repair
+ DBUG_RETURN(1);
+}
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 5fe21d79aa0..8ab7106978c 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -932,11 +932,16 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
/*
Test if the query is a SELECT
- (pre-space is removed in dispatch_command)
+ (pre-space is removed in dispatch_command).
+
+ First '/' looks like comment before command it is not
+ frequently appeared in real lihe, consequently we can
+ check all such queries, too.
*/
- if (my_toupper(system_charset_info, sql[0]) != 'S' ||
- my_toupper(system_charset_info, sql[1]) != 'E' ||
- my_toupper(system_charset_info,sql[2]) !='L')
+ if ((my_toupper(system_charset_info, sql[0]) != 'S' ||
+ my_toupper(system_charset_info, sql[1]) != 'E' ||
+ my_toupper(system_charset_info,sql[2]) !='L') &&
+ sql[0] != '/')
{
DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
goto err;
@@ -1118,7 +1123,7 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
using_transactions = using_transactions &&
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- for (; tables_used; tables_used=tables_used->next)
+ for (; tables_used; tables_used= tables_used->next_local)
{
DBUG_ASSERT(!using_transactions || tables_used->table!=0);
if (tables_used->derived)
@@ -1150,7 +1155,7 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
if (query_cache_size > 0)
{
DUMP(this);
- for (; tables_used; tables_used=tables_used->next)
+ for (; tables_used; tables_used= tables_used->next)
{
invalidate_table((byte*) tables_used->key, tables_used->key_length);
DBUG_PRINT("qcache", (" db %s, table %s", tables_used->key,
@@ -1183,7 +1188,7 @@ void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
if (query_cache_size > 0)
{
DUMP(this);
- for (; tables_used; tables_used= tables_used->next)
+ for (; tables_used; tables_used= tables_used->next_local)
{
if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE))
invalidate_table(tables_used->table);
@@ -2070,7 +2075,9 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
Query_cache_block_table *block_table = block->table(0);
- for (n=0; tables_used; tables_used=tables_used->next, n++, block_table++)
+ for (n= 0;
+ tables_used;
+ tables_used= tables_used->next_global, n++, block_table++)
{
DBUG_PRINT("qcache",
("table %s, db %s, openinfo at 0x%lx, keylen %u, key at 0x%lx",
@@ -2617,7 +2624,7 @@ TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len,
lex->select_lex.options,
(int) thd->variables.query_cache_type));
- for (; tables_used; tables_used= tables_used->next)
+ for (; tables_used; tables_used= tables_used->next_global)
{
table_count++;
DBUG_PRINT("qcache", ("table %s, db %s, type %u",
@@ -2684,7 +2691,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
{
DBUG_ENTER("Query_cache::ask_handler_allowance");
- for (; tables_used; tables_used= tables_used->next)
+ for (; tables_used; tables_used= tables_used->next_global)
{
TABLE *table= tables_used->table;
if (!ha_caching_allowed(thd, table->table_cache_key,
@@ -3133,7 +3140,7 @@ void Query_cache::wreck(uint line, const char *message)
DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line));
DBUG_PRINT("warning", ("=================================="));
if (thd)
- thd->killed = 1;
+ thd->killed= THD::KILL_CONNECTION;
cache_dump();
/* check_integrity(0); */ /* Can't call it here because of locks */
bins_dump();
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index b103ee29095..456b58ee95e 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -36,6 +36,9 @@
#endif
#include <mysys_err.h>
+#include "sp_rcontext.h"
+#include "sp_cache.h"
+
/*
The following is used to initialise Table_ident with a internal
table name
@@ -159,16 +162,17 @@ THD::THD()
:user_time(0), global_read_lock(0), is_fatal_error(0),
last_insert_id_used(0),
insert_id_used(0), rand_used(0), time_zone_used(0),
- in_lock_tables(0), bootstrap(0)
+ in_lock_tables(0), bootstrap(0), spcont(NULL)
{
current_arena= this;
- host= user= priv_user= db= ip=0;
+ host= user= priv_user= db= ip= 0;
+ catalog= (char*)"std"; // the only catalog we have for now
host_or_ip= "connecting host";
locked=some_tables_deleted=no_errors=password= 0;
- killed=0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
- db_length= col_access= 0;
+ killed= NOT_KILLED;
+ db_length= col_access=0;
query_error= tmp_table_used= 0;
next_insert_id=last_insert_id=0;
open_tables= temporary_tables= handler_tables= derived_tables= 0;
@@ -224,9 +228,12 @@ THD::THD()
clear_alloc_root(&transaction.mem_root);
init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
user_connect=(USER_CONN *)0;
- hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
+ hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
- (hash_free_key) free_user_var,0);
+ (hash_free_key) free_user_var, 0);
+
+ sp_proc_cache= NULL;
+ sp_func_cache= NULL;
/* For user vars replication*/
if (opt_bin_log)
@@ -254,7 +261,7 @@ THD::THD()
if (open_cached_file(&transaction.trans_log,
mysql_tmpdir, LOG_PREFIX, binlog_cache_size,
MYF(MY_WME)))
- killed=1;
+ killed= KILL_CONNECTION;
transaction.trans_log.end_of_file= max_binlog_cache_size;
}
#endif
@@ -327,9 +334,11 @@ void THD::change_user(void)
cleanup();
cleanup_done= 0;
init();
- hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
+ hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
(hash_free_key) free_user_var, 0);
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
}
@@ -355,6 +364,8 @@ void THD::cleanup(void)
my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR));
delete_dynamic(&user_var_events);
hash_free(&user_vars);
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
if (global_read_lock)
unlock_global_read_lock(this);
if (ull)
@@ -364,6 +375,7 @@ void THD::cleanup(void)
pthread_mutex_unlock(&LOCK_user_locks);
ull= 0;
}
+
cleanup_done=1;
DBUG_VOID_RETURN;
}
@@ -395,6 +407,9 @@ THD::~THD()
}
#endif
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
+
DBUG_PRINT("info", ("freeing host"));
if (host != my_localhost) // If not pointer to constant
safeFree(host);
@@ -407,7 +422,7 @@ THD::~THD()
mysys_var=0; // Safety (shouldn't be needed)
pthread_mutex_destroy(&LOCK_delete);
#ifndef DBUG_OFF
- dbug_sentry = THD_SENTRY_GONE;
+ dbug_sentry= THD_SENTRY_GONE;
#endif
/* Reset stmt_backup.mem_root to not double-free memory from thd.mem_root */
clear_alloc_root(&stmt_backup.mem_root);
@@ -415,14 +430,14 @@ THD::~THD()
}
-void THD::awake(bool prepare_to_die)
+void THD::awake(THD::killed_state state_to_set)
{
THD_CHECK_SENTRY(this);
safe_mutex_assert_owner(&LOCK_delete);
- if (prepare_to_die)
- killed = 1;
- thr_alarm_kill(real_id);
+ killed= state_to_set;
+ if (state_to_set != THD::KILL_QUERY)
+ thr_alarm_kill(real_id);
#ifdef SIGNAL_WITH_VIO_CLOSE
close_active_vio();
#endif
@@ -641,7 +656,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
{
my_error(EE_OUTOFMEMORY, MYF(ME_BELL),
ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
- killed= 1;
+ killed= KILL_CONNECTION;
return 0;
}
@@ -666,15 +681,16 @@ int THD::send_explain_fields(select_result *result)
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("key",NAME_LEN));
item->maybe_null=1;
- field_list.push_back(item=new Item_return_int("key_len",3,
- MYSQL_TYPE_LONGLONG));
+ field_list.push_back(item=new Item_empty_string("key_len",
+ NAME_LEN*MAX_KEY));
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("ref",
NAME_LEN*MAX_REF_PARTS));
item->maybe_null=1;
field_list.push_back(new Item_return_int("rows",10, MYSQL_TYPE_LONGLONG));
field_list.push_back(new Item_empty_string("Extra",255));
- return (result->send_fields(field_list,1));
+ return (result->send_fields(field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
#ifdef SIGNAL_WITH_VIO_CLOSE
@@ -727,9 +743,9 @@ sql_exchange::sql_exchange(char *name,bool flag)
escaped= &default_escaped;
}
-bool select_send::send_fields(List<Item> &list,uint flag)
+bool select_send::send_fields(List<Item> &list, uint flags)
{
- return thd->protocol->send_fields(&list,flag);
+ return thd->protocol->send_fields(&list, flags);
}
/* Send data to client. Returns 0 if ok */
@@ -1290,27 +1306,35 @@ bool select_exists_subselect::send_data(List<Item> &items)
int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
List_iterator_fast<Item> li(list);
- List_iterator_fast<LEX_STRING> gl(var_list);
+ List_iterator_fast<my_var> gl(var_list);
Item *item;
- LEX_STRING *ls;
+
+ local_vars.empty(); // Clear list if SP
+ unit= u;
+ row_count= 0;
+
if (var_list.elements != list.elements)
{
my_error(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, MYF(0));
return 1;
}
- unit=u;
while ((item=li++))
{
- ls= gl++;
- Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item);
- /*
- Item_func_set_user_var can't substitute something else on its place =>
- 0 can be passed as last argument (reference on item)
- */
- xx->fix_fields(thd,(TABLE_LIST*) thd->lex->select_lex.table_list.first,
- 0);
- xx->fix_length_and_dec();
- vars.push_back(xx);
+ my_var *mv= gl++;
+ if (mv->local)
+ (void)local_vars.push_back(new Item_splocal(mv->s, mv->offset));
+ else
+ {
+ Item_func_set_user_var *xx = new Item_func_set_user_var(mv->s, item);
+ /*
+ Item_func_set_user_var can't substitute something else on its place =>
+ 0 can be passed as last argument (reference on item)
+ */
+ xx->fix_fields(thd, (TABLE_LIST*) thd->lex->select_lex.table_list.first,
+ 0);
+ xx->fix_length_and_dec();
+ vars.push_back(xx);
+ }
}
return 0;
}
@@ -1353,7 +1377,7 @@ Item_arena::Item_arena(bool init_mem_root)
Item_arena::Type Item_arena::type() const
{
- DBUG_ASSERT("Item_arena::type()" == "abstract");
+ DBUG_ASSERT(0); /* Should never be called */
return STATEMENT;
}
@@ -1373,7 +1397,8 @@ Statement::Statement(THD *thd)
allow_sum_func(0),
lex(&main_lex),
query(0),
- query_length(0)
+ query_length(0),
+ cursor(0)
{
name.str= NULL;
}
@@ -1385,12 +1410,14 @@ Statement::Statement(THD *thd)
*/
Statement::Statement()
- :id(0),
+ :Item_arena(),
+ id(0),
set_query_id(1),
allow_sum_func(0), /* initialized later */
lex(&main_lex),
query(0), /* these two are set */
- query_length(0) /* in alloc_query() */
+ query_length(0), /* in alloc_query() */
+ cursor(0)
{
}
@@ -1409,6 +1436,7 @@ void Statement::set_statement(Statement *stmt)
lex= stmt->lex;
query= stmt->query;
query_length= stmt->query_length;
+ cursor= stmt->cursor;
}
@@ -1534,8 +1562,19 @@ int Statement_map::insert(Statement *statement)
bool select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<Item_func_set_user_var> li(vars);
+ List_iterator_fast<Item_splocal> var_li(local_vars);
+ List_iterator_fast<my_var> my_li(var_list);
+ List_iterator_fast<Item> it(items);
Item_func_set_user_var *xx;
+ Item_splocal *yy;
+ Item *item;
+ my_var *zz;
DBUG_ENTER("send_data");
+ if (unit->offset_limit_cnt)
+ { // using limit offset,count
+ unit->offset_limit_cnt--;
+ DBUG_RETURN(0);
+ }
if (unit->offset_limit_cnt)
{ // Using limit offset,count
@@ -1547,26 +1586,34 @@ bool select_dumpvar::send_data(List<Item> &items)
my_error(ER_TOO_MANY_ROWS, MYF(0));
DBUG_RETURN(1);
}
- while ((xx=li++))
+ while ((zz=my_li++) && (item=it++))
{
- xx->check();
- xx->update();
+ if (zz->local)
+ {
+ if ((yy=var_li++))
+ {
+ if (thd->spcont->set_item_eval(yy->get_offset(), item, zz->type))
+ DBUG_RETURN(1);
+ }
+ }
+ else
+ {
+ if ((xx=li++))
+ {
+ xx->check();
+ xx->update();
+ }
+ }
}
DBUG_RETURN(0);
}
bool select_dumpvar::send_eof()
{
- if (row_count)
- {
- ::send_ok(thd,row_count);
- return 0;
- }
- else
- {
- my_error(ER_EMPTY_QUERY,MYF(0));
- return 1;
- }
+ if (! row_count)
+ send_warning(thd, ER_SP_FETCH_NO_DATA);
+ ::send_ok(thd,row_count);
+ return 0;
}
/****************************************************************************
diff --git a/sql/sql_class.h b/sql/sql_class.h
index a8035cffd96..2b941b317f6 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -26,6 +26,9 @@
class Query_log_event;
class Load_log_event;
class Slave_log_event;
+class Format_description_log_event;
+class sp_rcontext;
+class sp_cache;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
@@ -97,7 +100,14 @@ class MYSQL_LOG
enum cache_type io_cache_type;
bool write_error, inited;
bool need_start_event;
- bool no_auto_events; // For relay binlog
+ /*
+ no_auto_events means we don't want any of these automatic events :
+ Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't want
+ a Rotate_log event to be written to the relay log. When we start a relay log
+ etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
+ In 5.0 it's 0 for relay logs too!
+ */
+ bool no_auto_events;
/*
The max size before rotation (usable only if log_type == LOG_BIN: binary
logs and relay logs).
@@ -114,6 +124,18 @@ class MYSQL_LOG
public:
MYSQL_LOG();
~MYSQL_LOG();
+
+ /*
+ These describe the log's format. This is used only for relay logs.
+ _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
+ necessary to have 2 distinct objects, because the I/O thread may be reading
+ events in a different format from what the SQL thread is reading (consider
+ the case of a master which has been upgraded from 5.0 to 5.1 without doing
+ RESET MASTER, or from 4.x to 5.0).
+ */
+ Format_description_log_event *description_event_for_exec,
+ *description_event_for_queue;
+
void reset_bytes_written()
{
bytes_written = 0;
@@ -142,7 +164,8 @@ public:
bool open(const char *log_name,enum_log_type log_type,
const char *new_name, const char *index_file_name_arg,
enum cache_type io_cache_type_arg,
- bool no_auto_events_arg, ulong max_size);
+ bool no_auto_events_arg, ulong max_size,
+ bool null_created);
void new_file(bool need_lock= 1);
bool write(THD *thd, enum enum_server_command command,
const char *format,...);
@@ -368,6 +391,8 @@ struct system_variables
ulong net_retry_count;
ulong net_wait_timeout;
ulong net_write_timeout;
+ ulong optimizer_prune_level;
+ ulong optimizer_search_depth;
ulong preload_buff_size;
ulong query_cache_type;
ulong read_buff_size;
@@ -378,6 +403,8 @@ struct system_variables
ulong tx_isolation;
/* Determines which non-standard SQL behaviour should be enabled */
ulong sql_mode;
+ /* check of key presence in updatable view */
+ ulong sql_updatable_view_key;
ulong default_week_format;
ulong max_seeks_for_key;
ulong range_alloc_block_size;
@@ -477,6 +504,8 @@ public:
};
+class Cursor;
+
/*
State of a single command executed against this connection.
One connection can contain a lot of simultaneously running statements,
@@ -533,6 +562,7 @@ public:
*/
char *query;
uint32 query_length; // current query length
+ Cursor *cursor;
public:
@@ -626,7 +656,7 @@ private:
a thread/connection descriptor
*/
-class THD :public ilink,
+class THD :public ilink,
public Statement
{
public:
@@ -689,9 +719,17 @@ public:
the connection
priv_user - The user privilege we are using. May be '' for anonymous user.
db - currently selected database
+ catalog - currently selected catalog
ip - client IP
+ WARNING: some members of THD (currently 'db', 'catalog' and 'query') are
+ set and alloced by the slave SQL thread (for the THD of that thread); that
+ thread is (and must remain, for now) the only responsible for freeing these
+ 3 members. If you add members here, and you add code to set them in
+ replication, don't forget to free_them_and_set_them_to_0 in replication
+ properly. For details see the 'err:' label of the pthread_handler_decl of
+ the slave SQL thread, in sql/slave.cc.
*/
- char *host,*user,*priv_user,*db,*ip;
+ char *host,*user,*priv_user,*db,*catalog,*ip;
char priv_host[MAX_HOSTNAME];
/* remote (peer) port */
uint16 peer_port;
@@ -862,10 +900,26 @@ public:
bool time_zone_used;
bool in_lock_tables;
bool query_error, bootstrap, cleanup_done;
+
+ enum killed_state { NOT_KILLED=0, KILL_CONNECTION=ER_SERVER_SHUTDOWN, KILL_QUERY=ER_QUERY_INTERRUPTED };
+ killed_state volatile killed;
+ inline int killed_errno() const
+ {
+ return killed;
+ }
+ inline void send_kill_message() const
+ {
+ my_error(killed_errno(), MYF(0));
+ }
+
bool tmp_table_used;
bool charset_is_system_charset, charset_is_collation_connection;
bool slow_command;
- my_bool volatile killed;
+
+ longlong row_count_func; /* For the ROW_COUNT() function */
+ sp_rcontext *spcont; // SP runtime context
+ sp_cache *sp_proc_cache;
+ sp_cache *sp_func_cache;
/*
If we do a purge of binary logs, log index info of the threads
@@ -913,7 +967,7 @@ public:
}
void close_active_vio();
#endif
- void awake(bool prepare_to_die);
+ void awake(THD::killed_state state_to_set);
/*
For enter_cond() / exit_cond() to work the mutex must be got before
enter_cond() (in 4.1 an assertion will soon ensure this); this mutex is
@@ -1079,7 +1133,7 @@ public:
unit= u;
return 0;
}
- virtual bool send_fields(List<Item> &list,uint flag)=0;
+ virtual bool send_fields(List<Item> &list, uint flags)=0;
virtual bool send_data(List<Item> &items)=0;
virtual bool initialize_tables (JOIN *join=0) { return 0; }
virtual void send_error(uint errcode,const char *err);
@@ -1096,7 +1150,7 @@ public:
class select_send :public select_result {
public:
select_send() {}
- bool send_fields(List<Item> &list,uint flag);
+ bool send_fields(List<Item> &list, uint flags);
bool send_data(List<Item> &items);
bool send_eof();
};
@@ -1114,7 +1168,7 @@ public:
select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L)
{ path[0]=0; }
~select_to_file();
- bool send_fields(List<Item> &list, uint flag) { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
void send_error(uint errcode,const char *err);
bool send_eof();
void cleanup();
@@ -1143,22 +1197,25 @@ public:
class select_insert :public select_result {
public:
+ TABLE_LIST *table_list;
TABLE *table;
List<Item> *fields;
ulonglong last_insert_id;
COPY_INFO info;
+ bool insert_into_view;
- select_insert(TABLE *table_par, List<Item> *fields_par,
- enum_duplicates duplic)
- :table(table_par), fields(fields_par), last_insert_id(0)
+ select_insert(TABLE_LIST *table_list_par, TABLE *table_par,
+ List<Item> *fields_par, enum_duplicates duplic)
+ :table_list(table_list_par), table(table_par), fields(fields_par),
+ last_insert_id(0),
+ insert_into_view(table_list_par && table_list_par->view != 0)
{
bzero((char*) &info,sizeof(info));
info.handle_duplicates=duplic;
}
~select_insert();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list, uint flag)
- { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
void send_error(uint errcode,const char *err);
bool send_eof();
@@ -1169,22 +1226,21 @@ class select_insert :public select_result {
class select_create: public select_insert {
ORDER *group;
- const char *db;
- const char *name;
+ TABLE_LIST *create_table;
List<create_field> *extra_fields;
List<Key> *keys;
HA_CREATE_INFO *create_info;
MYSQL_LOCK *lock;
Field **field;
public:
- select_create(const char *db_name, const char *table_name,
- HA_CREATE_INFO *create_info_par,
- List<create_field> &fields_par,
- List<Key> &keys_par,
- List<Item> &select_fields,enum_duplicates duplic)
- :select_insert (NULL, &select_fields, duplic), db(db_name),
- name(table_name), extra_fields(&fields_par),keys(&keys_par),
- create_info(create_info_par), lock(0)
+ select_create (TABLE_LIST *table,
+ HA_CREATE_INFO *create_info_par,
+ List<create_field> &fields_par,
+ List<Key> &keys_par,
+ List<Item> &select_fields,enum_duplicates duplic)
+ :select_insert (NULL, NULL, &select_fields, duplic), create_table(table),
+ extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par),
+ lock(0)
{}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &values);
@@ -1248,8 +1304,7 @@ class select_union :public select_result {
select_union(TABLE *table_par);
~select_union();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list, uint flag)
- { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
bool send_eof();
bool flush();
@@ -1263,7 +1318,7 @@ protected:
Item_subselect *item;
public:
select_subselect(Item_subselect *item);
- bool send_fields(List<Item> &list, uint flag) { return 0; };
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items)=0;
bool send_eof() { return 0; };
@@ -1370,8 +1425,13 @@ class user_var_entry
DTCollation collation;
};
-
-/* Class for unique (removing of duplicates) */
+/*
+ Unique -- class for unique (removing of duplicates).
+ Puts all values to the TREE. If the tree becomes too big,
+ it's dumped to the file. User can request sorted values, or
+ just iterate through them. In the last case tree merging is performed in
+ memory simultaneously with iteration, so it should be ~2-3x faster.
+ */
class Unique :public Sql_alloc
{
@@ -1385,10 +1445,10 @@ class Unique :public Sql_alloc
public:
ulong elements;
- Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
+ Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg,
uint size_arg, ulong max_in_memory_size_arg);
~Unique();
- inline bool unique_add(gptr ptr)
+ inline bool unique_add(void *ptr)
{
if (tree.elements_in_tree > max_elements && flush())
return 1;
@@ -1396,6 +1456,18 @@ public:
}
bool get(TABLE *table);
+ static double get_use_cost(uint *buffer, uint nkeys, uint key_size,
+ ulong max_in_memory_size);
+ inline static int get_cost_calc_buff_size(ulong nkeys, uint key_size,
+ ulong max_in_memory_size)
+ {
+ register ulong max_elems_in_tree=
+ (1 + max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size));
+ return sizeof(uint)*(1 + nkeys/max_elems_in_tree);
+ }
+
+ void reset();
+ bool walk(tree_walk_action action, void *walk_action_arg);
friend int unique_write_to_file(gptr key, element_count count, Unique *unique);
friend int unique_write_to_ptrs(gptr key, element_count count, Unique *unique);
@@ -1415,8 +1487,7 @@ public:
multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
~multi_delete();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list,
- uint flag) { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
void send_error(uint errcode,const char *err);
@@ -1444,7 +1515,7 @@ public:
List<Item> *values, enum_duplicates handle_duplicates);
~multi_update();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list, uint flag) { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
void send_error(uint errcode,const char *err);
@@ -1452,16 +1523,28 @@ public:
bool send_eof();
};
+class my_var : public Sql_alloc {
+public:
+ LEX_STRING s;
+ bool local;
+ uint offset;
+ enum_field_types type;
+ my_var (LEX_STRING& j, bool i, uint o, enum_field_types t)
+ :s(j), local(i), offset(o), type(t)
+ {}
+ ~my_var() {}
+};
class select_dumpvar :public select_result {
ha_rows row_count;
public:
- List<LEX_STRING> var_list;
+ List<my_var> var_list;
List<Item_func_set_user_var> vars;
- select_dumpvar(void) { var_list.empty(); vars.empty(); row_count=0;}
+ List<Item_splocal> local_vars;
+ select_dumpvar(void) { var_list.empty(); local_vars.empty(); vars.empty(); row_count=0;}
~select_dumpvar() {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list, uint flag) {return 0;}
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
bool send_eof();
void cleanup();
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index cfc75e3be95..f41e03b0602 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -20,6 +20,7 @@
#include "mysql_priv.h"
#include <mysys_err.h>
#include "sql_acl.h"
+#include "sp.h"
#include <my_dir.h>
#include <m_ctype.h>
#ifdef __WIN__
@@ -38,7 +39,8 @@ static TYPELIB known_extentions=
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
const char *db, const char *path,
uint level);
-
+static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
+static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
/* Database options hash */
static HASH dboptions;
static my_bool dboptions_init= 0;
@@ -469,7 +471,6 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
query= thd->query;
query_length= thd->query_length;
}
- mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0);
@@ -519,7 +520,6 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
thd->variables.collation_database= thd->db_charset;
}
- mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -552,7 +552,6 @@ exit2:
-1 Error generated
*/
-
int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
{
long deleted=0;
@@ -627,7 +626,6 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
query =thd->query;
query_length= thd->query_length;
}
- mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0);
@@ -638,6 +636,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
}
exit:
+ (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */
start_waiting_global_read_lock(thd);
/*
If this database was the client's selected database, we silently change the
@@ -706,9 +705,9 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
continue;
/* Check if file is a raid directory */
- if ((my_isdigit(&my_charset_latin1, file->name[0]) ||
+ if ((my_isdigit(system_charset_info, file->name[0]) ||
(file->name[0] >= 'a' && file->name[0] <= 'f')) &&
- (my_isdigit(&my_charset_latin1, file->name[1]) ||
+ (my_isdigit(system_charset_info, file->name[1]) ||
(file->name[1] >= 'a' && file->name[1] <= 'f')) &&
!file->name[2] && !level)
{
@@ -734,6 +733,25 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
found_other_files++;
continue;
}
+ else if (file->name[0] == 'a' && file->name[1] == 'r' &&
+ file->name[2] == 'c' && file->name[3] == '\0')
+ {
+ /* .frm archive */
+ char newpath[FN_REFLEN], *copy_of_path;
+ MY_DIR *new_dirp;
+ uint length;
+ strxmov(newpath, org_path, "/", "arc", NullS);
+ length= unpack_filename(newpath, newpath);
+ if ((new_dirp = my_dir(newpath, MYF(MY_DONT_SORT))))
+ {
+ DBUG_PRINT("my",("Archive subdir found: %s", newpath));
+ if ((mysql_rm_arc_files(thd, new_dirp, newpath)) < 0)
+ goto err;
+ continue;
+ }
+ found_other_files++;
+ continue;
+ }
extension= fn_ext(file->name);
if (find_type(extension, &deletable_extentions,1+2) <= 0)
{
@@ -756,7 +774,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
table_list->alias= table_list->real_name; // If lower_case_table_names=2
/* Link into list */
(*tot_list_next)= table_list;
- tot_list_next= &table_list->next;
+ tot_list_next= &table_list->next_local;
deleted++;
}
else
@@ -793,44 +811,140 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
}
else
{
- char tmp_path[FN_REFLEN], *pos;
- char *path= tmp_path;
- unpack_filename(tmp_path,org_path);
+ /* Don't give errors if we can't delete 'RAID' directory */
+ if (rm_dir_w_symlink(org_path, level == 0))
+ DBUG_RETURN(-1);
+ }
+
+ DBUG_RETURN(deleted);
+
+err:
+ my_dirend(dirp);
+ DBUG_RETURN(-1);
+}
+
+
+/*
+ Remove directory with symlink
+
+ SYNOPSIS
+ rm_dir_w_symlink()
+ org_path path of derictory
+ send_error send errors
+ RETURN
+ 0 OK
+ 1 ERROR
+*/
+
+static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error)
+{
+ char tmp_path[FN_REFLEN], tmp2_path[FN_REFLEN], *pos;
+ char *path= tmp_path;
+ DBUG_ENTER("rm_dir_w_symlink");
+ unpack_filename(tmp_path, org_path);
#ifdef HAVE_READLINK
- int error;
-
- /* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */
- pos=strend(path);
- if (pos > path && pos[-1] == FN_LIBCHAR)
- *--pos=0;
+ int error;
- if ((error=my_readlink(filePath, path, MYF(MY_WME))) < 0)
- DBUG_RETURN(-1);
- if (!error)
+ /* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */
+ pos= strend(path);
+ if (pos > path && pos[-1] == FN_LIBCHAR)
+ *--pos=0;
+
+ if ((error= my_readlink(tmp2_path, path, MYF(MY_WME))) < 0)
+ DBUG_RETURN(1);
+ if (!error)
+ {
+ if (my_delete(path, MYF(send_error ? MY_WME : 0)))
{
- if (my_delete(path,MYF(!level ? MY_WME : 0)))
- {
- /* Don't give errors if we can't delete 'RAID' directory */
- if (level)
- DBUG_RETURN(deleted);
- DBUG_RETURN(-1);
- }
- /* Delete directory symbolic link pointed at */
- path= filePath;
+ DBUG_RETURN(send_error);
}
+ /* Delete directory symbolic link pointed at */
+ path= tmp2_path;
+ }
#endif
- /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */
- pos=strend(path);
+ /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */
+ pos= strend(path);
- if (pos > path && pos[-1] == FN_LIBCHAR)
- *--pos=0;
- /* Don't give errors if we can't delete 'RAID' directory */
- if (rmdir(path) < 0 && !level)
+ if (pos > path && pos[-1] == FN_LIBCHAR)
+ *--pos=0;
+ if (rmdir(path) < 0 && send_error)
+ {
+ my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno);
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Remove .frm archives from directory
+
+ SYNOPSIS
+ thd thread handler
+ dirp list of files in archive directory
+ db data base name
+ org_path path of archive directory
+
+ RETURN
+ > 0 number of removed files
+ -1 error
+*/
+static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp,
+ const char *org_path)
+{
+ long deleted= 0;
+ ulong found_other_files= 0;
+ char filePath[FN_REFLEN];
+ DBUG_ENTER("mysql_rm_arc_files");
+ DBUG_PRINT("enter", ("path: %s", org_path));
+
+ for (uint idx=0 ;
+ idx < (uint) dirp->number_off_files && !thd->killed ;
+ idx++)
+ {
+ FILEINFO *file=dirp->dir_entry+idx;
+ char *extension, *revision;
+ DBUG_PRINT("info",("Examining: %s", file->name));
+
+ /* skiping . and .. */
+ if (file->name[0] == '.' && (!file->name[1] ||
+ (file->name[1] == '.' && !file->name[2])))
+ continue;
+
+ extension= fn_ext(file->name);
+ if (extension[0] != '.' ||
+ extension[1] != 'f' || extension[2] != 'r' ||
+ extension[3] != 'm' || extension[4] != '-')
{
- my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno);
- DBUG_RETURN(-1);
+ found_other_files++;
+ continue;
+ }
+ revision= extension+5;
+ while (*revision && my_isdigit(system_charset_info, *revision))
+ revision++;
+ if (*revision)
+ {
+ found_other_files++;
+ continue;
+ }
+ strxmov(filePath, org_path, "/", file->name, NullS);
+ if (my_delete_with_symlink(filePath,MYF(MY_WME)))
+ {
+ goto err;
}
}
+ if (thd->killed)
+ goto err;
+
+ my_dirend(dirp);
+
+ /*
+ If the directory is a symbolic link, remove the link first, then
+ remove the directory the symbolic link pointed at
+ */
+ if (!found_other_files &&
+ rm_dir_w_symlink(org_path, 0))
+ DBUG_RETURN(-1);
DBUG_RETURN(deleted);
err:
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index ffeeb98488a..05b2de8adfd 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -88,6 +88,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
{
delete select;
free_underlaid_joins(thd, &thd->lex->select_lex);
+ thd->row_count_func= 0;
send_ok(thd,0L);
DBUG_RETURN(0); // Nothing to delete
}
@@ -142,6 +143,13 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
select= 0;
}
+ /* If quick select is used, initialize it before retrieving rows. */
+ if (select && select->quick && select->quick->reset())
+ {
+ delete select;
+ free_underlaid_joins(thd, &thd->lex->select_lex);
+ DBUG_RETURN(-1); // This will force out message
+ }
init_read_record(&info,thd,table,select,1,1);
deleted=0L;
init_ftfuncs(thd, &thd->lex->select_lex, 1);
@@ -210,7 +218,6 @@ cleanup:
*/
if ((deleted || (error < 0)) && (error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
if (error <= 0)
@@ -236,9 +243,10 @@ cleanup:
}
free_underlaid_joins(thd, &thd->lex->select_lex);
if (error >= 0 || thd->net.report_error)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN: 0);
+ send_error(thd,thd->killed_errno());
else
{
+ thd->row_count_func= deleted;
send_ok(thd,deleted);
DBUG_PRINT("info",("%d records deleted",deleted));
}
@@ -252,7 +260,7 @@ cleanup:
SYNOPSIS
mysql_prepare_delete()
thd - thread handler
- table_list - global table list
+ table_list - global/local table list
conds - conditions
RETURN VALUE
@@ -262,19 +270,25 @@ cleanup:
*/
int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
{
- TABLE_LIST *delete_table_list= ((TABLE_LIST*) thd->lex->
- select_lex.table_list.first);
+ SELECT_LEX *select_lex= &thd->lex->select_lex;
DBUG_ENTER("mysql_prepare_delete");
- if (setup_conds(thd, delete_table_list, conds) ||
- setup_ftfuncs(&thd->lex->select_lex))
+ if (setup_tables(thd, table_list, conds) ||
+ setup_conds(thd, table_list, conds) ||
+ setup_ftfuncs(select_lex))
+ DBUG_RETURN(-1);
+ if (!table_list->updatable || check_key_in_view(thd, table_list))
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
DBUG_RETURN(-1);
- if (find_real_table_in_list(table_list->next,
+ }
+ if (find_table_in_global_list(table_list->next_global,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
+ select_lex->fix_prepare_information(thd, conds);
DBUG_RETURN(0);
}
@@ -285,12 +299,79 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
-extern "C" int refposcmp2(void* arg, const void *a,const void *b)
+extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b)
{
- /* arg is a pointer to file->ref_length */
- return memcmp(a,b, *(int*) arg);
+ handler *file= (handler*)arg;
+ return file->cmp_ref((const byte*)a, (const byte*)b);
}
+/*
+ make delete specific preparation and checks after opening tables
+
+ SYNOPSIS
+ mysql_multi_delete_prepare()
+ thd thread handler
+
+ RETURN
+ 0 OK
+ -1 Error
+*/
+
+int mysql_multi_delete_prepare(THD *thd)
+{
+ LEX *lex= thd->lex;
+ TABLE_LIST *aux_tables= (TABLE_LIST *)lex->auxilliary_table_list.first;
+ TABLE_LIST *target_tbl;
+ int res= 0;
+ DBUG_ENTER("mysql_multi_delete_prepare");
+
+ /*
+ setup_tables() need for VIEWs. JOIN::prepare() will not do it second
+ time.
+
+ lex->query_tables also point on local list of DELETE SELECT_LEX
+ */
+ if (setup_tables(thd, lex->query_tables, &lex->select_lex.where))
+ DBUG_RETURN(-1);
+
+ /* Fix tables-to-be-deleted-from list to point at opened tables */
+ for (target_tbl= (TABLE_LIST*) aux_tables;
+ target_tbl;
+ target_tbl= target_tbl->next_local)
+ {
+ target_tbl->table= target_tbl->correspondent_table->table;
+ if (!target_tbl->correspondent_table->updatable ||
+ check_key_in_view(thd, target_tbl->correspondent_table))
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
+ "DELETE");
+ DBUG_RETURN(-1);
+ }
+ /*
+ Check are deleted table used somewhere inside subqueries.
+
+ Multi-delete can't be constructed over-union => we always have
+ single SELECT on top and have to check underlaying SELECTs of it
+ */
+ for (SELECT_LEX_UNIT *un= lex->select_lex.first_inner_unit();
+ un;
+ un= un->next_unit())
+ {
+ if (un->first_select()->linkage != DERIVED_TABLE_TYPE &&
+ un->check_updateable(target_tbl->correspondent_table->db,
+ target_tbl->correspondent_table->real_name))
+ {
+ my_error(ER_UPDATE_TABLE_USED, MYF(0),
+ target_tbl->correspondent_table->real_name);
+ res= -1;
+ break;
+ }
+ }
+ }
+ DBUG_RETURN(res);
+}
+
+
multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
uint num_of_tables_arg)
: delete_tables(dt), thd(thd_arg), deleted(0), found(0),
@@ -323,7 +404,7 @@ multi_delete::initialize_tables(JOIN *join)
DBUG_RETURN(1);
table_map tables_to_delete_from=0;
- for (walk= delete_tables ; walk ; walk=walk->next)
+ for (walk= delete_tables; walk; walk= walk->next_local)
tables_to_delete_from|= walk->table->map;
walk= delete_tables;
@@ -335,7 +416,7 @@ multi_delete::initialize_tables(JOIN *join)
{
/* We are going to delete from this table */
TABLE *tbl=walk->table=tab->table;
- walk=walk->next;
+ walk= walk->next_local;
/* Don't use KEYREAD optimization on this table */
tbl->no_keyread=1;
tbl->used_keys.clear_all();
@@ -349,11 +430,11 @@ multi_delete::initialize_tables(JOIN *join)
}
walk= delete_tables;
tempfiles_ptr= tempfiles;
- for (walk=walk->next ; walk ; walk=walk->next)
+ for (walk= walk->next_local ;walk ;walk= walk->next_local)
{
TABLE *table=walk->table;
- *tempfiles_ptr++= new Unique (refposcmp2,
- (void *) &table->file->ref_length,
+ *tempfiles_ptr++= new Unique (refpos_order_cmp,
+ (void *) table->file,
table->file->ref_length,
MEM_STRIP_BUF_SIZE);
}
@@ -364,9 +445,9 @@ multi_delete::initialize_tables(JOIN *join)
multi_delete::~multi_delete()
{
- for (table_being_deleted=delete_tables ;
- table_being_deleted ;
- table_being_deleted=table_being_deleted->next)
+ for (table_being_deleted= delete_tables;
+ table_being_deleted;
+ table_being_deleted= table_being_deleted->next_local)
{
TABLE *t=table_being_deleted->table;
free_io_cache(t); // Alloced by unique
@@ -386,9 +467,9 @@ bool multi_delete::send_data(List<Item> &values)
int secure_counter= -1;
DBUG_ENTER("multi_delete::send_data");
- for (table_being_deleted=delete_tables ;
- table_being_deleted ;
- table_being_deleted=table_being_deleted->next, secure_counter++)
+ for (table_being_deleted= delete_tables;
+ table_being_deleted;
+ table_being_deleted= table_being_deleted->next_local, secure_counter++)
{
TABLE *table=table_being_deleted->table;
@@ -405,7 +486,8 @@ bool multi_delete::send_data(List<Item> &values)
table->status|= STATUS_DELETED;
if (!(error=table->file->delete_row(table->record[0])))
deleted++;
- else if (!table_being_deleted->next || table_being_deleted->table->file->has_transactions())
+ else if (!table_being_deleted->next_local ||
+ table_being_deleted->table->file->has_transactions())
{
table->file->print_error(error,MYF(0));
DBUG_RETURN(1);
@@ -475,9 +557,9 @@ int multi_delete::do_deletes(bool from_send_error)
if (from_send_error)
{
/* Found out table number for 'table_being_deleted*/
- for (TABLE_LIST *aux=delete_tables;
+ for (TABLE_LIST *aux= delete_tables;
aux != table_being_deleted;
- aux=aux->next)
+ aux= aux->next_local)
counter++;
}
else
@@ -486,9 +568,9 @@ int multi_delete::do_deletes(bool from_send_error)
do_delete= 0;
if (!found)
DBUG_RETURN(0);
- for (table_being_deleted=table_being_deleted->next;
- table_being_deleted ;
- table_being_deleted=table_being_deleted->next, counter++)
+ for (table_being_deleted= table_being_deleted->next_local;
+ table_being_deleted;
+ table_being_deleted= table_being_deleted->next_local, counter++)
{
TABLE *table = table_being_deleted->table;
if (tempfiles[counter]->get(table))
@@ -545,7 +627,9 @@ bool multi_delete::send_eof()
ha_autocommit_...
*/
if (deleted)
+ {
query_cache_invalidate3(thd, delete_tables, 1);
+ }
/*
Write the SQL statement to the binlog if we deleted
@@ -557,7 +641,6 @@ bool multi_delete::send_eof()
*/
if (deleted && (error <= 0 || normal_tables))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
if (error <= 0)
@@ -578,7 +661,10 @@ bool multi_delete::send_eof()
if (local_error)
::send_error(thd);
else
+ {
+ thd->row_count_func= deleted;
::send_ok(thd, deleted);
+ }
return 0;
}
@@ -664,7 +750,6 @@ end:
{
if (!error)
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 30b06e91082..3137890f2ba 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -52,15 +52,17 @@ mysql_handle_derived(LEX *lex)
{
for (TABLE_LIST *cursor= sl->get_table_list();
cursor;
- cursor= cursor->next)
+ cursor= cursor->next_local)
{
int res;
- if (cursor->derived && (res=mysql_derived(lex->thd, lex,
- cursor->derived,
- cursor)))
+ if (cursor->derived && (res= mysql_derived(lex->thd, lex,
+ cursor->derived,
+ cursor)))
{
return res;
}
+ else if (cursor->ancestor)
+ cursor->set_ancestor();
}
if (lex->describe)
{
@@ -148,10 +150,11 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
derived_result->set_table(table);
/*
- if it is preparation PS only then we do not need real data and we
- can skip execution (and parameters is not defined, too)
+ if it is preparation PS only or commands that need only VIEW structure
+ then we do not need real data and we can skip execution (and parameters
+ is not defined, too)
*/
- if (! thd->current_arena->is_stmt_prepare())
+ if (!thd->only_prepare() && !lex->only_view_structure())
{
if (is_union)
{
@@ -197,11 +200,6 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
{
org_table_list->real_name= table->real_name;
org_table_list->table= table;
- if (org_table_list->table_list)
- {
- org_table_list->table_list->real_name= table->real_name;
- org_table_list->table_list->table= table;
- }
table->derived_select_number= first_select->select_number;
table->tmp_table= TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index c94d5e52bcf..d68d62a8820 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -43,6 +43,7 @@ This file contains the implementation of error and warnings related
***********************************************************************/
#include "mysql_priv.h"
+#include "sp_rcontext.h"
/*
Store a new message in an error object
@@ -108,6 +109,9 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
MYSQL_ERROR *err= NULL;
+ if (thd->spcont && thd->spcont->find_handler(code))
+ DBUG_RETURN(NULL);
+
if (thd->warn_list.elements < thd->variables.max_error_count)
{
/*
@@ -181,7 +185,8 @@ my_bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE));
- if (thd->protocol->send_fields(&field_list,1))
+ if (thd->protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
MYSQL_ERROR *err;
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 56c1b0a1b51..0df3d617d7f 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -166,7 +166,7 @@ int mysql_ha_close_list(THD *thd, TABLE_LIST *tables, bool flushed)
if (tables)
{
- for (tl_item= tables ; tl_item; tl_item= tl_item->next)
+ for (tl_item= tables ; tl_item; tl_item= tl_item->next_local)
{
mysql_ha_close(thd, tl_item, /*dont_send_ok*/ 1,
/*dont_lock*/ 1, /*no_alias*/ 1);
@@ -194,13 +194,14 @@ int mysql_ha_close_list(THD *thd, TABLE_LIST *tables, bool flushed)
}
static enum enum_ha_read_modes rkey_to_rnext[]=
- { RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
+{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
int mysql_ha_read(THD *thd, TABLE_LIST *tables,
- enum enum_ha_read_modes mode, char *keyname, List<Item> *key_expr,
- enum ha_rkey_function ha_rkey_mode, Item *cond,
- ha_rows select_limit,ha_rows offset_limit)
+ enum enum_ha_read_modes mode, char *keyname,
+ List<Item> *key_expr,
+ enum ha_rkey_function ha_rkey_mode, Item *cond,
+ ha_rows select_limit,ha_rows offset_limit)
{
int err, keyno=-1;
bool was_flushed;
@@ -227,7 +228,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
if ((keyno=find_type(keyname, &table->keynames, 1+2)-1)<0)
{
my_printf_error(ER_KEY_DOES_NOT_EXITS,ER(ER_KEY_DOES_NOT_EXITS),MYF(0),
- keyname,tables->alias);
+ keyname,tables->alias);
return -1;
}
table->file->ha_index_or_rnd_end();
@@ -248,16 +249,16 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
it++; // Skip first NULL field
- insert_fields(thd,tables,tables->db,tables->alias,&it);
+ insert_fields(thd, tables, tables->db, tables->alias, &it, 0);
select_limit+=offset_limit;
- protocol->send_fields(&list,1);
+ protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
HANDLER_TABLES_HACK(thd);
MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1);
HANDLER_TABLES_HACK(thd);
if (!lock)
- goto err0; // mysql_lock_tables() printed error message already
+ goto err0; // mysql_lock_tables() printed error message already
/*
In ::external_lock InnoDB resets the fields which tell it that
@@ -290,7 +291,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
err=keyname ?
table->file->index_next(table->record[0]) :
table->file->rnd_next(table->record[0]);
- break;
+ break;
case RPREV:
DBUG_ASSERT(keyname != 0);
err=table->file->index_prev(table->record[0]);
@@ -442,6 +443,8 @@ static TABLE **find_table_ptr_by_name(THD *thd, const char *db,
{
if (!dont_lock)
VOID(pthread_mutex_lock(&LOCK_open));
+
+ table->file->ha_index_or_rnd_end();
if (close_thread_table(thd, table_ptr))
{
/* Tell threads waiting for refresh that something has happened */
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index eabe66d33bf..85d5271d4c3 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -84,12 +84,11 @@ static bool init_fields(THD *thd, TABLE_LIST *tables,
DBUG_ENTER("init_fields");
for (; count-- ; find_fields++)
{
- TABLE_LIST *not_used;
/* We have to use 'new' here as field will be re_linked on free */
Item_field *field= new Item_field("mysql", find_fields->table_name,
find_fields->field_name);
if (!(find_fields->field= find_field_in_tables(thd, field, tables,
- &not_used, TRUE)))
+ 0, TRUE, 1)))
DBUG_RETURN(1);
}
DBUG_RETURN(0);
@@ -427,7 +426,8 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3)
field_list.push_back(new Item_empty_string("description",1000));
field_list.push_back(new Item_empty_string("example",1000));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
protocol->prepare_for_resend();
@@ -469,7 +469,8 @@ int send_header_2(Protocol *protocol, bool for_category)
field_list.push_back(new Item_empty_string("source_category_name",64));
field_list.push_back(new Item_empty_string("name",64));
field_list.push_back(new Item_empty_string("is_it_category",1));
- DBUG_RETURN(protocol->send_fields(&field_list,1));
+ DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF));
}
/*
@@ -622,16 +623,15 @@ int mysqld_help(THD *thd, const char *mask)
bzero((gptr)tables,sizeof(tables));
tables[0].alias= tables[0].real_name= (char*) "help_topic";
tables[0].lock_type= TL_READ;
- tables[0].next= &tables[1];
+ tables[0].next_global= tables[0].next_local= &tables[1];
tables[1].alias= tables[1].real_name= (char*) "help_category";
tables[1].lock_type= TL_READ;
- tables[1].next= &tables[2];
+ tables[1].next_global= tables[1].next_local= &tables[2];
tables[2].alias= tables[2].real_name= (char*) "help_relation";
tables[2].lock_type= TL_READ;
- tables[2].next= &tables[3];
+ tables[2].next_global= tables[2].next_local= &tables[3];
tables[3].alias= tables[3].real_name= (char*) "help_keyword";
tables[3].lock_type= TL_READ;
- tables[3].next= 0;
tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql";
List<String> topics_list, categories_list, subcategories_list;
@@ -645,8 +645,12 @@ int mysqld_help(THD *thd, const char *mask)
res= -1;
goto end;
}
- /* Init tables and fields to be usable from items */
- setup_tables(tables);
+ /*
+ Init tables and fields to be usable from items
+
+ tables do not contain VIEWs => we can pass 0 as conds
+ */
+ setup_tables(thd, tables, 0);
memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields));
if (init_fields(thd, tables, used_fields, array_elements(used_fields)))
{
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 4cbd11c6a15..0e4f803536d 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -24,7 +24,7 @@ static int check_null_fields(THD *thd,TABLE *entry);
#ifndef EMBEDDED_LIBRARY
static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list);
static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup,
- char *query, uint query_length, int log_on);
+ char *query, uint query_length, bool log_on);
static void end_delayed_insert(THD *thd);
extern "C" pthread_handler_decl(handle_delayed_insert,arg);
static void unlink_blobs(register TABLE *table);
@@ -40,19 +40,18 @@ static void unlink_blobs(register TABLE *table);
#define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0))
#endif
-#define DELAYED_LOG_UPDATE 1
-#define DELAYED_LOG_BIN 2
-
/*
Check if insert fields are correct.
Sets table->timestamp_default_now/on_update_now to 0 o leaves it to point
to timestamp field, depending on if timestamp should be updated or not.
*/
-int
-check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
- List<Item> &values, ulong counter)
+static int
+check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields,
+ List<Item> &values, ulong counter, bool check_unique)
{
+ TABLE *table= table_list->table;
+
if (fields.elements == 0 && values.elements != 0)
{
if (values.elements != table->fields)
@@ -63,14 +62,22 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
return -1;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (grant_option &&
- check_grant_all_columns(thd,INSERT_ACL,table))
- return -1;
+ if (grant_option)
+ {
+ Field_iterator_table fields;
+ fields.set_table(table);
+ if (check_grant_all_columns(thd, INSERT_ACL, &table->grant,
+ table->table_cache_key, table->real_name,
+ &fields))
+ return -1;
+ }
#endif
table->timestamp_default_now= table->timestamp_on_update_now= 0;
}
else
{ // Part field list
+ TABLE_LIST *save_next;
+ int res;
if (fields.elements != values.elements)
{
my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
@@ -78,19 +85,18 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
MYF(0),counter);
return -1;
}
- TABLE_LIST table_list;
- bzero((char*) &table_list,sizeof(table_list));
- table_list.db= table->table_cache_key;
- table_list.real_name= table_list.alias= table->table_name;
- table_list.table=table;
- table_list.grant=table->grant;
thd->dupp_field=0;
- if (setup_tables(&table_list) ||
- setup_fields(thd, 0, &table_list,fields,1,0,0))
+ thd->lex->select_lex.no_wrap_view_item= 1;
+ save_next= table_list->next_local; // fields only from first table
+ table_list->next_local= 0;
+ res= setup_fields(thd, 0, table_list, fields, 1, 0, 0);
+ table_list->next_local= save_next;
+ thd->lex->select_lex.no_wrap_view_item= 0;
+ if (res)
return -1;
- if (thd->dupp_field)
+ if (check_unique && thd->dupp_field)
{
my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name);
return -1;
@@ -120,7 +126,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
By default, both logs are enabled (this won't cause problems if the server
runs without --log-update or --log-bin).
*/
- int log_on= DELAYED_LOG_UPDATE | DELAYED_LOG_BIN ;
+ bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->master_access & SUPER_ACL));
bool transactional_table, log_delayed;
uint value_count;
ulong counter = 1;
@@ -133,14 +139,8 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
char *query= thd->query;
#endif
thr_lock_type lock_type = table_list->lock_type;
- TABLE_LIST *insert_table_list= (TABLE_LIST*)
- thd->lex->select_lex.table_list.first;
DBUG_ENTER("mysql_insert");
- if (!(thd->options & OPTION_UPDATE_LOG))
- log_on&= ~(int) DELAYED_LOG_UPDATE;
- if (!(thd->options & OPTION_BIN_LOG))
- log_on&= ~(int) DELAYED_LOG_BIN;
/*
in safe mode or with skip-new change delayed insert to be regular
if we are told to replace duplicates, the insert cannot be concurrent
@@ -177,8 +177,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
if ((table= delayed_get_table(thd,table_list)) && !thd->is_fatal_error)
{
res= 0;
- if (table_list->next) /* if sub select */
- res= open_and_lock_tables(thd, table_list->next);
+ if (table_list->next_global) /* if sub select */
+ res= open_and_lock_tables(thd, table_list->next_global);
+ /*
+ First is not processed by open_and_lock_tables() => we need set
+ updateability flags "by hands".
+ */
+ if (!table_list->derived && !table_list->view)
+ table_list->updatable= 1; // usual table
}
else
{
@@ -201,17 +207,17 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
if (duplic == DUP_UPDATE && !table->insert_values)
{
/* it should be allocated before Item::fix_fields() */
- table->insert_values=
+ table->insert_values=
(byte *)alloc_root(&thd->mem_root, table->rec_buff_length);
if (!table->insert_values)
goto abort;
}
- if (mysql_prepare_insert(thd, table_list, insert_table_list, table,
- fields, values, update_fields,
- update_values, duplic))
+ if (mysql_prepare_insert(thd, table_list, table, fields, values,
+ update_fields, update_values, duplic))
goto abort;
+ // is table which we are changing used somewhere in other parts of query
value_count= values->elements;
while ((values= its++))
{
@@ -223,7 +229,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
MYF(0),counter);
goto abort;
}
- if (setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0))
+ if (setup_fields(thd, 0, table_list, *values, 0, 0, 0))
goto abort;
}
its.rewind ();
@@ -354,7 +360,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
before binlog writing and ha_autocommit_...
*/
if (info.copied || info.deleted || info.updated)
+ {
query_cache_invalidate3(thd, table_list, 1);
+ }
transactional_table= table->file->has_transactions();
@@ -362,7 +370,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
if ((info.copied || info.deleted || info.updated) &&
(error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
if (error <= 0)
@@ -401,7 +408,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
goto abort;
if (values_list.elements == 1 && (!(thd->options & OPTION_WARNINGS) ||
!thd->cuted_fields))
- send_ok(thd,info.copied+info.deleted+info.updated,id);
+ {
+ thd->row_count_func= info.copied+info.deleted+info.updated;
+ send_ok(thd, (ulong) thd->row_count_func, id);
+ }
else
{
char buff[160];
@@ -412,7 +422,8 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
else
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields);
- ::send_ok(thd,info.copied+info.deleted+info.updated,(ulonglong)id,buff);
+ thd->row_count_func= info.copied+info.deleted+info.updated;
+ ::send_ok(thd, (ulong) thd->row_count_func, id, buff);
}
free_underlaid_joins(thd, &thd->lex->select_lex);
table->insert_values=0;
@@ -430,38 +441,167 @@ abort:
/*
+ Additional check for insertability for VIEW
+
+ SYNOPSIS
+ check_view_insertability()
+ view - reference on VIEW
+
+ IMPLEMENTATION
+ A view is insertable if the folloings are true:
+ - All columns in the view are columns from a table
+ - All not used columns in table have a default values
+ - All field in view are unique (not referring to the same column)
+
+ RETURN
+ FALSE - OK
+ view->contain_auto_increment is 1 if and only if the view contains an
+ auto_increment field
+
+ TRUE - can't be used for insert
+*/
+
+static bool check_view_insertability(TABLE_LIST *view, ulong query_id)
+{
+ uint num= view->view->select_lex.item_list.elements;
+ TABLE *table= view->table;
+ Item **trans_start= view->field_translation, **trans_end=trans_start+num;
+ Item **trans;
+ Field **field_ptr= table->field;
+ ulong other_query_id= query_id - 1;
+ DBUG_ENTER("check_key_in_view");
+
+ DBUG_ASSERT(view->table != 0 && view->field_translation != 0);
+
+ view->contain_auto_increment= 0;
+ /* check simplicity and prepare unique test of view */
+ for (trans= trans_start; trans != trans_end; trans++)
+ {
+ Item_field *field;
+ /* simple SELECT list entry (field without expression) */
+ if ((*trans)->type() != Item::FIELD_ITEM)
+ DBUG_RETURN(TRUE);
+ field= (Item_field *)(*trans);
+ if (field->field->unireg_check == Field::NEXT_NUMBER)
+ view->contain_auto_increment= 1;
+ /* prepare unique test */
+ field->field->query_id= other_query_id;
+ }
+ /* unique test */
+ for (trans= trans_start; trans != trans_end; trans++)
+ {
+ /* Thanks to test above, we know that all columns are of type Item_field */
+ Item_field *field= (Item_field *)(*trans);
+ if (field->field->query_id == query_id)
+ DBUG_RETURN(TRUE);
+ field->field->query_id= query_id;
+ }
+
+ /* VIEW contain all fields without default value */
+ for (; *field_ptr; field_ptr++)
+ {
+ Field *field= *field_ptr;
+ /* field have not default value */
+ if ((field->type() == FIELD_TYPE_BLOB) &&
+ (table->timestamp_field != field ||
+ field->unireg_check == Field::TIMESTAMP_UN_FIELD))
+ {
+ for (trans= trans_start; ; trans++)
+ {
+ if (trans == trans_end)
+ DBUG_RETURN(TRUE); // Field was not part of view
+ if (((Item_field *)(*trans))->field == *field_ptr)
+ break; // ok
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Check if table can be updated
+
+ SYNOPSIS
+ mysql_prepare_insert_check_table()
+ thd Thread handle
+ table_list Table list (only one table)
+ fields List of fields to be updated
+ where Pointer to where clause
+
+ RETURN
+ 0 ok
+ 1 ERROR
+*/
+
+static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
+ List<Item> &fields, COND **where)
+{
+ bool insert_into_view= (table_list->view != 0);
+ DBUG_ENTER("mysql_prepare_insert_check_table");
+
+ if (setup_tables(thd, table_list, where))
+ DBUG_RETURN(1);
+
+ if (insert_into_view && !fields.elements)
+ {
+ thd->lex->empty_field_list_on_rset= 1;
+ insert_view_fields(&fields, table_list);
+ }
+
+ if (!table_list->updatable ||
+ check_key_in_view(thd, table_list) ||
+ (insert_into_view &&
+ check_view_insertability(table_list, thd->query_id)))
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT");
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
Prepare items in INSERT statement
SYNOPSIS
mysql_prepare_insert()
- thd - thread handler
- table_list - global table list
- insert_table_list - local table list of INSERT SELECT_LEX
+ thd Thread handler
+ table_list Global/local table list
RETURN VALUE
- 0 - OK
- -1 - error (message is not sent to user)
+ 0 OK
+ -1 error (message is not sent to user)
*/
-int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
- TABLE_LIST *insert_table_list, TABLE *table,
+
+int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
List<Item> &fields, List_item *values,
List<Item> &update_fields, List<Item> &update_values,
enum_duplicates duplic)
{
+ bool insert_into_view= (table_list->view != 0);
+ /* TODO: use this condition for 'WITH CHECK OPTION' */
+ Item *unused_conds= 0;
DBUG_ENTER("mysql_prepare_insert");
- if (check_insert_fields(thd, table, fields, *values, 1) ||
- setup_tables(insert_table_list) ||
- setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) ||
+
+ if (mysql_prepare_insert_check_table(thd, table_list, fields, &unused_conds))
+ DBUG_RETURN(-1);
+
+ if (check_insert_fields(thd, table_list, fields, *values, 1,
+ !insert_into_view) ||
+ setup_fields(thd, 0, table_list, *values, 0, 0, 0) ||
(duplic == DUP_UPDATE &&
- (setup_fields(thd, 0, insert_table_list, update_fields, 0, 0, 0) ||
- setup_fields(thd, 0, insert_table_list, update_values, 0, 0, 0))))
+ (setup_fields(thd, 0, table_list, update_fields, 0, 0, 0) ||
+ setup_fields(thd, 0, table_list, update_values, 0, 0, 0))))
DBUG_RETURN(-1);
- if (find_real_table_in_list(table_list->next,
+
+ if (find_table_in_global_list(table_list->next_global,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
+ thd->lex->select_lex.first_execution= 0;
DBUG_RETURN(0);
}
@@ -642,14 +782,13 @@ public:
char *record,*query;
enum_duplicates dup;
time_t start_time;
- bool query_start_used,last_insert_id_used,insert_id_used;
- int log_query;
+ bool query_start_used,last_insert_id_used,insert_id_used, log_query;
ulonglong last_insert_id;
ulong timestamp_default_now;
ulong timestamp_on_update_now;
uint query_length;
- delayed_row(enum_duplicates dup_arg, int log_query_arg)
+ delayed_row(enum_duplicates dup_arg, bool log_query_arg)
:record(0),query(0),dup(dup_arg),log_query(log_query_arg) {}
~delayed_row()
{
@@ -963,7 +1102,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
/* Put a question in queue */
static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic,
- char *query, uint query_length, int log_on)
+ char *query, uint query_length, bool log_on)
{
delayed_row *row=0;
delayed_insert *di=thd->di;
@@ -1044,7 +1183,7 @@ void kill_delayed_threads(void)
{
/* Ensure that the thread doesn't kill itself while we are looking at it */
pthread_mutex_lock(&tmp->mutex);
- tmp->thd.killed=1;
+ tmp->thd.killed= THD::KILL_CONNECTION;
if (tmp->thd.mysys_var)
{
pthread_mutex_lock(&tmp->thd.mysys_var->mutex);
@@ -1083,7 +1222,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
thd->thread_id=thread_id++;
thd->end_time();
threads.append(thd);
- thd->killed=abort_loop;
+ thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED;
pthread_mutex_unlock(&LOCK_thread_count);
pthread_mutex_lock(&di->mutex);
@@ -1136,7 +1275,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
for (;;)
{
- if (thd->killed)
+ if (thd->killed == THD::KILL_CONNECTION)
{
uint lock_count;
/*
@@ -1184,7 +1323,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
break;
if (error == ETIME || error == ETIMEDOUT)
{
- thd->killed=1;
+ thd->killed= THD::KILL_CONNECTION;
break;
}
}
@@ -1203,8 +1342,9 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
/* request for new delayed insert */
if (!(thd->lock=mysql_lock_tables(thd,&di->table,1)))
{
- di->dead= 1; // Some fatal error
- thd->killed= 1;
+ /* Fatal error */
+ di->dead= 1;
+ thd->killed= THD::KILL_CONNECTION;
}
pthread_cond_broadcast(&di->cond_client);
}
@@ -1212,8 +1352,9 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
{
if (di->handle_inserts())
{
- di->dead= 1; // Some fatal error
- thd->killed= 1;
+ /* Some fatal error */
+ di->dead= 1;
+ thd->killed= THD::KILL_CONNECTION;
}
}
di->status=0;
@@ -1243,7 +1384,7 @@ end:
close_thread_tables(thd); // Free the table
di->table=0;
di->dead= 1; // If error
- thd->killed= 1;
+ thd->killed= THD::KILL_CONNECTION; // If error
pthread_cond_broadcast(&di->cond_client); // Safety
pthread_mutex_unlock(&di->mutex);
@@ -1312,7 +1453,7 @@ bool delayed_insert::handle_inserts(void)
max_rows=delayed_insert_limit;
if (thd.killed || table->version != refresh_version)
{
- thd.killed=1;
+ thd.killed= THD::KILL_CONNECTION;
max_rows= ~0; // Do as much as possible
}
@@ -1357,15 +1498,10 @@ bool delayed_insert::handle_inserts(void)
using_ignore=0;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
}
- if (row->query)
+ if (row->query && row->log_query && using_bin_log)
{
- if (row->log_query & DELAYED_LOG_UPDATE)
- mysql_update_log.write(&thd,row->query, row->query_length);
- if (row->log_query & DELAYED_LOG_BIN && using_bin_log)
- {
- Query_log_event qinfo(&thd, row->query, row->query_length,0);
- mysql_bin_log.write(&qinfo);
- }
+ Query_log_event qinfo(&thd, row->query, row->query_length,0);
+ mysql_bin_log.write(&qinfo);
}
if (table->blob_fields)
free_delayed_insert_blobs(table);
@@ -1381,7 +1517,7 @@ bool delayed_insert::handle_inserts(void)
on this table until all entries has been processed
*/
if (group_count++ >= max_rows && (row= rows.head()) &&
- (!(row->log_query & DELAYED_LOG_BIN && using_bin_log) ||
+ (!(row->log_query & using_bin_log) ||
row->query))
{
group_count=0;
@@ -1445,13 +1581,39 @@ bool delayed_insert::handle_inserts(void)
Store records in INSERT ... SELECT *
***************************************************************************/
+
+/*
+ make insert specific preparation and checks after opening tables
+
+ SYNOPSIS
+ mysql_insert_select_prepare()
+ thd thread handler
+
+ RETURN
+ 0 OK
+ -1 Error
+*/
+
+int mysql_insert_select_prepare(THD *thd)
+{
+ LEX *lex= thd->lex;
+ DBUG_ENTER("mysql_insert_select_prepare");
+ if (mysql_prepare_insert_check_table(thd, lex->query_tables,
+ lex->field_list,
+ &lex->select_lex.where))
+ DBUG_RETURN(-1);
+ DBUG_RETURN(0);
+}
+
+
int
select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
{
DBUG_ENTER("select_insert::prepare");
unit= u;
- if (check_insert_fields(thd,table,*fields,values,1))
+ if (check_insert_fields(thd, table_list, *fields, values, 1,
+ !insert_into_view))
DBUG_RETURN(1);
restore_record(table,default_values); // Get empty record
@@ -1534,7 +1696,6 @@ void select_insert::send_error(uint errcode,const char *err)
{
if (last_insert_id)
thd->insert_id(last_insert_id); // For binary log
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@@ -1545,7 +1706,9 @@ void select_insert::send_error(uint errcode,const char *err)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
}
if (info.copied || info.deleted || info.updated)
+ {
query_cache_invalidate3(thd, table, 1);
+ }
ha_rollback_stmt(thd);
DBUG_VOID_RETURN;
}
@@ -1574,7 +1737,6 @@ bool select_insert::send_eof()
if (last_insert_id)
thd->insert_id(last_insert_id); // For binary log
/* Write to binlog before commiting transaction */
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
if (!error)
@@ -1599,7 +1761,8 @@ bool select_insert::send_eof()
else
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields);
- ::send_ok(thd,info.copied+info.deleted+info.updated,last_insert_id,buff);
+ thd->row_count_func= info.copied+info.deleted+info.updated;
+ ::send_ok(thd, (ulong) thd->row_count_func, last_insert_id, buff);
DBUG_RETURN(0);
}
@@ -1614,7 +1777,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_ENTER("select_create::prepare");
unit= u;
- table= create_table_from_items(thd, create_info, db, name,
+ table= create_table_from_items(thd, create_info, create_table,
extra_fields, keys, &values, &lock);
if (!table)
DBUG_RETURN(-1); // abort() deletes table
@@ -1706,10 +1869,10 @@ void select_create::abort()
{
hash_delete(&open_cache,(byte*) table);
if (!create_info->table_existed)
- quick_rm_table(table_type, db, name);
+ quick_rm_table(table_type, create_table->db, create_table->real_name);
}
else if (!create_info->table_existed)
- close_temporary_table(thd, db, name);
+ close_temporary_table(thd, create_table->db, create_table->real_name);
table=0;
}
VOID(pthread_mutex_unlock(&LOCK_open));
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index fab047002ad..b0707955522 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -21,6 +21,8 @@
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
+#include "sp.h"
+#include "sp_head.h"
/*
@@ -119,7 +121,8 @@ void lex_start(THD *thd, uchar *buf,uint length)
LEX *lex= thd->lex;
lex->thd= thd;
lex->next_state=MY_LEX_START;
- lex->end_of_query=(lex->ptr=buf)+length;
+ lex->buf= lex->ptr= buf;
+ lex->end_of_query=buf+length;
lex->yylineno = 1;
lex->in_comment=0;
lex->length=0;
@@ -134,6 +137,14 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
lex->sql_command=SQLCOM_END;
lex->duplicates= DUP_ERROR;
+ lex->sphead= NULL;
+ lex->spcont= NULL;
+
+ extern byte *sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first);
+ hash_free(&lex->spfuns);
+ hash_init(&lex->spfuns, system_charset_info, 0, 0, 0,
+ sp_lex_spfuns_key, 0, 0);
+
}
void lex_end(LEX *lex)
@@ -156,27 +167,6 @@ static int find_keyword(LEX *lex, uint len, bool function)
lex->yylval->symbol.length=len;
return symbol->tok;
}
-#ifdef HAVE_DLOPEN
- udf_func *udf;
- if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))
- {
- lex->safe_to_cache_query=0;
- lex->yylval->udf=udf;
- switch (udf->returns) {
- case STRING_RESULT:
- return (udf->type == UDFTYPE_FUNCTION) ? UDF_CHAR_FUNC : UDA_CHAR_SUM;
- case REAL_RESULT:
- return (udf->type == UDFTYPE_FUNCTION) ? UDF_FLOAT_FUNC : UDA_FLOAT_SUM;
- case INT_RESULT:
- return (udf->type == UDFTYPE_FUNCTION) ? UDF_INT_FUNC : UDA_INT_SUM;
- case ROW_RESULT:
- default:
- // This case should never be choosen
- DBUG_ASSERT(0);
- return 0;
- }
- }
-#endif
return 0;
}
@@ -261,7 +251,8 @@ static char *get_text(LEX *lex)
continue;
}
#endif
- if (c == '\\')
+ if (c == '\\' &&
+ !(lex->thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
{ // Escaped character
found_escape=1;
if (lex->ptr == lex->end_of_query)
@@ -1000,24 +991,31 @@ void st_select_lex_unit::init_query()
cleaned= 0;
item_list.empty();
describe= 0;
+ found_rows_for_union= 0;
}
void st_select_lex::init_query()
{
st_select_lex_node::init_query();
table_list.empty();
+ top_join_list.empty();
+ join_list= &top_join_list;
+ embedding= 0;
item_list.empty();
join= 0;
- where= 0;
+ where= prep_where= 0;
olap= UNSPECIFIED_OLAP_TYPE;
having_fix_field= 0;
resolve_mode= NOMATTER_MODE;
cond_count= with_wild= 0;
+ conds_processed_with_permanent_arena= 0;
ref_pointer_array= 0;
select_n_having_items= 0;
- prep_where= 0;
subquery_in_having= explicit_limit= 0;
- parsing_place= NO_MATTER;
+ first_execution= 1;
+ first_cond_optimization= 1;
+ parsing_place= SELECT_LEX_NODE::NO_MATTER;
+ no_wrap_view_item= 0;
}
void st_select_lex::init_select()
@@ -1279,147 +1277,6 @@ bool st_select_lex::test_limit()
return(0);
}
-/*
- Interface method of table list creation for query
-
- SYNOPSIS
- st_select_lex_unit::create_total_list()
- thd THD pointer
- result pointer on result list of tables pointer
- check_derived force derived table chacking (used for creating
- table list for derived query)
- DESCRIPTION
- This is used for UNION & subselect to create a new table list of all used
- tables.
- The table_list->table entry in all used tables are set to point
- to the entries in this list.
-
- RETURN
- 0 - OK
- !0 - error
-*/
-bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex,
- TABLE_LIST **result_arg)
-{
- *result_arg= 0;
- if (!(res= create_total_list_n_last_return(thd_arg, lex, &result_arg)))
- {
- /*
- If time zone tables were used implicitly in statement we should add
- them to global table list.
- */
- if (lex->time_zone_tables_used)
- {
- /*
- Altough we are modifying lex data, it won't raise any problem in
- case when this lex belongs to some prepared statement or stored
- procedure: such modification does not change any invariants imposed
- by requirement to reuse the same lex for multiple executions.
- */
- if ((lex->time_zone_tables_used= my_tz_get_table_list(thd)) !=
- &fake_time_zone_tables_list)
- {
- *result_arg= lex->time_zone_tables_used;
- }
- else
- {
- send_error(thd, 0);
- res= 1;
- }
- }
- }
- return res;
-}
-
-/*
- Table list creation for query
-
- SYNOPSIS
- st_select_lex_unit::create_total_list()
- thd THD pointer
- lex pointer on LEX stricture
- result pointer on pointer on result list of tables pointer
-
- DESCRIPTION
- This is used for UNION & subselect to create a new table list of all used
- tables.
- The table_list->table_list in all tables of global list are set to point
- to the local SELECT_LEX entries.
-
- RETURN
- 0 - OK
- !0 - error
-*/
-bool st_select_lex_unit::
-create_total_list_n_last_return(THD *thd_arg,
- st_lex *lex,
- TABLE_LIST ***result_arg)
-{
- TABLE_LIST *slave_list_first=0, **slave_list_last= &slave_list_first;
- TABLE_LIST **new_table_list= *result_arg, *aux;
- SELECT_LEX *sl= (SELECT_LEX*)slave;
-
- /*
- iterate all inner selects + fake_select (if exists),
- fake_select->next_select() always is 0
- */
- for (;
- sl;
- sl= (sl->next_select() ?
- sl->next_select() :
- (sl == fake_select_lex ?
- 0 :
- fake_select_lex)))
- {
- // check usage of ORDER BY in union
- if (sl->order_list.first && sl->next_select() && !sl->braces &&
- sl->linkage != GLOBAL_OPTIONS_TYPE)
- {
- net_printf(thd_arg,ER_WRONG_USAGE,"UNION","ORDER BY");
- return 1;
- }
-
- for (SELECT_LEX_UNIT *inner= sl->first_inner_unit();
- inner;
- inner= inner->next_unit())
- {
- if (inner->create_total_list_n_last_return(thd, lex,
- &slave_list_last))
- return 1;
- }
-
- if ((aux= (TABLE_LIST*) sl->table_list.first))
- {
- TABLE_LIST *next_table;
- for (; aux; aux= next_table)
- {
- TABLE_LIST *cursor;
- next_table= aux->next;
- /* Add to the total table list */
- if (!(cursor= (TABLE_LIST *) thd->memdup((char*) aux,
- sizeof(*aux))))
- {
- send_error(thd,0);
- return 1;
- }
- *new_table_list= cursor;
- cursor->table_list= aux;
- new_table_list= &cursor->next;
- *new_table_list= 0; // end result list
- aux->table_list= cursor;
- }
- }
- }
-
- if (slave_list_first)
- {
- *new_table_list= slave_list_first;
- new_table_list= slave_list_last;
- }
- *result_arg= new_table_list;
- return 0;
-}
-
st_select_lex_unit* st_select_lex_unit::master_unit()
{
@@ -1441,7 +1298,9 @@ bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc)
bool st_select_lex::add_item_to_list(THD *thd, Item *item)
{
- return item_list.push_back(item);
+ DBUG_ENTER("st_select_lex::add_item_to_list");
+ DBUG_PRINT("info", ("Item: %p", item));
+ DBUG_RETURN(item_list.push_back(item));
}
@@ -1529,10 +1388,10 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
*/
Item_arena *arena= thd->current_arena;
return (ref_pointer_array=
- (Item **)arena->alloc(sizeof(Item*) *
- (item_list.elements +
- select_n_having_items +
- order_group_num)* 5)) == 0;
+ (Item **)arena->alloc(sizeof(Item*) *
+ (item_list.elements +
+ select_n_having_items +
+ order_group_num)* 5)) == 0;
}
@@ -1572,7 +1431,7 @@ bool st_select_lex_unit::check_updateable(char *db, char *table)
*/
bool st_select_lex::check_updateable(char *db, char *table)
{
- if (find_real_table_in_list(get_table_list(), db, table))
+ if (find_table_in_local_list(get_table_list(), db, table))
return 1;
for (SELECT_LEX_UNIT *un= first_inner_unit();
@@ -1632,6 +1491,17 @@ void st_select_lex::print_order(String *str, ORDER *order)
void st_select_lex::print_limit(THD *thd, String *str)
{
+ SELECT_LEX_UNIT *unit= master_unit();
+ Item_subselect *item= unit->item;
+ if (item && unit->global_parameters == this &&
+ (item->substype() == Item_subselect::EXISTS_SUBS ||
+ item->substype() == Item_subselect::IN_SUBS ||
+ item->substype() == Item_subselect::ALL_SUBS))
+ {
+ DBUG_ASSERT(select_limit == 1L && offset_limit == 0L);
+ return;
+ }
+
if (!thd)
thd= current_thd;
@@ -1653,6 +1523,142 @@ void st_select_lex::print_limit(THD *thd, String *str)
}
+/*
+ Check is merging algorithm can be used on this VIEW
+
+ SYNOPSIS
+ st_lex::can_be_merged()
+
+ RETURN
+ FALSE - only temporary table algorithm can be used
+ TRUE - merge algorithm can be used
+*/
+
+bool st_lex::can_be_merged()
+{
+ // TODO: do not forget implement case when select_lex.table_list.elements==0
+
+ /* find non VIEW subqueries/unions */
+ uint selects= 0;
+ for (SELECT_LEX *sl= all_selects_list;
+ sl && selects <= 1;
+ sl= sl->next_select_in_list())
+ if (sl->parent_lex == this)
+ selects++;
+
+ return (selects <= 1 &&
+ select_lex.order_list.elements == 0 &&
+ select_lex.group_list.elements == 0 &&
+ select_lex.having == 0 &&
+ select_lex.with_sum_func == 0 &&
+ select_lex.table_list.elements == 1 &&
+ !(select_lex.options & SELECT_DISTINCT) &&
+ select_lex.select_limit == HA_POS_ERROR);
+}
+
+/*
+ check if command can use VIEW with MERGE algorithm (for top VIEWs)
+
+ SYNOPSIS
+ st_lex::can_use_merged()
+
+ RETURN
+ FALSE - command can't use merged VIEWs
+ TRUE - VIEWs with MERGE algorithms can be used
+*/
+
+bool st_lex::can_use_merged()
+{
+ switch (sql_command)
+ {
+ case SQLCOM_SELECT:
+ case SQLCOM_CREATE_TABLE:
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_INSERT:
+ case SQLCOM_INSERT_SELECT:
+ case SQLCOM_REPLACE:
+ case SQLCOM_REPLACE_SELECT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ check if command can't use merged views in any part of command
+
+ SYNOPSIS
+ st_lex::can_not_use_merged()
+
+ RETURN
+ FALSE - command can't use merged VIEWs
+ TRUE - VIEWs with MERGE algorithms can be used
+*/
+
+bool st_lex::can_not_use_merged()
+{
+ switch (sql_command)
+ {
+ case SQLCOM_CREATE_VIEW:
+ case SQLCOM_SHOW_CREATE:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ Detect that we need only table structure of derived table/view
+
+ SYNOPSIS
+ only_view_structure()
+
+ RETURN
+ TRUE yes, we need only structure
+ FALSE no, we need data
+*/
+bool st_lex::only_view_structure()
+{
+ switch(sql_command)
+ {
+ case SQLCOM_SHOW_CREATE:
+ case SQLCOM_SHOW_TABLES:
+ case SQLCOM_SHOW_FIELDS:
+ case SQLCOM_REVOKE_ALL:
+ case SQLCOM_REVOKE:
+ case SQLCOM_GRANT:
+ case SQLCOM_CREATE_VIEW:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+/*
+ initialize limit counters
+
+ SYNOPSIS
+ st_select_lex_unit::set_limit()
+ values - SELECT_LEX with initial values for counters
+ sl - SELECT_LEX for options set
+*/
+
+void st_select_lex_unit::set_limit(SELECT_LEX *values,
+ SELECT_LEX *sl)
+{
+ offset_limit_cnt= values->offset_limit;
+ select_limit_cnt= values->select_limit+values->offset_limit;
+ if (select_limit_cnt < values->select_limit)
+ select_limit_cnt= HA_POS_ERROR; // no limit
+ if (select_limit_cnt == HA_POS_ERROR)
+ sl->options&= ~OPTION_FOUND_ROWS;
+}
+
+
st_lex::st_lex()
:result(0)
{}
@@ -1664,35 +1670,85 @@ st_lex::st_lex()
SYNOPSIS
unlink_first_table()
- tables Global table list
- global_first Save first global table here
- local_first Save first local table here
+ link_to_local Set to 1 if caller should link this table to local
- NORES
- global_first & local_first are used to save result for link_first_table_back
+ NOTES
+ We rely on fact that first table in both list are same or local list
+ is empty
RETURN
- global list without first table
+ 0 If 'query_tables' == 0
+ unlinked table
+ In this case link_to_local is set.
*/
-TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables,
- TABLE_LIST **global_first,
- TABLE_LIST **local_first)
+TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local)
{
- *global_first= tables;
- *local_first= (TABLE_LIST*)select_lex.table_list.first;
- /*
- Exclude from global table list
- */
- tables= tables->next;
- /*
- and from local list if it is not the same
- */
- select_lex.table_list.first= ((&select_lex != all_selects_list) ?
- (byte*) (*local_first)->next :
- (byte*) tables);
- (*global_first)->next= 0;
- return tables;
+ TABLE_LIST *first;
+ if ((first= query_tables))
+ {
+ /*
+ Exclude from global table list
+ */
+ if ((query_tables= query_tables->next_global))
+ query_tables->prev_global= &query_tables;
+ first->next_global= 0;
+
+ /*
+ and from local list if it is not empty
+ */
+ if ((*link_to_local= test(select_lex.table_list.first)))
+ {
+ select_lex.table_list.first= (byte*) first->next_local;
+ select_lex.table_list.elements--; //safety
+ first->next_local= 0;
+ /*
+ reorder global list to keep first tables the same in both lists
+ (if it is need)
+ */
+ first_lists_tables_same();
+ }
+ }
+ return first;
+}
+
+
+/*
+ Bring first local table of first most outer select to first place in global
+ table list
+
+ SYNOPSYS
+ st_lex::first_lists_tables_same()
+
+ NOTES
+ In many cases first table of main SELECT_LEX have special meaning =>
+ check that it is first table in global list and relink it first in
+ queries_tables list if it is necessary (we need such relinking only
+ for queries with subqueries in select list, in this case tables of
+ subqueries will go to global list first)
+*/
+
+void st_lex::first_lists_tables_same()
+{
+ TABLE_LIST *first_table= (TABLE_LIST*) select_lex.table_list.first;
+ if (query_tables != first_table && first_table != 0)
+ {
+ TABLE_LIST *next;
+ if (query_tables_last == &first_table->next_global)
+ query_tables_last= first_table->prev_global;
+
+ if ((next= *first_table->prev_global= first_table->next_global))
+ next->prev_global= first_table->prev_global;
+ /* include in new place */
+ first_table->next_global= query_tables;
+ /*
+ we are sure that above is not 0, because first_table was not
+ first table in global list => we can do following without check
+ */
+ query_tables->prev_global= &first_table->next_global;
+ first_table->prev_global= &query_tables;
+ query_tables= first_table;
+ }
}
@@ -1701,36 +1757,54 @@ TABLE_LIST *st_lex::unlink_first_table(TABLE_LIST *tables,
SYNOPSIS
link_first_table_back()
- tables Global table list
- global_first Saved first global table
- local_first Saved first local table
+ link_to_local do we need link this table to local
RETURN
global list
*/
-TABLE_LIST *st_lex::link_first_table_back(TABLE_LIST *tables,
- TABLE_LIST *global_first,
- TABLE_LIST *local_first)
+
+void st_lex::link_first_table_back(TABLE_LIST *first,
+ bool link_to_local)
{
- global_first->next= tables;
- if (&select_lex != all_selects_list)
+ if (first)
{
- /*
- we do not touch local table 'next' field => we need just
- put the table in the list
- */
- select_lex.table_list.first= (byte*) local_first;
+ if ((first->next_global= query_tables))
+ query_tables->prev_global= &first->next_global;
+ query_tables= first;
+
+ if (link_to_local)
+ {
+ first->next_local= (TABLE_LIST*) select_lex.table_list.first;
+ select_lex.table_list.first= (byte*) first;
+ select_lex.table_list.elements++; //safety
+ }
+ }
+}
+
+
+/*
+ fix some structures at the end of preparation
+
+ SYNOPSIS
+ st_select_lex::fix_prepare_information
+ thd thread handler
+ conds pointer on conditions which will be used for execution statement
+*/
+
+void st_select_lex::fix_prepare_information(THD *thd, Item **conds)
+{
+ if (thd->current_arena && first_execution)
+ {
+ prep_where= where;
+ first_execution= 0;
}
- else
- select_lex.table_list.first= (byte*) global_first;
- return global_first;
}
/*
- There are st_select_lex::add_table_to_list &
+ There are st_select_lex::add_table_to_list &
st_select_lex::set_lock_for_tables are in sql_parse.cc
- st_select_lex::print is in sql_select.h
+ st_select_lex::print is in sql_select.cc
st_select_lex_unit::prepare, st_select_lex_unit::exec,
st_select_lex_unit::cleanup, st_select_lex_unit::reinit_exec_mechanism,
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 7342902c086..de0d5d90f16 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -21,6 +21,10 @@
class Table_ident;
class sql_exchange;
class LEX_COLUMN;
+class sp_head;
+class sp_name;
+class sp_instr;
+class sp_pcontext;
/*
The following hack is needed because mysql_yacc.cc does not define
@@ -75,8 +79,12 @@ enum enum_sql_command {
SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
-
+ SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
+ SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
+ SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC,
+ SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC,
SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
+ SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW,
/* This should be the last !!! */
SQLCOM_END
};
@@ -85,6 +93,27 @@ enum enum_sql_command {
#define DESCRIBE_NORMAL 1
#define DESCRIBE_EXTENDED 2
+enum suid_behaviour
+{
+ IS_DEFAULT_SUID= 0, IS_NOT_SUID, IS_SUID
+};
+
+#define DERIVED_SUBQUERY 1
+#define DERIVED_VIEW 2
+
+enum enum_view_create_mode
+{
+ VIEW_CREATE_NEW, // check that there are not such VIEW/table
+ VIEW_ALTER, // check that VIEW .frm with such name exists
+ VIEW_CREATE_OR_REPLACE // check only that there are not such table
+};
+
+enum enum_drop_mode
+{
+ DROP_DEFAULT, // mode is not specified
+ DROP_CASCADE, // CASCADE option
+ DROP_RESTRICT // RESTRICT option
+};
typedef List<Item> List_item;
@@ -275,6 +304,8 @@ public:
friend class st_select_lex_unit;
friend bool mysql_new_select(struct st_lex *lex, bool move_down);
+ friend my_bool mysql_make_view (File_parser *parser,
+ TABLE_LIST *table);
private:
void fast_exclude();
};
@@ -337,7 +368,6 @@ public:
bool describe; /* union exec() called for EXPLAIN */
void init_query();
- bool create_total_list(THD *thd, st_lex *lex, TABLE_LIST **result);
st_select_lex_unit* master_unit();
st_select_lex* outer_select();
st_select_lex* first_select()
@@ -368,12 +398,10 @@ public:
ulong init_prepare_fake_select_lex(THD *thd);
int change_result(select_subselect *result, select_subselect *old_result);
+ void set_limit(st_select_lex *values, st_select_lex *sl);
- friend void mysql_init_query(THD *thd, uchar *buf, uint length);
+ friend void mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly);
friend int subselect_union_engine::exec();
-private:
- bool create_total_list_n_last_return(THD *thd, st_lex *lex,
- TABLE_LIST ***result);
};
typedef class st_select_lex_unit SELECT_LEX_UNIT;
@@ -386,6 +414,8 @@ public:
char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */
Item *where, *having; /* WHERE & HAVING clauses */
Item *prep_where; /* saved WHERE clause for prepared statement processing */
+ /* point on lex in which it was created, used in view subquery detection */
+ st_lex *parent_lex;
enum olap_type olap;
SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */
List<Item> item_list; /* list of fields & expressions */
@@ -398,7 +428,10 @@ public:
List<Item_func_match> *ftfunc_list;
List<Item_func_match> ftfunc_list_alloc;
JOIN *join; /* after JOIN::prepare it is pointer to corresponding JOIN */
- const char *type; /* type of select for EXPLAIN */
+ List<TABLE_LIST> top_join_list; /* join list of the top level */
+ List<TABLE_LIST> *join_list; /* list for the currently parsed join */
+ TABLE_LIST *embedding; /* table embedding to the above list */
+ const char *type; /* type of select for EXPLAIN */
SQL_LIST order_list; /* ORDER clause */
List<List_item> expr_list;
@@ -417,6 +450,11 @@ public:
uint cond_count; /* number of arguments of and/or/xor in where/having */
enum_parsing_place parsing_place; /* where we are parsing expression */
bool with_sum_func; /* sum function indicator */
+ /*
+ PS or SP cond natural joins was alredy processed with permanent
+ arena and all additional items which we need alredy stored in it
+ */
+ bool conds_processed_with_permanent_arena;
ulong table_join_options;
uint in_sum_expr;
@@ -432,6 +470,10 @@ public:
query processing end even if we use temporary table
*/
bool subquery_in_having;
+ bool first_execution; /* first execution in SP or PS */
+ bool first_cond_optimization;
+ /* do not wrap view fields with Item_ref */
+ bool no_wrap_view_item;
/*
SELECT for SELECT command st_select_lex. Used to privent scaning
@@ -494,6 +536,12 @@ public:
List<String> *ignore_index= 0,
LEX_STRING *option= 0);
TABLE_LIST* get_table_list();
+ bool init_nested_join(THD *thd);
+ TABLE_LIST *end_nested_join(THD *thd);
+ TABLE_LIST *nest_last_join(THD *thd);
+ void save_names_for_using_list(TABLE_LIST *tab1, TABLE_LIST *tab2);
+ void add_joined_table(TABLE_LIST *table);
+ TABLE_LIST *convert_right_join();
List<Item>* get_item_list();
List<String>* get_use_index();
List<String>* get_ignore_index();
@@ -508,7 +556,7 @@ public:
bool test_limit();
- friend void mysql_init_query(THD *thd, uchar *buf, uint length);
+ friend void mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly);
st_select_lex() {}
void make_empty_select()
{
@@ -520,6 +568,7 @@ public:
void print(THD *thd, String *str);
static void print_order(String *str, ORDER *order);
void print_limit(THD *thd, String *str);
+ void fix_prepare_information(THD *thd, Item **conds);
};
typedef class st_select_lex SELECT_LEX;
@@ -546,6 +595,13 @@ typedef struct st_alter_info
void reset(){drop_list.empty();alter_list.empty();clear();}
} ALTER_INFO;
+struct st_sp_chistics
+{
+ LEX_STRING comment;
+ enum suid_behaviour suid;
+ bool detistic;
+};
+
/* The state of the lex parsing. This is saved in the THD struct */
typedef struct st_lex
@@ -558,6 +614,7 @@ typedef struct st_lex
SELECT_LEX *current_select;
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
+ uchar *buf; /* The beginning of string, used by SPs */
uchar *ptr,*tok_start,*tok_end,*end_of_query;
char *length,*dec,*change,*name;
char *help_arg;
@@ -575,6 +632,9 @@ typedef struct st_lex
gptr yacc_yyss,yacc_yyvs;
THD *thd;
CHARSET_INFO *charset;
+ TABLE_LIST *query_tables; /* global list of all tables in this query */
+ /* last element next_global of previous list */
+ TABLE_LIST **query_tables_last;
List<key_part_spec> col_list;
List<key_part_spec> ref_list;
@@ -587,6 +647,7 @@ typedef struct st_lex
List<List_item> many_values;
List<set_var_base> var_list;
List<Item_param> param_list;
+ List<LEX_STRING> view_list; // view list (list of field names in view)
SQL_LIST proc_list, auxilliary_table_list, save_list;
TYPELIB *interval;
create_field *last_field;
@@ -606,15 +667,21 @@ typedef struct st_lex
enum enum_ha_read_modes ha_read_mode;
enum ha_rkey_function ha_rkey_mode;
enum enum_var_type option_type;
+ enum enum_view_create_mode create_view_mode;
+ enum enum_drop_mode drop_mode;
uint uint_geom_type;
uint grant, grant_tot_col, which_columns;
uint fk_delete_opt, fk_update_opt, fk_match_option;
uint slave_thd_opt;
uint8 describe;
+ uint8 derived_tables;
+ uint8 create_view_algorithm;
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
bool in_comment, ignore_space, verbose, no_write_to_binlog;
- bool derived_tables;
+ /* special JOIN::prepare mode: changing of query is prohibited */
+ bool view_prepare_mode;
bool safe_to_cache_query;
+ bool variables_used;
ALTER_INFO alter_info;
/* Prepared statements SQL syntax:*/
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
@@ -633,7 +700,30 @@ typedef struct st_lex
list of those tables after they are opened.
*/
TABLE_LIST *time_zone_tables_used;
- st_lex();
+ sp_head *sphead;
+ sp_name *spname;
+ bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
+ sp_pcontext *spcont;
+ HASH spfuns; /* Called functions */
+ st_sp_chistics sp_chistics;
+ bool only_view; /* used for SHOW CREATE TABLE/VIEW */
+ /*
+ field_list was created for view and should be removed before PS/SP
+ rexecuton
+ */
+ bool empty_field_list_on_rset;
+
+ st_lex()
+ {
+ bzero((char *)&spfuns, sizeof(spfuns));
+ }
+
+ ~st_lex()
+ {
+ if (spfuns.array.buffer)
+ hash_free(&spfuns);
+ }
+
inline void uncacheable(uint8 cause)
{
safe_to_cache_query= 0;
@@ -653,15 +743,29 @@ typedef struct st_lex
un->uncacheable|= cause;
}
}
- TABLE_LIST *unlink_first_table(TABLE_LIST *tables,
- TABLE_LIST **global_first,
- TABLE_LIST **local_first);
- TABLE_LIST *link_first_table_back(TABLE_LIST *tables,
- TABLE_LIST *global_first,
- TABLE_LIST *local_first);
+ TABLE_LIST *unlink_first_table(bool *link_to_local);
+ void link_first_table_back(TABLE_LIST *first, bool link_to_local);
+ void first_lists_tables_same();
+
+ bool can_be_merged();
+ bool can_use_merged();
+ bool can_not_use_merged();
+ bool only_view_structure();
} LEX;
extern TABLE_LIST fake_time_zone_tables_list;
+struct st_lex_local: public st_lex
+{
+ static void *operator new(size_t size)
+ {
+ return (void*) sql_alloc((uint) size);
+ }
+ static void *operator new(size_t size, MEM_ROOT *mem_root)
+ {
+ return (void*) alloc_root(mem_root, (uint) size);
+ }
+ static void operator delete(void *ptr,size_t size) {}
+};
void lex_init(void);
void lex_free(void);
diff --git a/sql/sql_list.h b/sql/sql_list.h
index c3b9c7f87ea..a4379b74c17 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -141,6 +141,12 @@ public:
last= &first;
return tmp->info;
}
+ inline void concat(base_list *list)
+ {
+ *last= list->first;
+ last= list->last;
+ elements+= list->elements;
+ }
inline list_node* last_node() { return *last; }
inline list_node* first_node() { return first;}
inline void *head() { return first->info; }
@@ -261,6 +267,7 @@ public:
}
empty();
}
+ inline void concat(List<T> *list) { base_list::concat(list); }
};
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 1ad9a6aa952..44c6f71c9bd 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -113,8 +113,16 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
MYF(0));
DBUG_RETURN(-1);
}
- if (!(table = open_ltable(thd,table_list,lock_type)))
+ table_list->lock_type= lock_type;
+ if (open_and_lock_tables(thd, table_list))
DBUG_RETURN(-1);
+ /* TODO: add key check when we will support VIEWs in LOAD */
+ if (!table_list->updatable)
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "LOAD");
+ DBUG_RETURN(-1);
+ }
+ table= table_list->table;
transactional_table= table->file->has_transactions();
log_delayed= (transactional_table || table->tmp_table);
@@ -127,7 +135,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
else
{ // Part field list
thd->dupp_field=0;
- if (setup_tables(table_list) ||
+ /* TODO: use this conds for 'WITH CHECK OPTIONS' */
+ Item *unused_conds= 0;
+ if (setup_tables(thd, table_list, &unused_conds) ||
setup_fields(thd, 0, table_list, fields, 1, 0, 0))
DBUG_RETURN(-1);
if (thd->dupp_field)
@@ -344,9 +354,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
(ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
send_ok(thd,info.copied+info.deleted,0L,name);
- // on the slave thd->query is never initialized
- if (!thd->slave_thread)
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (!log_delayed)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -400,7 +407,7 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
DBUG_RETURN(1);
}
if (skip_lines)
@@ -499,7 +506,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
DBUG_RETURN(1);
}
while ((sql_field=(Item_field*) it++))
diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc
index 46f1e6c156e..20fd7fe2ee0 100644
--- a/sql/sql_olap.cc
+++ b/sql/sql_olap.cc
@@ -152,7 +152,8 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex)
List<Item> all_fields(select_lex->item_list);
- if (setup_tables((TABLE_LIST *)select_lex->table_list.first) ||
+ if (setup_tables(lex->thd, (TABLE_LIST *)select_lex->table_list.first
+ &select_lex->where) ||
setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first,
select_lex->item_list, 1, &all_fields,1) ||
setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first,
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 8cf057a8532..59a3e4b545b 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -26,6 +26,9 @@
#include "ha_innodb.h"
#endif
+#include "sp_head.h"
+#include "sp.h"
+
#ifdef HAVE_OPENSSL
/*
Without SSL the handshake consists of one packet. This packet
@@ -44,6 +47,15 @@
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL */
+/* Used in error handling only */
+#define SP_TYPE_STRING(LP) \
+ ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
+#define SP_COM_STRING(LP) \
+ ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
+ (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
+ (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
+ "FUNCTION" : "PROCEDURE")
+
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
#endif
@@ -66,6 +78,7 @@ const char *command_name[]={
"Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user",
"Binlog Dump","Table Dump", "Connect Out", "Register Slave",
"Prepare", "Prepare Execute", "Long Data", "Close stmt",
+ "Reset stmt", "Set option", "Fetch",
"Error" // Last command number
};
@@ -283,9 +296,10 @@ int check_user(THD *thd, enum enum_server_command command,
thd->user, thd->host_or_ip);
DBUG_RETURN(-1);
}
+ /* We have to read very specific packet size */
if (send_old_password_request(thd) ||
- my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
- { // specific packet size
+ my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
+ {
inc_host_errors(&thd->remote.sin_addr);
DBUG_RETURN(ER_HANDSHAKE_ERROR);
}
@@ -527,6 +541,8 @@ void init_update_queries(void)
uc_update_queries[SQLCOM_DELETE_MULTI]=1;
uc_update_queries[SQLCOM_DROP_INDEX]=1;
uc_update_queries[SQLCOM_UPDATE_MULTI]=1;
+ uc_update_queries[SQLCOM_CREATE_VIEW]=1;
+ uc_update_queries[SQLCOM_DROP_VIEW]=1;
}
bool is_update_query(enum enum_sql_command command)
@@ -614,8 +630,9 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
uc->conn_per_hour=0;
}
}
- else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES
+ else
{
+ /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
for (uint idx=0;idx < hash_user_connections.records; idx++)
{
USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections,
@@ -939,7 +956,7 @@ pthread_handler_decl(handle_one_connection,arg)
pthread_detach_this_thread();
#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create
- // The following calls needs to be done before we call DBUG_ macros
+ /* The following calls needs to be done before we call DBUG_ macros */
if (!(test_flags & TEST_NO_THREADS) & my_thread_init())
{
close_connection(thd, ER_OUT_OF_RESOURCES, 1);
@@ -958,7 +975,7 @@ pthread_handler_decl(handle_one_connection,arg)
*/
DBUG_PRINT("info", ("handle_one_connection called by thread %d\n",
thd->thread_id));
- // now that we've called my_thread_init(), it is safe to call DBUG_*
+ /* now that we've called my_thread_init(), it is safe to call DBUG_* */
#if defined(__WIN__)
init_signals(); // IRENA; testing ?
@@ -1005,20 +1022,19 @@ pthread_handler_decl(handle_one_connection,arg)
{
execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
if (thd->query_error)
- thd->killed= 1;
+ thd->killed= THD::KILL_CONNECTION;
}
thd->proc_info=0;
thd->set_time();
thd->init_for_queries();
- while (!net->error && net->vio != 0 && !thd->killed)
+ while (!net->error && net->vio != 0 && !(thd->killed == THD::KILL_CONNECTION))
{
if (do_command(thd))
break;
}
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
- free_root(&thd->mem_root,MYF(0));
if (net->error && net->vio != 0 && net->report_error)
{
if (!thd->killed && thd->variables.log_warnings > 1)
@@ -1108,6 +1124,10 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg)
thd->query_length=length;
thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1);
thd->query[length] = '\0';
+ /*
+ We don't need to obtain LOCK_thread_count here because in bootstrap
+ mode we have only one thread.
+ */
thd->query_id=query_id++;
if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END))
{
@@ -1141,8 +1161,10 @@ end:
void free_items(Item *item)
{
+ DBUG_ENTER("free_items");
for (; item ; item=item->next)
item->delete_self();
+ DBUG_VOID_RETURN;
}
/* This works because items are allocated with sql_alloc() */
@@ -1162,10 +1184,10 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
db = (db && db[0]) ? db : thd->db;
if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST))))
DBUG_RETURN(1); // out of memory
- table_list->db = db;
- table_list->real_name = table_list->alias = tbl_name;
- table_list->lock_type = TL_READ_NO_INSERT;
- table_list->next = 0;
+ table_list->db= db;
+ table_list->real_name= table_list->alias= tbl_name;
+ table_list->lock_type= TL_READ_NO_INSERT;
+ table_list->prev_global= &table_list; // can be removed after merge with 4.1
if (!db || check_db_name(db))
{
@@ -1229,7 +1251,7 @@ bool do_command(THD *thd)
packet=0;
old_timeout=net->read_timeout;
- // Wait max for 8 hours
+ /* Wait max for 8 hours */
net->read_timeout=(uint) thd->variables.net_wait_timeout;
thd->clear_error(); // Clear error message
@@ -1251,6 +1273,9 @@ bool do_command(THD *thd)
}
else
{
+ if (thd->killed == THD::KILL_QUERY)
+ thd->killed= THD::NOT_KILLED;
+
packet=(char*) net->read_pos;
command = (enum enum_server_command) (uchar) packet[0];
if (command >= COM_END)
@@ -1428,6 +1453,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
mysql_stmt_execute(thd, packet, packet_length);
break;
}
+ case COM_FETCH:
+ {
+ mysql_stmt_fetch(thd, packet, packet_length);
+ break;
+ }
case COM_LONG_DATA:
{
mysql_stmt_get_longdata(thd, packet, packet_length);
@@ -1521,13 +1551,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
send_error(thd,ER_NO_DB_ERROR);
break;
}
- thd->free_list=0;
pend= strend(packet);
thd->convert_string(&conv_name, system_charset_info,
packet, (uint) (pend-packet), thd->charset());
table_list.alias= table_list.real_name= conv_name.str;
packet= pend+1;
- // command not cachable => no gap for data base name
+ /* command not cachable => no gap for data base name */
if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1)))
break;
mysql_log.write(thd,command,"%s %s",table_list.real_name,fields);
@@ -1543,7 +1572,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
mysqld_list_fields(thd,&table_list,fields);
free_items(thd->free_list);
- thd->free_list= 0;
+ thd->free_list= 0; /* free_list should never point to garbage */
break;
}
#endif
@@ -1575,7 +1604,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status);
char *db=thd->strdup(packet), *alias;
- // null test to handle EOM
+ /* null test to handle EOM */
if (!db || !(alias= thd->strdup(db)) || check_db_name(db))
{
net_printf(thd,ER_WRONG_DB_NAME, db ? db : "NULL");
@@ -1613,7 +1642,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->server_id = slave_server_id;
mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags);
unregister_slave(thd,1,1);
- // fake COM_QUIT -- if we get here, the thread needs to terminate
+ /* fake COM_QUIT -- if we get here, the thread needs to terminate */
error = TRUE;
net->error = 0;
break;
@@ -1666,8 +1695,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
close_connection(thd, 0, 1);
close_thread_tables(thd); // Free before kill
- free_root(&thd->mem_root,MYF(0));
- free_root(&thd->transaction.mem_root,MYF(0));
kill_mysql();
error=TRUE;
break;
@@ -1718,7 +1745,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
ulong id=(ulong) uint4korr(packet);
- kill_one_thread(thd,id);
+ kill_one_thread(thd,id,false);
break;
}
case COM_SET_OPTION:
@@ -1793,6 +1820,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+
free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
}
@@ -1852,23 +1880,54 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/
-void
+int
mysql_execute_command(THD *thd)
{
int res= 0;
LEX *lex= thd->lex;
- TABLE_LIST *tables= (TABLE_LIST*) lex->select_lex.table_list.first;
+ /* first table of first SELECT_LEX */
+ TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first;
+ /* list of all tables in query */
+ TABLE_LIST *all_tables;
+ /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
SELECT_LEX *select_lex= &lex->select_lex;
+ /* most outer SELECT_LEX_UNIT of query */
SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_execute_command");
/*
+ In many cases first table of main SELECT_LEX have special meaning =>
+ check that it is first table in global list and relink it first in
+ queries_tables list if it is necessary (we need such relinking only
+ for queries with subqueries in select list, in this case tables of
+ subqueries will go to global list first)
+
+ all_tables will differ from first_table only if most upper SELECT_LEX
+ do not contain tables.
+
+ Because of above in place where should be at least one table in most
+ outer SELECT_LEX we have following check:
+ DBUG_ASSERT(first_table == all_tables);
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ */
+ lex->first_lists_tables_same();
+ /* should be assigned after making first tables same */
+ all_tables= lex->query_tables;
+
+ if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
+ lex->sql_command != SQLCOM_CREATE_SPFUNCTION)
+ {
+ if (sp_cache_functions(thd, lex))
+ DBUG_RETURN(-1);
+ }
+
+ /*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
that is not a SHOW command or a select that only access local
variables, but for now this is probably good enough.
*/
- if (tables || &lex->select_lex != lex->all_selects_list)
+ if (all_tables || &lex->select_lex != lex->all_selects_list)
mysql_reset_errors(thd);
#ifdef HAVE_REPLICATION
@@ -1878,11 +1937,11 @@ mysql_execute_command(THD *thd)
Skip if we are in the slave thread, some table rules have been
given and the table list says the query should not be replicated
*/
- if (all_tables_not_ok(thd,tables))
+ if (all_tables_not_ok(thd, all_tables))
{
/* we warn the slave SQL thread */
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
#ifndef TO_BE_DELETED
/*
@@ -1898,10 +1957,6 @@ mysql_execute_command(THD *thd)
#endif
}
#endif /* !HAVE_REPLICATION */
- if ((&lex->select_lex != lex->all_selects_list ||
- lex->time_zone_tables_used) &&
- lex->unit.create_total_list(thd, lex, &tables))
- DBUG_VOID_RETURN;
/*
When option readonly is set deny operations which change tables.
@@ -1912,7 +1967,7 @@ mysql_execute_command(THD *thd)
(uc_update_queries[lex->sql_command] > 0))
{
net_printf(thd, ER_OPTION_PREVENTS_STATEMENT, "--read-only");
- DBUG_VOID_RETURN;
+ DBUG_RETURN(-1);
}
statistic_increment(com_stat[lex->sql_command],&LOCK_status);
@@ -1927,42 +1982,31 @@ mysql_execute_command(THD *thd)
}
select_result *result=lex->result;
- if (tables)
+ if (all_tables)
{
- res=check_table_access(thd,
- lex->exchange ? SELECT_ACL | FILE_ACL :
- SELECT_ACL,
- tables,0);
+ res= check_table_access(thd,
+ lex->exchange ? SELECT_ACL | FILE_ACL :
+ SELECT_ACL,
+ all_tables, 0);
}
else
- res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
- any_db,0,0,0);
+ res= check_access(thd,
+ lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
+ any_db, 0, 0, 0);
if (res)
{
res=0;
break; // Error message is given
}
- /*
- In case of single SELECT unit->global_parameters points on first SELECT
- TODO: move counters to SELECT_LEX
- */
- unit->offset_limit_cnt= (ha_rows) unit->global_parameters->offset_limit;
- unit->select_limit_cnt= (ha_rows) (unit->global_parameters->select_limit+
- unit->global_parameters->offset_limit);
- if (unit->select_limit_cnt <
- (ha_rows) unit->global_parameters->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // no limit
- if (unit->select_limit_cnt == HA_POS_ERROR && !select_lex->next_select())
- select_lex->options&= ~OPTION_FOUND_ROWS;
- if (!(res=open_and_lock_tables(thd,tables)))
+ if (!(res= open_and_lock_tables(thd, all_tables)))
{
if (lex->describe)
{
if (!(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ goto error;
}
else
thd->send_explain_fields(result);
@@ -1987,7 +2031,7 @@ mysql_execute_command(THD *thd)
res= -1;
break;
}
- query_cache_store_query(thd, tables);
+ query_cache_store_query(thd, all_tables);
res= handle_select(thd, lex, result);
if (result != lex->result)
delete result;
@@ -2088,8 +2132,9 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_DO:
- if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
- (res= open_and_lock_tables(thd,tables))))
+ if (all_tables &&
+ ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) ||
+ (res= open_and_lock_tables(thd, all_tables))))
break;
res= mysql_do(thd, *lex->insert_list);
@@ -2110,7 +2155,7 @@ mysql_execute_command(THD *thd)
{
if (check_global_access(thd, SUPER_ACL))
goto error;
- // PURGE MASTER LOGS TO 'file'
+ /* PURGE MASTER LOGS TO 'file' */
res = purge_master_logs(thd, lex->to_log);
break;
}
@@ -2118,7 +2163,7 @@ mysql_execute_command(THD *thd)
{
if (check_global_access(thd, SUPER_ACL))
goto error;
- // PURGE MASTER LOGS BEFORE 'data'
+ /* PURGE MASTER LOGS BEFORE 'data' */
res = purge_master_logs_before_date(thd, lex->purge_time);
break;
}
@@ -2171,41 +2216,45 @@ mysql_execute_command(THD *thd)
case SQLCOM_BACKUP_TABLE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL, tables,0) ||
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL, all_tables, 0) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_backup_table(thd, tables);
+ res = mysql_backup_table(thd, first_table);
break;
}
case SQLCOM_RESTORE_TABLE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, INSERT_ACL, tables,0) ||
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, INSERT_ACL, all_tables, 0) ||
check_global_access(thd, FILE_ACL))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_restore_table(thd, tables);
+ res = mysql_restore_table(thd, first_table);
break;
}
case SQLCOM_ASSIGN_TO_KEYCACHE:
{
- if (check_db_used(thd, tables) ||
- check_access(thd, INDEX_ACL, tables->db,
- &tables->grant.privilege, 0, 0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, INDEX_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0))
goto error;
- res= mysql_assign_to_keycache(thd, tables, &lex->name_and_length);
+ res= mysql_assign_to_keycache(thd, first_table, &lex->name_and_length);
break;
}
case SQLCOM_PRELOAD_KEYS:
{
- if (check_db_used(thd, tables) ||
- check_access(thd, INDEX_ACL, tables->db,
- &tables->grant.privilege, 0, 0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, INDEX_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0))
goto error;
- res = mysql_preload_keys(thd, tables);
+ res = mysql_preload_keys(thd, first_table);
break;
}
#ifdef HAVE_REPLICATION
@@ -2258,19 +2307,21 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
case SQLCOM_LOAD_MASTER_TABLE:
{
- if (!tables->db)
- tables->db=thd->db;
- if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege,0,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (!first_table->db)
+ first_table->db= thd->db;
+ if (check_access(thd, CREATE_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0))
goto error; /* purecov: inspected */
if (grant_option)
{
/* Check that the first table has CREATE privilege */
- if (check_grant(thd, CREATE_ACL, tables, 0, 1, 0))
+ if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0))
goto error;
}
- if (strlen(tables->real_name) > NAME_LEN)
+ if (strlen(first_table->real_name) > NAME_LEN)
{
- net_printf(thd,ER_WRONG_TABLE_NAME, tables->real_name);
+ net_printf(thd, ER_WRONG_TABLE_NAME, first_table->real_name);
break;
}
pthread_mutex_lock(&LOCK_active_mi);
@@ -2278,7 +2329,7 @@ mysql_execute_command(THD *thd)
fetch_master_table will send the error to the client on failure.
Give error if the table already exists.
*/
- if (!fetch_master_table(thd, tables->db, tables->real_name,
+ if (!fetch_master_table(thd, first_table->db, first_table->real_name,
active_mi, 0, 0))
{
send_ok(thd);
@@ -2290,12 +2341,13 @@ mysql_execute_command(THD *thd)
case SQLCOM_CREATE_TABLE:
{
- /* Skip first table, which is the table we are creating */
- TABLE_LIST *create_table, *create_table_local;
- tables= lex->unlink_first_table(tables, &create_table,
- &create_table_local);
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ bool link_to_local;
+ // Skip first table, which is the table we are creating
+ TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
+ TABLE_LIST *select_tables= lex->query_tables;
- if ((res= create_table_precheck(thd, tables, create_table)))
+ if ((res= create_table_precheck(thd, select_tables, create_table)))
goto unsent_create_error;
#ifndef HAVE_READLINK
@@ -2304,7 +2356,7 @@ mysql_execute_command(THD *thd)
/* Fix names if symlinked tables */
if (append_file_to_dir(thd, &lex->create_info.data_file_name,
create_table->real_name) ||
- append_file_to_dir(thd,&lex->create_info.index_file_name,
+ append_file_to_dir(thd, &lex->create_info.index_file_name,
create_table->real_name))
{
res=-1;
@@ -2327,22 +2379,25 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements) // With select
{
select_result *result;
-
+ /*
+ Is table which we are changing used somewhere in other parts
+ of query
+ */
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
- find_real_table_in_list(tables, create_table->db,
- create_table->real_name))
+ find_table_in_global_list(select_tables, create_table->db,
+ create_table->real_name))
{
- net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name);
+ net_printf(thd, ER_UPDATE_TABLE_USED, create_table->real_name);
goto create_error;
}
if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
{
TABLE_LIST *tab;
- for (tab= tables; tab; tab= tab->next)
+ for (tab= select_tables; tab; tab= tab->next)
{
if (find_real_table_in_list((TABLE_LIST*) lex->create_info.
merge_list.first,
- tables->db, tab->real_name))
+ select_tables->db, tab->real_name))
{
net_printf(thd, ER_UPDATE_TABLE_USED, tab->real_name);
goto create_error;
@@ -2350,24 +2405,21 @@ mysql_execute_command(THD *thd)
}
}
- if (tables && check_table_access(thd, SELECT_ACL, tables,0))
+ if (select_tables &&
+ check_table_access(thd, SELECT_ACL, select_tables, 0))
goto create_error; // Error message is given
select_lex->options|= SELECT_NO_UNLOCK;
- unit->offset_limit_cnt= select_lex->offset_limit;
- unit->select_limit_cnt= select_lex->select_limit+
- select_lex->offset_limit;
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // No limit
+ unit->set_limit(select_lex, select_lex);
- if (!(res=open_and_lock_tables(thd,tables)))
+ if (!(res= open_and_lock_tables(thd, select_tables)))
{
res= -1; // If error
- if ((result=new select_create(create_table->db,
- create_table->real_name,
- &lex->create_info,
- lex->create_list,
- lex->key_list,
- select_lex->item_list,lex->duplicates)))
+ if ((result= new select_create(create_table,
+ &lex->create_info,
+ lex->create_list,
+ lex->key_list,
+ select_lex->item_list,
+ lex->duplicates)))
{
/*
CREATE from SELECT give its SELECT_LEX for SELECT,
@@ -2377,48 +2429,46 @@ mysql_execute_command(THD *thd)
res=handle_select(thd, lex, result);
select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
}
- //reset for PS
+ /* reset for PS */
lex->create_list.empty();
lex->key_list.empty();
}
}
- else // regular create
+ else
{
+ /* regular create */
if (lex->name)
res= mysql_create_like_table(thd, create_table, &lex->create_info,
(Table_ident *)lex->name);
else
{
- res= mysql_create_table(thd,create_table->db,
- create_table->real_name, &lex->create_info,
- lex->create_list,
- lex->key_list,0,0);
+ res= mysql_create_table(thd, create_table->db,
+ create_table->real_name, &lex->create_info,
+ lex->create_list,
+ lex->key_list, 0, 0);
}
if (!res)
send_ok(thd);
}
-
- // put tables back for PS rexecuting
- tables= lex->link_first_table_back(tables, create_table,
- create_table_local);
+ lex->link_first_table_back(create_table, link_to_local);
break;
create_error:
- res= 1; //error reported
+ res= 1; //error reported
unsent_create_error:
- // put tables back for PS rexecuting
- tables= lex->link_first_table_back(tables, create_table,
- create_table_local);
+ /* put tables back for PS rexecuting */
+ lex->link_first_table_back(create_table, link_to_local);
break;
}
case SQLCOM_CREATE_INDEX:
- if (check_one_table_access(thd, INDEX_ACL, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_one_table_access(thd, INDEX_ACL, all_tables))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
if (end_active_trans(thd))
res= -1;
else
- res = mysql_create_index(thd, tables, lex->key_list);
+ res = mysql_create_index(thd, first_table, lex->key_list);
break;
#ifdef HAVE_REPLICATION
@@ -2446,7 +2496,7 @@ unsent_create_error:
if (thd->locked_tables || thd->active_transaction())
{
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- break;
+ goto error;
}
{
pthread_mutex_lock(&LOCK_active_mi);
@@ -2457,9 +2507,10 @@ unsent_create_error:
#endif /* HAVE_REPLICATION */
case SQLCOM_ALTER_TABLE:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- break;
+ goto error;
#else
{
ulong priv=0;
@@ -2470,16 +2521,17 @@ unsent_create_error:
break;
}
if (!select_lex->db)
- select_lex->db=tables->db;
- if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege,0,0) ||
+ select_lex->db= first_table->db;
+ if (check_access(thd, ALTER_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0) ||
check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)||
- check_merge_table_access(thd, tables->db,
+ check_merge_table_access(thd, first_table->db,
(TABLE_LIST *)
lex->create_info.merge_list.first))
goto error; /* purecov: inspected */
if (grant_option)
{
- if (check_grant(thd, ALTER_ACL, tables, 0, UINT_MAX, 0))
+ if (check_grant(thd, ALTER_ACL, all_tables, 0, UINT_MAX, 0))
goto error;
if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
{ // Rename of table
@@ -2503,7 +2555,7 @@ unsent_create_error:
thd->slow_command=TRUE;
res= mysql_alter_table(thd, select_lex->db, lex->name,
&lex->create_info,
- tables, lex->create_list,
+ first_table, lex->create_list,
lex->key_list,
select_lex->order_list.elements,
(ORDER *) select_lex->order_list.first,
@@ -2514,38 +2566,37 @@ unsent_create_error:
#endif /*DONT_ALLOW_SHOW_COMMANDS*/
case SQLCOM_RENAME_TABLE:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *table;
- if (check_db_used(thd,tables))
+ if (check_db_used(thd, all_tables))
goto error;
- for (table=tables ; table ; table=table->next->next)
+ for (table= first_table; table; table= table->next_local->next_local)
{
if (check_access(thd, ALTER_ACL | DROP_ACL, table->db,
&table->grant.privilege,0,0) ||
- check_access(thd, INSERT_ACL | CREATE_ACL, table->next->db,
- &table->next->grant.privilege,0,0))
+ check_access(thd, INSERT_ACL | CREATE_ACL, table->next_local->db,
+ &table->next_local->grant.privilege, 0, 0))
goto error;
if (grant_option)
{
- TABLE_LIST old_list,new_list;
+ TABLE_LIST old_list, new_list;
/*
we do not need initialize old_list and new_list because we will
come table[0] and table->next[0] there
*/
- old_list=table[0];
- new_list=table->next[0];
- old_list.next=new_list.next=0;
- if (check_grant(thd, ALTER_ACL, &old_list, 0, UINT_MAX, 0) ||
- (!test_all_bits(table->next->grant.privilege,
+ old_list= table[0];
+ new_list= table->next_local[0];
+ if (check_grant(thd, ALTER_ACL, &old_list, 0, 1, 0) ||
+ (!test_all_bits(table->next_local->grant.privilege,
INSERT_ACL | CREATE_ACL) &&
- check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0,
- UINT_MAX, 0)))
+ check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0)))
goto error;
}
}
- query_cache_invalidate3(thd, tables, 0);
+ query_cache_invalidate3(thd, first_table, 0);
if (end_active_trans(thd))
res= -1;
- else if (mysql_rename_tables(thd,tables))
+ else if (mysql_rename_tables(thd, first_table))
res= -1;
break;
}
@@ -2553,7 +2604,7 @@ unsent_create_error:
case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (check_global_access(thd, SUPER_ACL))
@@ -2564,38 +2615,40 @@ unsent_create_error:
#endif
#endif /* EMBEDDED_LIBRARY */
case SQLCOM_SHOW_CREATE:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
- if (check_db_used(thd, tables) ||
- check_access(thd, SELECT_ACL | EXTRA_ACL, tables->db,
- &tables->grant.privilege,0,0))
+ if (check_db_used(thd, all_tables) ||
+ check_access(thd, SELECT_ACL | EXTRA_ACL, first_table->db,
+ &first_table->grant.privilege, 0, 0))
goto error;
- res = mysqld_show_create(thd, tables);
+ res = mysqld_show_create(thd, first_table);
break;
}
#endif
case SQLCOM_CHECKSUM:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, 0))
goto error; /* purecov: inspected */
- res = mysql_checksum_table(thd, tables, &lex->check_opt);
+ res = mysql_checksum_table(thd, first_table, &lex->check_opt);
break;
}
case SQLCOM_REPAIR:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_repair_table(thd, tables, &lex->check_opt);
+ res= mysql_repair_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2606,24 +2659,25 @@ unsent_create_error:
}
case SQLCOM_CHECK:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, 0))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_check_table(thd, tables, &lex->check_opt);
+ res = mysql_check_table(thd, first_table, &lex->check_opt);
break;
}
case SQLCOM_ANALYZE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
- res = mysql_analyze_table(thd, tables, &lex->check_opt);
+ res = mysql_analyze_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2635,17 +2689,17 @@ unsent_create_error:
case SQLCOM_OPTIMIZE:
{
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, 0))
goto error; /* purecov: inspected */
thd->slow_command=TRUE;
res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
- mysql_recreate_table(thd, tables, 1) :
- mysql_optimize_table(thd, tables, &lex->check_opt);
+ mysql_recreate_table(thd, first_table, 1) :
+ mysql_optimize_table(thd, first_table, &lex->check_opt);
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2655,9 +2709,10 @@ unsent_create_error:
break;
}
case SQLCOM_UPDATE:
- if (update_precheck(thd, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (update_precheck(thd, all_tables))
break;
- res= mysql_update(thd,tables,
+ res= mysql_update(thd, all_tables,
select_lex->item_list,
lex->value_list,
select_lex->where,
@@ -2670,9 +2725,10 @@ unsent_create_error:
break;
case SQLCOM_UPDATE_MULTI:
{
- if ((res= multi_update_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= multi_update_precheck(thd, all_tables)))
break;
- res= mysql_multi_update(thd,tables,
+ res= mysql_multi_update(thd, all_tables,
&select_lex->item_list,
&lex->value_list,
select_lex->where,
@@ -2683,57 +2739,58 @@ unsent_create_error:
case SQLCOM_REPLACE:
case SQLCOM_INSERT:
{
- my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0);
- if ((res= insert_precheck(thd, tables, update)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0);
+ if ((res= insert_precheck(thd, all_tables, update)))
break;
- res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
- select_lex->item_list, lex->value_list,
- (update ? DUP_UPDATE : lex->duplicates));
+ res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
+ select_lex->item_list, lex->value_list,
+ (update ? DUP_UPDATE : lex->duplicates));
if (thd->net.report_error)
res= -1;
+ if (first_table->view && !first_table->contain_auto_increment)
+ thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it
break;
}
case SQLCOM_REPLACE_SELECT:
case SQLCOM_INSERT_SELECT:
{
- TABLE_LIST *first_local_table= (TABLE_LIST *) select_lex->table_list.first;
- if ((res= insert_select_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= insert_select_precheck(thd, all_tables)))
break;
/* Fix lock for first table */
- if (tables->lock_type == TL_WRITE_DELAYED)
- tables->lock_type= TL_WRITE;
+ if (first_table->lock_type == TL_WRITE_DELAYED)
+ first_table->lock_type= TL_WRITE;
/* Don't unlock tables until command is written to binary log */
select_lex->options|= SELECT_NO_UNLOCK;
select_result *result;
- unit->offset_limit_cnt= select_lex->offset_limit;
- unit->select_limit_cnt= select_lex->select_limit+select_lex->offset_limit;
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // No limit
+ unit->set_limit(select_lex, select_lex);
- if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
+ // is table which we are changing used somewhere in other parts of query
+ if (find_table_in_global_list(all_tables->next_global,
+ first_table->db, first_table->real_name))
{
/* Using same table for INSERT and SELECT */
select_lex->options |= OPTION_BUFFER_RESULT;
}
-
- if (!(res= open_and_lock_tables(thd, tables)) &&
- (result= new select_insert(tables->table, &lex->field_list,
- lex->duplicates)))
+ if (!(res= open_and_lock_tables(thd, all_tables)) &&
+ !(res= mysql_insert_select_prepare(thd)) &&
+ (result= new select_insert(first_table, first_table->table,
+ &lex->field_list, lex->duplicates)))
{
/* Skip first table, which is the table we are inserting in */
- lex->select_lex.table_list.first= (byte*) first_local_table->next;
+ lex->select_lex.table_list.first= (byte*) first_table->next_local;
/*
insert/replace from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
res= handle_select(thd, lex, result);
- /* revert changes for SP */
- lex->select_lex.table_list.first= (byte*) first_local_table;
+ lex->select_lex.table_list.first= (byte*) first_table;
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
delete result;
if (thd->net.report_error)
@@ -2741,10 +2798,15 @@ unsent_create_error:
}
else
res= -1;
+
+ if (first_table->view && !first_table->contain_auto_increment)
+ thd->last_insert_id= 0; // do not show last insert ID if VIEW have not it
+
break;
}
case SQLCOM_TRUNCATE:
- if (check_one_table_access(thd, DELETE_ACL, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_one_table_access(thd, DELETE_ACL, all_tables))
goto error;
/*
Don't allow this within a transaction because we want to use
@@ -2755,13 +2817,15 @@ unsent_create_error:
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS);
goto error;
}
- res=mysql_truncate(thd, tables, 0);
+
+ res= mysql_truncate(thd, first_table, 0);
break;
case SQLCOM_DELETE:
{
- if ((res= delete_precheck(thd, tables)))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if ((res= delete_precheck(thd, all_tables)))
break;
- res = mysql_delete(thd,tables, select_lex->where,
+ res = mysql_delete(thd, all_tables, select_lex->where,
&select_lex->order_list,
select_lex->select_limit, select_lex->options);
if (thd->net.report_error)
@@ -2770,13 +2834,13 @@ unsent_create_error:
}
case SQLCOM_DELETE_MULTI:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables=
(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
- TABLE_LIST *target_tbl;
uint table_count;
multi_delete *result;
- if ((res= multi_delete_precheck(thd, tables, &table_count)))
+ if ((res= multi_delete_precheck(thd, all_tables, &table_count)))
break;
/* condition will be TRUE on SP re-excuting */
@@ -2789,33 +2853,11 @@ unsent_create_error:
}
thd->proc_info="init";
- if ((res=open_and_lock_tables(thd,tables)))
+ if ((res= open_and_lock_tables(thd, all_tables)))
+ break;
+
+ if ((res= mysql_multi_delete_prepare(thd)))
break;
- /* Fix tables-to-be-deleted-from list to point at opened tables */
- for (target_tbl= (TABLE_LIST*) aux_tables;
- target_tbl;
- target_tbl= target_tbl->next)
- {
- target_tbl->table= target_tbl->table_list->table;
- /*
- Multi-delete can't be constructed over-union => we always have
- single SELECT on top and have to check underlaying SELECTs of it
- */
- for (SELECT_LEX_UNIT *un= lex->select_lex.first_inner_unit();
- un;
- un= un->next_unit())
- {
- if (un->first_select()->linkage != DERIVED_TABLE_TYPE &&
- un->check_updateable(target_tbl->table_list->db,
- target_tbl->table_list->real_name))
- {
- my_error(ER_UPDATE_TABLE_USED, MYF(0),
- target_tbl->table_list->real_name);
- res= -1;
- break;
- }
- }
- }
if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
table_count)))
@@ -2841,9 +2883,10 @@ unsent_create_error:
}
case SQLCOM_DROP_TABLE:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (!lex->drop_temporary)
{
- if (check_table_access(thd,DROP_ACL,tables,0))
+ if (check_table_access(thd, DROP_ACL, all_tables, 0))
goto error; /* purecov: inspected */
if (end_active_trans(thd))
{
@@ -2864,21 +2907,23 @@ unsent_create_error:
if (thd->slave_thread)
lex->drop_if_exists= 1;
}
- res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary);
+ res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
+ lex->drop_temporary);
}
break;
case SQLCOM_DROP_INDEX:
- if (check_one_table_access(thd, INDEX_ACL, tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_one_table_access(thd, INDEX_ACL, all_tables))
goto error; /* purecov: inspected */
if (end_active_trans(thd))
res= -1;
else
- res = mysql_drop_index(thd, tables, &lex->alter_info);
+ res = mysql_drop_index(thd, first_table, &lex->alter_info);
break;
case SQLCOM_SHOW_DATABASES:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
check_global_access(thd, SHOW_DB_ACL))
@@ -2914,7 +2959,7 @@ unsent_create_error:
case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0))
@@ -2927,13 +2972,13 @@ unsent_create_error:
/* FALL THROUGH */
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=select_lex->db ? select_lex->db : thd->db;
if (!db)
{
- send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
+ send_error(thd,ER_NO_DB_ERROR); /* purecov: inspected */
goto error; /* purecov: inspected */
}
remove_escape(db); // Fix escaped '_'
@@ -2972,40 +3017,42 @@ unsent_create_error:
res= mysqld_show_collations(thd,(lex->wild ? lex->wild->ptr() : NullS));
break;
case SQLCOM_SHOW_FIELDS:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
- char *db=tables->db;
+ char *db= first_table->db;
remove_escape(db); // Fix escaped '_'
- remove_escape(tables->real_name);
+ remove_escape(first_table->real_name);
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
- &tables->grant.privilege, 0, 0))
+ &first_table->grant.privilege, 0, 0))
goto error; /* purecov: inspected */
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
+ if (grant_option && check_grant(thd, SELECT_ACL, first_table, 2, UINT_MAX, 0))
goto error;
- res= mysqld_show_fields(thd,tables,
+ res= mysqld_show_fields(thd, first_table,
(lex->wild ? lex->wild->ptr() : NullS),
lex->verbose);
break;
}
#endif
case SQLCOM_SHOW_KEYS:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
- char *db=tables->db;
+ char *db= first_table->db;
remove_escape(db); // Fix escaped '_'
- remove_escape(tables->real_name);
+ remove_escape(first_table->real_name);
if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,
- &tables->grant.privilege, 0, 0))
+ &first_table->grant.privilege, 0, 0))
goto error; /* purecov: inspected */
- if (grant_option && check_grant(thd, SELECT_ACL, tables, 2, UINT_MAX, 0))
+ if (grant_option && check_grant(thd, SELECT_ACL, all_tables, 2, UINT_MAX, 0))
goto error;
- res= mysqld_show_keys(thd,tables);
+ res= mysqld_show_keys(thd, first_table);
break;
}
#endif
@@ -3015,12 +3062,13 @@ unsent_create_error:
case SQLCOM_LOAD:
{
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
uint privilege= (lex->duplicates == DUP_REPLACE ?
INSERT_ACL | DELETE_ACL : INSERT_ACL);
if (!lex->local_file)
{
- if (check_access(thd,privilege | FILE_ACL,tables->db,0,0,0))
+ if (check_access(thd, privilege | FILE_ACL, first_table->db, 0, 0, 0))
goto error;
}
else
@@ -3031,19 +3079,20 @@ unsent_create_error:
send_error(thd,ER_NOT_ALLOWED_COMMAND);
goto error;
}
- if (check_one_table_access(thd, privilege, tables))
+ if (check_one_table_access(thd, privilege, all_tables))
goto error;
}
- res=mysql_load(thd, lex->exchange, tables, lex->field_list,
- lex->duplicates, (bool) lex->local_file, lex->lock_option);
+ res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
+ lex->duplicates, (bool) lex->local_file, lex->lock_option);
break;
}
case SQLCOM_SET_OPTION:
{
List<set_var_base> *lex_var_list= &lex->var_list;
- if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
- (res= open_and_lock_tables(thd,tables))))
+ if (all_tables &&
+ ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) ||
+ (res= open_and_lock_tables(thd, all_tables))))
break;
if (lex->one_shot_set && not_all_support_one_shot(lex_var_list))
{
@@ -3079,17 +3128,20 @@ purposes internal to the MySQL server", MYF(0));
break;
case SQLCOM_LOCK_TABLES:
unlock_locked_tables(thd);
- if (check_db_used(thd,tables) || end_active_trans(thd))
+ if (check_db_used(thd, all_tables) || end_active_trans(thd))
goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables,0))
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0))
goto error;
thd->in_lock_tables=1;
thd->options|= OPTION_TABLE_LOCK;
- if (!(res= open_and_lock_tables(thd, tables)))
+
+
+
+ if (!(res= open_and_lock_tables(thd, all_tables)))
{
#ifdef HAVE_QUERY_CACHE
if (thd->variables.query_cache_wlock_invalidate)
- query_cache.invalidate_locked_for_write(tables);
+ query_cache.invalidate_locked_for_write(first_table);
#endif /*HAVE_QUERY_CACHE*/
thd->locked_tables=thd->lock;
thd->lock=0;
@@ -3214,26 +3266,24 @@ purposes internal to the MySQL server", MYF(0));
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
break;
}
- case SQLCOM_CREATE_FUNCTION:
+ case SQLCOM_CREATE_FUNCTION: // UDF function
+ {
+ sp_head *sph;
if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
break;
#ifdef HAVE_DLOPEN
+ if ((sph= sp_find_function(thd, lex->spname)))
+ {
+ net_printf(thd, ER_UDF_EXISTS, lex->spname->m_name.str);
+ goto error;
+ }
if (!(res = mysql_create_function(thd,&lex->udf)))
send_ok(thd);
#else
res= -1;
#endif
break;
- case SQLCOM_DROP_FUNCTION:
- if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
- break;
-#ifdef HAVE_DLOPEN
- if (!(res = mysql_drop_function(thd,&lex->udf.name)))
- send_ok(thd);
-#else
- res= -1;
-#endif
- break;
+ }
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_DROP_USER:
{
@@ -3241,7 +3291,6 @@ purposes internal to the MySQL server", MYF(0));
break;
if (!(res= mysql_drop_user(thd, lex->users_list)))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3257,7 +3306,6 @@ purposes internal to the MySQL server", MYF(0));
break;
if (!(res = mysql_revoke_all(thd, lex->users_list)))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3271,9 +3319,10 @@ purposes internal to the MySQL server", MYF(0));
case SQLCOM_GRANT:
{
if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
- tables && tables->db ? tables->db : select_lex->db,
- tables ? &tables->grant.privilege : 0,
- tables ? 0 : 1,0))
+ ((first_table && first_table->db) ?
+ first_table->db : select_lex->db),
+ first_table ? &first_table->grant.privilege : 0,
+ first_table ? 0 : 1, 0))
goto error;
/*
@@ -3295,7 +3344,7 @@ purposes internal to the MySQL server", MYF(0));
{
if (check_access(thd, UPDATE_ACL, "mysql",0,1,0))
goto error;
- break; // We are allowed to do changes
+ break; // We are allowed to do changes
}
}
}
@@ -3312,24 +3361,21 @@ purposes internal to the MySQL server", MYF(0));
user->host.str);
}
}
- if (tables)
+ if (first_table)
{
if (grant_option && check_grant(thd,
(lex->grant | lex->grant_tot_col |
GRANT_ACL),
- tables, 0, UINT_MAX, 0))
+ all_tables, 0, UINT_MAX, 0))
goto error;
- if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
- lex->grant,
- lex->sql_command == SQLCOM_REVOKE)))
+ if (!(res = mysql_table_grant(thd, all_tables, lex->users_list,
+ lex->columns, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE)) &&
+ mysql_bin_log.is_open())
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
- mysql_bin_log.write(&qinfo);
- }
+ thd->clear_error();
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ mysql_bin_log.write(&qinfo);
}
}
else
@@ -3344,7 +3390,6 @@ purposes internal to the MySQL server", MYF(0));
lex->sql_command == SQLCOM_REVOKE);
if (!res)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -3371,14 +3416,14 @@ purposes internal to the MySQL server", MYF(0));
lex->no_write_to_binlog= 1;
case SQLCOM_FLUSH:
{
- if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables))
+ if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, all_tables))
goto error;
/*
reload_acl_and_cache() will tell us if we are allowed to write to the
binlog or not.
*/
bool write_to_binlog;
- if (reload_acl_and_cache(thd, lex->type, tables, &write_to_binlog))
+ if (reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
send_error(thd, 0);
else
{
@@ -3388,7 +3433,6 @@ purposes internal to the MySQL server", MYF(0));
*/
if (!lex->no_write_to_binlog && write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3400,7 +3444,7 @@ purposes internal to the MySQL server", MYF(0));
break;
}
case SQLCOM_KILL:
- kill_one_thread(thd,lex->thread_id);
+ kill_one_thread(thd,lex->thread_id, lex->type & ONLY_KILL_QUERY);
break;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_SHOW_GRANTS:
@@ -3414,27 +3458,30 @@ purposes internal to the MySQL server", MYF(0));
break;
#endif
case SQLCOM_HA_OPEN:
- if (check_db_used(thd,tables) ||
- check_table_access(thd,SELECT_ACL, tables,0))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables) ||
+ check_table_access(thd, SELECT_ACL, all_tables, 0))
goto error;
- res = mysql_ha_open(thd, tables);
+ res= mysql_ha_open(thd, first_table);
break;
case SQLCOM_HA_CLOSE:
- if (check_db_used(thd,tables))
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
+ if (check_db_used(thd, all_tables))
goto error;
- res = mysql_ha_close(thd, tables);
+ res= mysql_ha_close(thd, first_table);
break;
case SQLCOM_HA_READ:
+ DBUG_ASSERT(first_table == all_tables && first_table != 0);
/*
There is no need to check for table permissions here, because
if a user has no permissions to read a table, he won't be
able to open it (with SQLCOM_HA_OPEN) in the first place.
*/
- if (check_db_used(thd,tables))
+ if (check_db_used(thd, all_tables))
goto error;
- res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir,
- lex->insert_list, lex->ha_rkey_mode, select_lex->where,
- select_lex->select_limit, select_lex->offset_limit);
+ res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->backup_dir,
+ lex->insert_list, lex->ha_rkey_mode, select_lex->where,
+ select_lex->select_limit, select_lex->offset_limit);
break;
case SQLCOM_BEGIN:
@@ -3512,40 +3559,357 @@ purposes internal to the MySQL server", MYF(0));
else
res= -1;
break;
+ case SQLCOM_CREATE_PROCEDURE:
+ case SQLCOM_CREATE_SPFUNCTION:
+ {
+ if (!lex->sphead)
+ {
+ res= -1; // Shouldn't happen
+ break;
+ }
+ uint namelen;
+ char *name= lex->sphead->name(&namelen);
+#ifdef HAVE_DLOPEN
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
+ {
+ udf_func *udf = find_udf(name, namelen);
+
+ if (udf)
+ {
+ net_printf(thd, ER_UDF_EXISTS, name);
+ delete lex->sphead;
+ lex->sphead=0;
+ goto error;
+ }
+ }
+#endif
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
+ !lex->sphead->m_has_return)
+ {
+ net_printf(thd, ER_SP_NORETURN, name);
+ delete lex->sphead;
+ lex->sphead=0;
+ goto error;
+ }
+
+ res= lex->sphead->create(thd);
+ switch (res) {
+ case SP_OK:
+ send_ok(thd);
+ lex->unit.cleanup();
+ delete lex->sphead;
+ lex->sphead= 0;
+ break;
+ case SP_WRITE_ROW_FAILED:
+ net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name);
+ lex->unit.cleanup();
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ case SP_NO_DB_ERROR:
+ net_printf(thd, ER_BAD_DB_ERROR, lex->sphead->m_db.str);
+ lex->unit.cleanup();
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ default:
+ net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name);
+ lex->unit.cleanup();
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_CALL:
+ {
+ sp_head *sp;
+
+ if (!(sp= sp_find_procedure(thd, lex->spname)))
+ {
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE",
+ lex->spname->m_qname.str);
+ goto error;
+ }
+ else
+ {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ st_sp_security_context save_ctx;
+#endif
+ ha_rows select_limit;
+ uint smrx;
+ LINT_INIT(smrx);
+
+ /* In case the arguments are subselects... */
+ if (all_tables &&
+ ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) ||
+ (res= open_and_lock_tables(thd, all_tables))))
+ {
+ break;
+ }
+
+#ifndef EMBEDDED_LIBRARY
+ /*
+ When executing substatements, they're assumed to send_error when
+ it happens, but not to send_ok.
+ */
+ my_bool nsok= thd->net.no_send_ok;
+ thd->net.no_send_ok= TRUE;
+#endif
+ if (sp->m_multi_results)
+ {
+ if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
+ {
+ send_error(thd, ER_SP_BADSELECT);
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ goto error;
+ }
+ smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS;
+ thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_change_security_context(thd, sp, &save_ctx);
+#endif
+ select_limit= thd->variables.select_limit;
+ thd->variables.select_limit= HA_POS_ERROR;
+
+ res= sp->execute_procedure(thd, &lex->value_list);
+
+ thd->variables.select_limit= select_limit;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, sp, &save_ctx);
+#endif
+
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ if (sp->m_multi_results)
+ {
+ if (! smrx)
+ thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ }
+
+ if (res == 0)
+ send_ok(thd, (ulong) (thd->row_count_func < 0 ? 0 : thd->row_count_func));
+ else
+ goto error; // Substatement should already have sent error
+ }
+ break;
+ }
+ case SQLCOM_ALTER_PROCEDURE:
+ case SQLCOM_ALTER_FUNCTION:
+ {
+ res= -1;
+ uint newname_len= 0;
+ if (lex->name)
+ newname_len= strlen(lex->name);
+ if (newname_len > NAME_LEN)
+ {
+ net_printf(thd, ER_TOO_LONG_IDENT, lex->name);
+ goto error;
+ }
+ if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
+ res= sp_update_procedure(thd, lex->spname,
+ lex->name, newname_len, &lex->sp_chistics);
+ else
+ res= sp_update_function(thd, lex->spname,
+ lex->name, newname_len, &lex->sp_chistics);
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
+ lex->spname->m_qname.str);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex),
+ lex->spname->m_qname.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_DROP_PROCEDURE:
+ case SQLCOM_DROP_FUNCTION:
+ {
+ if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
+ res= sp_drop_procedure(thd, lex->spname);
+ else
+ {
+ res= sp_drop_function(thd, lex->spname);
+#ifdef HAVE_DLOPEN
+ if (res == SP_KEY_NOT_FOUND)
+ {
+ udf_func *udf = find_udf(lex->spname->m_name.str,
+ lex->spname->m_name.length);
+ if (udf)
+ {
+ if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0))
+ goto error;
+ if (!(res = mysql_drop_function(thd,&lex->spname->m_name)))
+ {
+ send_ok(thd);
+ break;
+ }
+ }
+ }
+#endif
+ }
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ if (lex->drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ res= 0;
+ send_ok(thd);
+ break;
+ }
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
+ lex->spname->m_qname.str);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex),
+ lex->spname->m_qname.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_CREATE_PROC:
+ {
+ res= -1;
+ if (lex->spname->m_name.length > NAME_LEN)
+ {
+ net_printf(thd, ER_TOO_LONG_IDENT, lex->spname->m_name.str);
+ goto error;
+ }
+ res= sp_show_create_procedure(thd, lex->spname);
+ if (res != SP_OK)
+ { /* We don't distinguish between errors for now */
+ net_printf(thd, ER_SP_DOES_NOT_EXIST,
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ res= 0;
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_CREATE_FUNC:
+ {
+ if (lex->spname->m_name.length > NAME_LEN)
+ {
+ net_printf(thd, ER_TOO_LONG_IDENT, lex->spname->m_name.str);
+ goto error;
+ }
+ res= sp_show_create_function(thd, lex->spname);
+ if (res != SP_OK)
+ { /* We don't distinguish between errors for now */
+ net_printf(thd, ER_SP_DOES_NOT_EXIST,
+ SP_COM_STRING(lex), lex->spname->m_name.str);
+ res= 0;
+ goto error;
+ }
+ res= 0;
+ break;
+ }
+ case SQLCOM_SHOW_STATUS_PROC:
+ {
+ res= sp_show_status_procedure(thd, (lex->wild ?
+ lex->wild->ptr() : NullS));
+ break;
+ }
+ case SQLCOM_SHOW_STATUS_FUNC:
+ {
+ res= sp_show_status_function(thd, (lex->wild ?
+ lex->wild->ptr() : NullS));
+ break;
+ }
+ case SQLCOM_CREATE_VIEW:
+ {
+ res= mysql_create_view(thd, thd->lex->create_view_mode);
+ break;
+ }
+ case SQLCOM_DROP_VIEW:
+ {
+ if (check_table_access(thd, DROP_ACL, all_tables, 0))
+ goto error;
+ if (end_active_trans(thd))
+ {
+ res= -1;
+ break;
+ }
+ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode);
+ break;
+ }
default: /* Impossible */
send_ok(thd);
break;
}
- thd->proc_info="query end"; // QQ
+ thd->proc_info="query end";
if (thd->one_shot_set)
+ {
+ /*
+ If this is a SET, do nothing. This is to allow mysqlbinlog to print
+ many SET commands (in this case we want the charset temp setting to
+ live until the real query). This is also needed so that SET
+ CHARACTER_SET_CLIENT... does not cancel itself immediately.
+ */
+ if (lex->sql_command != SQLCOM_SET_OPTION)
{
- /*
- If this is a SET, do nothing. This is to allow mysqlbinlog to print
- many SET commands (in this case we want the charset temp setting to
- live until the real query). This is also needed so that SET
- CHARACTER_SET_CLIENT... does not cancel itself immediately.
- */
- if (lex->sql_command != SQLCOM_SET_OPTION)
- {
- thd->variables.character_set_client=
- global_system_variables.character_set_client;
- thd->variables.collation_connection=
- global_system_variables.collation_connection;
- thd->variables.collation_database=
- global_system_variables.collation_database;
- thd->variables.collation_server=
- global_system_variables.collation_server;
- thd->update_charset();
- thd->variables.time_zone=
- global_system_variables.time_zone;
- thd->one_shot_set= 0;
- }
+ thd->variables.character_set_client=
+ global_system_variables.character_set_client;
+ thd->variables.collation_connection=
+ global_system_variables.collation_connection;
+ thd->variables.collation_database=
+ global_system_variables.collation_database;
+ thd->variables.collation_server=
+ global_system_variables.collation_server;
+ thd->update_charset();
+ thd->variables.time_zone=
+ global_system_variables.time_zone;
+ thd->one_shot_set= 0;
}
+ }
+
+ /*
+ The return value for ROW_COUNT() is "implementation dependent" if
+ the statement is not DELETE, INSERT or UPDATE (or a CALL executing
+ such a statement), but -1 is what JDBC and ODBC wants.
+ */
+ switch (lex->sql_command) {
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ case SQLCOM_REPLACE:
+ case SQLCOM_INSERT:
+ case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_INSERT_SELECT:
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_CALL:
+ break;
+ default:
+ thd->row_count_func= -1;
+ }
+
+ /*
+ We end up here if res == 0 and send_ok() has been done,
+ or res != 0 and no send_error() has yet been done.
+ */
if (res < 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ send_error(thd,thd->killed_errno());
+ DBUG_RETURN(res);
error:
- DBUG_VOID_RETURN;
+ /* We end up here if send_error() has already been done. */
+ DBUG_RETURN(-1);
}
@@ -3557,27 +3921,28 @@ error:
check_one_table_access()
thd Thread handler
privilege requested privelage
- tables table list of command
+ all_tables global table list of query
RETURN
0 - OK
1 - access denied, error is sent to client
*/
-int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables)
+bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
{
- if (check_access(thd, privilege, tables->db, &tables->grant.privilege,0,0))
+ if (check_access(thd, privilege, all_tables->db,
+ &all_tables->grant.privilege, 0, 0))
return 1;
/* Show only 1 table for check_grant */
- if (grant_option && check_grant(thd, privilege, tables, 0, 1, 0))
+ if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0))
return 1;
/* Check rights on tables of subselect (if exists) */
TABLE_LIST *subselects_tables;
- if ((subselects_tables= tables->next))
+ if ((subselects_tables= all_tables->next_global))
{
- if ((check_table_access(thd, SELECT_ACL, subselects_tables,0)))
+ if ((check_table_access(thd, SELECT_ACL, subselects_tables, 0)))
return 1;
}
return 0;
@@ -3607,13 +3972,13 @@ bool
check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
bool dont_check_global_grants, bool no_errors)
{
- DBUG_ENTER("check_access");
- DBUG_PRINT("enter",("want_access: %lu master_access: %lu", want_access,
- thd->master_access));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
ulong db_access;
#endif
ulong dummy;
+ DBUG_ENTER("check_access");
+ DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu",
+ db ? db : "", want_access, thd->master_access));
if (save_priv)
*save_priv=0;
else
@@ -3621,8 +3986,9 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
{
+ DBUG_PRINT("error",("No database"));
if (!no_errors)
- send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
+ send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */
DBUG_RETURN(TRUE); /* purecov: tested */
}
@@ -3647,6 +4013,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) ||
! db && dont_check_global_grants)
{ // We can never grant this
+ DBUG_PRINT("error",("No possible access"));
if (!no_errors)
net_printf(thd,ER_ACCESS_DENIED_ERROR,
thd->priv_user,
@@ -3663,15 +4030,19 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
thd->priv_user, db, test(want_access & GRANT_ACL));
else
db_access=thd->db_access;
- // Remove SHOW attribute and access rights we already have
+ /* Remove SHOW attribute and access rights we already have */
want_access &= ~(thd->master_access | EXTRA_ACL);
+ DBUG_PRINT("info",("db_access: %lu want_access: %lu",
+ db_access, want_access));
db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
/* grant_option is set if there exists a single table or column grant */
if (db_access == want_access ||
- ((grant_option && !dont_check_global_grants) &&
+ (grant_option && !dont_check_global_grants &&
!(want_access & ~(db_access | TABLE_ACLS))))
DBUG_RETURN(FALSE); /* Ok */
+
+ DBUG_PRINT("error",("Access denied"));
if (!no_errors)
net_printf(thd,ER_DBACCESS_DENIED_ERROR,
thd->priv_user,
@@ -3729,7 +4100,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
uint found=0;
ulong found_access=0;
TABLE_LIST *org_tables=tables;
- for (; tables ; tables=tables->next)
+ for (; tables; tables= tables->next_global)
{
if (tables->derived || (tables->table && (int)tables->table->tmp_table))
continue;
@@ -3759,6 +4130,42 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
return FALSE;
}
+
+/*
+ Check if the given table has any of the asked privileges
+
+ SYNOPSIS
+ check_some_access()
+ thd Thread handler
+ want_access Bitmap of possible privileges to check for
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+
+bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
+{
+ ulong access;
+ DBUG_ENTER("check_some_access");
+
+ /* This loop will work as long as we have less than 32 privileges */
+ for (access= 1; access < want_access ; access<<= 1)
+ {
+ if (access & want_access)
+ {
+ if (!check_access(thd, access, table->db,
+ &table->grant.privilege, 0, 1) &&
+ !grant_option || !check_grant(thd, access, table, 0, 1, 1))
+ DBUG_RETURN(0);
+ }
+ }
+ DBUG_PRINT("exit",("no matching access rights"));
+ DBUG_RETURN(1);
+}
+
+
bool check_merge_table_access(THD *thd, char *db,
TABLE_LIST *table_list)
{
@@ -3767,7 +4174,7 @@ bool check_merge_table_access(THD *thd, char *db,
{
/* Check that all tables use the current database */
TABLE_LIST *tmp;
- for (tmp=table_list; tmp ; tmp=tmp->next)
+ for (tmp= table_list; tmp; tmp= tmp->next_local)
{
if (!tmp->db || !tmp->db[0])
tmp->db=db;
@@ -3781,7 +4188,7 @@ bool check_merge_table_access(THD *thd, char *db,
static bool check_db_used(THD *thd,TABLE_LIST *tables)
{
- for (; tables ; tables=tables->next)
+ for (; tables; tables= tables->next_global)
{
if (!tables->db)
{
@@ -3865,7 +4272,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize)
****************************************************************************/
void
-mysql_init_query(THD *thd, uchar *buf, uint length)
+mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly)
{
DBUG_ENTER("mysql_init_query");
LEX *lex= thd->lex;
@@ -3875,6 +4282,7 @@ mysql_init_query(THD *thd, uchar *buf, uint length)
lex->select_lex.init_query();
lex->value_list.empty();
lex->param_list.empty();
+ lex->view_list.empty();
lex->unit.next= lex->unit.master=
lex->unit.link_next= lex->unit.return_to=0;
lex->unit.prev= lex->unit.link_prev= 0;
@@ -3885,26 +4293,37 @@ mysql_init_query(THD *thd, uchar *buf, uint length)
lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
lex->select_lex.options=0;
+ lex->select_lex.init_order();
+ lex->select_lex.group_list.empty();
lex->describe= 0;
- lex->derived_tables= FALSE;
+ lex->derived_tables= 0;
+ lex->view_prepare_mode= FALSE;
lex->lock_option= TL_READ;
lex->found_colon= 0;
lex->safe_to_cache_query= 1;
lex->time_zone_tables_used= 0;
+ lex->query_tables= 0;
+ lex->query_tables_last= &lex->query_tables;
+ lex->variables_used= 0;
+ lex->select_lex.parent_lex= lex;
+ lex->empty_field_list_on_rset= 0;
lex_start(thd, buf, length);
- thd->select_number= lex->select_lex.select_number= 1;
- thd->free_list= 0;
- thd->total_warn_count=0; // Warnings for this query
- thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
- thd->sent_row_count= thd->examined_row_count= 0;
- thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
- thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
- SERVER_QUERY_NO_INDEX_USED |
- SERVER_QUERY_NO_GOOD_INDEX_USED);
- thd->tmp_table_used= 0;
- if (opt_bin_log)
- reset_dynamic(&thd->user_var_events);
- thd->clear_error();
+ if (! lexonly)
+ {
+ thd->select_number= lex->select_lex.select_number= 1;
+ thd->free_list= 0;
+ thd->total_warn_count=0; // Warnings for this query
+ thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
+ thd->sent_row_count= thd->examined_row_count= 0;
+ thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
+ thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
+ SERVER_QUERY_NO_INDEX_USED |
+ SERVER_QUERY_NO_GOOD_INDEX_USED);
+ thd->tmp_table_used= 0;
+ if (opt_bin_log)
+ reset_dynamic(&thd->user_var_events);
+ thd->clear_error();
+ }
DBUG_VOID_RETURN;
}
@@ -3933,6 +4352,7 @@ mysql_new_select(LEX *lex, bool move_down)
select_lex->select_number= ++lex->thd->select_number;
select_lex->init_query();
select_lex->init_select();
+ select_lex->parent_lex= lex;
if (move_down)
{
/* first select_lex of subselect or derived table */
@@ -3947,10 +4367,15 @@ mysql_new_select(LEX *lex, bool move_down)
unit->link_prev= 0;
unit->return_to= lex->current_select;
select_lex->include_down(unit);
- // TODO: assign resolve_mode for fake subquery after merging with new tree
+ /* TODO: assign resolve_mode for fake subquery after merging with new tree */
}
else
{
+ if (lex->current_select->order_list.first && !lex->current_select->braces)
+ {
+ net_printf(lex->thd, ER_WRONG_USAGE, "UNION", "ORDER BY");
+ return 1;
+ }
select_lex->include_neighbour(lex->current_select);
SELECT_LEX_UNIT *unit= select_lex->master_unit();
SELECT_LEX *fake= unit->fake_select_lex;
@@ -4017,6 +4442,8 @@ void mysql_init_multi_delete(LEX *lex)
lex->select_lex.select_limit= lex->unit.select_limit_cnt=
HA_POS_ERROR;
lex->select_lex.table_list.save_and_clear(&lex->auxilliary_table_list);
+ lex->query_tables= 0;
+ lex->query_tables_last= &lex->query_tables;
}
@@ -4045,19 +4472,38 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
#endif
{
if (thd->net.report_error)
+ {
send_error(thd, 0, NullS);
+ if (thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
+ }
else
{
mysql_execute_command(thd);
query_cache_end_of_result(thd);
}
}
+ lex->unit.cleanup();
}
else
{
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error));
query_cache_abort(&thd->net);
+ lex->unit.cleanup();
+ if (thd->lex->sphead)
+ {
+ /* Clean up after failed stored procedure/function */
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
}
thd->proc_info="freeing items";
thd->end_statement();
@@ -4080,13 +4526,14 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
{
LEX *lex= thd->lex;
bool error= 0;
+ DBUG_ENTER("mysql_test_parse_for_slave");
mysql_init_query(thd, (uchar*) inBuf, length);
if (!yyparse((void*) thd) && ! thd->is_fatal_error &&
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
- error= 1; /* Ignore question */
+ error= 1; /* Ignore question */
thd->end_statement();
- return error;
+ DBUG_RETURN(error);
}
#endif
@@ -4583,6 +5030,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
{
register TABLE_LIST *ptr;
char *alias_str;
+ LEX *lex= thd->lex;
DBUG_ENTER("add_table_to_list");
if (!table)
@@ -4634,6 +5082,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
ptr->derived= table->sel;
+ ptr->select_lex= lex->current_select;
ptr->cacheable_table= 1;
if (use_index_arg)
ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg,
@@ -4647,7 +5096,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
{
for (TABLE_LIST *tables=(TABLE_LIST*) table_list.first ;
tables ;
- tables=tables->next)
+ tables=tables->next_local)
{
if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) &&
!strcmp(ptr->db, tables->db))
@@ -4657,12 +5106,247 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
}
}
}
- table_list.link_in_list((byte*) ptr, (byte**) &ptr->next);
+ /* Link table in local list (list for current select) */
+ table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
+ /* Link table in global list (all used tables) */
+ *(ptr->prev_global= lex->query_tables_last)= ptr;
+ lex->query_tables_last= &ptr->next_global;
DBUG_RETURN(ptr);
}
/*
+ Initialize a new table list for a nested join
+
+ SYNOPSIS
+ init_table_list()
+ thd current thread
+
+ DESCRIPTION
+ The function initializes a structure of the TABLE_LIST type
+ for a nested join. It sets up its nested join list as empty.
+ The created structure is added to the front of the current
+ join list in the st_select_lex object. Then the function
+ changes the current nest level for joins to refer to the newly
+ created empty list after having saved the info on the old level
+ in the initialized structure.
+
+ RETURN VALUE
+ 0, if success
+ 1, otherwise
+*/
+
+bool st_select_lex::init_nested_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ NESTED_JOIN *nested_join;
+ DBUG_ENTER("init_nested_join");
+
+ if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) ||
+ !(nested_join= ptr->nested_join=
+ (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
+ DBUG_RETURN(1);
+ join_list->push_front(ptr);
+ ptr->embedding= embedding;
+ ptr->join_list= join_list;
+ embedding= ptr;
+ join_list= &nested_join->join_list;
+ join_list->empty();
+ DBUG_RETURN(0);
+}
+
+
+/*
+ End a nested join table list
+
+ SYNOPSIS
+ end_nested_join()
+ thd current thread
+
+ DESCRIPTION
+ The function returns to the previous join nest level.
+ If the current level contains only one member, the function
+ moves it one level up, eliminating the nest.
+
+ RETURN VALUE
+ Pointer to TABLE_LIST element added to the total table list, if success
+ 0, otherwise
+*/
+
+TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ DBUG_ENTER("end_nested_join");
+ ptr= embedding;
+ join_list= ptr->join_list;
+ embedding= ptr->embedding;
+ NESTED_JOIN *nested_join= ptr->nested_join;
+ if (nested_join->join_list.elements == 1)
+ {
+ TABLE_LIST *embedded= nested_join->join_list.head();
+ join_list->pop();
+ embedded->join_list= join_list;
+ embedded->embedding= embedding;
+ join_list->push_front(embedded);
+ ptr= embedded;
+ }
+ DBUG_RETURN(ptr);
+}
+
+
+/*
+ Nest last join operation
+
+ SYNOPSIS
+ nest_last_join()
+ thd current thread
+
+ DESCRIPTION
+ The function nest last join operation as if it was enclosed in braces.
+
+ RETURN VALUE
+ Pointer to TABLE_LIST element created for the new nested join, if success
+ 0, otherwise
+*/
+
+TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
+{
+ TABLE_LIST *ptr;
+ NESTED_JOIN *nested_join;
+ DBUG_ENTER("nest_last_join");
+
+ if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) ||
+ !(nested_join= ptr->nested_join=
+ (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
+ DBUG_RETURN(0);
+ ptr->embedding= embedding;
+ ptr->join_list= join_list;
+ List<TABLE_LIST> *embedded_list= &nested_join->join_list;
+ embedded_list->empty();
+ for (int i=0; i < 2; i++)
+ {
+ TABLE_LIST *table= join_list->pop();
+ table->join_list= embedded_list;
+ table->embedding= ptr;
+ embedded_list->push_back(table);
+ }
+ join_list->push_front(ptr);
+ nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
+ DBUG_RETURN(ptr);
+}
+
+
+/*
+ Save names for a join with using clase
+
+ SYNOPSIS
+ save_names_for_using_list
+ tab1 left table in join
+ tab2 right table in join
+
+ DESCRIPTION
+ The function saves the full names of the tables in st_select_lex
+ to be able to build later an on expression to replace the using clause.
+
+ RETURN VALUE
+ None
+*/
+
+void st_select_lex::save_names_for_using_list(TABLE_LIST *tab1,
+ TABLE_LIST *tab2)
+{
+ while (tab1->nested_join)
+ {
+ tab1= tab1->nested_join->join_list.head();
+ }
+ db1= tab1->db;
+ table1= tab1->alias;
+ while (tab2->nested_join)
+ {
+ TABLE_LIST *next;
+ List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list);
+ tab2= it++;
+ while ((next= it++))
+ tab2= next;
+ }
+ db2= tab2->db;
+ table2= tab2->alias;
+}
+
+
+/*
+ Add a table to the current join list
+
+ SYNOPSIS
+ add_joined_table()
+ table the table to add
+
+ DESCRIPTION
+ The function puts a table in front of the current join list
+ of st_select_lex object.
+ Thus, joined tables are put into this list in the reverse order
+ (the most outer join operation follows first).
+
+ RETURN VALUE
+ None
+*/
+
+void st_select_lex::add_joined_table(TABLE_LIST *table)
+{
+ DBUG_ENTER("add_joined_table");
+ join_list->push_front(table);
+ table->join_list= join_list;
+ table->embedding= embedding;
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Convert a right join into equivalent left join
+
+ SYNOPSIS
+ convert_right_join()
+ thd current thread
+
+ DESCRIPTION
+ The function takes the current join list t[0],t[1] ... and
+ effectively converts it into the list t[1],t[0] ...
+ Although the outer_join flag for the new nested table contains
+ JOIN_TYPE_RIGHT, it will be handled as the inner table of a left join
+ operation.
+
+ EXAMPLES
+ SELECT * FROM t1 RIGHT JOIN t2 ON on_expr =>
+ SELECT * FROM t2 LEFT JOIN t1 ON on_expr
+
+ SELECT * FROM t1,t2 RIGHT JOIN t3 ON on_expr =>
+ SELECT * FROM t1,t3 LEFT JOIN t2 ON on_expr
+
+ SELECT * FROM t1,t2 RIGHT JOIN (t3,t4) ON on_expr =>
+ SELECT * FROM t1,(t3,t4) LEFT JOIN t2 ON on_expr
+
+ SELECT * FROM t1 LEFT JOIN t2 ON on_expr1 RIGHT JOIN t3 ON on_expr2 =>
+ SELECT * FROM t3 LEFT JOIN (t1 LEFT JOIN t2 ON on_expr2) ON on_expr1
+
+ RETURN
+ Pointer to the table representing the inner table, if success
+ 0, otherwise
+*/
+
+TABLE_LIST *st_select_lex::convert_right_join()
+{
+ TABLE_LIST *tab2= join_list->pop();
+ TABLE_LIST *tab1= join_list->pop();
+ DBUG_ENTER("convert_right_join");
+
+ join_list->push_front(tab2);
+ join_list->push_front(tab1);
+ tab1->outer_join|= JOIN_TYPE_RIGHT;
+
+ DBUG_RETURN(tab1);
+}
+
+/*
Set lock for all tables in current select level
SYNOPSIS:
@@ -4682,9 +5366,9 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type,
for_update));
- for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first ;
- tables ;
- tables=tables->next)
+ for (TABLE_LIST *tables= (TABLE_LIST*) table_list.first;
+ tables;
+ tables= tables->next_local)
{
tables->lock_type= lock_type;
tables->updating= for_update;
@@ -4701,7 +5385,7 @@ void add_join_on(TABLE_LIST *b,Item *expr)
b->on_expr=expr;
else
{
- // This only happens if you have both a right and left join
+ /* This only happens if you have both a right and left join */
b->on_expr=new Item_cond_and(b->on_expr,expr);
}
b->on_expr->top_level_item();
@@ -4781,7 +5465,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
*/
tmp_write_to_binlog= 0;
mysql_log.new_file(1);
- mysql_update_log.new_file(1);
mysql_bin_log.new_file(1);
mysql_slow_log.new_file(1);
#ifdef HAVE_REPLICATION
@@ -4804,7 +5487,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
if (options & REFRESH_QUERY_CACHE_FREE)
{
query_cache.pack(); // FLUSH QUERY CACHE
- options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory
+ options &= ~REFRESH_QUERY_CACHE; // Don't flush cache, just free memory
}
if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE))
{
@@ -4884,7 +5567,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
This is written such that we have a short lock on LOCK_thread_count
*/
-void kill_one_thread(THD *thd, ulong id)
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
{
THD *tmp;
uint error=ER_NO_SUCH_THREAD;
@@ -4904,7 +5587,7 @@ void kill_one_thread(THD *thd, ulong id)
if ((thd->master_access & SUPER_ACL) ||
!strcmp(thd->user,tmp->user))
{
- tmp->awake(1 /*prepare to die*/);
+ tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
error=0;
}
else
@@ -5120,7 +5803,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
SYNOPSIS
multi_update_precheck()
thd Thread handler
- tables Global table list
+ tables Global/local table list (have to be the same)
RETURN VALUE
0 OK
@@ -5130,12 +5813,11 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
int multi_update_precheck(THD *thd, TABLE_LIST *tables)
{
- DBUG_ENTER("multi_update_precheck");
const char *msg= 0;
TABLE_LIST *table;
LEX *lex= thd->lex;
SELECT_LEX *select_lex= &lex->select_lex;
- TABLE_LIST *update_list= (TABLE_LIST*)select_lex->table_list.first;
+ DBUG_ENTER("multi_update_precheck");
if (select_lex->item_list.elements != lex->value_list.elements)
{
@@ -5146,7 +5828,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
Ensure that we have UPDATE or SELECT privilege for each table
The exact privilege is checked in mysql_multi_update()
*/
- for (table= update_list; table; table= table->next)
+ for (table= tables; table; table= table->next_local)
{
if ((check_access(thd, UPDATE_ACL, table->db,
&table->grant.privilege, 0, 1) ||
@@ -5156,31 +5838,16 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
grant_option && check_grant(thd, SELECT_ACL, table, 0, 1, 0)))
DBUG_RETURN(1);
- /*
- We assign following flag only to copy of table, because it will
- be checked only if query contains subqueries i.e. only if copy exists
- */
- if (table->table_list)
- table->table_list->table_in_update_from_clause= 1;
+ table->table_in_first_from_clause= 1;
}
/*
Is there tables of subqueries?
*/
if (&lex->select_lex != lex->all_selects_list)
{
- for (table= tables; table; table= table->next)
+ for (table= tables; table; table= table->next_global)
{
- if (table->table_in_update_from_clause)
- {
- /*
- If we check table by local TABLE_LIST copy then we should copy
- grants to global table list, because it will be used for table
- opening.
- */
- if (table->table_list)
- table->grant= table->table_list->grant;
- }
- else
+ if (!table->table_in_first_from_clause)
{
if (check_access(thd, SELECT_ACL, table->db,
&table->grant.privilege, 0, 0) ||
@@ -5209,7 +5876,7 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
SYNOPSIS
multi_delete_precheck()
thd Thread handler
- tables Global table list
+ tables Global/local table list
table_count Pointer to table counter
RETURN VALUE
@@ -5219,12 +5886,11 @@ int multi_update_precheck(THD *thd, TABLE_LIST *tables)
*/
int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
{
- DBUG_ENTER("multi_delete_precheck");
SELECT_LEX *select_lex= &thd->lex->select_lex;
TABLE_LIST *aux_tables=
(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
- TABLE_LIST *delete_tables= (TABLE_LIST *)select_lex->table_list.first;
TABLE_LIST *target_tbl;
+ DBUG_ENTER("multi_delete_precheck");
*table_count= 0;
@@ -5239,12 +5905,12 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, MYF(0));
DBUG_RETURN(-1);
}
- for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next)
+ for (target_tbl= aux_tables; target_tbl; target_tbl= target_tbl->next_local)
{
(*table_count)++;
/* All tables in aux_tables must be found in FROM PART */
TABLE_LIST *walk;
- for (walk= delete_tables; walk; walk= walk->next)
+ for (walk= tables; walk; walk= walk->next_local)
{
if (!my_strcasecmp(table_alias_charset,
target_tbl->alias, walk->alias) &&
@@ -5257,14 +5923,8 @@ int multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count)
"MULTI DELETE");
DBUG_RETURN(-1);
}
- if (walk->derived)
- {
- my_error(ER_NON_UPDATABLE_TABLE, MYF(0), target_tbl->real_name,
- "DELETE");
- DBUG_RETURN(-1);
- }
walk->lock_type= target_tbl->lock_type;
- target_tbl->table_list= walk; // Remember corresponding table
+ target_tbl->correspondent_table= walk; // Remember corresponding table
}
DBUG_RETURN(0);
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 1b6c7dbc9bc..5fccdd624de 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -71,9 +71,12 @@ Long data handling:
#include "sql_acl.h"
#include "sql_select.h" // for JOIN
#include <m_ctype.h> // for isspace()
+#include "sp_head.h"
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
#include <mysql.h>
+#else
+#include <mysql_com.h>
#endif
/******************************************************************************
@@ -105,7 +108,7 @@ public:
};
static void execute_stmt(THD *thd, Prepared_statement *stmt,
- String *expanded_query, bool set_context);
+ String *expanded_query);
/******************************************************************************
Implementation
@@ -164,7 +167,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
return my_net_write(net, buff, sizeof(buff)) ||
(stmt->param_count &&
stmt->thd->protocol_simple.send_fields((List<Item> *)
- &stmt->lex->param_list, 0)) ||
+ &stmt->lex->param_list,
+ Protocol::SEND_EOF)) ||
net_flush(net);
return 0;
}
@@ -873,7 +877,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
SYNOPSIS
mysql_test_insert()
stmt prepared statemen handler
- tables list of tables queries
+ tables global/local table list
RETURN VALUE
0 ok
@@ -893,8 +897,6 @@ static int mysql_test_insert(Prepared_statement *stmt,
List_iterator_fast<List_item> its(values_list);
List_item *values;
int res= -1;
- TABLE_LIST *insert_table_list=
- (TABLE_LIST*) lex->select_lex.table_list.first;
my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0);
DBUG_ENTER("mysql_test_insert");
@@ -915,9 +917,9 @@ static int mysql_test_insert(Prepared_statement *stmt,
uint value_count;
ulong counter= 0;
- if ((res= mysql_prepare_insert(thd, table_list, insert_table_list,
- table_list->table, fields, values,
- update_fields, update_values, duplic)))
+ if ((res= mysql_prepare_insert(thd, table_list, table_list->table,
+ fields, values, update_fields,
+ update_values, duplic)))
goto error;
value_count= values->elements;
@@ -933,7 +935,7 @@ static int mysql_test_insert(Prepared_statement *stmt,
MYF(0), counter);
goto error;
}
- if (setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0))
+ if (setup_fields(thd, 0, table_list, *values, 0, 0, 0))
goto error;
}
}
@@ -973,18 +975,24 @@ static int mysql_test_update(Prepared_statement *stmt,
res= -1;
else
{
- TABLE_LIST *update_table_list= (TABLE_LIST *)select->table_list.first;
if (!(res= mysql_prepare_update(thd, table_list,
- update_table_list,
&select->where,
select->order_list.elements,
(ORDER *) select->order_list.first)))
{
- if (setup_fields(thd, 0, update_table_list,
- select->item_list, 1, 0, 0) ||
- setup_fields(thd, 0, update_table_list,
- stmt->lex->value_list, 0, 0, 0))
- res= -1;
+ thd->lex->select_lex.no_wrap_view_item= 1;
+ if (setup_fields(thd, 0, table_list, select->item_list, 1, 0, 0))
+ {
+ res= -1;
+ thd->lex->select_lex.no_wrap_view_item= 0;
+ }
+ else
+ {
+ thd->lex->select_lex.no_wrap_view_item= 0;
+ if (setup_fields(thd, 0, table_list,
+ stmt->lex->value_list, 0, 0, 0))
+ res= -1;
+ }
}
stmt->lex->unit.cleanup();
}
@@ -1089,7 +1097,8 @@ static int mysql_test_select(Prepared_statement *stmt,
else
{
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
- thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
+ thd->protocol_simple.send_fields(&lex->select_lex.item_list,
+ Protocol::SEND_EOF)
#ifndef EMBEDDED_LIBRARY
|| net_flush(&thd->net)
#endif
@@ -1192,8 +1201,9 @@ error:
SYNOPSIS
select_like_statement_test()
- stmt - prepared table handler
- tables - global list of tables
+ stmt - prepared table handler
+ tables - global list of tables
+ specific_prepare - function of command specific prepare
RETURN VALUE
0 success
@@ -1201,7 +1211,8 @@ error:
-1 error, not sent to client
*/
static int select_like_statement_test(Prepared_statement *stmt,
- TABLE_LIST *tables)
+ TABLE_LIST *tables,
+ int (*specific_prepare)(THD *thd))
{
DBUG_ENTER("select_like_statement_test");
THD *thd= stmt->thd;
@@ -1211,6 +1222,9 @@ static int select_like_statement_test(Prepared_statement *stmt,
if (tables && (res= open_and_lock_tables(thd, tables)))
goto end;
+ if (specific_prepare && (res= (*specific_prepare)(thd)))
+ goto end;
+
thd->used_tables= 0; // Updated by setup_fields
// JOIN::prepare calls
@@ -1237,31 +1251,28 @@ end:
1 error, sent to client
-1 error, not sent to client
*/
-static int mysql_test_create_table(Prepared_statement *stmt,
- TABLE_LIST *tables)
+static int mysql_test_create_table(Prepared_statement *stmt)
{
DBUG_ENTER("mysql_test_create_table");
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
SELECT_LEX *select_lex= &lex->select_lex;
int res= 0;
-
/* Skip first table, which is the table we are creating */
- TABLE_LIST *create_table, *create_table_local;
- tables= lex->unlink_first_table(tables, &create_table,
- &create_table_local);
+ bool link_to_local;
+ TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
+ TABLE_LIST *tables= lex->query_tables;
if (!(res= create_table_precheck(thd, tables, create_table)) &&
select_lex->item_list.elements)
{
select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
- res= select_like_statement_test(stmt, tables);
+ res= select_like_statement_test(stmt, tables, 0);
select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
}
/* put tables back for PS rexecuting */
- tables= lex->link_first_table_back(tables, create_table,
- create_table_local);
+ lex->link_first_table_back(create_table, link_to_local);
DBUG_RETURN(res);
}
@@ -1285,7 +1296,7 @@ static int mysql_test_multiupdate(Prepared_statement *stmt,
int res;
if ((res= multi_update_precheck(stmt->thd, tables)))
return res;
- return select_like_statement_test(stmt, tables);
+ return select_like_statement_test(stmt, tables, &mysql_multi_update_prepare);
}
@@ -1313,7 +1324,7 @@ static int mysql_test_multidelete(Prepared_statement *stmt,
uint fake_counter;
if ((res= multi_delete_precheck(stmt->thd, tables, &fake_counter)))
return res;
- return select_like_statement_test(stmt, tables);
+ return select_like_statement_test(stmt, tables, &mysql_multi_delete_prepare);
}
@@ -1339,14 +1350,15 @@ static int mysql_test_insert_select(Prepared_statement *stmt,
return res;
TABLE_LIST *first_local_table=
(TABLE_LIST *)lex->select_lex.table_list.first;
+ DBUG_ASSERT(first_local_table != 0);
/* Skip first table, which is the table we are inserting in */
- lex->select_lex.table_list.first= (byte*) first_local_table->next;
+ lex->select_lex.table_list.first= (byte*) first_local_table->next_local;
/*
insert/replace from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
- res= select_like_statement_test(stmt, tables);
+ res= select_like_statement_test(stmt, tables, &mysql_insert_select_prepare);
/* revert changes*/
lex->select_lex.table_list.first= (byte*) first_local_table;
lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
@@ -1368,7 +1380,7 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
THD *thd= stmt->thd;
LEX *lex= stmt->lex;
SELECT_LEX *select_lex= &lex->select_lex;
- TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
+ TABLE_LIST *tables;
enum enum_sql_command sql_command= lex->sql_command;
int res= 0;
DBUG_ENTER("send_prepare_results");
@@ -1376,12 +1388,9 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
DBUG_PRINT("enter",("command: %d, param_count: %ld",
sql_command, stmt->param_count));
- if ((&lex->select_lex != lex->all_selects_list ||
- lex->time_zone_tables_used) &&
- lex->unit.create_total_list(thd, lex, &tables))
- DBUG_RETURN(1);
+ lex->first_lists_tables_same();
+ tables= lex->query_tables;
-
switch (sql_command) {
case SQLCOM_REPLACE:
case SQLCOM_INSERT:
@@ -1407,7 +1416,7 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
DBUG_RETURN(0);
case SQLCOM_CREATE_TABLE:
- res= mysql_test_create_table(stmt, tables);
+ res= mysql_test_create_table(stmt);
break;
case SQLCOM_DO:
@@ -1448,6 +1457,12 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
case SQLCOM_SHOW_GRANTS:
case SQLCOM_DROP_TABLE:
case SQLCOM_RENAME_TABLE:
+ case SQLCOM_ALTER_TABLE:
+ case SQLCOM_COMMIT:
+ case SQLCOM_CREATE_INDEX:
+ case SQLCOM_DROP_INDEX:
+ case SQLCOM_ROLLBACK:
+ case SQLCOM_TRUNCATE:
break;
default:
@@ -1462,7 +1477,7 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
DBUG_RETURN(text_protocol? 0 : send_prep_stmt(stmt, 0));
error:
if (res < 0)
- send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ send_error(thd,thd->killed_errno());
DBUG_RETURN(1);
}
@@ -1601,6 +1616,13 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
+ if (error && thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
lex_end(lex);
thd->restore_backup_statement(stmt, &thd->stmt_backup);
cleanup_items(stmt->free_list);
@@ -1625,61 +1647,52 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
optimisation.
*/
for (; sl; sl= sl->next_select_in_list())
- {
sl->prep_where= sl->where;
- }
stmt->state= Item_arena::PREPARED;
}
-
DBUG_RETURN(!stmt);
}
/* Reinit statement before execution */
-static void reset_stmt_for_execute(Prepared_statement *stmt)
+void reset_stmt_for_execute(THD *thd, LEX *lex)
{
- THD *thd= stmt->thd;
- LEX *lex= stmt->lex;
SELECT_LEX *sl= lex->all_selects_list;
+ if (lex->empty_field_list_on_rset)
+ {
+ lex->empty_field_list_on_rset= 0;
+ lex->field_list.empty();
+ }
for (; sl; sl= sl->next_select_in_list())
{
- /* remove option which was put by mysql_explain_union() */
- sl->options&= ~SELECT_DESCRIBE;
- /*
- Copy WHERE clause pointers to avoid damaging they by optimisation
- */
- if (sl->prep_where)
+ if (!sl->first_execution)
{
- sl->where= sl->prep_where->copy_andor_structure(thd);
- sl->where->cleanup();
- }
- DBUG_ASSERT(sl->join == 0);
- ORDER *order;
- /* Fix GROUP list */
- for (order= (ORDER *)sl->group_list.first; order; order= order->next)
- order->item= &order->item_ptr;
- /* Fix ORDER list */
- for (order= (ORDER *)sl->order_list.first; order; order= order->next)
- order->item= &order->item_ptr;
+ /* remove option which was put by mysql_explain_union() */
+ sl->options&= ~SELECT_DESCRIBE;
- /*
- TODO: When the new table structure is ready, then have a status bit
- to indicate the table is altered, and re-do the setup_*
- and open the tables back.
- */
- for (TABLE_LIST *tables= (TABLE_LIST*) sl->table_list.first;
- tables;
- tables= tables->next)
- {
/*
- Reset old pointers to TABLEs: they are not valid since the tables
- were closed in the end of previous prepare or execute call.
+ Copy WHERE clause pointers to avoid damaging they by optimisation
*/
- tables->table= 0;
- tables->table_list= 0;
+ if (sl->prep_where)
+ {
+ sl->where= sl->prep_where->copy_andor_structure(thd);
+ sl->where->cleanup();
+ }
+ DBUG_ASSERT(sl->join == 0);
+ ORDER *order;
+ /* Fix GROUP list */
+ for (order= (ORDER *)sl->group_list.first; order; order= order->next)
+ order->item= &order->item_ptr;
+ /* Fix ORDER list */
+ for (order= (ORDER *)sl->order_list.first; order; order= order->next)
+ order->item= &order->item_ptr;
+ }
+ {
+ TABLE_LIST *tables= (TABLE_LIST *)sl->table_list.first;
+ if (tables)
+ tables->setup_is_done= 0;
}
-
{
SELECT_LEX_UNIT *unit= sl->master_unit();
unit->unclean();
@@ -1688,6 +1701,22 @@ static void reset_stmt_for_execute(Prepared_statement *stmt)
unit->reinit_exec_mechanism();
}
}
+
+ /*
+ TODO: When the new table structure is ready, then have a status bit
+ to indicate the table is altered, and re-do the setup_*
+ and open the tables back.
+ */
+ for (TABLE_LIST *tables= lex->query_tables;
+ tables;
+ tables= tables->next_global)
+ {
+ /*
+ Reset old pointers to TABLEs: they are not valid since the tables
+ were closed in the end of previous prepare or execute call.
+ */
+ tables->table= 0;
+ }
lex->current_select= &lex->select_lex;
if (lex->result)
lex->result->cleanup();
@@ -1725,6 +1754,7 @@ static void reset_stmt_params(Prepared_statement *stmt)
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
{
ulong stmt_id= uint4korr(packet);
+ ulong flags= (ulong) ((uchar) packet[4]);
/*
Query text for binary log, or empty string if the query is not put into
binary log.
@@ -1751,6 +1781,28 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN;
}
+ if (flags & (ulong) CURSOR_TYPE_READ_ONLY)
+ {
+ if (stmt->lex->result)
+ {
+ /*
+ If lex->result is set in the parser, this is not a SELECT
+ statement: we can't open a cursor for it.
+ */
+ flags= 0;
+ }
+ else
+ {
+ if (!stmt->cursor &&
+ !(stmt->cursor= new (&stmt->mem_root) Cursor()))
+ {
+ send_error(thd, ER_OUT_OF_RESOURCES);
+ DBUG_VOID_RETURN;
+ }
+ /* If lex->result is set, mysql_execute_command will use it */
+ stmt->lex->result= &stmt->cursor->result;
+ }
+ }
#ifndef EMBEDDED_LIBRARY
if (stmt->param_count)
{
@@ -1770,14 +1822,55 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
goto set_params_data_err;
#endif
DBUG_ASSERT(thd->free_list == NULL);
+ thd->stmt_backup.set_statement(thd);
+ thd->set_statement(stmt);
+ thd->current_arena= stmt;
+ reset_stmt_for_execute(thd, stmt->lex);
+ /* From now cursors assume that thd->mem_root is clean */
+ if (expanded_query.length() &&
+ alloc_query(thd, (char *)expanded_query.ptr(),
+ expanded_query.length()+1))
+ {
+ my_error(ER_OUTOFMEMORY, 0, expanded_query.length());
+ goto err;
+ }
+
thd->protocol= &thd->protocol_prep; // Switch to binary protocol
- execute_stmt(thd, stmt, &expanded_query, true);
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(),QUERY_PRIOR);
+ mysql_execute_command(thd);
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(), WAIT_PRIOR);
thd->protocol= &thd->protocol_simple; // Use normal protocol
+
+ if (flags & (ulong) CURSOR_TYPE_READ_ONLY)
+ {
+ if (stmt->cursor->is_open())
+ stmt->cursor->init_from_thd(thd);
+ thd->set_item_arena(&thd->stmt_backup);
+ }
+ else
+ {
+ thd->lex->unit.cleanup();
+ cleanup_items(stmt->free_list);
+ reset_stmt_params(stmt);
+ close_thread_tables(thd); /* to close derived tables */
+ /*
+ Free items that were created during this execution of the PS by
+ query optimizer.
+ */
+ free_items(thd->free_list);
+ thd->free_list= 0;
+ }
+
+ thd->set_statement(&thd->stmt_backup);
+ thd->current_arena= 0;
DBUG_VOID_RETURN;
set_params_data_err:
reset_stmt_params(stmt);
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
+err:
send_error(thd);
DBUG_VOID_RETURN;
}
@@ -1823,7 +1916,9 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
send_error(thd);
}
- execute_stmt(thd, stmt, &expanded_query, false);
+ thd->current_arena= stmt;
+ execute_stmt(thd, stmt, &expanded_query);
+ thd->current_arena= 0;
DBUG_VOID_RETURN;
}
@@ -1838,17 +1933,16 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
placeholders replaced with actual values. Otherwise empty
string.
NOTES
- Caller must set parameter values and thd::protocol.
- thd->free_list is assumed to be garbage.
+ Caller must set parameter values and thd::protocol.
*/
static void execute_stmt(THD *thd, Prepared_statement *stmt,
- String *expanded_query, bool set_context)
+ String *expanded_query)
{
DBUG_ENTER("execute_stmt");
- if (set_context)
- thd->set_n_backup_statement(stmt, &thd->stmt_backup);
- reset_stmt_for_execute(stmt);
+
+ thd->set_n_backup_statement(stmt, &thd->stmt_backup);
+ reset_stmt_for_execute(thd, stmt->lex);
if (expanded_query->length() &&
alloc_query(thd, (char *)expanded_query->ptr(),
@@ -1868,22 +1962,77 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
mysql_execute_command(thd);
- thd->lex->unit.cleanup();
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
+ thd->lex->unit.cleanup();
+ cleanup_items(stmt->free_list);
+ reset_stmt_params(stmt);
+ close_thread_tables(thd); // to close derived tables
+ thd->set_statement(&thd->stmt_backup);
/* Free Items that were created during this execution of the PS. */
free_items(thd->free_list);
+ /*
+ In the rest of prepared statements code we assume that free_list
+ never points to garbage: keep this predicate true.
+ */
thd->free_list= 0;
if (stmt->state == Item_arena::PREPARED)
{
thd->current_arena= thd;
stmt->state= Item_arena::EXECUTED;
}
- cleanup_items(stmt->free_list);
- reset_stmt_params(stmt);
- close_thread_tables(thd); // to close derived tables
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ COM_FETCH handler: fetches requested amount of rows from cursor
+ SYNOPSIS
+*/
+
+void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
+{
+ /* assume there is always place for 8-16 bytes */
+ ulong stmt_id= uint4korr(packet);
+ ulong num_rows= uint4korr(packet+=4);
+ Statement *stmt;
+ int error;
+
+ DBUG_ENTER("mysql_stmt_fetch");
+
+ if (!(stmt= thd->stmt_map.find(stmt_id)) ||
+ !stmt->cursor ||
+ !stmt->cursor->is_open())
+ {
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "fetch");
+ send_error(thd);
+ DBUG_VOID_RETURN;
+ }
+
+ thd->stmt_backup.set_statement(thd);
+ thd->stmt_backup.set_item_arena(thd);
+ thd->set_statement(stmt);
+ stmt->cursor->init_thd(thd);
+
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(), QUERY_PRIOR);
+
+ thd->protocol= &thd->protocol_prep; // Switch to binary protocol
+ error= stmt->cursor->fetch(num_rows);
+ thd->protocol= &thd->protocol_simple; // Use normal protocol
+
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(), WAIT_PRIOR);
+
+ /* Restore THD state */
+ stmt->cursor->reset_thd(thd);
thd->set_statement(&thd->stmt_backup);
+ thd->set_item_arena(&thd->stmt_backup);
+
+ if (error && error != -4)
+ send_error(thd, ER_OUT_OF_RESOURCES);
+
DBUG_VOID_RETURN;
}
@@ -2062,8 +2211,11 @@ void Prepared_statement::setup_set_params()
}
}
+
Prepared_statement::~Prepared_statement()
{
+ if (cursor)
+ cursor->Cursor::~Cursor();
free_items(free_list);
delete lex->result;
}
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index afaf2ed0923..3298eb68a91 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -64,10 +64,10 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
table_list= reverse_table_list(table_list);
/* Find the last renamed table */
- for (table=table_list ;
- table->next != ren_table ;
- table=table->next->next) ;
- table=table->next->next; // Skip error table
+ for (table= table_list;
+ table->next_local != ren_table ;
+ table= table->next_local->next_local) ;
+ table= table->next_local->next_local; // Skip error table
/* Revert to old names */
rename_tables(thd, table, 1);
@@ -80,7 +80,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
/* Lets hope this doesn't fail as the result will be messy */
if (!error)
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -90,7 +89,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
send_ok(thd);
}
- unlock_table_names(thd,table_list);
+ unlock_table_names(thd, table_list);
err:
pthread_mutex_unlock(&LOCK_open);
@@ -115,8 +114,8 @@ static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list)
while (table_list)
{
- TABLE_LIST *next= table_list->next;
- table_list->next= prev;
+ TABLE_LIST *next= table_list->next_local;
+ table_list->next_local= prev;
prev= table_list;
table_list= next;
}
@@ -135,13 +134,13 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error)
TABLE_LIST *ren_table,*new_table;
DBUG_ENTER("rename_tables");
- for (ren_table=table_list ; ren_table ; ren_table=new_table->next)
+ for (ren_table= table_list; ren_table; ren_table= new_table->next_local)
{
db_type table_type;
char name[FN_REFLEN];
const char *new_alias, *old_alias;
- new_table=ren_table->next;
+ new_table= ren_table->next_local;
if (lower_case_table_names == 2)
{
old_alias= ren_table->alias;
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 32c5f0bfdab..9e38a65d412 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -48,16 +48,34 @@ int check_binlog_magic(IO_CACHE* log, const char** errmsg)
return 0;
}
+ /*
+ fake_rotate_event() builds a fake (=which does not exist physically in any
+ binlog) Rotate event, which contains the name of the binlog we are going to
+ send to the slave (because the slave may not know it if it just asked for
+ MASTER_LOG_FILE='', MASTER_LOG_POS=4).
+ < 4.0.14, fake_rotate_event() was called only if the requested pos was
+ 4. After this version we always call it, so that a 3.23.58 slave can rely on
+ it to detect if the master is 4.0 (and stop) (the _fake_ Rotate event has
+ zeros in the good positions which, by chance, make it possible for the 3.23
+ slave to detect that this event is unexpected) (this is luck which happens
+ because the master and slave disagree on the size of the header of
+ Log_event).
+
+ Relying on the event length of the Rotate event instead of these well-placed
+ zeros was not possible as Rotate events have a variable-length part.
+*/
+
static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
- ulonglong position, const char**errmsg)
+ ulonglong position, const char** errmsg)
{
+ DBUG_ENTER("fake_rotate_event");
char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN];
- memset(header, 0, 4); // when does not matter
+ memset(header, 0, 4); // 'when' (the timestamp) does not matter, is set to 0
header[EVENT_TYPE_OFFSET] = ROTATE_EVENT;
char* p = log_file_name+dirname_length(log_file_name);
uint ident_len = (uint) strlen(p);
- ulong event_len = ident_len + ROTATE_EVENT_OVERHEAD;
+ ulong event_len = ident_len + LOG_EVENT_HEADER_LEN + ROTATE_HEADER_LEN;
int4store(header + SERVER_ID_OFFSET, server_id);
int4store(header + EVENT_LEN_OFFSET, event_len);
int2store(header + FLAGS_OFFSET, 0);
@@ -72,9 +90,9 @@ static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
if (my_net_write(net, (char*)packet->ptr(), packet->length()))
{
*errmsg = "failed on my_net_write()";
- return -1;
+ DBUG_RETURN(-1);
}
- return 0;
+ DBUG_RETURN(0);
}
static int send_file(THD *thd)
@@ -310,6 +328,36 @@ int purge_master_logs_before_date(THD* thd, time_t purge_time)
return purge_error_message(thd ,res);
}
+int test_for_non_eof_log_read_errors(int error, const char **errmsg)
+{
+ if (error == LOG_READ_EOF)
+ return 0;
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ switch (error) {
+ case LOG_READ_BOGUS:
+ *errmsg = "bogus data in log event";
+ break;
+ case LOG_READ_TOO_LARGE:
+ *errmsg = "log event entry exceeded max_allowed_packet; \
+Increase max_allowed_packet on master";
+ break;
+ case LOG_READ_IO:
+ *errmsg = "I/O error reading log event";
+ break;
+ case LOG_READ_MEM:
+ *errmsg = "memory allocation failed reading log event";
+ break;
+ case LOG_READ_TRUNC:
+ *errmsg = "binlog truncated in the middle of event";
+ break;
+ default:
+ *errmsg = "unknown error reading log event on the master";
+ break;
+ }
+ return error;
+}
+
+
/*
TODO: Clean up loop to only have one call to send_file()
*/
@@ -326,6 +374,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
int error;
const char *errmsg = "Unknown error";
NET* net = &thd->net;
+ pthread_mutex_t *log_lock;
#ifndef DBUG_OFF
int left_events = max_binlog_dump_events;
#endif
@@ -385,18 +434,25 @@ impossible position";
goto err;
}
- my_b_seek(&log, pos); // Seek will done on next read
/*
We need to start a packet with something other than 255
- to distiquish it from error
+ to distinguish it from error
*/
- packet->set("\0", 1, &my_charset_bin);
+ packet->set("\0", 1, &my_charset_bin); /* This is the start of a new packet */
/*
+ Tell the client about the log name with a fake Rotate event;
+ this is needed even if we also send a Format_description_log_event just
+ after, because that event does not contain the binlog's name.
+ Note that as this Rotate event is sent before Format_description_log_event,
+ the slave cannot have any info to understand this event's format, so the
+ header len of Rotate_log_event is FROZEN
+ (so in 5.0 it will have a header shorter than other events except
+ FORMAT_DESCRIPTION_EVENT).
Before 4.0.14 we called fake_rotate_event below only if
(pos == BIN_LOG_HEADER_SIZE), because if this is false then the slave
already knows the binlog's name.
- Now we always call fake_rotate_event; if the slave already knew the log's
+ Since, we always call fake_rotate_event; if the slave already knew the log's
name (ex: CHANGE MASTER TO MASTER_LOG_FILE=...) this is useless but does
not harm much. It is nice for 3.23 (>=.58) slaves which test Rotate events
to see if the master is 4.0 (then they choose to stop because they can't
@@ -413,15 +469,72 @@ impossible position";
*/
if (fake_rotate_event(net, packet, log_file_name, pos, &errmsg))
{
+ /*
+ This error code is not perfect, as fake_rotate_event() does not read
+ anything from the binlog; if it fails it's because of an error in
+ my_net_write(), fortunately it will say it in errmsg.
+ */
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
goto err;
}
packet->set("\0", 1, &my_charset_bin);
+ /*
+ We can set log_lock now, it does not move (it's a member of mysql_bin_log,
+ and it's already inited, and it will be destroyed only at shutdown).
+ */
+ log_lock = mysql_bin_log.get_log_lock();
+ if (pos > BIN_LOG_HEADER_SIZE)
+ {
+ /* Try to find a Format_description_log_event at the beginning of the binlog */
+ if (!(error = Log_event::read_log_event(&log, packet, log_lock)))
+ {
+ /*
+ The packet has offsets equal to the normal offsets in a binlog event
+ +1 (the first character is \0).
+ */
+ DBUG_PRINT("info",
+ ("Looked for a Format_description_log_event, found event type %d",
+ (*packet)[EVENT_TYPE_OFFSET+1]));
+ if ((*packet)[EVENT_TYPE_OFFSET+1] == FORMAT_DESCRIPTION_EVENT)
+ {
+ /*
+ mark that this event with "log_pos=0", so the slave
+ should not increment master's binlog position
+ (rli->group_master_log_pos)
+ */
+ int4store(packet->c_ptr() +LOG_POS_OFFSET+1,0);
+ /* send it */
+ if (my_net_write(net, (char*)packet->ptr(), packet->length()))
+ {
+ errmsg = "Failed on my_net_write()";
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ /*
+ No need to save this event. We are only doing simple reads (no real
+ parsing of the events) so we don't need it. And so we don't need the
+ artificial Format_description_log_event of 3.23&4.x.
+ */
+ }
+ }
+ else
+ if (test_for_non_eof_log_read_errors(error, &errmsg))
+ goto err;
+ /*
+ else: it's EOF, nothing to do, go on reading next events, the
+ Format_description_log_event will be found naturally if it is written.
+ */
+ /* reset the packet as we wrote to it in any case */
+ packet->set("\0", 1, &my_charset_bin);
+ } /* end of if (pos > BIN_LOG_HEADER_SIZE); if false, the Format_description_log_event
+ event will be found naturally. */
+
+ /* seek to the requested position, to start the requested dump */
+ my_b_seek(&log, pos); // Seek will done on next read
+
while (!net->error && net->vio != 0 && !thd->killed)
{
- pthread_mutex_t *log_lock = mysql_bin_log.get_log_lock();
-
while (!(error = Log_event::read_log_event(&log, packet, log_lock)))
{
#ifndef DBUG_OFF
@@ -433,7 +546,7 @@ impossible position";
goto err;
}
#endif
- if (my_net_write(net, (char*)packet->ptr(), packet->length()) )
+ if (my_net_write(net, (char*)packet->ptr(), packet->length()))
{
errmsg = "Failed on my_net_write()";
my_errno= ER_UNKNOWN_ERROR;
@@ -454,34 +567,14 @@ impossible position";
}
/*
TODO: now that we are logging the offset, check to make sure
- the recorded offset and the actual match
+ the recorded offset and the actual match.
+ Guilhem 2003-06: this is not true if this master is a slave <4.0.15
+ running with --log-slave-updates, because then log_pos may be the offset
+ in the-master-of-this-master's binlog.
*/
- if (error != LOG_READ_EOF)
- {
- my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
- switch (error) {
- case LOG_READ_BOGUS:
- errmsg = "bogus data in log event";
- break;
- case LOG_READ_TOO_LARGE:
- errmsg = "log event entry exceeded max_allowed_packet; \
-Increase max_allowed_packet on master";
- break;
- case LOG_READ_IO:
- errmsg = "I/O error reading log event";
- break;
- case LOG_READ_MEM:
- errmsg = "memory allocation failed reading log event";
- break;
- case LOG_READ_TRUNC:
- errmsg = "binlog truncated in the middle of event";
- break;
- default:
- errmsg = "unknown error reading log event on the master";
- break;
- }
+
+ if (test_for_non_eof_log_read_errors(error, &errmsg))
goto err;
- }
if (!(flags & BINLOG_DUMP_NON_BLOCK) &&
mysql_bin_log.is_active(log_file_name))
@@ -615,8 +708,13 @@ Increase max_allowed_packet on master";
(void) my_close(file, MYF(MY_WME));
/*
- Even if the previous log contained a Rotate_log_event, we still fake
- one.
+ Call fake_rotate_event() in case the previous log (the one which we have
+ just finished reading) did not contain a Rotate event (for example (I
+ don't know any other example) the previous log was the last one before
+ the master was shutdown & restarted).
+ This way we tell the slave about the new log's name and position.
+ If the binlog is 5.0, the next event we are going to read and send is
+ Format_description_log_event.
*/
if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 ||
fake_rotate_event(net, packet, log_file_name, BIN_LOG_HEADER_SIZE, &errmsg))
@@ -950,7 +1048,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
it will be slow because it will iterate through the list
again. We just to do kill the thread ourselves.
*/
- tmp->awake(1/*prepare to die*/);
+ tmp->awake(THD::KILL_QUERY);
pthread_mutex_unlock(&tmp->LOCK_delete);
}
}
@@ -1110,7 +1208,7 @@ int change_master(THD* thd, MASTER_INFO* mi)
mi->rli.group_relay_log_name,
mi->rli.group_relay_log_pos,
0 /*no data lock*/,
- &msg))
+ &msg, 0))
{
net_printf(thd,0,"Failed initializing relay log position: %s",msg);
unlock_slave_threads(mi);
@@ -1195,9 +1293,12 @@ int show_binlog_events(THD* thd)
const char *errmsg = 0;
IO_CACHE log;
File file = -1;
+ Format_description_log_event *description_event= new
+ Format_description_log_event(3); /* MySQL 4.0 by default */
Log_event::init_show_field_list(&field_list);
- if (protocol-> send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
if (mysql_bin_log.is_open())
@@ -1233,10 +1334,38 @@ int show_binlog_events(THD* thd)
goto err;
pthread_mutex_lock(log_lock);
+
+ /*
+ open_binlog() sought to position 4.
+ Read the first event in case it's a Format_description_log_event, to know the
+ format. If there's no such event, we are 3.23 or 4.x. This code, like
+ before, can't read 3.23 binlogs.
+ This code will fail on a mixed relay log (one which has Format_desc then
+ Rotate then Format_desc).
+ */
+
+ ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,description_event);
+ if (ev)
+ {
+ if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
+ {
+ delete description_event;
+ description_event= (Format_description_log_event*) ev;
+ }
+ else
+ delete ev;
+ }
+
my_b_seek(&log, pos);
+ if (!description_event->is_valid())
+ {
+ errmsg="Invalid Format_description event; could be out of memory";
+ goto err;
+ }
+
for (event_count = 0;
- (ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,0)); )
+ (ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,description_event)); )
{
if (event_count >= limit_start &&
ev->net_send(protocol, linfo.log_file_name, pos))
@@ -1265,6 +1394,7 @@ int show_binlog_events(THD* thd)
}
err:
+ delete description_event;
if (file >= 0)
{
end_io_cache(&log);
@@ -1297,7 +1427,8 @@ int show_binlog_info(THD* thd)
field_list.push_back(new Item_empty_string("Binlog_Do_DB",255));
field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
protocol->prepare_for_resend();
@@ -1347,7 +1478,8 @@ int show_binlogs(THD* thd)
}
field_list.push_back(new Item_empty_string("Log_name", 255));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
mysql_bin_log.lock_index();
index_file=mysql_bin_log.get_index_file();
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index e7001c1fe1e..c39ef90114d 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -20,7 +20,7 @@ extern I_List<i_string> binlog_do_db, binlog_ignore_db;
extern int max_binlog_dump_events;
extern my_bool opt_sporadic_binlog_dump_fail;
-#define KICK_SLAVE(thd) { pthread_mutex_lock(&(thd)->LOCK_delete); (thd)->awake(0 /* do not prepare to die*/); pthread_mutex_unlock(&(thd)->LOCK_delete); }
+#define KICK_SLAVE(thd) { pthread_mutex_lock(&(thd)->LOCK_delete); (thd)->awake(THD::NOT_KILLED); pthread_mutex_unlock(&(thd)->LOCK_delete); }
File open_binlog(IO_CACHE *log, const char *log_file_name,
const char **errmsg);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index f3eed672231..db3a0c90141 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -30,7 +30,8 @@
const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref",
"MAYBE_REF","ALL","range","index","fulltext",
- "ref_or_null","unique_subquery","index_subquery"
+ "ref_or_null","unique_subquery","index_subquery",
+ "index_merge"
};
const key_map key_map_empty(0);
@@ -47,7 +48,25 @@ static int sort_keyuse(KEYUSE *a,KEYUSE *b);
static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key);
static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
table_map used_tables);
-static void find_best_combination(JOIN *join,table_map rest_tables);
+static void choose_plan(JOIN *join,table_map join_tables);
+
+static void best_access_path(JOIN *join, JOIN_TAB *s, THD *thd,
+ table_map remaining_tables, uint idx,
+ double record_count, double read_time);
+static void optimize_straight_join(JOIN *join, table_map join_tables);
+static void greedy_search(JOIN *join, table_map remaining_tables,
+ uint depth, uint prune_level);
+static void best_extension_by_limited_search(JOIN *join,
+ table_map remaining_tables,
+ uint idx, double record_count,
+ double read_time, uint depth,
+ uint prune_level);
+static uint determine_search_depth(JOIN* join);
+static int join_tab_cmp(const void* ptr1, const void* ptr2);
+/*
+ TODO: 'find_best' is here only temporarily until 'greedy_search' is
+ tested and approved.
+*/
static void find_best(JOIN *join,table_map rest_tables,uint index,
double record_count,double read_time);
static uint cache_record_length(JOIN *join,uint index);
@@ -58,6 +77,7 @@ static store_key *get_store_key(THD *thd,
KEY_PART_INFO *key_part, char *key_buff,
uint maybe_null);
static bool make_simple_join(JOIN *join,TABLE *tmp_table);
+static void make_outerjoin_info(JOIN *join);
static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item);
static void make_join_readinfo(JOIN *join,uint options);
static bool only_eq_ref_tables(JOIN *join, ORDER *order, table_map tables);
@@ -70,8 +90,11 @@ static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables,
uint select_options, const char *info,
Item *having, Procedure *proc,
SELECT_LEX_UNIT *unit);
-static COND *optimize_cond(THD *thd, COND *conds,
+static COND *simplify_joins(JOIN *join, List<TABLE_LIST> *join_list,
+ COND *conds, bool top);
+static COND *optimize_cond(JOIN *join, COND *conds,
Item::cond_result *cond_value);
+static bool resolve_nested_join (TABLE_LIST *table);
static COND *remove_eq_conds(THD *thd, COND *cond,
Item::cond_result *cond_value);
static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
@@ -116,7 +139,7 @@ static int join_read_next_same_or_null(READ_RECORD *info);
static COND *make_cond_for_table(COND *cond,table_map table,
table_map used_table);
static Item* part_of_refkey(TABLE *form,Field *field);
-static uint find_shortest_key(TABLE *table, const key_map *usable_keys);
+uint find_shortest_key(TABLE *table, const key_map *usable_keys);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
ha_rows select_limit, bool no_changes);
static int create_sort_index(THD *thd, JOIN *join, ORDER *order,
@@ -127,6 +150,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *entry, Field **field,
ulong offset,Item *having);
static int remove_dup_with_hash_index(THD *thd,TABLE *table,
uint field_count, Field **first_field,
+
ulong key_length,Item *having);
static int join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count);
static ulong used_blob_length(CACHE_FIELD **ptr);
@@ -177,8 +201,11 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
DBUG_ENTER("handle_select");
if (select_lex->next_select())
- res=mysql_union(thd, lex, result, &lex->unit);
+ res= mysql_union(thd, lex, result, &lex->unit);
else
+ {
+ SELECT_LEX_UNIT *unit= &lex->unit;
+ unit->set_limit(unit->global_parameters, select_lex);
res= mysql_select(thd, &select_lex->ref_pointer_array,
(TABLE_LIST*) select_lex->table_list.first,
select_lex->with_wild, select_lex->item_list,
@@ -190,19 +217,23 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
select_lex->having,
(ORDER*) lex->proc_list.first,
select_lex->options | thd->options,
- result, &(lex->unit), &(lex->select_lex));
+ result, unit, select_lex);
+ }
/* Don't set res if it's -1 as we may want this later */
DBUG_PRINT("info",("res: %d report_error: %d", res,
thd->net.report_error));
if (thd->net.report_error)
res= 1;
- if (res)
+ if (unlikely(res))
{
- result->send_error(0, NullS);
+ if (res > 0)
+ result->send_error(0, NullS);
result->abort();
res= 1; // Error sent to client
}
+ if (result != lex->result)
+ delete result;
DBUG_RETURN(res);
}
@@ -266,11 +297,12 @@ JOIN::prepare(Item ***rref_pointer_array,
tables_list= tables_init;
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);
/* Check that all tables, fields, conds and order are ok */
- if (setup_tables(tables_list) ||
+ if (setup_tables(thd, tables_list, &conds) ||
setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) ||
select_lex->setup_ref_array(thd, og_num) ||
setup_fields(thd, (*rref_pointer_array), tables_list, fields_list, 1,
@@ -297,15 +329,19 @@ JOIN::prepare(Item ***rref_pointer_array,
having->split_sum_func(ref_pointer_array, all_fields);
}
- // Is it subselect
+ if (!thd->lex->view_prepare_mode)
{
Item_subselect *subselect;
+ /* Is it subselect? */
if ((subselect= select_lex->master_unit()->item))
{
Item_subselect::trans_res res;
if ((res= subselect->select_transformer(this)) !=
Item_subselect::RES_OK)
+ {
+ select_lex->fix_prepare_information(thd, &conds);
DBUG_RETURN((res == Item_subselect::RES_ERROR));
+ }
}
}
@@ -339,7 +375,7 @@ JOIN::prepare(Item ***rref_pointer_array,
}
}
TABLE_LIST *table_ptr;
- for (table_ptr= tables_list ; table_ptr ; table_ptr= table_ptr->next)
+ for (table_ptr= tables_list; table_ptr; table_ptr= table_ptr->next_local)
tables++;
}
{
@@ -408,6 +444,7 @@ JOIN::prepare(Item ***rref_pointer_array,
if (alloc_func_list())
goto err;
+ select_lex->fix_prepare_information(thd, &conds);
DBUG_RETURN(0); // All OK
err:
@@ -453,7 +490,6 @@ bool JOIN::test_in_subselect(Item **where)
return 0;
}
-
/*
global select optimisation.
return 0 - success
@@ -491,7 +527,7 @@ JOIN::optimize()
}
#endif
- conds= optimize_cond(thd, conds, &cond_value);
+ conds= optimize_cond(this, conds,&cond_value);
if (thd->net.report_error)
{
error= 1;
@@ -502,6 +538,7 @@ JOIN::optimize()
if (cond_value == Item::COND_FALSE ||
(!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
{ /* Impossible cond */
+ DBUG_PRINT("info", ("Impossible WHERE"));
zero_result_cause= "Impossible WHERE";
error= 0;
DBUG_RETURN(0);
@@ -519,20 +556,24 @@ JOIN::optimize()
{
if (res > 1)
{
+ DBUG_PRINT("error",("Error from opt_sum_query"));
DBUG_RETURN(1);
}
if (res < 0)
{
+ DBUG_PRINT("info",("No matching min/max row"));
zero_result_cause= "No matching min/max row";
error=0;
DBUG_RETURN(0);
}
+ DBUG_PRINT("info",("Select tables optimized away"));
zero_result_cause= "Select tables optimized away";
tables_list= 0; // All tables resolved
}
}
if (!tables_list)
{
+ DBUG_PRINT("info",("No tables"));
error= 0;
DBUG_RETURN(0);
}
@@ -592,6 +633,9 @@ JOIN::optimize()
DBUG_PRINT("error",("Error: make_select() failed"));
DBUG_RETURN(1);
}
+
+ make_outerjoin_info(this);
+
if (make_join_select(this, select, conds))
{
zero_result_cause=
@@ -966,16 +1010,16 @@ JOIN::reinit()
{
DBUG_ENTER("JOIN::reinit");
/* TODO move to unit reinit */
- unit->offset_limit_cnt =select_lex->offset_limit;
- unit->select_limit_cnt =select_lex->select_limit+select_lex->offset_limit;
- if (unit->select_limit_cnt < select_lex->select_limit)
- unit->select_limit_cnt= HA_POS_ERROR; // no limit
- if (unit->select_limit_cnt == HA_POS_ERROR)
- select_lex->options&= ~OPTION_FOUND_ROWS;
-
- if (setup_tables(tables_list))
- DBUG_RETURN(1);
-
+ unit->set_limit(select_lex, select_lex);
+
+ /* conds should not be used here, it is added just for safety */
+ if (tables_list)
+ {
+ tables_list->setup_is_done= 0;
+ if (setup_tables(thd, tables_list, &conds))
+ DBUG_RETURN(1);
+ }
+
/* Reset of sum functions */
first_record= 0;
@@ -1053,7 +1097,8 @@ JOIN::exec()
(zero_result_cause?zero_result_cause:"No tables used"));
else
{
- result->send_fields(fields_list,1);
+ result->send_fields(fields_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
if (!having || having->val_int())
{
if (do_send_rows && (procedure ? (procedure->send_row(fields_list) ||
@@ -1430,7 +1475,7 @@ JOIN::exec()
at least one row generated from the table.
*/
if (curr_table->select_cond ||
- (curr_table->keyuse && !curr_table->on_expr))
+ (curr_table->keyuse && !curr_table->first_inner))
{
/* We have to sort all rows */
curr_join->select_limit= HA_POS_ERROR;
@@ -1460,12 +1505,45 @@ JOIN::exec()
DBUG_VOID_RETURN;
}
}
+ /* XXX: When can we have here thd->net.report_error not zero? */
+ if (thd->net.report_error)
+ {
+ error= thd->net.report_error;
+ DBUG_VOID_RETURN;
+ }
curr_join->having= curr_join->tmp_having;
- thd->proc_info="Sending data";
- error= thd->net.report_error ||
- do_select(curr_join, curr_fields_list, NULL, procedure);
- thd->limit_found_rows= curr_join->send_records;
- thd->examined_row_count= curr_join->examined_rows;
+ curr_join->fields= curr_fields_list;
+ curr_join->procedure= procedure;
+
+ if (unit == &thd->lex->unit &&
+ (unit->fake_select_lex == 0 || select_lex == unit->fake_select_lex) &&
+ thd->cursor && tables != const_tables)
+ {
+ /*
+ We are here if this is JOIN::exec for the last select of the main unit
+ and the client requested to open a cursor.
+ We check that not all tables are constant because this case is not
+ handled by do_select() separately, and this case is not implemented
+ for cursors yet.
+ */
+ DBUG_ASSERT(error == 0);
+ /*
+ curr_join is used only for reusable joins - that is,
+ to perform SELECT for each outer row (like in subselects).
+ This join is main, so we know for sure that curr_join == join.
+ */
+ DBUG_ASSERT(curr_join == this);
+ /* Open cursor for the last join sweep */
+ error= thd->cursor->open(this);
+ }
+ else
+ {
+ thd->proc_info="Sending data";
+ error= do_select(curr_join, curr_fields_list, NULL, procedure);
+ thd->limit_found_rows= curr_join->send_records;
+ thd->examined_row_count= curr_join->examined_rows;
+ }
+
DBUG_VOID_RETURN;
}
@@ -1514,6 +1592,306 @@ JOIN::cleanup()
}
+/************************* Cursor ******************************************/
+
+void
+Cursor::init_from_thd(THD *thd)
+{
+ /*
+ We need to save and reset thd->mem_root, otherwise it'll be freed
+ later in mysql_parse.
+ */
+ mem_root= thd->mem_root;
+ init_sql_alloc(&thd->mem_root,
+ thd->variables.query_alloc_block_size,
+ thd->variables.query_prealloc_size);
+
+ /*
+ The same is true for open tables and lock: save tables and zero THD
+ pointers to prevent table close in close_thread_tables (This is a part
+ of the temporary solution to make cursors work with minimal changes to
+ the current source base).
+ */
+ derived_tables= thd->derived_tables;
+ open_tables= thd->open_tables;
+ lock= thd->lock;
+ query_id= thd->query_id;
+ free_list= thd->free_list;
+ reset_thd(thd);
+ /*
+ XXX: thd->locked_tables is not changed.
+ What problems can we have with it if cursor is open?
+ */
+ /*
+ TODO: grab thd->free_list here?
+ */
+}
+
+
+void
+Cursor::init_thd(THD *thd)
+{
+ thd->mem_root= mem_root;
+
+ DBUG_ASSERT(thd->derived_tables == 0);
+ thd->derived_tables= derived_tables;
+
+ DBUG_ASSERT(thd->open_tables == 0);
+ thd->open_tables= open_tables;
+
+ DBUG_ASSERT(thd->lock== 0);
+ thd->lock= lock;
+ thd->query_id= query_id;
+ thd->free_list= free_list;
+}
+
+
+void
+Cursor::reset_thd(THD *thd)
+{
+ thd->derived_tables= 0;
+ thd->open_tables= 0;
+ thd->lock= 0;
+ thd->free_list= 0;
+}
+
+
+int
+Cursor::open(JOIN *join_arg)
+{
+ join= join_arg;
+
+ THD *thd= join->thd;
+
+ /* First non-constant table */
+ JOIN_TAB *join_tab= join->join_tab + join->const_tables;
+
+ /*
+ Send fields description to the client; server_status is sent
+ in 'EOF' packet, which ends send_fields().
+ */
+ thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
+ join->result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS);
+ ::send_eof(thd);
+ thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
+
+ /* Prepare JOIN for reading rows. */
+
+ Next_select_func end_select= join->sort_and_group || join->procedure &&
+ join->procedure->flags & PROC_GROUP ?
+ end_send_group : end_send;
+
+ join->join_tab[join->tables-1].next_select= end_select;
+ join->send_records= 0;
+ join->fetch_limit= join->unit->offset_limit_cnt;
+
+ /* Disable JOIN CACHE as it is not working with cursors yet */
+ for (JOIN_TAB *tab= join_tab; tab != join->join_tab + join->tables - 1; ++tab)
+ {
+ if (tab->next_select == sub_select_cache)
+ tab->next_select= sub_select;
+ }
+
+ DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0);
+ DBUG_ASSERT(join_tab->not_used_in_distinct == 0);
+ /*
+ null_row is set only if row not found and it's outer join: should never
+ happen for the first table in join_tab list
+ */
+ DBUG_ASSERT(join_tab->table->null_row == 0);
+
+ return join_tab->read_first_record(join_tab);
+}
+
+
+/*
+ DESCRIPTION
+ Fetch next num_rows rows from the cursor and sent them to the client
+ PRECONDITION:
+ Cursor is open
+ RETURN VALUES:
+ -4 there are more rows, send_eof sent to the client
+ 0 no more rows, send_eof was sent to the client, cursor is closed
+ other fatal fetch error, cursor is closed (error is not reported)
+*/
+
+int
+Cursor::fetch(ulong num_rows)
+{
+ THD *thd= join->thd;
+ JOIN_TAB *join_tab= join->join_tab + join->const_tables;;
+ COND *on_expr= join_tab->on_expr;
+ COND *select_cond= join_tab->select_cond;
+ READ_RECORD *info= &join_tab->read_record;
+
+ int error= 0;
+
+ join->fetch_limit+= num_rows;
+
+ /*
+ Run while there are new rows in the first table;
+ For each row, satisfying ON and WHERE clauses (those parts of them which
+ can be evaluated early), call next_select.
+ */
+ do
+ {
+ int no_more_rows;
+
+ join->examined_rows++;
+
+ if (thd->killed) /* Aborted by user */
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ return -1;
+ }
+
+ if (on_expr == 0 || on_expr->val_int())
+ {
+ if (select_cond == 0 || select_cond->val_int())
+ {
+ /*
+ TODO: call table->unlock_row() to unlock row failed selection,
+ when this feature will be used.
+ */
+ error= join_tab->next_select(join, join_tab + 1, 0);
+ DBUG_ASSERT(error <= 0);
+ if (error)
+ {
+ /* real error or LIMIT/FETCH LIMIT worked */
+ if (error == -4)
+ {
+ /*
+ FETCH LIMIT, read ahead one row, and close cursor
+ if there is no more rows XXX: to be fixed to support
+ non-equi-joins!
+ */
+ if ((no_more_rows= info->read_record(info)))
+ error= no_more_rows > 0 ? -1: 0;
+ }
+ break;
+ }
+ }
+ }
+ /* read next row; break loop if there was an error */
+ if ((no_more_rows= info->read_record(info)))
+ {
+ if (no_more_rows > 0)
+ error= -1;
+ else
+ {
+ enum { END_OF_RECORDS= 1 };
+ error= join_tab->next_select(join, join_tab+1, (int) END_OF_RECORDS);
+ }
+ break;
+ }
+ }
+ while (thd->net.report_error == 0);
+
+ if (thd->net.report_error)
+ error= -1;
+
+ switch (error) {
+ /* Fetch limit worked, possibly more rows are there */
+ case -4:
+ if (thd->transaction.all.innobase_tid)
+ ha_release_temporary_latches(thd);
+ thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
+ ::send_eof(thd);
+ thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
+ /* save references to memory, allocated during fetch */
+ mem_root= thd->mem_root;
+ free_list= thd->free_list;
+ break;
+ /* Limit clause worked: this is the same as 'no more rows' */
+ case -3: /* LIMIT clause worked */
+ error= 0;
+ /* fallthrough */
+ case 0: /* No more rows */
+ if (thd->transaction.all.innobase_tid)
+ ha_release_temporary_latches(thd);
+ close();
+ thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
+ ::send_eof(thd);
+ thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
+ join= 0;
+ unit= 0;
+ free_items(thd->free_list);
+ thd->free_list= free_list= 0;
+ /*
+ Must be last, as some memory might be allocated for free purposes,
+ like in free_tmp_table() (TODO: fix this issue)
+ */
+ mem_root= thd->mem_root;
+ free_root(&mem_root, MYF(0));
+ break;
+ default:
+ close();
+ join= 0;
+ unit= 0;
+ free_items(thd->free_list);
+ thd->free_list= free_list= 0;
+ /*
+ Must be last, as some memory might be allocated for free purposes,
+ like in free_tmp_table() (TODO: fix this issue)
+ */
+ mem_root= thd->mem_root;
+ free_root(&mem_root, MYF(0));
+ break;
+ }
+ return error;
+}
+
+
+void
+Cursor::close()
+{
+ THD *thd= join->thd;
+ join->join_free(0);
+ if (unit)
+ {
+ /* In case of UNIONs JOIN is freed inside unit->cleanup() */
+ unit->cleanup();
+ }
+ else
+ {
+ join->cleanup();
+ delete join;
+ }
+ /* XXX: Another hack: closing tables used in the cursor */
+ {
+ DBUG_ASSERT(lock || open_tables || derived_tables);
+
+ TABLE *tmp_open_tables= thd->open_tables;
+ TABLE *tmp_derived_tables= thd->derived_tables;
+ MYSQL_LOCK *tmp_lock= thd->lock;
+
+ thd->open_tables= open_tables;
+ thd->derived_tables= derived_tables;
+ thd->lock= lock;
+ close_thread_tables(thd);
+
+ thd->open_tables= tmp_derived_tables;
+ thd->derived_tables= tmp_derived_tables;
+ thd->lock= tmp_lock;
+ }
+}
+
+
+Cursor::~Cursor()
+{
+ if (is_open())
+ close();
+ free_items(free_list);
+ /*
+ Must be last, as some memory might be allocated for free purposes,
+ like in free_tmp_table() (TODO: fix this issue)
+ */
+ free_root(&mem_root, MYF(0));
+}
+
+/*********************************************************************/
+
+
int
mysql_select(THD *thd, Item ***rref_pointer_array,
TABLE_LIST *tables, uint wild_num, List<Item> &fields,
@@ -1585,6 +1963,16 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
join->exec();
+ if (thd->cursor && thd->cursor->is_open())
+ {
+ /*
+ A cursor was opened for the last sweep in exec().
+ We are here only if this is mysql_select for top-level SELECT_LEX_UNIT
+ and there were no error.
+ */
+ free_join= 0;
+ }
+
if (thd->lex->describe & DESCRIBE_EXTENDED)
{
select_lex->where= join->conds_history;
@@ -1646,6 +2034,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
DYNAMIC_ARRAY *keyuse_array)
{
int error;
+ TABLE *table;
uint i,table_count,const_count,key;
table_map found_const_table_map, all_table_map, found_ref, refs;
key_map const_ref, eq_part;
@@ -1669,15 +2058,18 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
found_const_table_map= all_table_map=0;
const_count=0;
- for (s=stat,i=0 ; tables ; s++,tables=tables->next,i++)
+ for (s= stat, i= 0;
+ tables;
+ s++, tables= tables->next_local, i++)
{
- TABLE *table;
+ TABLE_LIST *embedding= tables->embedding;
stat_vector[i]=s;
s->keys.init();
s->const_keys.init();
s->checked_keys.init();
s->needed_reg.init();
table_vector[i]=s->table=table=tables->table;
+ table->pos_in_table_list= tables;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);// record count
table->quick_keys.clear_all();
table->reginfo.join_tab=s;
@@ -1686,29 +2078,36 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
all_table_map|= table->map;
s->join=join;
s->info=0; // For describe
+
+ s->dependent= tables->dep_tables;
+ s->key_dependent= 0;
+
if ((s->on_expr=tables->on_expr))
{
- /* Left join */
+ /* s is the only inner table of an outer join */
if (!table->file->records)
{ // Empty table
- s->key_dependent=s->dependent=0; // Ignore LEFT JOIN depend.
+ s->dependent= 0; // Ignore LEFT JOIN depend.
set_position(join,const_count++,s,(KEYUSE*) 0);
continue;
}
- s->key_dependent=s->dependent=
- s->on_expr->used_tables() & ~(table->map);
- if (table->outer_join & JOIN_TYPE_LEFT)
- s->dependent|=stat_vector[i-1]->dependent | table_vector[i-1]->map;
- if (tables->outer_join & JOIN_TYPE_RIGHT)
- s->dependent|=tables->next->table->map;
- outer_join|=table->map;
+ outer_join|= table->map;
continue;
}
- if (tables->straight) // We don't have to move this
- s->dependent= table_vector[i-1]->map | stat_vector[i-1]->dependent;
- else
- s->dependent=(table_map) 0;
- s->key_dependent=(table_map) 0;
+ if (embedding)
+ {
+ /* s belongs to a nested join, maybe to several embedded joins */
+ do
+ {
+ NESTED_JOIN *nested_join= embedding->nested_join;
+ s->dependent|= embedding->dep_tables;
+ embedding= embedding->embedding;
+ outer_join|= nested_join->used_tables;
+ }
+ while (embedding);
+ continue;
+ }
+
if ((table->system || table->file->records <= 1) && ! s->dependent &&
!(table->file->table_flags() & HA_NOT_EXACT_COUNT) &&
!table->fulltext_searched)
@@ -1719,39 +2118,35 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
stat_vector[i]=0;
join->outer_join=outer_join;
- /*
- If outer join: Re-arrange tables in stat_vector so that outer join
- tables are after all tables it is dependent of.
- For example: SELECT * from A LEFT JOIN B ON B.c=C.c, C WHERE A.C=C.C
- Will shift table B after table C.
- */
- if (outer_join)
+ if (join->outer_join)
{
- table_map used_tables=0L;
- for (i=0 ; i < join->tables-1 ; i++)
+ /*
+ Build transitive closure for relation 'to be dependent on'.
+ This will speed up the plan search for many cases with outer joins,
+ as well as allow us to catch illegal cross references/
+ Warshall's algorithm is used to build the transitive closure.
+ As we use bitmaps to represent the relation the complexity
+ of the algorithm is O((number of tables)^2).
+ */
+ for (i= 0, s= stat ; i < table_count ; i++, s++)
{
- if (stat_vector[i]->dependent & ~used_tables)
+ for (uint j= 0 ; j < table_count ; j++)
{
- JOIN_TAB *save= stat_vector[i];
- uint j;
- for (j=i+1;
- j < join->tables && stat_vector[j]->dependent & ~used_tables;
- j++)
- {
- JOIN_TAB *tmp=stat_vector[j]; // Move element up
- stat_vector[j]=save;
- save=tmp;
- }
- if (j == join->tables)
- {
- join->tables=0; // Don't use join->table
- my_error(ER_WRONG_OUTER_JOIN,MYF(0));
- DBUG_RETURN(1);
- }
- stat_vector[i]=stat_vector[j];
- stat_vector[j]=save;
+ table= stat[j].table;
+ if (s->dependent & table->map)
+ s->dependent |= table->reginfo.join_tab->dependent;
}
- used_tables|= stat_vector[i]->table->map;
+ }
+ /* Catch illegal cross references for outer joins */
+ for (i= 0, s= stat ; i < table_count ; i++, s++)
+ {
+ if (s->dependent & s->table->map)
+ {
+ join->tables=0; // Don't use join->table
+ my_error(ER_WRONG_OUTER_JOIN,MYF(0));
+ DBUG_RETURN(1);
+ }
+ s->key_dependent= s->dependent;
}
}
@@ -1794,14 +2189,15 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
for (JOIN_TAB **pos=stat_vector+const_count ; (s= *pos) ; pos++)
{
- TABLE *table=s->table;
+ table=s->table;
if (s->dependent) // If dependent on some table
{
// All dep. must be constants
if (s->dependent & ~(found_const_table_map))
continue;
if (table->file->records <= 1L &&
- !(table->file->table_flags() & HA_NOT_EXACT_COUNT))
+ !(table->file->table_flags() & HA_NOT_EXACT_COUNT) &&
+ !table->pos_in_table_list->embedding)
{ // system table
int tmp= 0;
s->type=JT_SYSTEM;
@@ -1900,7 +2296,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
if (s->worst_seeks < 2.0) // Fix for small tables
s->worst_seeks=2.0;
- if (! s->const_keys.is_clear_all())
+ if (!s->const_keys.is_clear_all() &&
+ !s->table->pos_in_table_list->embedding)
{
ha_rows records;
SQL_SELECT *select;
@@ -1952,7 +2349,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
if (join->const_tables != join->tables)
{
optimize_keyuse(join, keyuse_array);
- find_best_combination(join,all_table_map & ~join->const_table_map);
+ choose_plan(join, all_table_map & ~join->const_table_map);
}
else
{
@@ -2473,11 +2870,32 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
}
for (i=0 ; i < tables ; i++)
{
+ /*
+ Block the creation of keys for inner tables of outer joins.
+ Here only the outer joins that can not be converted to
+ inner joins are left and all nests that can be eliminated
+ are flattened.
+ In the future when we introduce conditional accesses
+ for inner tables in outer joins these keys will be taken
+ into account as well.
+ */
if (join_tab[i].on_expr)
{
add_key_fields(join_tab,&end,&and_level,join_tab[i].on_expr,
join_tab[i].table->map);
}
+ else
+ {
+ TABLE_LIST *tab= join_tab[i].table->pos_in_table_list;
+ TABLE_LIST *embedding= tab->embedding;
+ if (embedding)
+ {
+ NESTED_JOIN *nested_join= embedding->nested_join;
+ if (nested_join->join_list.head() == tab)
+ add_key_fields(join_tab, &end, &and_level, embedding->on_expr,
+ nested_join->used_tables);
+ }
+ }
}
/* fill keyuse with found key parts */
for ( ; field != end ; field++)
@@ -2540,7 +2958,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
}
/*
- Update some values in keyuse for faster find_best_combination() loop
+ Update some values in keyuse for faster choose_plan() loop
*/
static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
@@ -2608,16 +3026,956 @@ set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
}
+/*
+ Find the best access path for an extension of a partial execution plan and
+ add this path to the plan.
+
+ SYNOPSIS
+ best_access_path()
+ join pointer to the structure providing all context info
+ for the query
+ s the table to be joined by the function
+ thd thread for the connection that submitted the query
+ remaining_tables set of tables not included into the partial plan yet
+ idx the length of the partial plan
+ record_count estimate for the number of records returned by the partial
+ plan
+ read_time the cost of the partial plan
+
+ DESCRIPTION
+ The function finds the best access path to table 's' from the passed
+ partial plan where an access path is the general term for any means to
+ access the data in 's'. An access path may use either an index or a scan,
+ whichever is cheaper. The input partial plan is passed via the array
+ 'join->positions' of length 'idx'. The chosen access method for 's' and its
+ cost are stored in 'join->positions[idx]'.
+
+ RETURN
+ None
+*/
+
+static void
+best_access_path(JOIN *join,
+ JOIN_TAB *s,
+ THD *thd,
+ table_map remaining_tables,
+ uint idx,
+ double record_count,
+ double read_time)
+{
+ KEYUSE *best_key= 0;
+ uint best_max_key_part= 0;
+ my_bool found_constraint= 0;
+ double best= DBL_MAX;
+ double best_time= DBL_MAX;
+ double records= DBL_MAX;
+ double tmp;
+ ha_rows rec;
+
+ DBUG_ENTER("best_access_path");
+
+ if (s->keyuse)
+ { /* Use key if possible */
+ TABLE *table= s->table;
+ KEYUSE *keyuse,*start_key=0;
+ double best_records= DBL_MAX;
+ uint max_key_part=0;
+
+ /* Test how we can use keys */
+ rec= s->records/MATCHING_ROWS_IN_OTHER_TABLE; // Assumed records/key
+ for (keyuse=s->keyuse ; keyuse->table == table ;)
+ {
+ key_part_map found_part= 0;
+ table_map found_ref= 0;
+ uint found_ref_or_null= 0;
+ uint key= keyuse->key;
+ KEY *keyinfo= table->key_info+key;
+ bool ft_key= (keyuse->keypart == FT_KEYPART);
+
+ /* Calculate how many key segments of the current key we can use */
+ start_key= keyuse;
+ do
+ { /* for each keypart */
+ uint keypart= keyuse->keypart;
+ uint found_part_ref_or_null= KEY_OPTIMIZE_REF_OR_NULL;
+ do
+ {
+ if (!(remaining_tables & keyuse->used_tables) &&
+ !(found_ref_or_null & keyuse->optimize))
+ {
+ found_part|= keyuse->keypart_map;
+ found_ref|= keyuse->used_tables;
+ if (rec > keyuse->ref_table_rows)
+ rec= keyuse->ref_table_rows;
+ found_part_ref_or_null&= keyuse->optimize;
+ }
+ keyuse++;
+ found_ref_or_null|= found_part_ref_or_null;
+ } while (keyuse->table == table && keyuse->key == key &&
+ keyuse->keypart == keypart);
+ } while (keyuse->table == table && keyuse->key == key);
+
+ /*
+ Assume that that each key matches a proportional part of table.
+ */
+ if (!found_part && !ft_key)
+ continue; // Nothing usable found
+
+ if (rec < MATCHING_ROWS_IN_OTHER_TABLE)
+ rec= MATCHING_ROWS_IN_OTHER_TABLE; // Fix for small tables
+
+ /*
+ ft-keys require special treatment
+ */
+ if (ft_key)
+ {
+ /*
+ Really, there should be records=0.0 (yes!)
+ but 1.0 would be probably safer
+ */
+ tmp= prev_record_reads(join, found_ref);
+ records= 1.0;
+ }
+ else
+ {
+ found_constraint= 1;
+ /*
+ Check if we found full key
+ */
+ if (found_part == PREV_BITS(uint,keyinfo->key_parts) &&
+ !found_ref_or_null)
+ { /* use eq key */
+ max_key_part= (uint) ~0;
+ if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
+ {
+ tmp = prev_record_reads(join, found_ref);
+ records=1.0;
+ }
+ else
+ {
+ if (!found_ref)
+ { /* We found a const key */
+ if (table->quick_keys.is_set(key))
+ records= (double) table->quick_rows[key];
+ else
+ {
+ /* quick_range couldn't use key! */
+ records= (double) s->records/rec;
+ }
+ }
+ else
+ {
+ if (!(records=keyinfo->rec_per_key[keyinfo->key_parts-1]))
+ { /* Prefer longer keys */
+ records=
+ ((double) s->records / (double) rec *
+ (1.0 +
+ ((double) (table->max_key_length-keyinfo->key_length) /
+ (double) table->max_key_length)));
+ if (records < 2.0)
+ records=2.0; /* Can't be as good as a unique */
+ }
+ }
+ /* Limit the number of matched rows */
+ tmp = records;
+ set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key);
+ if (table->used_keys.is_set(key))
+ {
+ /* we can use only index tree */
+ uint keys_per_block= table->file->block_size/2/
+ (keyinfo->key_length+table->file->ref_length)+1;
+ tmp = record_count*(tmp+keys_per_block-1)/keys_per_block;
+ }
+ else
+ tmp = record_count*min(tmp,s->worst_seeks);
+ }
+ }
+ else
+ {
+ /*
+ Use as much key-parts as possible and a uniq key is better
+ than a not unique key
+ Set tmp to (previous record count) * (records / combination)
+ */
+ if ((found_part & 1) &&
+ (!(table->file->index_flags(key, 0, 0) & HA_ONLY_WHOLE_INDEX) ||
+ found_part == PREV_BITS(uint,keyinfo->key_parts)))
+ {
+ max_key_part=max_part_bit(found_part);
+ /*
+ Check if quick_range could determinate how many rows we
+ will match
+ */
+ if (table->quick_keys.is_set(key) &&
+ table->quick_key_parts[key] == max_key_part)
+ tmp= records= (double) table->quick_rows[key];
+ else
+ {
+ /* Check if we have statistic about the distribution */
+ if ((records = keyinfo->rec_per_key[max_key_part-1]))
+ tmp = records;
+ else
+ {
+ /*
+ Assume that the first key part matches 1% of the file
+ and that the hole key matches 10 (duplicates) or 1
+ (unique) records.
+ Assume also that more key matches proportionally more
+ records
+ This gives the formula:
+ records = (x * (b-a) + a*c-b)/(c-1)
+
+ b = records matched by whole key
+ a = records matched by first key part (10% of all records?)
+ c = number of key parts in key
+ x = used key parts (1 <= x <= c)
+ */
+ double rec_per_key;
+ if (!(rec_per_key=(double)
+ keyinfo->rec_per_key[keyinfo->key_parts-1]))
+ rec_per_key=(double) s->records/rec+1;
+
+ if (!s->records)
+ tmp = 0;
+ else if (rec_per_key/(double) s->records >= 0.01)
+ tmp = rec_per_key;
+ else
+ {
+ double a=s->records*0.01;
+ tmp = (max_key_part * (rec_per_key - a) +
+ a*keyinfo->key_parts - rec_per_key)/
+ (keyinfo->key_parts-1);
+ set_if_bigger(tmp,1.0);
+ }
+ records = (ulong) tmp;
+ }
+ /*
+ If quick_select was used on a part of this key, we know
+ the maximum number of rows that the key can match.
+ */
+ if (table->quick_keys.is_set(key) &&
+ table->quick_key_parts[key] <= max_key_part &&
+ records > (double) table->quick_rows[key])
+ tmp= records= (double) table->quick_rows[key];
+ else if (found_ref_or_null)
+ {
+ /* We need to do two key searches to find key */
+ tmp *= 2.0;
+ records *= 2.0;
+ }
+ }
+ /* Limit the number of matched rows */
+ set_if_smaller(tmp, (double) thd->variables.max_seeks_for_key);
+ if (table->used_keys.is_set(key))
+ {
+ /* we can use only index tree */
+ uint keys_per_block= table->file->block_size/2/
+ (keyinfo->key_length+table->file->ref_length)+1;
+ tmp = record_count*(tmp+keys_per_block-1)/keys_per_block;
+ }
+ else
+ tmp = record_count*min(tmp,s->worst_seeks);
+ }
+ else
+ tmp = best_time; // Do nothing
+ }
+ } /* not ft_key */
+ if (tmp < best_time - records/(double) TIME_FOR_COMPARE)
+ {
+ best_time= tmp + records/(double) TIME_FOR_COMPARE;
+ best= tmp;
+ best_records= records;
+ best_key= start_key;
+ best_max_key_part= max_key_part;
+ }
+ }
+ records= best_records;
+ }
+
+ /*
+ Don't test table scan if it can't be better.
+ Prefer key lookup if we would use the same key for scanning.
+
+ Don't do a table scan on InnoDB tables, if we can read the used
+ parts of the row from any of the used index.
+ This is because table scans uses index and we would not win
+ anything by using a table scan.
+ */
+ if ((records >= s->found_records || best > s->read_time) &&
+ !(s->quick && best_key && s->quick->index == best_key->key &&
+ best_max_key_part >= s->table->quick_key_parts[best_key->key]) &&
+ !((s->table->file->table_flags() & HA_TABLE_SCAN_ON_INDEX) &&
+ ! s->table->used_keys.is_clear_all() && best_key) &&
+ !(s->table->force_index && best_key))
+ { // Check full join
+ ha_rows rnd_records= s->found_records;
+ /*
+ If there is a restriction on the table, assume that 25% of the
+ rows can be skipped on next part.
+ This is to force tables that this table depends on before this
+ table
+ */
+ if (found_constraint)
+ rnd_records-= rnd_records/4;
+
+ /*
+ Range optimizer never proposes a RANGE if it isn't better
+ than FULL: so if RANGE is present, it's always preferred to FULL.
+ Here we estimate its cost.
+ */
+ if (s->quick)
+ {
+ /*
+ For each record we:
+ - read record range through 'quick'
+ - skip rows which does not satisfy WHERE constraints
+ */
+ tmp= record_count *
+ (s->quick->read_time +
+ (s->found_records - rnd_records)/(double) TIME_FOR_COMPARE);
+ }
+ else
+ {
+ /* Estimate cost of reading table. */
+ tmp= s->table->file->scan_time();
+ if (s->table->map & join->outer_join) // Can't use join cache
+ {
+ /*
+ For each record we have to:
+ - read the whole table record
+ - skip rows which does not satisfy join condition
+ */
+ tmp= record_count *
+ (tmp +
+ (s->records - rnd_records)/(double) TIME_FOR_COMPARE);
+ }
+ else
+ {
+ /* We read the table as many times as join buffer becomes full. */
+ tmp*= (1.0 + floor((double) cache_record_length(join,idx) *
+ record_count /
+ (double) thd->variables.join_buff_size));
+ /*
+ We don't make full cartesian product between rows in the scanned
+ table and existing records because we skip all rows from the
+ scanned table, which does not satisfy join condition when
+ we read the table (see flush_cached_records for details). Here we
+ take into account cost to read and skip these records.
+ */
+ tmp+= (s->records - rnd_records)/(double) TIME_FOR_COMPARE;
+ }
+ }
+
+ /*
+ We estimate the cost of evaluating WHERE clause for found records
+ as record_count * rnd_records / TIME_FOR_COMPARE. This cost plus
+ tmp give us total cost of using TABLE SCAN
+ */
+ if (best == DBL_MAX ||
+ (tmp + record_count/(double) TIME_FOR_COMPARE*rnd_records <
+ best + record_count/(double) TIME_FOR_COMPARE*records))
+ {
+ /*
+ If the table has a range (s->quick is set) make_join_select()
+ will ensure that this will be used
+ */
+ best= tmp;
+ records= rows2double(rnd_records);
+ best_key= 0;
+ }
+ }
+
+ /* Update the cost information for the current partial plan */
+ join->positions[idx].records_read= records;
+ join->positions[idx].read_time= best;
+ join->positions[idx].key= best_key;
+ join->positions[idx].table= s;
+
+ if (!best_key &&
+ idx == join->const_tables &&
+ s->table == join->sort_by_table &&
+ join->unit->select_limit_cnt >= records)
+ join->sort_by_table= (TABLE*) 1; // Must use temporary table
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Selects and invokes a search strategy for an optimal query plan.
+
+ SYNOPSIS
+ choose_plan()
+ join pointer to the structure providing all context info for
+ the query
+ join_tables set of the tables in the query
+
+ DESCRIPTION
+ The function checks user-configurable parameters that control the search
+ strategy for an optimal plan, selects the search method and then invokes
+ it. Each specific optimization procedure stores the final optimal plan in
+ the array 'join->best_positions', and the cost of the plan in
+ 'join->best_read'.
+
+ RETURN
+ None
+*/
+
+static void
+choose_plan(JOIN *join, table_map join_tables)
+{
+ uint search_depth= join->thd->variables.optimizer_search_depth;
+ uint prune_level= join->thd->variables.optimizer_prune_level;
+
+ DBUG_ENTER("choose_plan");
+
+ if (join->select_options & SELECT_STRAIGHT_JOIN)
+ {
+ optimize_straight_join(join, join_tables);
+ }
+ else
+ {
+ /*
+ Heuristic: pre-sort all access plans with respect to the number of
+ records accessed.
+ */
+ qsort(join->best_ref + join->const_tables, join->tables - join->const_tables,
+ sizeof(JOIN_TAB*), join_tab_cmp);
+
+ if (search_depth == MAX_TABLES+2)
+ { /*
+ TODO: 'MAX_TABLES+2' denotes the old implementation of find_best before
+ the greedy version. Will be removed when greedy_search is approved.
+ */
+ join->best_read= DBL_MAX;
+ find_best(join, join_tables, join->const_tables, 1.0, 0.0);
+ }
+ else
+ {
+ if (search_depth == 0)
+ /* Automatically determine a reasonable value for 'search_depth' */
+ search_depth= determine_search_depth(join);
+ greedy_search(join, join_tables, search_depth, prune_level);
+ }
+ }
+
+ /* Store the cost of this query into a user variable */
+ last_query_cost= join->best_read;
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Compare two JOIN_TAB objects based on the number of accessed records.
+
+ SYNOPSIS
+ join_tab_cmp()
+ ptr1 pointer to first JOIN_TAB object
+ ptr2 pointer to second JOIN_TAB object
+
+ RETURN
+ 1 if first is bigger
+ -1 if second is bigger
+ 0 if equal
+*/
+
+static int
+join_tab_cmp(const void* ptr1, const void* ptr2)
+{
+ JOIN_TAB *jt1= *(JOIN_TAB**) ptr1;
+ JOIN_TAB *jt2= *(JOIN_TAB**) ptr2;
+
+ if (jt1->dependent & jt2->table->map)
+ return 1;
+ if (jt2->dependent & jt1->table->map)
+ return -1;
+ if (jt1->found_records > jt2->found_records)
+ return 1;
+ if (jt1->found_records < jt2->found_records)
+ return -1;
+ return jt1 > jt2 ? 1 : (jt1 < jt2 ? -1 : 0);
+}
+
+
+/*
+ Heuristic procedure to automatically guess a reasonable degree of
+ exhaustiveness for the greedy search procedure.
+
+ SYNOPSIS
+ determine_search_depth()
+ join pointer to the structure providing all context info for the query
+
+ DESCRIPTION
+ The procedure estimates the optimization time and selects a search depth
+ big enough to result in a near-optimal QEP, that doesn't take too long to
+ find. If the number of tables in the query exceeds some constant, then
+ search_depth is set to this constant.
+
+ NOTES
+ This is an extremely simplistic implementation that serves as a stub for a
+ more advanced analysis of the join. Ideally the search depth should be
+ determined by learning from previous query optimizations, because it will
+ depend on the CPU power (and other factors).
+
+ RETURN
+ A positive integer that specifies the search depth (and thus the
+ exhaustiveness) of the depth-first search algorithm used by
+ 'greedy_search'.
+*/
+
+static uint
+determine_search_depth(JOIN *join)
+{
+ uint table_count= join->tables - join->const_tables;
+ uint search_depth;
+ /* TODO: this value should be determined dynamically, based on statistics: */
+ uint max_tables_for_exhaustive_opt= 7;
+
+ if (table_count <= max_tables_for_exhaustive_opt)
+ search_depth= table_count+1; // use exhaustive for small number of tables
+ else
+ /*
+ TODO: this value could be determined by some mapping of the form:
+ depth : table_count -> [max_tables_for_exhaustive_opt..MAX_EXHAUSTIVE]
+ */
+ search_depth= max_tables_for_exhaustive_opt; // use greedy search
+
+ return search_depth;
+}
+
+
+/*
+ Select the best ways to access the tables in a query without reordering them.
+
+ SYNOPSIS
+ optimize_straight_join()
+ join pointer to the structure providing all context info for
+ the query
+ join_tables set of the tables in the query
+
+ DESCRIPTION
+ Find the best access paths for each query table and compute their costs
+ according to their order in the array 'join->best_ref' (thus without
+ reordering the join tables). The function calls sequentially
+ 'best_access_path' for each table in the query to select the best table
+ access method. The final optimal plan is stored in the array
+ 'join->best_positions', and the corresponding cost in 'join->best_read'.
+
+ NOTES
+ This function can be applied to:
+ - queries with STRAIGHT_JOIN
+ - internally to compute the cost of an arbitrary QEP
+ Thus 'optimize_straight_join' can be used at any stage of the query
+ optimization process to finalize a QEP as it is.
+
+ RETURN
+ None
+*/
+
+static void
+optimize_straight_join(JOIN *join, table_map join_tables)
+{
+ JOIN_TAB *s;
+ uint idx= join->const_tables;
+ double record_count= 1.0;
+ double read_time= 0.0;
+
+ for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
+ {
+ /* Find the best access method from 's' to the current partial plan */
+ best_access_path(join, s, join->thd, join_tables, idx, record_count, read_time);
+ /* compute the cost of the new plan extended with 's' */
+ record_count*= join->positions[idx].records_read;
+ read_time+= join->positions[idx].read_time;
+ join_tables&= ~(s->table->map);
+ ++idx;
+ }
+
+ read_time+= record_count / (double) TIME_FOR_COMPARE;
+ if (join->sort_by_table &&
+ join->sort_by_table != join->positions[join->const_tables].table->table)
+ read_time+= record_count; // We have to make a temp table
+ memcpy((gptr) join->best_positions, (gptr) join->positions,
+ sizeof(POSITION)*idx);
+ join->best_read= read_time;
+}
+
+
+/*
+ Find a good, possibly optimal, query execution plan (QEP) by a greedy search.
+
+ SYNOPSIS
+ join pointer to the structure providing all context info
+ for the query
+ remaining_tables set of tables not included into the partial plan yet
+ search_depth controlls the exhaustiveness of the search
+ prune_level the pruning heuristics that should be applied during
+ search
+
+ DESCRIPTION
+ The search procedure uses a hybrid greedy/exhaustive search with controlled
+ exhaustiveness. The search is performed in N = card(remaining_tables)
+ steps. Each step evaluates how promising is each of the unoptimized tables,
+ selects the most promising table, and extends the current partial QEP with
+ that table. Currenly the most 'promising' table is the one with least
+ expensive extension.
+ There are two extreme cases:
+ 1. When (card(remaining_tables) < search_depth), the estimate finds the best
+ complete continuation of the partial QEP. This continuation can be
+ used directly as a result of the search.
+ 2. When (search_depth == 1) the 'best_extension_by_limited_search'
+ consideres the extension of the current QEP with each of the remaining
+ unoptimized tables.
+ All other cases are in-between these two extremes. Thus the parameter
+ 'search_depth' controlls the exhaustiveness of the search. The higher the
+ value, the longer the optimizaton time and possibly the better the
+ resulting plan. The lower the value, the fewer alternative plans are
+ estimated, but the more likely to get a bad QEP.
+
+ All intermediate and final results of the procedure are stored in 'join':
+ join->positions modified for every partial QEP that is explored
+ join->best_positions modified for the current best complete QEP
+ join->best_read modified for the current best complete QEP
+ join->best_ref might be partially reordered
+ The final optimal plan is stored in 'join->best_positions', and its
+ corresponding cost in 'join->best_read'.
+
+ NOTES
+ The following pseudocode describes the algorithm of 'greedy_search':
+
+ procedure greedy_search
+ input: remaining_tables
+ output: pplan;
+ {
+ pplan = <>;
+ do {
+ (t, a) = best_extension(pplan, remaining_tables);
+ pplan = concat(pplan, (t, a));
+ remaining_tables = remaining_tables - t;
+ } while (remaining_tables != {})
+ return pplan;
+ }
+
+ where 'best_extension' is a placeholder for a procedure that selects the
+ most "promising" of all tables in 'remaining_tables'.
+ Currently this estimate is performed by calling
+ 'best_extension_by_limited_search' to evaluate all extensions of the
+ current QEP of size 'search_depth', thus the complexity of 'greedy_search'
+ mainly depends on that of 'best_extension_by_limited_search'.
+
+ If 'best_extension()' == 'best_extension_by_limited_search()', then the
+ worst-case complexity of this algorithm is <=
+ O(N*N^search_depth/search_depth). When serch_depth >= N, then the
+ complexity of greedy_search is O(N!).
+
+ In the future, 'greedy_search' might be extended to support other
+ implementations of 'best_extension', e.g. some simpler quadratic procedure.
+
+ RETURN
+ None
+*/
+
static void
-find_best_combination(JOIN *join, table_map rest_tables)
+greedy_search(JOIN *join,
+ table_map remaining_tables,
+ uint search_depth,
+ uint prune_level)
{
- DBUG_ENTER("find_best_combination");
- join->best_read=DBL_MAX;
- find_best(join,rest_tables, join->const_tables,1.0,0.0);
+ double record_count= 1.0;
+ double read_time= 0.0;
+ uint idx= join->const_tables; // index into 'join->best_ref'
+ uint best_idx;
+ uint rem_size; // cardinality of remaining_tables
+ POSITION best_pos;
+ JOIN_TAB *best_table; // the next plan node to be added to the curr QEP
+
+ DBUG_ENTER("greedy_search");
+
+ /* number of tables that remain to be optimized */
+ rem_size= my_count_bits(remaining_tables);
+
+ do {
+ /* Find the extension of the current QEP with the lowest cost */
+ join->best_read= DBL_MAX;
+ best_extension_by_limited_search(join, remaining_tables, idx, record_count,
+ read_time, search_depth, prune_level);
+
+ if (rem_size <= search_depth)
+ {
+ /*
+ 'join->best_positions' contains a complete optimal extension of the
+ current partial QEP.
+ */
+ DBUG_EXECUTE("opt", print_plan(join, read_time, record_count,
+ join->tables, "optimal"););
+ DBUG_VOID_RETURN;
+ }
+
+ /* select the first table in the optimal extension as most promising */
+ best_pos= join->best_positions[idx];
+ best_table= best_pos.table;
+ /*
+ Each subsequent loop of 'best_extension_by_limited_search' uses
+ 'join->positions' for cost estimates, therefore we have to update its
+ value.
+ */
+ join->positions[idx]= best_pos;
+
+ /* find the position of 'best_table' in 'join->best_ref' */
+ best_idx= idx;
+ JOIN_TAB *pos= join->best_ref[best_idx];
+ while (pos && best_table != pos)
+ pos= join->best_ref[++best_idx];
+ DBUG_ASSERT((pos != NULL)); // should always find 'best_table'
+ /* move 'best_table' at the first free position in the array of joins */
+ swap_variables(JOIN_TAB*, join->best_ref[idx], join->best_ref[best_idx]);
+
+ /* compute the cost of the new plan extended with 'best_table' */
+ record_count*= join->positions[idx].records_read;
+ read_time+= join->positions[idx].read_time;
+
+ remaining_tables&= ~(best_table->table->map);
+ --rem_size;
+ ++idx;
+
+ DBUG_EXECUTE("opt",
+ print_plan(join, read_time, record_count, idx, "extended"););
+ } while (TRUE);
+}
+
+
+/*
+ Find a good, possibly optimal, query execution plan (QEP) by a possibly
+ exhaustive search.
+
+ SYNOPSIS
+ best_extension_by_limited_search()
+ join pointer to the structure providing all context info for
+ the query
+ remaining_tables set of tables not included into the partial plan yet
+ idx length of the partial QEP in 'join->positions';
+ since a depth-first search is used, also corresponds to
+ the current depth of the search tree;
+ also an index in the array 'join->best_ref';
+ record_count estimate for the number of records returned by the best
+ partial plan
+ read_time the cost of the best partial plan
+ search_depth maximum depth of the recursion and thus size of the found
+ optimal plan (0 < search_depth <= join->tables+1).
+ prune_level pruning heuristics that should be applied during optimization
+ (values: 0 = EXHAUSTIVE, 1 = PRUNE_BY_TIME_OR_ROWS)
+
+ DESCRIPTION
+ The procedure searches for the optimal ordering of the query tables in set
+ 'remaining_tables' of size N, and the corresponding optimal access paths to each
+ table. The choice of a table order and an access path for each table
+ constitutes a query execution plan (QEP) that fully specifies how to
+ execute the query.
+
+ The maximal size of the found plan is controlled by the parameter
+ 'search_depth'. When search_depth == N, the resulting plan is complete and
+ can be used directly as a QEP. If search_depth < N, the found plan consists
+ of only some of the query tables. Such "partial" optimal plans are useful
+ only as input to query optimization procedures, and cannot be used directly
+ to execute a query.
+
+ The algorithm begins with an empty partial plan stored in 'join->positions'
+ and a set of N tables - 'remaining_tables'. Each step of the algorithm
+ evaluates the cost of the partial plan extended by all access plans for
+ each of the relations in 'remaining_tables', expands the current partial
+ plan with the access plan that results in lowest cost of the expanded
+ partial plan, and removes the corresponding relation from
+ 'remaining_tables'. The algorithm continues until it either constructs a
+ complete optimal plan, or constructs an optimal plartial plan with size =
+ search_depth.
+
+ The final optimal plan is stored in 'join->best_positions'. The
+ corresponding cost of the optimal plan is in 'join->best_read'.
+
+ NOTES
+ The procedure uses a recursive depth-first search where the depth of the
+ recursion (and thus the exhaustiveness of the search) is controlled by the
+ parameter 'search_depth'.
+
+ The pseudocode below describes the algorithm of
+ 'best_extension_by_limited_search'. The worst-case complexity of this
+ algorithm is O(N*N^search_depth/search_depth). When serch_depth >= N, then
+ the complexity of greedy_search is O(N!).
+
+ procedure best_extension_by_limited_search(
+ pplan in, // in, partial plan of tables-joined-so-far
+ pplan_cost, // in, cost of pplan
+ remaining_tables, // in, set of tables not referenced in pplan
+ best_plan_so_far, // in/out, best plan found so far
+ best_plan_so_far_cost,// in/out, cost of best_plan_so_far
+ search_depth) // in, maximum size of the plans being considered
+ {
+ for each table T from remaining_tables
+ {
+ // Calculate the cost of using table T as above
+ cost = complex-series-of-calculations;
+
+ // Add the cost to the cost so far.
+ pplan_cost+= cost;
+
+ if (pplan_cost >= best_plan_so_far_cost)
+ // pplan_cost already too great, stop search
+ continue;
+
+ pplan= expand pplan by best_access_method;
+ remaining_tables= remaining_tables - table T;
+ if (remaining_tables is not an empty set
+ and
+ search_depth > 1)
+ {
+ best_extension_by_limited_search(pplan, pplan_cost,
+ remaining_tables,
+ best_plan_so_far,
+ best_plan_so_far_cost,
+ search_depth - 1);
+ }
+ else
+ {
+ best_plan_so_far_cost= pplan_cost;
+ best_plan_so_far= pplan;
+ }
+ }
+ }
+
+ IMPLEMENTATION
+ When 'best_extension_by_limited_search' is called for the first time,
+ 'join->best_read' must be set to the largest possible value (e.g. DBL_MAX).
+ The actual implementation provides a way to optionally use pruning
+ heuristic (controlled by the parameter 'prune_level') to reduce the search
+ space by skipping some partial plans.
+ The parameter 'search_depth' provides control over the recursion
+ depth, and thus the size of the resulting optimal plan.
+
+ RETURN
+ None
+*/
+
+static void
+best_extension_by_limited_search(JOIN *join,
+ table_map remaining_tables,
+ uint idx,
+ double record_count,
+ double read_time,
+ uint search_depth,
+ uint prune_level)
+{
+ THD *thd= join->thd;
+ if (thd->killed) // Abort
+ return;
+
+ DBUG_ENTER("best_extension_by_limited_search");
+
+ /*
+ 'join' is a partial plan with lower cost than the best plan so far,
+ so continue expanding it further with the tables in 'remaining_tables'.
+ */
+ JOIN_TAB *s;
+ double best_record_count= DBL_MAX;
+ double best_read_time= DBL_MAX;
+
+ DBUG_EXECUTE("opt",
+ print_plan(join, read_time, record_count, idx, "part_plan"););
+
+ for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
+ {
+ table_map real_table_bit= s->table->map;
+ if ((remaining_tables & real_table_bit) && !(remaining_tables & s->dependent))
+ {
+ double current_record_count, current_read_time;
+
+ /* Find the best access method from 's' to the current partial plan */
+ best_access_path(join, s, thd, remaining_tables, idx, record_count, read_time);
+ /* Compute the cost of extending the plan with 's' */
+ current_record_count= record_count * join->positions[idx].records_read;
+ current_read_time= read_time + join->positions[idx].read_time;
+
+ /* Expand only partial plans with lower cost than the best QEP so far */
+ if ((current_read_time +
+ current_record_count / (double) TIME_FOR_COMPARE) >= join->best_read)
+ {
+ DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx,
+ "prune_by_cost"););
+ continue;
+ }
+
+ /*
+ Prune some less promising partial plans. This heuristic may miss
+ the optimal QEPs, thus it results in a non-exhaustive search.
+ */
+ if (prune_level == 1)
+ {
+ if (best_record_count > current_record_count ||
+ best_read_time > current_read_time ||
+ idx == join->const_tables && // 's' is the first table in the QEP
+ s->table == join->sort_by_table)
+ {
+ if (best_record_count >= current_record_count &&
+ best_read_time >= current_read_time &&
+ /* TODO: What is the reasoning behind this condition? */
+ (!(s->key_dependent & remaining_tables) ||
+ join->positions[idx].records_read < 2.0))
+ {
+ best_record_count= current_record_count;
+ best_read_time= current_read_time;
+ }
+ }
+ else
+ {
+ DBUG_EXECUTE("opt", print_plan(join, read_time, record_count, idx,
+ "pruned_by_heuristic"););
+ continue;
+ }
+ }
+
+ if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) )
+ { /* Recursively expand the current partial plan */
+ swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
+ best_extension_by_limited_search(join,
+ remaining_tables & ~real_table_bit,
+ idx + 1,
+ current_record_count,
+ current_read_time,
+ search_depth - 1,
+ prune_level);
+ if (thd->killed)
+ DBUG_VOID_RETURN;
+ swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
+ }
+ else
+ { /*
+ 'join' is either the best partial QEP with 'search_depth' relations,
+ or the best complete QEP so far, whichever is smaller.
+ */
+ current_read_time+= current_record_count / (double) TIME_FOR_COMPARE;
+ if (join->sort_by_table &&
+ join->sort_by_table != join->positions[join->const_tables].table->table)
+ /* We have to make a temp table */
+ current_read_time+= current_record_count;
+ if ((search_depth == 1) || (current_read_time < join->best_read))
+ {
+ memcpy((gptr) join->best_positions, (gptr) join->positions,
+ sizeof(POSITION) * (idx + 1));
+ join->best_read= current_read_time - 0.001;
+ }
+ DBUG_EXECUTE("opt",
+ print_plan(join, current_read_time, current_record_count, idx, "full_plan"););
+ }
+ }
+ }
DBUG_VOID_RETURN;
}
+/*
+ TODO: this function is here only temporarily until 'greedy_search' is
+ tested and accepted.
+*/
static void
find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
double read_time)
@@ -2937,7 +4295,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
{
/* Estimate cost of reading table. */
tmp= s->table->file->scan_time();
- if (s->on_expr) // Can't use join cache
+ if (s->table->map & join->outer_join) // Can't use join cache
{
/*
For each record we have to:
@@ -3383,6 +4741,8 @@ make_simple_join(JOIN *join,TABLE *tmp_table)
join_tab->keys.init(~0); /* test everything in quick */
join_tab->info=0;
join_tab->on_expr=0;
+ join_tab->last_inner= 0;
+ join_tab->first_unmatched= 0;
join_tab->ref.key = -1;
join_tab->not_used_in_distinct=0;
join_tab->read_first_record= join_init_read_record;
@@ -3394,6 +4754,132 @@ make_simple_join(JOIN *join,TABLE *tmp_table)
}
+/*
+ Build a predicate guarded by match variables for embedding outer joins
+
+ SYNOPSIS
+ add_found_match_trig_cond()
+ tab the first inner table for most nested outer join
+ cond the predicate to be guarded
+ root_tab the first inner table to stop
+
+ DESCRIPTION
+ The function recursively adds guards for predicate cond
+ assending from tab to the first inner table next embedding
+ nested outer join and so on until it reaches root_tab
+ (root_tab can be 0).
+
+ RETURN VALUE
+ pointer to the guarded predicate, if success
+ 0, otherwise
+*/
+
+static COND*
+add_found_match_trig_cond(JOIN_TAB *tab, COND *cond, JOIN_TAB *root_tab)
+{
+ COND *tmp;
+ if (tab == root_tab || !cond)
+ return cond;
+ if ((tmp= add_found_match_trig_cond(tab->first_upper, cond, root_tab)))
+ {
+ tmp= new Item_func_trig_cond(tmp, &tab->found);
+ }
+ if (!tmp)
+ tmp->quick_fix_field();
+ return tmp;
+}
+
+
+/*
+ Fill in outer join related info for the execution plan structure
+
+ SYNOPSIS
+ make_outerjoin_info()
+ join - reference to the info fully describing the query
+
+ DESCRIPTION
+ For each outer join operation left after simplification of the
+ original query the function set up the following pointers in the linear
+ structure join->join_tab representing the selected execution plan.
+ The first inner table t0 for the operation is set to refer to the last
+ inner table tk through the field t0->last_inner.
+ Any inner table ti for the operation are set to refer to the first
+ inner table ti->first_inner.
+ The first inner table t0 for the operation is set to refer to the
+ first inner table of the embedding outer join operation, if there is any,
+ through the field t0->first_upper.
+ The on expression for the outer join operation is attached to the
+ corresponding first inner table through the field t0->on_expr.
+ Here ti are structures of the JOIN_TAB type.
+
+ EXAMPLE
+ For the query:
+ SELECT * FROM t1
+ LEFT JOIN
+ (t2, t3 LEFT JOIN t4 ON t3.a=t4.a)
+ ON (t1.a=t2.a AND t1.b=t3.b)
+ WHERE t1.c > 5,
+ given the execution plan with the table order t1,t2,t3,t4
+ is selected, the following references will be set;
+ t4->last_inner=[t4], t4->first_inner=[t4], t4->first_upper=[t2]
+ t2->last_inner=[t4], t2->first_inner=t3->first_inner=[t2],
+ on expression (t1.a=t2.a AND t1.b=t3.b) will be attached to t2->on_expr,
+ while t3.a=t4.a will be attached to t4->on_expr.
+
+ NOTES
+ The function assumes that the simplification procedure has been
+ already applied to the join query (see simplify_joins).
+ This function can be called only after the execution plan
+ has been chosen.
+*/
+
+static void
+make_outerjoin_info(JOIN *join)
+{
+ for (uint i=join->const_tables ; i < join->tables ; i++)
+ {
+ JOIN_TAB *tab=join->join_tab+i;
+ TABLE *table=tab->table;
+ TABLE_LIST *tbl= table->pos_in_table_list;
+ TABLE_LIST *embedding= tbl->embedding;
+
+ if (tbl->outer_join)
+ {
+ /*
+ Table tab is the only one inner table for outer join.
+ (Like table t4 for the table reference t3 LEFT JOIN t4 ON t3.a=t4.a
+ is in the query above.)
+ */
+ tab->last_inner= tab->first_inner= tab;
+ tab->on_expr= tbl->on_expr;
+ if (embedding)
+ tab->first_upper= embedding->nested_join->first_nested;
+ }
+ for ( ; embedding ; embedding= embedding->embedding)
+ {
+ NESTED_JOIN *nested_join= embedding->nested_join;
+ if (!nested_join->counter)
+ {
+ /*
+ Table tab is the first inner table for nested_join.
+ Save reference to it in the nested join structure.
+ */
+ nested_join->first_nested= tab;
+ tab->on_expr= embedding->on_expr;
+ if (embedding->embedding)
+ tab->first_upper= embedding->embedding->nested_join->first_nested;
+ }
+ if (!tab->first_inner)
+ tab->first_inner= nested_join->first_nested;
+ if (++nested_join->counter < nested_join->join_list.elements)
+ break;
+ /* Table tab is the last inner table for nested join. */
+ nested_join->first_nested->last_inner= tab;
+ }
+ }
+}
+
+
static bool
make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
{
@@ -3422,6 +4908,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
for (uint i=join->const_tables ; i < join->tables ; i++)
{
JOIN_TAB *tab=join->join_tab+i;
+ JOIN_TAB *first_inner_tab= tab->first_inner;
table_map current_map= tab->table->map;
/*
Following force including random expression in last table condition.
@@ -3461,7 +4948,16 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
join->thd->memdup((gptr) select, sizeof(SQL_SELECT));
if (!sel)
DBUG_RETURN(1); // End of memory
+ /*
+ If tab is an inner table of an outer join operation,
+ add a match guard to the pushed down predicate.
+ The guard will turn the predicate on only after
+ the first match for outer tables is encountered.
+ */
+ if (!(tmp= add_found_match_trig_cond(first_inner_tab, tmp, 0)))
+ DBUG_RETURN(1);
tab->select_cond=sel->cond=tmp;
+
sel->head=tab->table;
if (tab->quick)
{
@@ -3469,7 +4965,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
with key reading */
if (tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF
&& tab->type != JT_FT && (tab->type != JT_REF ||
- (uint) tab->ref.key == tab->quick->index))
+ (uint) tab->ref.key == tab->quick->index))
{
sel->quick=tab->quick; // Use value from get_quick_...
sel->quick_keys.clear_all();
@@ -3571,13 +5067,65 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
}
}
}
+ }
+
+ /*
+ Push down all predicates from on expressions.
+ Each of these predicated are guarded by a variable
+ that turns if off just before null complemented row for
+ outer joins is formed. Thus, the predicates from an
+ 'on expression' are guaranteed not to be checked for
+ the null complemented row.
+ */
+ JOIN_TAB *last_tab= tab;
+ while (first_inner_tab && first_inner_tab->last_inner == last_tab)
+ {
+ /*
+ Table tab is the last inner table of an outer join.
+ An on expression is always attached to it.
+ */
+ COND *on_expr= first_inner_tab->on_expr;
+
+ table_map used_tables= join->const_table_map |
+ OUTER_REF_TABLE_BIT | RAND_TABLE_BIT;
+ for (tab= join->join_tab+join->const_tables; tab <= last_tab ; tab++)
+ {
+ current_map= tab->table->map;
+ used_tables|= current_map;
+ COND *tmp= make_cond_for_table(on_expr, used_tables, current_map);
+ if (tmp)
+ {
+ JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab;
+ /*
+ First add the guards for match variables of
+ all embedding outer join operations.
+ */
+ if (!(tmp= add_found_match_trig_cond(cond_tab->first_inner,
+ tmp, first_inner_tab)))
+ DBUG_RETURN(1);
+ /*
+ Now add the guard turning the predicate off for
+ the null complemented row.
+ */
+ tmp= new Item_func_trig_cond(tmp,
+ &first_inner_tab->not_null_compl);
+ if (tmp)
+ tmp->quick_fix_field();
+ /* Add the predicate to other pushed down predicates */
+ cond_tab->select_cond= !cond_tab->select_cond ? tmp :
+ new Item_cond_and(cond_tab->select_cond,tmp);
+ if (!cond_tab->select_cond)
+ DBUG_RETURN(1);
+ cond_tab->select_cond->quick_fix_field();
+ }
+ }
+ first_inner_tab= first_inner_tab->first_upper;
}
}
}
DBUG_RETURN(0);
}
-
static void
make_join_readinfo(JOIN *join, uint options)
{
@@ -3665,7 +5213,7 @@ make_join_readinfo(JOIN *join, uint options)
*/
table->status=STATUS_NO_RECORD;
if (i != join->const_tables && !(options & SELECT_NO_JOIN_CACHE) &&
- tab->use_quick != 2 && !tab->on_expr)
+ tab->use_quick != 2 && !tab->first_inner)
{
if ((options & SELECT_DESCRIBE) ||
!join_init_cache(join->thd,join->join_tab+join->const_tables,
@@ -4111,12 +5659,13 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
if (send_row)
{
- for (TABLE_LIST *table=tables; table ; table=table->next)
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
mark_as_null_row(table->table); // All fields are NULL
if (having && having->val_int() == 0)
send_row=0;
}
- if (!(result->send_fields(fields,1)))
+ if (!(result->send_fields(fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
{
if (send_row)
{
@@ -4339,28 +5888,289 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_father,
}
+/*
+ Simplify joins replacing outer joins by inner joins whenever it's possible
+
+ SYNOPSIS
+ simplify_joins()
+ join reference to the query info
+ join_list list representation of the join to be converted
+ conds conditions to add on expressions for converted joins
+ top true <=> conds is the where condition
+
+ DESCRIPTION
+ The function, during a retrieval of join_list, eliminates those
+ outer joins that can be converted into inner join, possibly nested.
+ It also moves the on expressions for the converted outer joins
+ and from inner joins to conds.
+ The function also calculates some attributes for nested joins:
+ - used_tables
+ - not_null_tables
+ - dep_tables.
+ - on_expr_dep_tables
+ The first two attributes are used to test whether an outer join can
+ be substituted for an inner join. The third attribute represents the
+ relation 'to be dependent on' for tables. If table t2 is dependent
+ on table t1, then in any evaluated execution plan table access to
+ table t2 must precede access to table t2. This relation is used also
+ to check whether the query contains invalid cross-references.
+ The forth attribute is an auxiliary one and is used to calculate
+ dep_tables.
+ As the attribute dep_tables qualifies possibles orders of tables in the
+ execution plan, the dependencies required by the straight join
+ modifiers are reflected in this attribute as well.
+ The function also removes all braces that can be removed from the join
+ expression without changing its meaning.
+
+ NOTES
+ An outer join can be replaced by an inner join if the where condition
+ or the on expression for an embedding nested join contains a conjunctive
+ predicate rejecting null values for some attribute of the inner tables.
+
+ E.g. in the query:
+ SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
+ the predicate t2.b < 5 rejects nulls.
+ The query is converted first to:
+ SELECT * FROM t1 INNER JOIN t2 ON t2.a=t1.a WHERE t2.b < 5
+ then to the equivalent form:
+ SELECT * FROM t1, t2 ON t2.a=t1.a WHERE t2.b < 5 AND t2.a=t1.a.
+
+ Similarly the following query:
+ SELECT * from t1 LEFT JOIN (t2, t3) ON t2.a=t1.a t3.b=t1.b
+ WHERE t2.c < 5
+ is converted to:
+ SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a t3.b=t1.b
+
+ One conversion might trigger another:
+ SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a
+ LEFT JOIN t3 ON t3.b=t2.b
+ WHERE t3 IS NOT NULL =>
+ SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t1.a, t3
+ WHERE t3 IS NOT NULL AND t3.b=t2.b =>
+ SELECT * FROM t1, t2, t3
+ WHERE t3 IS NOT NULL AND t3.b=t2.b AND t2.a=t1.a
+
+ The function removes all unnecessary braces from the expression
+ produced by the conversions.
+ E.g. SELECT * FROM t1, (t2, t3) WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
+ finally is converted to:
+ SELECT * FROM t1, t2, t3 WHERE t2.c < 5 AND t2.a=t1.a AND t3.b=t1.b
+
+ It also will remove braces from the following queries:
+ SELECT * from (t1 LEFT JOIN t2 ON t2.a=t1.a) LEFT JOIN t3 ON t3.b=t2.b
+ SELECT * from (t1, (t2,t3)) WHERE t1.a=t2.a AND t2.b=t3.b.
+
+ The benefit of this simplification procedure is that it might return
+ a query for which the optimizer can evaluate execution plan with more
+ join orders. With a left join operation the optimizer does not
+ consider any plan where one of the inner tables is before some of outer
+ tables.
+
+ IMPLEMENTATION.
+ The function is implemented by a recursive procedure. On the recursive
+ ascent all attributes are calculated, all outer joins that can be
+ converted are replaced and then all unnecessary braces are removed.
+ As join list contains join tables in the reverse order sequential
+ elimination of outer joins does not requite extra recursive calls.
+
+ EXAMPLES
+ Here is an example of a join query with invalid cross references:
+ SELECT * FROM t1 LEFT JOIN t2 ON t2.a=t3.a LEFT JOIN ON t3.b=t1.b
+
+ RETURN VALUE
+ The new condition, if success
+ 0, otherwise
+*/
+
static COND *
-optimize_cond(THD *thd, COND *conds, Item::cond_result *cond_value)
+simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top)
+{
+ TABLE_LIST *table;
+ NESTED_JOIN *nested_join;
+ TABLE_LIST *prev_table= 0;
+ List_iterator<TABLE_LIST> li(*join_list);
+
+ /*
+ Try to simplify join operations from join_list.
+ The most outer join operation is checked for conversion first.
+ */
+ while ((table= li++))
+ {
+ table_map used_tables;
+ table_map not_null_tables= (table_map) 0;
+
+ if ((nested_join= table->nested_join))
+ {
+ /*
+ If the element of join_list is a nested join apply
+ the procedure to its nested join list first.
+ */
+ if (table->on_expr)
+ {
+ /*
+ If an on expression E is attached to the table,
+ check all null rejected predicates in this expression.
+ If such a predicate over an attribute belonging to
+ an inner table of an embedded outer join is found,
+ the outer join is converted to an inner join and
+ the corresponding on expression is added to E.
+ */
+ table->on_expr= simplify_joins(join, &nested_join->join_list,
+ table->on_expr, FALSE);
+ }
+ nested_join->used_tables= (table_map) 0;
+ nested_join->not_null_tables=(table_map) 0;
+ conds= simplify_joins(join, &nested_join->join_list, conds, top);
+ used_tables= nested_join->used_tables;
+ not_null_tables= nested_join->not_null_tables;
+ }
+ else
+ {
+ used_tables= table->table->map;
+ if (conds)
+ not_null_tables= conds->not_null_tables();
+ }
+
+ if (table->embedding)
+ {
+ table->embedding->nested_join->used_tables|= used_tables;
+ table->embedding->nested_join->not_null_tables|= not_null_tables;
+ }
+
+ if (!table->outer_join || (used_tables & not_null_tables))
+ {
+ /*
+ For some of the inner tables there are conjunctive predicates
+ that reject nulls => the outer join can be replaced by an inner join.
+ */
+ table->outer_join= 0;
+ if (table->on_expr)
+ {
+ /* Add on expression to the where condition. */
+ if (conds)
+ {
+ conds= and_conds(conds, table->on_expr);
+ conds->fix_fields(join->thd, 0, &conds);
+ }
+ else
+ conds= table->on_expr;
+ table->on_expr= 0;
+ }
+ }
+
+ if (!top)
+ continue;
+
+ /*
+ Only inner tables of non-convertible outer joins
+ remain with on_expr.
+ */
+ if (table->on_expr)
+ {
+ table->dep_tables|= table->on_expr->used_tables();
+ if (table->embedding)
+ {
+ table->dep_tables&= ~table->embedding->nested_join->used_tables;
+ /*
+ Embedding table depends on tables used
+ in embedded on expressions.
+ */
+ table->embedding->on_expr_dep_tables|= table->on_expr->used_tables();
+ }
+ else
+ table->dep_tables&= ~table->table->map;
+ }
+
+ if (prev_table)
+ {
+ /* The order of tables is reverse: prev_table follows table */
+ if (prev_table->straight)
+ prev_table->dep_tables|= used_tables;
+ if (prev_table->on_expr)
+ {
+ prev_table->dep_tables|= table->on_expr_dep_tables;
+ table_map prev_used_tables= prev_table->nested_join ?
+ prev_table->nested_join->used_tables :
+ prev_table->table->map;
+ /*
+ If on expression contains only references to inner tables
+ we still make the inner tables dependent on the outer tables.
+ It would be enough to set dependency only on one outer table
+ for them. Yet this is really a rare case.
+ */
+ if (!(prev_table->on_expr->used_tables() & ~prev_used_tables))
+ prev_table->dep_tables|= used_tables;
+ }
+ }
+ prev_table= table;
+ }
+
+ /* Flatten nested joins that can be flattened. */
+ li.rewind();
+ while((table= li++))
+ {
+ nested_join= table->nested_join;
+ if (nested_join && !table->on_expr)
+ {
+ TABLE_LIST *tbl;
+ List_iterator<TABLE_LIST> it(nested_join->join_list);
+ while ((tbl= it++))
+ {
+ tbl->embedding= table->embedding;
+ tbl->join_list= table->join_list;
+ }
+ li.replace(nested_join->join_list);
+ }
+ }
+ return conds;
+}
+
+
+static COND *
+optimize_cond(JOIN *join, COND *conds, Item::cond_result *cond_value)
{
SELECT_LEX *select= thd->lex->current_select;
DBUG_ENTER("optimize_cond");
- if (conds)
+
+ THD *thd= join->thd;
+ SELECT_LEX *select= thd->lex->current_select;
+ if (select->first_cond_optimization)
+ {
+ Item_arena *arena, backup;
+ select->first_cond_optimization= 0;
+
+ arena= thd->current_arena;
+ if (!arena->is_stmt_prepare())
+ arena= 0;
+ else
+ thd->set_n_backup_item_arena(arena, &backup);
+
+ /* Convert all outer joins to inner joins if possible */
+ conds= simplify_joins(join, join->join_list, conds, TRUE);
+
+ select->prep_where= conds ? conds->copy_andor_structure(thd) : 0;
+ select->first_cond_optimization= 0;
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ }
+
+ if (!conds)
+ {
+ *cond_value= Item::COND_TRUE;
+ select->prep_where= 0;
+ }
+ else
{
- DBUG_EXECUTE("where", print_where(conds, "original"););
+ DBUG_EXECUTE("where", print_where(conds, "after negation elimination"););
/* change field = field to field = const for each found field = const */
- propagate_cond_constants((I_List<COND_CMP> *) 0, conds, conds);
+ propagate_cond_constants((I_List<COND_CMP> *) 0,conds,conds);
/*
Remove all instances of item == item
Remove all and-levels where CONST item != CONST item
*/
- DBUG_EXECUTE("where", print_where(conds, "after const change"););
- conds= remove_eq_conds(thd, conds, cond_value);
- DBUG_EXECUTE("info", print_where(conds, "after remove"););
- }
- else
- {
- *cond_value= Item::COND_TRUE;
- select->prep_where= 0;
+ DBUG_EXECUTE("where",print_where(conds,"after const change"););
+ conds= remove_eq_conds(thd, conds, cond_value) ;
+ DBUG_EXECUTE("info",print_where(conds,"after remove"););
}
DBUG_RETURN(conds);
}
@@ -4564,7 +6374,6 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
return 0;
}
-
/****************************************************************************
Create internal temporary table
****************************************************************************/
@@ -5564,7 +7373,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
{
int error= 0;
JOIN_TAB *join_tab;
- int (*end_select)(JOIN *, struct st_join_table *,bool);
+ Next_select_func end_select;
DBUG_ENTER("do_select");
join->procedure=procedure;
@@ -5572,7 +7381,8 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
Tell the client how many fields there are in a row
*/
if (!table)
- join->result->send_fields(*fields,1);
+ join->result->send_fields(*fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
else
{
VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
@@ -5625,7 +7435,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
if (join->tables == join->const_tables)
{
/*
- HAVING will be chcked after processing aggregate functions,
+ HAVING will be checked after processing aggregate functions,
But WHERE should checkd here (we alredy have read tables)
*/
if (!join->conds || join->conds->val_int())
@@ -5701,7 +7511,7 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
}
if (join->thd->killed) // If aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; /* purecov: inspected */
}
if (join_tab->use_quick != 2 || test_if_quick_select(join_tab) <= 0)
@@ -5715,20 +7525,148 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
return sub_select(join,join_tab,end_of_records); /* Use ordinary select */
}
+/*
+ Retrieve records ends with a given beginning from the result of a join
+
+ SYNPOSIS
+ sub_select()
+ join pointer to the structure providing all context info for the query
+ join_tab the first next table of the execution plan to be retrieved
+ end_records true when we need to perform final steps of retrival
+
+ DESCRIPTION
+ For a given partial join record consisting of records from the tables
+ preceding the table join_tab in the execution plan, the function
+ retrieves all matching full records from the result set and
+ send them to the result set stream.
+
+ NOTES
+ The function effectively implements the final (n-k) nested loops
+ of nested loops join algorithm, where k is the ordinal number of
+ the join_tab table and n is the total number of tables in the join query.
+ It performs nested loops joins with all conjunctive predicates from
+ the where condition pushed as low to the tables as possible.
+ E.g. for the query
+ SELECT * FROM t1,t2,t3
+ WHERE t1.a=t2.a AND t2.b=t3.b AND t1.a BETWEEN 5 AND 9
+ the predicate (t1.a BETWEEN 5 AND 9) will be pushed to table t1,
+ given the selected plan prescribes to nest retrievals of the
+ joined tables in the following order: t1,t2,t3.
+ A pushed down predicate are attached to the table which it pushed to,
+ at the field select_cond.
+ When executing a nested loop of level k the function runs through
+ the rows of 'join_tab' and for each row checks the pushed condition
+ attached to the table.
+ If it is false the function moves to the next row of the
+ table. If the condition is true the function recursively executes (n-k-1)
+ remaining embedded nested loops.
+ The situation becomes more complicated if outer joins are involved in
+ the execution plan. In this case the pushed down predicates can be
+ checked only at certain conditions.
+ Suppose for the query
+ SELECT * FROM t1 LEFT JOIN (t2,t3) ON t3.a=t1.a
+ WHERE t1>2 AND (t2.b>5 OR t2.b IS NULL)
+ the optimizer has chosen a plan with the table order t1,t2,t3.
+ The predicate P1=t1>2 will be pushed down to the table t1, while the
+ predicate P2=(t2.b>5 OR t2.b IS NULL) will be attached to the table
+ t2. But the second predicate can not be unconditionally tested right
+ after a row from t2 has been read. This can be done only after the
+ first row with t3.a=t1.a has been encountered.
+ Thus, the second predicate P2 is supplied with a guarded value that are
+ stored in the field 'found' of the first inner table for the outer join
+ (table t2). When the first row with t3.a=t1.a for the current row
+ of table t1 appears, the value becomes true. For now on the predicate
+ is evaluated immediately after the row of table t2 has been read.
+ When the first row with t3.a=t1.a has been encountered all
+ conditions attached to the inner tables t2,t3 must be evaluated.
+ Only when all of them are true the row is sent to the output stream.
+ If not, the function returns to the lowest nest level that has a false
+ attached condition.
+ The predicates from on expressions are also pushed down. If in the
+ the above example the on expression were (t3.a=t1.a AND t2.a=t1.a),
+ then t1.a=t2.a would be pushed down to table t2, and without any
+ guard.
+ If after the run through all rows of table t2, the first inner table
+ for the outer join operation, it turns out that no matches are
+ found for the current row of t1, then current row from table t1
+ is complemented by nulls for t2 and t3. Then the pushed down predicates
+ are checked for the composed row almost in the same way as it had
+ been done for the first row with a match. The only difference is
+ the predicates from on expressions are not checked.
+
+ IMPLEMENTATION
+ The function forms output rows for a current partial join of k
+ tables tables recursively.
+ For each partial join record ending with a certain row from
+ join_tab it calls sub_select that builds all possible matching
+ tails from the result set.
+ To be able check predicates conditionally items of the class
+ Item_func_trig_cond are employed.
+ An object of this class is constructed from an item of class COND
+ and a pointer to a guarding boolean variable.
+ When the value of the guard variable is true the value of the object
+ is the same as the value of the predicate, otherwise it's just returns
+ true.
+ To carry out a return to a nested loop level of join table t the pointer
+ to t is remembered in the field 'return_tab' of the join structure.
+ Consider the following query:
+ SELECT * FROM t1,
+ LEFT JOIN
+ (t2, t3 LEFT JOIN (t4,t5) ON t5.a=t3.a)
+ ON t4.a=t2.a
+ WHERE (t2.b=5 OR t2.b IS NULL) AND (t4.b=2 OR t4.b IS NULL)
+ Suppose the chosen execution plan dictates the order t1,t2,t3,t4,t5
+ and suppose for a given joined rows from tables t1,t2,t3 there are
+ no rows in the result set yet.
+ When first row from t5 that satisfies the on condition
+ t5.a=t3.a is found, the pushed down predicate t4.b=2 OR t4.b IS NULL
+ becomes 'activated', as well the predicate t4.a=t2.a. But
+ the predicate (t2.b=5 OR t2.b IS NULL) can not be checked until
+ t4.a=t2.a becomes true.
+ In order not to re-evaluate the predicates that were already evaluated
+ as attached pushed down predicates, a pointer to the the first
+ most inner unmatched table is maintained in join_tab->first_unmatched.
+ Thus, when the first row from t5 with t5.a=t3.a is found
+ this pointer for t5 is changed from t4 to t2.
+
+ STRUCTURE NOTES
+ join_tab->first_unmatched points always backwards to the first inner
+ table of the embedding nested join, if any.
+
+ RETURN
+ 0, if success
+ # of the error, otherwise
+*/
static int
sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
-
join_tab->table->null_row=0;
if (end_of_records)
return (*join_tab->next_select)(join,join_tab+1,end_of_records);
- /* Cache variables for faster loop */
int error;
- bool found=0;
- COND *on_expr=join_tab->on_expr, *select_cond=join_tab->select_cond;
+ JOIN_TAB *first_unmatched;
+ JOIN_TAB *tab;
+ bool found= 0;
+ /* Cache variables for faster loop */
+ COND *select_cond= join_tab->select_cond;
+ JOIN_TAB *first_inner_tab= join_tab->first_inner;
+
my_bool *report_error= &(join->thd->net.report_error);
+ join->return_tab= join_tab;
+
+ if (join_tab->last_inner)
+ {
+ /* join_tab is the first inner table for an outer join operation. */
+
+ /* Set initial state of guard variables for this table.*/
+ join_tab->found=0;
+ join_tab->not_null_compl= 1;
+
+ /* Set first_unmatched for the last inner table of this group */
+ join_tab->last_inner->first_unmatched= join_tab;
+ }
if (!(error=(*join_tab->read_first_record)(join_tab)))
{
@@ -5742,20 +7680,80 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; /* purecov: inspected */
}
- join->examined_rows++;
- join->thd->row_count++;
- if (!on_expr || on_expr->val_int())
+ if (!select_cond || select_cond->val_int())
{
- found=1;
- if (not_exists_optimize)
- break; // Searching after not null columns
- if (!select_cond || select_cond->val_int())
- {
- if ((error=(*join_tab->next_select)(join,join_tab+1,0)) < 0)
+ /*
+ There is no select condition or the attached pushed down
+ condition is true => a match is found.
+ */
+ bool found= 1;
+ while (join_tab->first_unmatched && found)
+ {
+ /*
+ The while condition is always false if join_tab is not
+ the last inner join table of an outer join operation.
+ */
+ first_unmatched= join_tab->first_unmatched;
+ /*
+ Mark that a match for current outer table is found.
+ This activates push down conditional predicates attached
+ to the all inner tables of the outer join.
+ */
+ first_unmatched->found= 1;
+ for (tab= first_unmatched; tab <= join_tab; tab++)
+ {
+ /* Check all predicates that has just been activated. */
+ /*
+ Actually all predicates non-guarded by first_unmatched->found
+ will be re-evaluated again. It could be fixed, but, probably,
+ it's not worth doing now.
+ */
+ if (tab->select_cond && !tab->select_cond->val_int())
+ {
+ /* The condition attached to table tab is false */
+ if (tab == join_tab)
+ found= 0;
+ else
+ {
+ /*
+ Set a return point if rejected predicate is attached
+ not to the last table of the current nest level.
+ */
+ join->return_tab= tab;
+ return 0;
+ }
+ }
+ }
+ /*
+ Check whether join_tab is not the last inner table
+ for another embedding outer join.
+ */
+ if ((first_unmatched= first_unmatched->first_upper) &&
+ first_unmatched->last_inner != join_tab)
+ first_unmatched= 0;
+ join_tab->first_unmatched= first_unmatched;
+ }
+
+ /*
+ It was not just a return to lower loop level when one
+ of the newly activated predicates is evaluated as false
+ (See above join->return_tab= tab).
+ */
+ join->examined_rows++;
+ join->thd->row_count++;
+
+ if (found)
+ {
+ if (not_exists_optimize)
+ break;
+ /* A match from join_tab is found for the current partial join. */
+ if ((error=(*join_tab->next_select)(join, join_tab+1, 0)) < 0)
return error;
+ if (join->return_tab < join_tab)
+ return 0;
/*
Test if this was a SELECT DISTINCT query on a table that
was not in the field list; In this case we can abort if
@@ -5765,30 +7763,77 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
return 0;
}
else
- {
- /*
- This row failed selection, release lock on it.
- XXX: There is no table handler in MySQL which makes use of this
- call. It's kept from Gemini times. A lot of new code was added
- recently (i. e. subselects) without having it in mind.
- */
- info->file->unlock_row();
- }
+ info->file->unlock_row();
+ }
+ else
+ {
+ /*
+ The condition pushed down to the table join_tab rejects all rows
+ with the beginning coinciding with the current partial join.
+ */
+ join->examined_rows++;
+ join->thd->row_count++;
}
+
} while (!(error=info->read_record(info)) && !(*report_error));
}
if (error > 0 || (*report_error)) // Fatal error
return -1;
- if (!found && on_expr)
- { // OUTER JOIN
- restore_record(join_tab->table,default_values); // Make empty record
- mark_as_null_row(join_tab->table); // For group by without error
- if (!select_cond || select_cond->val_int())
- {
- if ((error=(*join_tab->next_select)(join,join_tab+1,0)) < 0)
- return error; /* purecov: inspected */
+ if (join_tab->last_inner && !join_tab->found)
+ {
+ /*
+ The table join_tab is the first inner table of a outer join operation
+ and no matches has been found for the current outer row.
+ */
+ JOIN_TAB *last_inner_tab= join_tab->last_inner;
+ for ( ; join_tab <= last_inner_tab ; join_tab++)
+ {
+ /* Change the the values of guard predicate variables. */
+ join_tab->found= 1;
+ join_tab->not_null_compl= 0;
+ /* The outer row is complemented by nulls for each inner tables */
+ restore_record(join_tab->table,default_values); // Make empty record
+ mark_as_null_row(join_tab->table); // For group by without error
+ select_cond= join_tab->select_cond;
+ /* Check all attached conditions for inner table rows. */
+ if (select_cond && !select_cond->val_int())
+ return 0;
+ }
+ join_tab--;
+ /*
+ The row complemented by nulls might be the first row
+ of embedding outer joins.
+ If so, perform the same actions as in the code
+ for the first regular outer join row above.
+ */
+ for ( ; ; )
+ {
+ first_unmatched= join_tab->first_unmatched;
+ if ((first_unmatched= first_unmatched->first_upper) &&
+ first_unmatched->last_inner != join_tab)
+ first_unmatched= 0;
+ join_tab->first_unmatched= first_unmatched;
+ if (!first_unmatched)
+ break;
+ first_unmatched->found= 1;
+ for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++)
+ {
+ if (tab->select_cond && !tab->select_cond->val_int())
+ {
+ join->return_tab= tab;
+ return 0;
+ }
+ }
}
+ /*
+ The row complemented by nulls satisfies all conditions
+ attached to inner tables.
+ Send the row complemented by nulls to be joined with the
+ remaining tables.
+ */
+ if ((error=(*join_tab->next_select)(join, join_tab+1 ,0)) < 0)
+ return error;
}
return 0;
}
@@ -5830,7 +7875,7 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
{
if (join->thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; // Aborted by user /* purecov: inspected */
}
SQL_SELECT *select=join_tab->select;
@@ -6161,8 +8206,8 @@ test_if_quick_select(JOIN_TAB *tab)
static int
join_init_read_record(JOIN_TAB *tab)
{
- if (tab->select && tab->select->quick)
- tab->select->quick->reset();
+ if (tab->select && tab->select->quick && tab->select->quick->reset())
+ return 1;
init_read_record(&tab->read_record, tab->join->thd, tab->table,
tab->select,1,1);
return (*tab->read_record.read_record)(&tab->read_record);
@@ -6370,6 +8415,14 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
DBUG_RETURN(-3); // Abort nicely
}
+ else if (join->send_records >= join->fetch_limit)
+ {
+ /*
+ There is a server side cursor and all rows for
+ this fetch request are sent.
+ */
+ DBUG_RETURN(-4);
+ }
}
else
{
@@ -6444,6 +8497,14 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
join->do_send_rows=0;
join->unit->select_limit_cnt = HA_POS_ERROR;
}
+ else if (join->send_records >= join->fetch_limit)
+ {
+ /*
+ There is a server side cursor and all rows
+ for this fetch request are sent.
+ */
+ DBUG_RETURN(-4);
+ }
}
}
else
@@ -6482,7 +8543,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
if (!end_of_records)
@@ -6550,7 +8611,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(0);
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
@@ -6619,7 +8680,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(0);
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
@@ -6666,7 +8727,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->thd->killed)
{ // Aborted by user
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
if (!join->first_record || end_of_records ||
@@ -6929,7 +8990,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
}
-static uint find_shortest_key(TABLE *table, const key_map *usable_keys)
+uint find_shortest_key(TABLE *table, const key_map *usable_keys)
{
uint min_length= (uint) ~0;
uint best= MAX_KEY;
@@ -7066,6 +9127,17 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
else if (select && select->quick) // Range found by opt_range
{
+ int quick_type= select->quick->get_type();
+ /*
+ assume results are not ordered when index merge is used
+ TODO: sergeyp: Results of all index merge selects actually are ordered
+ by clustered PK values.
+ */
+
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT)
+ DBUG_RETURN(0);
ref_key= select->quick->index;
ref_key_parts= select->quick->used_key_parts;
}
@@ -7099,7 +9171,11 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
else
{
- select->quick->file->ha_index_end();
+ select->quick->head->file->ha_index_end();
+ /*
+ We have verified above that select->quick is not
+ index_merge quick select.
+ */
select->quick->index= new_ref_key;
select->quick->init();
}
@@ -7121,12 +9197,17 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
*/
if (!select->quick->reverse_sorted())
{
- // here used_key_parts >0
+ int quick_type= select->quick->get_type();
+ /* here used_key_parts >0 */
if (!(table->file->index_flags(ref_key,used_key_parts-1, 1)
- & HA_READ_PREV))
+ & HA_READ_PREV) ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION)
DBUG_RETURN(0); // Use filesort
- // ORDER BY range_key DESC
- QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick,
+
+ /* ORDER BY range_key DESC */
+ QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
used_key_parts);
if (!tmp || tmp->error)
{
@@ -7277,8 +9358,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{
select->quick=tab->quick;
tab->quick=0;
- /* We can only use 'Only index' if quick key is same as ref_key */
- if (table->key_read && (uint) tab->ref.key != select->quick->index)
+ /*
+ We can only use 'Only index' if quick key is same as ref_key
+ and in index_merge 'Only index' cannot be used
+ */
+ if (table->key_read && ((uint) tab->ref.key != select->quick->index))
{
table->key_read=0;
table->file->extra(HA_EXTRA_NO_KEYREAD);
@@ -7309,6 +9393,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
tab->select= 0;
}
tab->select_cond=0;
+ tab->last_inner= 0;
+ tab->first_unmatched= 0;
tab->type=JT_ALL; // Read with normal read_record
tab->read_first_record= join_init_read_record;
tab->join->examined_rows+=examined_rows;
@@ -7466,7 +9552,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error=0;
goto err;
}
@@ -7578,7 +9664,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error=0;
goto err;
}
@@ -8269,7 +10355,7 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables)
if (!map || (map & (RAND_TABLE_BIT | OUTER_REF_TABLE_BIT)))
DBUG_RETURN(0);
- for (; !(map & tables->table->map) ; tables=tables->next) ;
+ for (; !(map & tables->table->map); tables= tables->next_local);
if (map != tables->table->map)
DBUG_RETURN(0); // More than one table
DBUG_PRINT("exit",("sort by table: %d",tables->table->tablenr));
@@ -9119,6 +11205,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
select_result *result=join->result;
Item *item_null= new Item_null();
CHARSET_INFO *cs= system_charset_info;
+ int quick_type;
DBUG_ENTER("select_describe");
DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
(ulong)join->select_lex, join->select_lex->type,
@@ -9210,14 +11297,20 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table;
- char buff[512],*buff_ptr=buff;
- char buff1[512], buff2[512];
+ char buff[512];
+ char buff1[512], buff2[512], buff3[512];
+ char keylen_str_buf[64];
+ String extra(buff, sizeof(buff),cs);
char table_name_buffer[NAME_LEN];
String tmp1(buff1,sizeof(buff1),cs);
String tmp2(buff2,sizeof(buff2),cs);
+ String tmp3(buff3,sizeof(buff3),cs);
+ extra.length(0);
tmp1.length(0);
tmp2.length(0);
+ tmp3.length(0);
+ quick_type= -1;
item_list.empty();
/* id */
item_list.push_back(new Item_uint((uint32)
@@ -9227,7 +11320,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
strlen(join->select_lex->type),
cs));
if (tab->type == JT_ALL && tab->select && tab->select->quick)
- tab->type= JT_RANGE;
+ {
+ quick_type= tab->select->quick->get_type();
+ if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
+ tab->type = JT_INDEX_MERGE;
+ else
+ tab->type = JT_RANGE;
+ }
/* table */
if (table->derived_select_number)
{
@@ -9246,7 +11347,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
strlen(join_type_str[tab->type]),
cs));
uint j;
- /* possible_keys */
+ /* Build "possible_keys" value and add it to item_list */
if (!tab->keys.is_clear_all())
{
for (j=0 ; j < table->keys ; j++)
@@ -9265,14 +11366,19 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length(),cs));
else
item_list.push_back(item_null);
- /* key key_len ref */
+
+ /* Build "key", "key_len", and "ref" values and add them to item_list */
if (tab->ref.key_parts)
{
KEY *key_info=table->key_info+ tab->ref.key;
+ register uint length;
item_list.push_back(new Item_string(key_info->name,
strlen(key_info->name),
system_charset_info));
- item_list.push_back(new Item_int((int32) tab->ref.key_length));
+ length= longlong2str(tab->ref.key_length, keylen_str_buf, 10) -
+ keylen_str_buf;
+ item_list.push_back(new Item_string(keylen_str_buf, length,
+ system_charset_info));
for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
{
if (tmp2.length())
@@ -9285,18 +11391,21 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
else if (tab->type == JT_NEXT)
{
KEY *key_info=table->key_info+ tab->index;
+ register uint length;
item_list.push_back(new Item_string(key_info->name,
strlen(key_info->name),cs));
- item_list.push_back(new Item_int((int32) key_info->key_length));
+ length= longlong2str(key_info->key_length, keylen_str_buf, 10) -
+ keylen_str_buf;
+ item_list.push_back(new Item_string(keylen_str_buf,
+ length,
+ system_charset_info));
item_list.push_back(item_null);
}
else if (tab->select && tab->select->quick)
{
- KEY *key_info=table->key_info+ tab->select->quick->index;
- item_list.push_back(new Item_string(key_info->name,
- strlen(key_info->name),cs));
- item_list.push_back(new Item_int((int32) tab->select->quick->
- max_used_key_length));
+ tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
+ item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
+ item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
item_list.push_back(item_null);
}
else
@@ -9305,52 +11414,68 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
item_list.push_back(item_null);
item_list.push_back(item_null);
}
- /* rows */
+ /* Add "rows" field to item_list. */
item_list.push_back(new Item_int((longlong) (ulonglong)
join->best_positions[i]. records_read,
21));
- /* extra */
+ /* Build "Extra" field and add it to item_list. */
my_bool key_read=table->key_read;
if ((tab->type == JT_NEXT || tab->type == JT_CONST) &&
table->used_keys.is_set(tab->index))
key_read=1;
-
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
+ !((QUICK_ROR_INTERSECT_SELECT*)tab->select->quick)->need_to_fetch_row)
+ key_read=1;
+
if (tab->info)
item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
else
{
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
+ {
+ extra.append("; Using ");
+ tab->select->quick->add_info_string(&extra);
+ }
if (tab->select)
{
if (tab->use_quick == 2)
{
char buf[MAX_KEY/8+1];
- sprintf(buff_ptr,"; Range checked for each record (index map: 0x%s)",
- tab->keys.print(buf));
- buff_ptr=strend(buff_ptr);
+ extra.append("; Range checked for each record (index map: 0x");
+ extra.append(tab->keys.print(buf));
+ extra.append(')');
}
else
- buff_ptr=strmov(buff_ptr,"; Using where");
+ extra.append("; Using where");
}
if (key_read)
- buff_ptr= strmov(buff_ptr,"; Using index");
+ extra.append("; Using index");
if (table->reginfo.not_exists_optimize)
- buff_ptr= strmov(buff_ptr,"; Not exists");
+ extra.append("; Not exists");
if (need_tmp_table)
{
need_tmp_table=0;
- buff_ptr= strmov(buff_ptr,"; Using temporary");
+ extra.append("; Using temporary");
}
if (need_order)
{
need_order=0;
- buff_ptr= strmov(buff_ptr,"; Using filesort");
+ extra.append("; Using filesort");
}
if (distinct & test_all_bits(used_tables,thd->used_tables))
- buff_ptr= strmov(buff_ptr,"; Distinct");
- if (buff_ptr == buff)
- buff_ptr+= 2; // Skip inital "; "
- item_list.push_back(new Item_string(buff+2,(uint) (buff_ptr - buff)-2,
- cs));
+ extra.append("; Distinct");
+
+ /* Skip initial "; "*/
+ const char *str= extra.ptr();
+ uint32 len= extra.length();
+ if (len)
+ {
+ str += 2;
+ len -= 2;
+ }
+ item_list.push_back(new Item_string(str, len, cs));
}
// For next iteration
used_tables|=table->map;
@@ -9409,6 +11534,7 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
else
{
thd->lex->current_select= first;
+ unit->set_limit(unit->global_parameters, first);
res= mysql_select(thd, &first->ref_pointer_array,
(TABLE_LIST*) first->table_list.first,
first->with_wild, first->item_list,
@@ -9428,14 +11554,109 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
}
+/*
+ Print joins from the FROM clause
+
+ SYNOPSIS
+ print_join()
+ thd thread handler
+ str string where table should be printed
+ tables list of tables in join
+*/
+
+static void print_join(THD *thd, String *str, List<TABLE_LIST> *tables)
+{
+ /* List is reversed => we should reverse it before using */
+ List_iterator_fast<TABLE_LIST> ti(*tables);
+ TABLE_LIST **table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) *
+ tables->elements);
+ if (table == 0)
+ return; // out of memory
+
+ for (TABLE_LIST **t= table + (tables->elements - 1); t >= table; t--)
+ *t= ti++;
+
+ DBUG_ASSERT(tables->elements >= 1);
+ (*table)->print(thd, str);
+
+ TABLE_LIST **end= table + tables->elements;
+ for(TABLE_LIST **tbl= table + 1; tbl < end; tbl++)
+ {
+ TABLE_LIST *curr= *tbl;
+ if (curr->outer_join)
+ str->append(" left join ", 11); // MySQL converg right to left joins
+ else if (curr->straight)
+ str->append(" straight_join ", 15);
+ else
+ str->append(" join ", 6);
+ curr->print(thd, str);
+ if (curr->on_expr)
+ {
+ str->append(" on(", 4);
+ curr->on_expr->print(str);
+ str->append(')');
+ }
+ }
+}
+
+
+/*
+ Print table as it should be in join list
+
+ SYNOPSIS
+ st_table_list::print();
+ str string where table should bbe printed
+*/
+
+void st_table_list::print(THD *thd, String *str)
+{
+ if (nested_join)
+ {
+ str->append('(');
+ print_join(thd, str, &nested_join->join_list);
+ str->append(')');
+ }
+ else
+ {
+ const char *cmp_name; // Name to compare with alias
+ if (view_name.str)
+ {
+ append_identifier(thd, str, view_db.str, view_db.length);
+ str->append('.');
+ append_identifier(thd, str, view_name.str, view_name.length);
+ cmp_name= view_name.str;
+ }
+ else if (derived)
+ {
+ str->append('(');
+ derived->print(str);
+ str->append(')');
+ cmp_name= ""; // Force printing of alias
+ }
+ else
+ {
+ append_identifier(thd, str, db, db_length);
+ str->append('.');
+ append_identifier(thd, str, real_name, real_name_length);
+ cmp_name= real_name;
+ }
+ if (my_strcasecmp(table_alias_charset, cmp_name, alias))
+ {
+ str->append(' ');
+ append_identifier(thd, str, alias, strlen(alias));
+ }
+ }
+}
+
+
void st_select_lex::print(THD *thd, String *str)
{
if (!thd)
thd= current_thd;
str->append("select ", 7);
-
- //options
+
+ /* First add options */
if (options & SELECT_STRAIGHT_JOIN)
str->append("straight_join ", 14);
if ((thd->lex->lock_option == TL_READ_HIGH_PRIORITY) &&
@@ -9476,60 +11697,8 @@ void st_select_lex::print(THD *thd, String *str)
if (table_list.elements)
{
str->append(" from ", 6);
- Item *next_on= 0;
- for (TABLE_LIST *table= (TABLE_LIST *) table_list.first;
- table;
- table= table->next)
- {
- if (table->derived)
- {
- str->append('(');
- table->derived->print(str);
- str->append(") ");
- str->append(table->alias);
- }
- else
- {
- str->append(table->db);
- str->append('.');
- str->append(table->real_name);
- if (my_strcasecmp(table_alias_charset, table->real_name, table->alias))
- {
- str->append(' ');
- str->append(table->alias);
- }
- }
-
- if (table->on_expr && ((table->outer_join & JOIN_TYPE_LEFT) ||
- !(table->outer_join & JOIN_TYPE_RIGHT)))
- next_on= table->on_expr;
-
- if (next_on)
- {
- str->append(" on(", 4);
- next_on->print(str);
- str->append(')');
- next_on= 0;
- }
-
- TABLE_LIST *next_table;
- if ((next_table= table->next))
- {
- if (table->outer_join & JOIN_TYPE_RIGHT)
- {
- str->append(" right join ", 12);
- if (!(table->outer_join & JOIN_TYPE_LEFT) &&
- table->on_expr)
- next_on= table->on_expr;
- }
- else if (next_table->straight)
- str->append(" straight_join ", 15);
- else if (next_table->outer_join & JOIN_TYPE_LEFT)
- str->append(" left join ", 11);
- else
- str->append(" join ", 6);
- }
- }
+ /* go through join tree */
+ print_join(thd, str, &top_join_list);
}
// Where
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 34eaa7e272d..a1487693b79 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -75,23 +75,32 @@ typedef struct st_join_cache {
/*
** The structs which holds the join connections and join states
*/
-
enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF,
JT_ALL, JT_RANGE, JT_NEXT, JT_FT, JT_REF_OR_NULL,
- JT_UNIQUE_SUBQUERY, JT_INDEX_SUBQUERY};
+ JT_UNIQUE_SUBQUERY, JT_INDEX_SUBQUERY, JT_INDEX_MERGE};
class JOIN;
+typedef int (*Next_select_func)(JOIN *,struct st_join_table *,bool);
+typedef int (*Read_record_func)(struct st_join_table *tab);
+
+
typedef struct st_join_table {
TABLE *table;
KEYUSE *keyuse; /* pointer to first used key */
SQL_SELECT *select;
COND *select_cond;
- QUICK_SELECT *quick;
- Item *on_expr;
+ QUICK_SELECT_I *quick;
+ Item *on_expr; /* associated on expression */
+ st_join_table *first_inner; /* first inner table for including outerjoin */
+ bool found; /* true after all matches or null complement */
+ bool not_null_compl;/* true before null complement is added */
+ st_join_table *last_inner; /* last table table for embedding outer join */
+ st_join_table *first_upper; /* first inner table for embedding outer join */
+ st_join_table *first_unmatched; /* used for optimization purposes only */
const char *info;
- int (*read_first_record)(struct st_join_table *tab);
- int (*next_select)(JOIN *,struct st_join_table *,bool);
+ Read_record_func read_first_record;
+ Next_select_func next_select;
READ_RECORD read_record;
double worst_seeks;
key_map const_keys; /* Keys with constant part */
@@ -116,6 +125,7 @@ typedef struct st_join_table {
typedef struct st_position /* Used in find_best */
{
double records_read;
+ double read_time;
JOIN_TAB *table;
KEYUSE *key;
} POSITION;
@@ -133,8 +143,9 @@ typedef struct st_rollup
class JOIN :public Sql_alloc
{
public:
- JOIN_TAB *join_tab,**best_ref,**map2table;
- JOIN_TAB *join_tab_save; //saved join_tab for subquery reexecution
+ JOIN_TAB *join_tab,**best_ref;
+ JOIN_TAB **map2table; // mapping between table indexes and JOIN_TABs
+ JOIN_TAB *join_tab_save; // saved join_tab for subquery reexecution
TABLE **table,**all_tables,*sort_by_table;
uint tables,const_tables;
uint send_group_parts;
@@ -142,6 +153,16 @@ class JOIN :public Sql_alloc
bool do_send_rows;
table_map const_table_map,found_const_table_map,outer_join;
ha_rows send_records,found_records,examined_rows,row_limit, select_limit;
+ /*
+ Used to fetch no more than given amount of rows per one
+ fetch operation of server side cursor.
+ The value is checked in end_send and end_send_group in fashion, similar
+ to offset_limit_cnt:
+ - fetch_limit= HA_POS_ERROR if there is no cursor.
+ - when we open a cursor, we set fetch_limit to 0,
+ - on each fetch iteration we add num_rows to fetch to fetch_limit
+ */
+ ha_rows fetch_limit;
POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1];
double best_read;
List<Item> *fields;
@@ -199,8 +220,10 @@ class JOIN :public Sql_alloc
ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select
COND *conds; // ---"---
Item *conds_history; // store WHERE for explain
- TABLE_LIST *tables_list; //hold 'tables' parameter of mysql_selec
+ TABLE_LIST *tables_list; //hold 'tables' parameter of mysql_select
+ List<TABLE_LIST> *join_list; // list of joined tables in reverse order
SQL_SELECT *select; //created in optimisation phase
+ JOIN_TAB *return_tab; //used only for outer joins
Item **ref_pointer_array; //used pointer reference for this select
// Copy of above to be used with different lists
Item **items0, **items1, **items2, **items3, **current_ref_pointer_array;
@@ -224,11 +247,13 @@ class JOIN :public Sql_alloc
table= 0;
tables= 0;
const_tables= 0;
+ join_list= 0;
sort_and_group= 0;
first_record= 0;
do_send_rows= 1;
send_records= 0;
found_records= 0;
+ fetch_limit= HA_POS_ERROR;
examined_rows= 0;
exec_tmp_table1= 0;
exec_tmp_table2= 0;
@@ -254,6 +279,7 @@ class JOIN :public Sql_alloc
fields_list= fields_arg;
error= 0;
select= 0;
+ return_tab= 0;
ref_pointer_array= items0= items1= items2= items3= 0;
ref_pointer_array_size= 0;
zero_result_cause= 0;
@@ -308,6 +334,44 @@ class JOIN :public Sql_alloc
};
+/*
+ Server-side cursor (now stands only for basic read-only cursor)
+ See class implementation in sql_select.cc
+*/
+
+class Cursor: public Sql_alloc, public Item_arena
+{
+ JOIN *join;
+ SELECT_LEX_UNIT *unit;
+
+ TABLE *open_tables;
+ MYSQL_LOCK *lock;
+ TABLE *derived_tables;
+ /* List of items created during execution */
+ ulong query_id;
+public:
+ select_send result;
+
+ /* Temporary implementation as now we replace THD state by value */
+ /* Save THD state into cursor */
+ void init_from_thd(THD *thd);
+ /* Restore THD from cursor to continue cursor execution */
+ void init_thd(THD *thd);
+ /* bzero cursor state in THD */
+ void reset_thd(THD *thd);
+
+ int open(JOIN *join);
+ int fetch(ulong num_rows);
+ void reset() { join= 0; }
+ bool is_open() const { return join != 0; }
+ void close();
+
+ void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
+ Cursor() :join(0), unit(0) {}
+ ~Cursor();
+};
+
+
typedef struct st_select_check {
uint const_ref,reg_ref;
} SELECT_CHECK;
@@ -332,10 +396,14 @@ void copy_fields(TMP_TABLE_PARAM *param);
void copy_funcs(Item **func_ptr);
bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
int error, bool ignore_last_dupp_error);
+uint find_shortest_key(TABLE *table, const key_map *usable_keys);
/* functions from opt_sum.cc */
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds);
+/* from sql_delete.cc, used by opt_range.cc */
+extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b);
+
/* class to copying an field/item to a key struct */
class store_key :public Sql_alloc
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 57c5f01d0bf..6322d99582d 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -39,6 +39,8 @@ static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **),
static int
store_create_info(THD *thd, TABLE *table, String *packet);
+static int
+view_store_create_info(THD *thd, TABLE_LIST *table, String *packet);
/*
@@ -64,7 +66,8 @@ mysqld_show_dbs(THD *thd,const char *wild)
strxmov(end," (",wild,")",NullS);
field_list.push_back(field);
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
if (mysql_find_files(thd,&files,NullS,mysql_data_home,wild,1))
DBUG_RETURN(1);
@@ -105,7 +108,8 @@ int mysqld_show_open_tables(THD *thd,const char *wild)
field_list.push_back(new Item_return_int("In_use", 1, MYSQL_TYPE_TINY));
field_list.push_back(new Item_return_int("Name_locked", 4, MYSQL_TYPE_TINY));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
if (!(open_list=list_open_tables(thd,wild)) && thd->is_fatal_error)
@@ -141,6 +145,9 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild)
List<char> files;
char *file_name;
Protocol *protocol= thd->protocol;
+ uint len;
+ bool show_type = !test(thd->variables.sql_mode &
+ (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323));
DBUG_ENTER("mysqld_show_tables");
field->name=(char*) thd->alloc(20+(uint) strlen(db)+
@@ -149,10 +156,14 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild)
if (wild && wild[0])
strxmov(end," (",wild,")",NullS);
field->max_length=NAME_LEN;
- (void) sprintf(path,"%s/%s",mysql_data_home,db);
- (void) unpack_dirname(path,path);
+ (void) my_snprintf(path, FN_LEN, "%s/%s", mysql_data_home, db);
+ end= path + (len= unpack_dirname(path,path));
+ len= FN_LEN - len;
field_list.push_back(field);
- if (protocol->send_fields(&field_list,1))
+ if (show_type)
+ field_list.push_back(new Item_empty_string("table_type", 10));
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
if (mysql_find_files(thd,&files,db,path,wild,0))
DBUG_RETURN(-1);
@@ -161,6 +172,24 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild)
{
protocol->prepare_for_resend();
protocol->store(file_name, system_charset_info);
+ if (show_type)
+ {
+ my_snprintf(end, len, "/%s%s", file_name, reg_ext);
+ switch (mysql_frm_type(path))
+ {
+ case FRMTYPE_ERROR:
+ protocol->store("ERROR", system_charset_info);
+ break;
+ case FRMTYPE_TABLE:
+ protocol->store("BASE TABLE", system_charset_info);
+ break;
+ case FRMTYPE_VIEW:
+ protocol->store("VIEW", system_charset_info);
+ break;
+ default:
+ DBUG_ASSERT(0); // this should be impossible
+ }
+ }
if (protocol->write())
DBUG_RETURN(-1);
}
@@ -182,7 +211,8 @@ int mysqld_show_storage_engines(THD *thd)
field_list.push_back(new Item_empty_string("Support",10));
field_list.push_back(new Item_empty_string("Comment",80));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
const char *default_type_name=
@@ -221,10 +251,11 @@ struct show_privileges_st {
static struct show_privileges_st sys_privileges[]=
{
{"Alter", "Tables", "To alter the table"},
- {"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"},
{"Create", "Databases,Tables,Indexes", "To create new databases and tables"},
+ {"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"},
+ {"Create view", "Tables", "To create new views"},
{"Delete", "Tables", "To delete existing rows"},
- {"Drop", "Databases,Tables", "To drop databases and tables"},
+ {"Drop", "Databases,Tables", "To drop databases, tables, and views"},
{"File", "File access on server", "To read and write files on the server"},
{"Grant option", "Databases,Tables", "To give to other users those privileges you possess"},
{"Index", "Tables", "To create or drop indexes"},
@@ -237,7 +268,8 @@ static struct show_privileges_st sys_privileges[]=
{"Replication slave","Server Admin","To read binary log events from the master"},
{"Select", "Tables", "To retrieve rows from table"},
{"Show databases","Server Admin","To see all databases with SHOW DATABASES"},
- {"Shutdown","Server Admin", "To shutdown the server"},
+ {"Show view","Tables","To see views with SHOW CREATE VIEW"},
+ {"Shutdown","Server Admin", "To shut down the server"},
{"Super","Server Admin","To use KILL thread, SET GLOBAL, CHANGE MASTER, etc."},
{"Update", "Tables", "To update existing rows"},
{"Usage","Server Admin","No privileges - allow connect only"},
@@ -254,7 +286,8 @@ int mysqld_show_privileges(THD *thd)
field_list.push_back(new Item_empty_string("Context",15));
field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
show_privileges_st *privilege= sys_privileges;
@@ -329,7 +362,8 @@ int mysqld_show_column_types(THD *thd)
field_list.push_back(new Item_empty_string("Default",NAME_LEN));
field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
/* TODO: Change the loop to not use 'i' */
@@ -502,7 +536,8 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("Comment",80));
item->maybe_null=1;
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
if (mysql_find_files(thd,&files,db,path,wild,0))
@@ -516,9 +551,10 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
protocol->store(file_name, system_charset_info);
table_list.db=(char*) db;
table_list.real_name= table_list.alias= file_name;
+ table_list.select_lex= &thd->lex->select_lex;
if (lower_case_table_names)
my_casedn_str(files_charset_info, file_name);
- if (!(table = open_ltable(thd, &table_list, TL_READ)))
+ if (open_and_lock_tables(thd, &table_list))
{
for (uint i=2 ; i < field_list.elements ; i++)
protocol->store_null();
@@ -526,10 +562,16 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
protocol->store(thd->net.last_error, system_charset_info);
thd->clear_error();
}
+ else if (table_list.view)
+ {
+ for (uint i= 2; i < field_list.elements; i++)
+ protocol->store_null();
+ protocol->store("view", system_charset_info);
+ }
else
{
const char *str;
- handler *file=table->file;
+ handler *file= (table= table_list.table)->file;
file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK);
protocol->store(file->table_type(), system_charset_info);
protocol->store((ulonglong) table->frm_version);
@@ -630,8 +672,8 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
if (comment != table->comment)
my_free(comment,MYF(0));
}
- close_thread_tables(thd,0);
}
+ close_thread_tables(thd, 0);
if (protocol->write())
DBUG_RETURN(-1);
}
@@ -658,11 +700,13 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
table_list->real_name));
- if (!(table = open_ltable(thd, table_list, TL_UNLOCK)))
+ table_list->lock_type= TL_UNLOCK;
+ if (open_and_lock_tables(thd, table_list))
{
send_error(thd);
DBUG_RETURN(1);
}
+ table= table_list->table;
file=table->file;
file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -685,7 +729,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
}
// Send first number of fields and records
if (protocol->send_records_num(&field_list, (ulonglong)file->records) ||
- protocol->send_fields(&field_list,0))
+ protocol->send_fields(&field_list, Protocol::SEND_EOF))
DBUG_RETURN(1);
restore_record(table,default_values); // Get empty record
@@ -771,7 +815,10 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
/* Add grant options & comments */
end=tmp;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- col_access= get_column_grant(thd,table_list,field) & COL_ACLS;
+ col_access= get_column_grant(thd, &table_list->grant,
+ table_list->db,
+ table_list->real_name,
+ field->field_name) & COL_ACLS;
for (uint bitnr=0; col_access ; col_access>>=1,bitnr++)
{
if (col_access & 1)
@@ -809,14 +856,24 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
table_list->real_name));
- /* Only one table for now */
- if (!(table = open_ltable(thd, table_list, TL_UNLOCK)))
+ /* Only one table for now, but VIEW can involve several tables */
+ if (open_and_lock_tables(thd, table_list))
{
send_error(thd);
DBUG_RETURN(1);
}
+ /* TODO: add environment variables show when it become possible */
+ if (thd->lex->only_view && !table_list->view)
+ {
+ my_error(ER_WRONG_OBJECT, MYF(0), table_list->db,
+ table_list->real_name, "VIEW");
+ DBUG_RETURN(-1);
+ }
+ table= table_list->table;
- if (store_create_info(thd, table, &buffer))
+ if ((table_list->view ?
+ view_store_create_info(thd, table_list, &buffer) :
+ store_create_info(thd, table, &buffer)))
DBUG_RETURN(-1);
List<Item> field_list;
@@ -825,14 +882,25 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
field_list.push_back(new Item_empty_string("Create Table",
max(buffer.length(),1024)));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
protocol->prepare_for_resend();
- protocol->store(table->table_name, system_charset_info);
buffer.length(0);
- if (store_create_info(thd, table, &buffer))
- DBUG_RETURN(-1);
+ if (table_list->view)
+ {
+ protocol->store(table_list->view_name.str, system_charset_info);
+ if (view_store_create_info(thd, table_list, &buffer))
+ DBUG_RETURN(-1);
+ }
+ else
+ {
+ protocol->store(table->table_name, system_charset_info);
+ if (store_create_info(thd, table, &buffer))
+ DBUG_RETURN(-1);
+ }
protocol->store(buffer.ptr(), buffer.length(), buffer.charset());
+
if (protocol->write())
DBUG_RETURN(1);
send_eof(thd);
@@ -899,7 +967,8 @@ int mysqld_show_create_db(THD *thd, char *dbname,
field_list.push_back(new Item_empty_string("Database",NAME_LEN));
field_list.push_back(new Item_empty_string("Create Database",1024));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
protocol->prepare_for_resend();
@@ -941,7 +1010,8 @@ mysqld_show_logs(THD *thd)
field_list.push_back(new Item_empty_string("Type",10));
field_list.push_back(new Item_empty_string("Status",10));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
#ifdef HAVE_BERKELEY_DB
@@ -990,7 +1060,8 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list)
field_list.push_back(new Item_empty_string("Comment",255));
item->maybe_null=1;
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
KEY *key_info=table->key_info;
@@ -1077,7 +1148,8 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
field_list.push_back(new Item_field(field));
}
restore_record(table,default_values); // Get empty record
- if (thd->protocol->send_fields(&field_list,2))
+ if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS |
+ Protocol::SEND_EOF))
DBUG_VOID_RETURN;
net_flush(&thd->net);
DBUG_VOID_RETURN;
@@ -1499,6 +1571,35 @@ store_create_info(THD *thd, TABLE *table, String *packet)
}
+static int
+view_store_create_info(THD *thd, TABLE_LIST *table, String *buff)
+{
+ my_bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL |
+ MODE_ORACLE |
+ MODE_MSSQL |
+ MODE_DB2 |
+ MODE_MAXDB |
+ MODE_ANSI)) != 0;
+ buff->append("CREATE ", 7);
+ if (!foreign_db_mode && (table->algorithm == VIEW_ALGORITHM_MERGE ||
+ table->algorithm == VIEW_ALGORITHM_TMPTABLE))
+ {
+ buff->append("ALGORITHM=", 10);
+ if (table->algorithm == VIEW_ALGORITHM_TMPTABLE)
+ buff->append("TMPTABLE ", 9);
+ else
+ buff->append("MERGE ", 6);
+ }
+ buff->append("VIEW ", 5);
+ append_identifier(thd, buff, table->view_db.str, table->view_db.length);
+ buff->append('.');
+ append_identifier(thd, buff, table->view_name.str, table->view_name.length);
+ buff->append(" AS ", 4);
+ buff->append(table->query.str, table->query.length);
+ return 0;
+}
+
+
/****************************************************************************
Return info about all processes
returns for each thread: thread id, user, host, db, command, info
@@ -1542,7 +1643,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
field->maybe_null=1;
field_list.push_back(field=new Item_empty_string("Info",max_query_length));
field->maybe_null=1;
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_VOID_RETURN;
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
@@ -1575,7 +1677,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thd_info->command=(int) tmp->command;
if ((mysys_var= tmp->mysys_var))
pthread_mutex_lock(&mysys_var->mutex);
- thd_info->proc_info= (char*) (tmp->killed ? "Killed" : 0);
+ thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0);
#ifndef EMBEDDED_LIBRARY
thd_info->state_info= (char*) (tmp->locked ? "Locked" :
tmp->net.reading_or_writing ?
@@ -1678,7 +1780,8 @@ int mysqld_show_collations(THD *thd, const char *wild)
field_list.push_back(new Item_empty_string("Compiled",30));
field_list.push_back(new Item_return_int("Sortlen",3, FIELD_TYPE_SHORT));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ )
@@ -1731,7 +1834,8 @@ int mysqld_show_charsets(THD *thd, const char *wild)
field_list.push_back(new Item_empty_string("Default collation",60));
field_list.push_back(new Item_return_int("Maxlen",3, FIELD_TYPE_SHORT));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ )
@@ -1765,7 +1869,8 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables,
field_list.push_back(new Item_empty_string("Variable_name",30));
field_list.push_back(new Item_empty_string("Value",256));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1); /* purecov: inspected */
null_lex_str.str= 0; // For sys_var->value_ptr()
null_lex_str.length= 0;
@@ -1856,6 +1961,11 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables,
end= strend(pos);
break;
}
+ case SHOW_DOUBLE:
+ {
+ end= buff + sprintf(buff, "%f", *(double*) value);
+ break;
+ }
#ifdef HAVE_OPENSSL
/* First group - functions relying on CTX */
case SHOW_SSL_CTX_SESS_ACCEPT:
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index 9f95ffa4884..1831c4a2f3d 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -78,3 +78,4 @@ int merge_buffers(SORTPARAM *param,IO_CACHE *from_file,
IO_CACHE *to_file, uchar *sort_buffer,
BUFFPEK *lastbuff,BUFFPEK *Fb,
BUFFPEK *Tb,int flag);
+void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length);
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 1ec0faafa8f..ba9431f27c4 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -671,6 +671,19 @@ void String::qs_append(double *d)
qs_append(ld);
}
+void String::qs_append(int i)
+{
+ char *buff = Ptr + str_length;
+ sprintf(buff,"%d", i);
+ str_length += strlen(buff);
+}
+
+void String::qs_append(uint i)
+{
+ char *buff = Ptr + str_length;
+ sprintf(buff,"%u", i);
+ str_length += strlen(buff);
+}
/*
Compare strings according to collation, without end space.
diff --git a/sql/sql_string.h b/sql/sql_string.h
index d8c4c3a87a1..ad32305e9b4 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -282,6 +282,8 @@ public:
Ptr[str_length]= c;
str_length++;
}
+ void qs_append(int i);
+ void qs_append(uint i);
/* Inline (general) functions used by the protocol functions */
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 58e3bc1d9ac..03c20be198e 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -89,7 +89,7 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
}
}
- error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary, 0);
+ error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
err:
pthread_mutex_unlock(&LOCK_open);
@@ -134,8 +134,8 @@ int mysql_rm_table_part2_with_lock(THD *thd,
thd->mysys_var->current_cond= &COND_refresh;
VOID(pthread_mutex_lock(&LOCK_open));
- error=mysql_rm_table_part2(thd,tables, if_exists, drop_temporary,
- dont_log_query);
+ error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 1,
+ dont_log_query);
pthread_mutex_unlock(&LOCK_open);
@@ -157,6 +157,7 @@ int mysql_rm_table_part2_with_lock(THD *thd,
if_exists If set, don't give an error if table doesn't exists.
In this case we give an warning of level 'NOTE'
drop_temporary Only drop temporary tables
+ drop_view Allow to delete VIEW .frm
dont_log_query Don't log the query
TODO:
@@ -176,7 +177,8 @@ int mysql_rm_table_part2_with_lock(THD *thd,
*/
int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
- bool drop_temporary, bool dont_log_query)
+ bool drop_temporary, bool drop_view,
+ bool dont_log_query)
{
TABLE_LIST *table;
char path[FN_REFLEN], *alias;
@@ -188,7 +190,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (lock_table_names(thd, tables))
DBUG_RETURN(1);
- for (table=tables ; table ; table=table->next)
+ for (table= tables; table; table= table->next_local)
{
char *db=table->db;
mysql_ha_close(thd, table, /*dont_send_ok*/ 1, /*dont_lock*/ 1);
@@ -216,7 +218,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
strxmov(path, mysql_data_home, "/", db, "/", alias, reg_ext, NullS);
(void) unpack_filename(path,path);
}
- if (drop_temporary || access(path,F_OK))
+ if (drop_temporary || access(path,F_OK) ||
+ (!drop_view && mysql_frm_type(path) != FRMTYPE_TABLE))
{
if (if_exists)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
@@ -267,17 +270,13 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (some_tables_deleted || tmp_table_deleted || !error)
{
query_cache_invalidate3(thd, tables, 0);
- if (!dont_log_query)
+ if (!dont_log_query && mysql_bin_log.is_open())
{
- mysql_update_log.write(thd, thd->query,thd->query_length);
- if (mysql_bin_log.is_open())
- {
- if (!error)
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- tmp_table_deleted && !some_tables_deleted);
- mysql_bin_log.write(&qinfo);
- }
+ if (!error)
+ thd->clear_error();
+ Query_log_event qinfo(thd, thd->query, thd->query_length,
+ tmp_table_deleted && !some_tables_deleted);
+ mysql_bin_log.write(&qinfo);
}
}
@@ -1276,18 +1275,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
}
thd->tmp_table_used= 1;
}
- if (!tmp_table)
+ if (!tmp_table && mysql_bin_log.is_open())
{
- // Must be written before unlock
- mysql_update_log.write(thd,thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- test(create_info->options &
- HA_LEX_CREATE_TMP_TABLE));
- mysql_bin_log.write(&qinfo);
- }
+ thd->clear_error();
+ Query_log_event qinfo(thd, thd->query, thd->query_length,
+ test(create_info->options &
+ HA_LEX_CREATE_TMP_TABLE));
+ mysql_bin_log.write(&qinfo);
}
error=0;
end:
@@ -1341,7 +1335,7 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end)
****************************************************************************/
TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
- const char *db, const char *name,
+ TABLE_LIST *create_table,
List<create_field> *extra_fields,
List<Key> *keys,
List<Item> *items,
@@ -1382,9 +1376,9 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
}
/* create and lock table */
/* QQ: This should be done atomic ! */
- /* We don't log the statement, it will be logged later */
- if (mysql_create_table(thd,db,name,create_info,*extra_fields,
- *keys,0,select_field_count))
+ if (mysql_create_table(thd, create_table->db, create_table->real_name,
+ create_info, *extra_fields, *keys, 0,
+ select_field_count))
DBUG_RETURN(0);
/*
If this is a HEAP table, the automatic DELETE FROM which is written to the
@@ -1393,18 +1387,20 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
don't want to delete from it) 2) it would be written before the CREATE
TABLE, which is a wrong order. So we keep binary logging disabled.
*/
- if (!(table=open_table(thd,db,name,name,(bool*) 0)))
+ if (!(table= open_table(thd, create_table, 0, (bool*) 0)))
{
- quick_rm_table(create_info->db_type,db,table_case_name(create_info,name));
+ quick_rm_table(create_info->db_type, create_table->db,
+ table_case_name(create_info, create_table->real_name));
DBUG_RETURN(0);
}
table->reginfo.lock_type=TL_WRITE;
- if (!((*lock)=mysql_lock_tables(thd,&table,1)))
+ if (!((*lock)= mysql_lock_tables(thd, &table,1)))
{
VOID(pthread_mutex_lock(&LOCK_open));
hash_delete(&open_cache,(byte*) table);
VOID(pthread_mutex_unlock(&LOCK_open));
- quick_rm_table(create_info->db_type,db,table_case_name(create_info, name));
+ quick_rm_table(create_info->db_type, create_table->db,
+ table_case_name(create_info, create_table->real_name));
DBUG_RETURN(0);
}
table->file->extra(HA_EXTRA_WRITE_CACHE);
@@ -1750,11 +1746,12 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
item->maybe_null = 1;
field_list.push_back(item = new Item_empty_string("Msg_text", 255));
item->maybe_null = 1;
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
mysql_ha_close(thd, tables, /*dont_send_ok*/ 1, /*dont_lock*/ 1);
- for (table = tables; table; table = table->next)
+ for (table= tables; table; table= table->next_local)
{
char table_name[NAME_LEN*2+2];
char* db = (table->db) ? table->db : thd->db;
@@ -1892,8 +1889,9 @@ send_result_message:
reopen the table and do ha_innobase::analyze() on it.
*/
close_thread_tables(thd);
- TABLE_LIST *save_next= table->next;
- table->next= 0;
+ TABLE_LIST *save_next_local= table->next_local,
+ *save_next_global= table->next_global;
+ table->next_local= table->next_global= 0;
result_code= mysql_recreate_table(thd, table, 0);
close_thread_tables(thd);
if (!result_code) // recreation went ok
@@ -1903,7 +1901,8 @@ send_result_message:
result_code= 0; // analyze went ok
}
result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
- table->next= save_next;
+ table->next_local= save_next_local;
+ table->next_global= save_next_global;
goto send_result_message;
}
@@ -2117,9 +2116,9 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table,
DBUG_RETURN(-1);
}
+ bzero((gptr)&src_tables_list, sizeof(src_tables_list));
src_tables_list.db= table_ident->db.str ? table_ident->db.str : thd->db;
src_tables_list.real_name= table_ident->table.str;
- src_tables_list.next= 0;
if (lock_and_wait_for_table_name(thd, &src_tables_list))
goto err;
@@ -2193,7 +2192,6 @@ int mysql_create_like_table(THD* thd, TABLE_LIST* table,
}
// Must be written before unlock
- mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -2304,7 +2302,6 @@ int mysql_discard_or_import_tablespace(THD *thd,
error=1;
if (error)
goto err;
- mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2689,7 +2686,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
if (!error)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -3026,7 +3022,13 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
DBUG_RETURN(error);
}
if (table->tmp_table)
- new_table=open_table(thd,new_db,tmp_name,tmp_name,0);
+ {
+ TABLE_LIST tbl;
+ bzero((void*) &tbl, sizeof(tbl));
+ tbl.db= new_db;
+ tbl.real_name= tbl.alias= tmp_name;
+ new_table= open_table(thd, &tbl, 0, 0);
+ }
else
{
char path[FN_REFLEN];
@@ -3087,7 +3089,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
my_free((gptr) new_table,MYF(0));
goto err;
}
- mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -3222,7 +3223,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
goto err;
}
thd->proc_info="end";
- mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -3363,7 +3363,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error= 1;
break;
}
@@ -3470,10 +3470,11 @@ int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
item->maybe_null= 1;
field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21));
item->maybe_null= 1;
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
- for (table= tables; table; table= table->next)
+ for (table= tables; table; table= table->next_local)
{
char table_name[NAME_LEN*2+2];
TABLE *t;
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index d992c93f8fc..5c467402497 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -181,9 +181,10 @@ TEST_join(JOIN *join)
" quick select checked for each record (keys: %s)\n",
tab->select->quick_keys.print(buf));
else if (tab->select->quick)
- fprintf(DBUG_FILE," quick select used on key %s, length: %d\n",
- form->key_info[tab->select->quick->index].name,
- tab->select->quick->max_used_key_length);
+ {
+ fprintf(DBUG_FILE, " quick select used:\n");
+ tab->select->quick->dbug_dump(18, false);
+ }
else
VOID(fputs(" select used\n",DBUG_FILE));
}
@@ -202,6 +203,104 @@ TEST_join(JOIN *join)
DBUG_VOID_RETURN;
}
+
+/*
+ Print the current state during query optimization.
+
+ SYNOPSIS
+ print_plan()
+ join pointer to the structure providing all context info for
+ the query
+ read_time the cost of the best partial plan
+ record_count estimate for the number of records returned by the best
+ partial plan
+ idx length of the partial QEP in 'join->positions';
+ also an index in the array 'join->best_ref';
+ info comment string to appear above the printout
+
+ DESCRIPTION
+ This function prints to the log file DBUG_FILE the members of 'join' that
+ are used during query optimization (join->positions, join->best_positions,
+ and join->best_ref) and few other related variables (read_time,
+ record_count).
+ Useful to trace query optimizer functions.
+
+ RETURN
+ None
+*/
+
+void
+print_plan(JOIN* join, double read_time, double record_count,
+ uint idx, const char *info)
+{
+ uint i;
+ POSITION pos;
+ JOIN_TAB *join_table;
+ JOIN_TAB **plan_nodes;
+ TABLE* table;
+
+ if (info == 0)
+ info= "";
+
+ DBUG_LOCK_FILE;
+ if (join->best_read == DBL_MAX)
+ {
+ fprintf(DBUG_FILE,"%s; idx:%u, best: DBL_MAX, current:%g\n",
+ info, idx, read_time);
+ }
+ else
+ {
+ fprintf(DBUG_FILE,"%s; idx: %u, best: %g, current: %g\n",
+ info, idx, join->best_read, read_time);
+ }
+
+ /* Print the tables in JOIN->positions */
+ fputs(" POSITIONS: ", DBUG_FILE);
+ for (i= 0; i < idx ; i++)
+ {
+ pos = join->positions[i];
+ table= pos.table->table;
+ if (table)
+ fputs(table->real_name, DBUG_FILE);
+ fputc(' ', DBUG_FILE);
+ }
+ fputc('\n', DBUG_FILE);
+
+ /*
+ Print the tables in JOIN->best_positions only if at least one complete plan
+ has been found. An indicator for this is the value of 'join->best_read'.
+ */
+ fputs("BEST_POSITIONS: ", DBUG_FILE);
+ if (join->best_read < DBL_MAX)
+ {
+ for (i= 0; i < idx ; i++)
+ {
+ pos= join->best_positions[i];
+ table= pos.table->table;
+ if (table)
+ fputs(table->real_name, DBUG_FILE);
+ fputc(' ', DBUG_FILE);
+ }
+ }
+ fputc('\n', DBUG_FILE);
+
+ /* Print the tables in JOIN->best_ref */
+ fputs(" BEST_REF: ", DBUG_FILE);
+ for (plan_nodes= join->best_ref ; *plan_nodes ; plan_nodes++)
+ {
+ join_table= (*plan_nodes);
+ fputs(join_table->table->real_name, DBUG_FILE);
+ fprintf(DBUG_FILE, "(%lu,%lu,%lu)",
+ (ulong) join_table->found_records,
+ (ulong) join_table->records,
+ (ulong) join_table->read_time);
+ fputc(' ', DBUG_FILE);
+ }
+ fputc('\n', DBUG_FILE);
+
+ DBUG_UNLOCK_FILE;
+}
+
#endif
typedef struct st_debug_lock
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 1e8c6576dec..827d75a9848 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -28,11 +28,17 @@ int mysql_union(THD *thd, LEX *lex, select_result *result,
SELECT_LEX_UNIT *unit)
{
DBUG_ENTER("mysql_union");
- int res= 0;
+ int res, res_cln;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK)))
res= unit->exec();
- res|= unit->cleanup();
- DBUG_RETURN(res);
+ if (res == 0 && thd->cursor && thd->cursor->is_open())
+ {
+ thd->cursor->set_unit(unit);
+ res_cln= 0;
+ }
+ else
+ res_cln= unit->cleanup();
+ DBUG_RETURN(res?res:res_cln);
}
@@ -136,7 +142,7 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd)
fake_select_lex->ftfunc_list= &fake_select_lex->ftfunc_list_alloc;
fake_select_lex->table_list.link_in_list((byte *)&result_table_list,
(byte **)
- &result_table_list.next);
+ &result_table_list.next_local);
return options_tmp;
}
@@ -211,11 +217,8 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
goto err;
thd_arg->lex->current_select= sl;
- offset_limit_cnt= sl->offset_limit;
- select_limit_cnt= sl->select_limit+sl->offset_limit;
- if (select_limit_cnt < sl->select_limit)
- select_limit_cnt= HA_POS_ERROR; // no limit
- if (select_limit_cnt == HA_POS_ERROR || sl->braces)
+ set_limit(sl, sl);
+ if (sl->braces)
sl->options&= ~OPTION_FOUND_ROWS;
res= join->prepare(&sl->ref_pointer_array,
@@ -287,8 +290,11 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
thd_arg->lex->current_select= lex_select_save;
if (!item_list.elements)
{
- Item_arena *arena= thd->current_arena, backup;
- if (arena->is_stmt_prepare())
+ Item_arena *arena= thd->current_arena;
+ Item_arena backup;
+ if (!arena->is_stmt_prepare())
+ arena= 0
+ else
thd->set_n_backup_item_arena(arena, &backup);
Field **field;
for (field= table->field; *field; field++)
@@ -296,17 +302,22 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
Item_field *item= new Item_field(*field);
if (!item || item_list.push_back(item))
{
- if (arena->is_stmt_prepare())
+ if (arena)
thd->restore_backup_item_arena(arena, &backup);
DBUG_RETURN(-1);
}
}
- if (arena->is_stmt_prepare())
+ if (arena)
{
thd->restore_backup_item_arena(arena, &backup);
/* prepare fake select to initialize it correctly */
ulong options_tmp= init_prepare_fake_select_lex(thd);
+ /*
+ it should be done only once (because item_list builds only onece
+ per statement)
+ */
+ DBUG_ASSERT(fake_select_lex->join == 0);
if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->options,
result)))
{
@@ -468,8 +479,8 @@ int st_select_lex_unit::exec()
allocate JOIN for fake select only once (prevent
mysql_select automatic allocation)
*/
- if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->options,
- result)))
+ if (!(fake_select_lex->join= new JOIN(thd, item_list,
+ fake_select_lex->options, result)))
{
fake_select_lex->table_list.empty();
DBUG_RETURN(-1);
@@ -484,12 +495,14 @@ int st_select_lex_unit::exec()
else
{
JOIN_TAB *tab,*end;
- for (tab=join->join_tab,end=tab+join->tables ; tab != end ; tab++)
+ for (tab=join->join_tab, end=tab+join->tables ;
+ tab && tab != end ;
+ tab++)
{
delete tab->select;
delete tab->quick;
}
- join->init(thd, item_list, thd->options, result);
+ join->init(thd, item_list, fake_select_lex->options, result);
}
res= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list,
@@ -497,7 +510,7 @@ int st_select_lex_unit::exec()
global_parameters->order_list.elements,
(ORDER*)global_parameters->order_list.first,
(ORDER*) NULL, NULL, (ORDER*) NULL,
- options_tmp | SELECT_NO_UNLOCK,
+ fake_select_lex->options | SELECT_NO_UNLOCK,
result, this, fake_select_lex);
fake_select_lex->table_list.empty();
@@ -517,6 +530,7 @@ int st_select_lex_unit::exec()
int st_select_lex_unit::cleanup()
{
int error= 0;
+ JOIN *join;
DBUG_ENTER("st_select_lex_unit::cleanup");
if (cleaned)
@@ -533,9 +547,8 @@ int st_select_lex_unit::cleanup()
free_tmp_table(thd, table);
table= 0; // Safety
}
- JOIN *join;
- SELECT_LEX *sl= first_select_in_union();
- for (; sl; sl= sl->next_select())
+
+ for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select())
{
if ((join= sl->join))
{
@@ -560,6 +573,7 @@ int st_select_lex_unit::cleanup()
error|= join->cleanup();
delete join;
}
+
DBUG_RETURN(error);
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index db4edff4fa1..acc93b10910 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -48,6 +48,43 @@ static bool compare_record(TABLE *table, ulong query_id)
}
+/*
+ check that all fields are real fields
+
+ SYNOPSIS
+ check_fields()
+ thd thread handler
+ items Items for check
+
+ RETURN
+ TRUE Items can't be used in UPDATE
+ FALSE Items are OK
+*/
+
+static bool check_fields(THD *thd, List<Item> &items)
+{
+ List_iterator<Item> it(items);
+ Item *item;
+ while ((item= it++))
+ {
+ if (item->type() != Item::FIELD_ITEM)
+ {
+ /* as far as item comes from VIEW select list it has name */
+ my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
+ return TRUE;
+ }
+ /*
+ we make temporary copy of Item_field, to avoid influence of changing
+ result_field on Item_ref which refer on this field
+ */
+ Item_field *field= new Item_field(thd, (Item_field *)item);
+ it.replace(field);
+ ((Item_field *)item)->register_item_tree_changing(it.ref());
+ }
+ return FALSE;
+}
+
+
int mysql_update(THD *thd,
TABLE_LIST *table_list,
List<Item> &fields,
@@ -71,8 +108,6 @@ int mysql_update(THD *thd,
TABLE *table;
SQL_SELECT *select;
READ_RECORD info;
- TABLE_LIST *update_table_list= ((TABLE_LIST*)
- thd->lex->select_lex.table_list.first);
DBUG_ENTER("mysql_update");
LINT_INIT(used_index);
@@ -89,10 +124,12 @@ int mysql_update(THD *thd,
table->quick_keys.clear_all();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- want_privilege= table->grant.want_privilege;
+ /* In case of view TABLE_LIST contain right privilages request */
+ want_privilege= (table_list->view ?
+ table_list->grant.want_privilege :
+ table->grant.want_privilege);
#endif
- if ((error= mysql_prepare_update(thd, table_list, update_table_list,
- &conds, order_num, order)))
+ if ((error= mysql_prepare_update(thd, table_list, &conds, order_num, order)))
DBUG_RETURN(error);
old_used_keys= table->used_keys; // Keys used in WHERE
@@ -108,10 +145,24 @@ int mysql_update(THD *thd,
/* Check the fields we are going to modify */
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- table->grant.want_privilege=want_privilege;
+ table_list->grant.want_privilege= table->grant.want_privilege= want_privilege;
#endif
- if (setup_fields(thd, 0, update_table_list, fields, 1, 0, 0))
- DBUG_RETURN(-1); /* purecov: inspected */
+ {
+ thd->lex->select_lex.no_wrap_view_item= 1;
+ int res= setup_fields(thd, 0, table_list, fields, 1, 0, 0);
+ thd->lex->select_lex.no_wrap_view_item= 0;
+ if (res)
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ if (table_list->view && check_fields(thd, fields))
+ {
+ DBUG_RETURN(-1);
+ }
+ if (!table_list->updatable || check_key_in_view(thd, table_list))
+ {
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
+ DBUG_RETURN(-1);
+ }
if (table->timestamp_field)
{
// Don't set timestamp column if this is modified
@@ -123,9 +174,10 @@ int mysql_update(THD *thd,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Check values */
- table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
+ table_list->grant.want_privilege= table->grant.want_privilege=
+ (SELECT_ACL & ~table->grant.privilege);
#endif
- if (setup_fields(thd, 0, update_table_list, values, 0, 0, 0))
+ if (setup_fields(thd, 0, table_list, values, 0, 0, 0))
{
free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(-1); /* purecov: inspected */
@@ -159,11 +211,13 @@ int mysql_update(THD *thd,
}
init_ftfuncs(thd, &thd->lex->select_lex, 1);
/* Check if we are modifying a key that we are used to search with */
+
if (select && select->quick)
+ {
+ used_index= select->quick->index;
used_key_is_modified= (!select->quick->unique_key_range() &&
- check_if_key_used(table,
- (used_index=select->quick->index),
- fields));
+ select->quick->check_if_keys_used(&fields));
+ }
else if ((used_index=table->file->key_used_on_scan) < MAX_KEY)
used_key_is_modified=check_if_key_used(table, used_index, fields);
else
@@ -175,7 +229,7 @@ int mysql_update(THD *thd,
matching rows before updating the table!
*/
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (old_used_keys.is_set(used_index))
+ if ( (used_index != MAX_KEY) && old_used_keys.is_set(used_index))
{
table->key_read=1;
table->file->extra(HA_EXTRA_KEYREAD);
@@ -221,8 +275,12 @@ int mysql_update(THD *thd,
if (open_cached_file(&tempfile, mysql_tmpdir,TEMP_PREFIX,
DISK_BUFFER_SIZE, MYF(MY_WME)))
goto err;
-
+
+ /* If quick select is used, initialize it before retrieving rows. */
+ if (select && select->quick && select->quick->reset())
+ goto err;
init_read_record(&info,thd,table,select,0,1);
+
thd->proc_info="Searching rows for update";
uint tmp_limit= limit;
@@ -277,6 +335,9 @@ int mysql_update(THD *thd,
if (handle_duplicates == DUP_IGNORE)
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+
+ if (select && select->quick && select->quick->reset())
+ goto err;
init_read_record(&info,thd,table,select,0,1);
updated= found= 0;
@@ -331,13 +392,14 @@ int mysql_update(THD *thd,
This must be before binlog writing and ha_autocommit_...
*/
if (updated)
+ {
query_cache_invalidate3(thd, table_list, 1);
+ }
transactional_table= table->file->has_transactions();
log_delayed= (transactional_table || table->tmp_table);
if ((updated || (error < 0)) && (error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
if (error <= 0)
@@ -364,14 +426,15 @@ int mysql_update(THD *thd,
free_underlaid_joins(thd, &thd->lex->select_lex);
if (error >= 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); /* purecov: inspected */
+ send_error(thd,thd->killed_errno()); /* purecov: inspected */
else
{
char buff[80];
sprintf(buff, ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated,
(ulong) thd->cuted_fields);
- send_ok(thd,
- (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
+ thd->row_count_func=
+ (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
+ send_ok(thd, (ulong) thd->row_count_func,
thd->insert_id_used ? thd->insert_id() : 0L,buff);
DBUG_PRINT("info",("%d records updated",updated));
}
@@ -396,8 +459,7 @@ err:
SYNOPSIS
mysql_prepare_update()
thd - thread handler
- table_list - global table list
- update_table_list - local table list of UPDATE SELECT_LEX
+ table_list - global/local table list
conds - conditions
order_num - number of ORDER BY list entries
order - ORDER BY clause list
@@ -408,38 +470,39 @@ err:
-1 - error (message is not sent to user)
*/
int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
- TABLE_LIST *update_table_list,
Item **conds, uint order_num, ORDER *order)
{
TABLE *table= table_list->table;
TABLE_LIST tables;
List<Item> all_fields;
+ SELECT_LEX *select_lex= &thd->lex->select_lex;
DBUG_ENTER("mysql_prepare_update");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
+ table_list->grant.want_privilege= table->grant.want_privilege=
+ (SELECT_ACL & ~table->grant.privilege);
#endif
bzero((char*) &tables,sizeof(tables)); // For ORDER BY
tables.table= table;
tables.alias= table_list->alias;
- if (setup_tables(update_table_list) ||
- setup_conds(thd, update_table_list, conds) ||
- thd->lex->select_lex.setup_ref_array(thd, order_num) ||
- setup_order(thd, thd->lex->select_lex.ref_pointer_array,
- update_table_list, all_fields, all_fields, order) ||
- setup_ftfuncs(&thd->lex->select_lex))
+ if (setup_tables(thd, table_list, conds) ||
+ setup_conds(thd, table_list, conds) ||
+ select_lex->setup_ref_array(thd, order_num) ||
+ setup_order(thd, select_lex->ref_pointer_array,
+ table_list, all_fields, all_fields, order) ||
+ setup_ftfuncs(select_lex))
DBUG_RETURN(-1);
/* Check that we are not using table that we are updating in a sub select */
- if (find_real_table_in_list(table_list->next,
+ if (find_table_in_global_list(table_list->next_global,
table_list->db, table_list->real_name))
{
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
DBUG_RETURN(-1);
}
-
+ select_lex->fix_prepare_information(thd, conds);
DBUG_RETURN(0);
}
@@ -452,32 +515,33 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
Setup multi-update handling and call SELECT to do the join
*/
-int mysql_multi_update(THD *thd,
- TABLE_LIST *table_list,
- List<Item> *fields,
- List<Item> *values,
- COND *conds,
- ulong options,
- enum enum_duplicates handle_duplicates,
- SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
-{
- int res;
- multi_update *result;
- TABLE_LIST *tl;
- TABLE_LIST *update_list= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
- table_map item_tables= 0, derived_tables= 0;
- DBUG_ENTER("mysql_multi_update");
+/*
+ make update specific preparation and checks after opening tables
- if ((res=open_and_lock_tables(thd,table_list)))
- DBUG_RETURN(res);
+ SYNOPSIS
+ mysql_multi_update_prepare()
+ thd thread handler
- select_lex->select_limit= HA_POS_ERROR;
+ RETURN
+ 0 OK
+ -1 Error
+*/
+int mysql_multi_update_prepare(THD *thd)
+{
+ LEX *lex= thd->lex;
+ TABLE_LIST *table_list= lex->query_tables;
+ List<Item> *fields= &lex->select_lex.item_list;
+ TABLE_LIST *tl;
+ table_map tables_for_update= 0, readonly_tables= 0;
+ int res;
+ bool update_view= 0;
+ DBUG_ENTER("mysql_multi_update_prepare");
/*
Ensure that we have update privilege for all tables and columns in the
SET part
*/
- for (tl= update_list; tl; tl= tl->next)
+ for (tl= table_list; tl; tl= tl->next_local)
{
TABLE *table= tl->table;
/*
@@ -487,71 +551,106 @@ int mysql_multi_update(THD *thd,
"Target table ... is not updatable"
*/
if (!tl->derived)
- table->grant.want_privilege= (UPDATE_ACL & ~table->grant.privilege);
+ tl->grant.want_privilege= table->grant.want_privilege=
+ (UPDATE_ACL & ~table->grant.privilege);
}
- if (thd->lex->derived_tables)
+ /*
+ setup_tables() need for VIEWs. JOIN::prepare() will not do it second
+ time.
+ */
+ if (setup_tables(thd, table_list, &lex->select_lex.where) ||
+ (thd->lex->select_lex.no_wrap_view_item= 1,
+ res= setup_fields(thd, 0, table_list, *fields, 1, 0, 0),
+ thd->lex->select_lex.no_wrap_view_item= 0,
+ res))
+ DBUG_RETURN(-1);
+
+ for (tl= table_list; tl ; tl= tl->next_local)
{
- // Assign table map values to check updatability of derived tables
- uint tablenr=0;
- for (TABLE_LIST *table_list= update_list;
- table_list;
- table_list= table_list->next, tablenr++)
+ if (tl->view)
{
- table_list->table->map= (table_map) 1 << tablenr;
+ update_view= 1;
+ break;
}
}
- if (setup_fields(thd, 0, update_list, *fields, 1, 0, 0))
+
+ if (update_view && check_fields(thd, *fields))
+ {
DBUG_RETURN(-1);
- if (thd->lex->derived_tables)
+ }
+
{
// Find tables used in items
List_iterator_fast<Item> it(*fields);
Item *item;
while ((item= it++))
{
- item_tables|= item->used_tables();
+ tables_for_update|= item->used_tables();
}
}
/*
Count tables and setup timestamp handling
*/
- for (tl= update_list; tl; tl= tl->next)
+ for (tl= table_list; tl ; tl= tl->next_local)
{
TABLE *table= tl->table;
/* We only need SELECT privilege for columns in the values list */
- table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
+ tl->grant.want_privilege= table->grant.want_privilege=
+ (SELECT_ACL & ~table->grant.privilege);
// Only set timestamp column if this is not modified
if (table->timestamp_field &&
table->timestamp_field->query_id == thd->query_id)
table->timestamp_on_update_now= 0;
-
- if (tl->derived)
- derived_tables|= table->map;
+
+ if (!tl->updatable || check_key_in_view(thd, tl))
+ readonly_tables|= table->map;
}
- if (thd->lex->derived_tables && (item_tables & derived_tables))
+ if (tables_for_update & readonly_tables)
{
- // find derived table which cause error
- for (tl= update_list; tl; tl= tl->next)
+ // find readonly table/view which cause error
+ for (tl= table_list; tl ; tl= tl->next_local)
{
- if (tl->derived && (item_tables & tl->table->map))
+ if ((readonly_tables & tl->table->map) &&
+ (tables_for_update & tl->table->map))
{
- my_printf_error(ER_NON_UPDATABLE_TABLE, ER(ER_NON_UPDATABLE_TABLE),
- MYF(0), tl->alias, "UPDATE");
+ my_error(ER_NON_UPDATABLE_TABLE, MYF(0), tl->alias, "UPDATE");
DBUG_RETURN(-1);
}
}
}
+ DBUG_RETURN (0);
+}
+
+
+int mysql_multi_update(THD *thd,
+ TABLE_LIST *table_list,
+ List<Item> *fields,
+ List<Item> *values,
+ COND *conds,
+ ulong options,
+ enum enum_duplicates handle_duplicates,
+ SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
+{
+ int res;
+ multi_update *result;
+ DBUG_ENTER("mysql_multi_update");
+
+ if ((res= open_and_lock_tables(thd, table_list)))
+ DBUG_RETURN(res);
+
+ if ((res= mysql_multi_update_prepare(thd)))
+ DBUG_RETURN(res);
- if (!(result=new multi_update(thd, update_list, fields, values,
- handle_duplicates)))
+ if (!(result= new multi_update(thd, table_list, fields, values,
+ handle_duplicates)))
DBUG_RETURN(-1);
List<Item> total_list;
res= mysql_select(thd, &select_lex->ref_pointer_array,
- select_lex->get_table_list(), select_lex->with_wild,
+ table_list, select_lex->with_wild,
total_list,
conds, 0, (ORDER *) NULL, (ORDER *)NULL, (Item *) NULL,
(ORDER *)NULL,
@@ -616,7 +715,7 @@ int multi_update::prepare(List<Item> &not_used_values,
*/
update.empty();
- for (table_ref= all_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
if (tables_to_update & table->map)
@@ -625,7 +724,7 @@ int multi_update::prepare(List<Item> &not_used_values,
sizeof(*tl));
if (!tl)
DBUG_RETURN(1);
- update.link_in_list((byte*) tl, (byte**) &tl->next);
+ update.link_in_list((byte*) tl, (byte**) &tl->next_local);
tl->shared= table_count++;
table->no_keyread=1;
table->used_keys.clear_all();
@@ -685,11 +784,11 @@ int multi_update::prepare(List<Item> &not_used_values,
which will cause an error when reading a row.
(This issue is mostly relevent for MyISAM tables)
*/
- for (table_ref= all_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= all_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
if (!(tables_to_update & table->map) &&
- find_real_table_in_list(update_tables, table_ref->db,
+ find_table_in_global_list(update_tables, table_ref->db,
table_ref->real_name))
table->no_cache= 1; // Disable row cache
}
@@ -720,7 +819,7 @@ multi_update::initialize_tables(JOIN *join)
table_to_update= 0;
/* Create a temporary table for keys to all tables, except main table */
- for (table_ref= update_tables; table_ref; table_ref=table_ref->next)
+ for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local)
{
TABLE *table=table_ref->table;
uint cnt= table_ref->shared;
@@ -816,8 +915,7 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
case JT_ALL:
/* If range search on index */
if (join_tab->quick)
- return !check_if_key_used(table, join_tab->quick->index,
- *fields);
+ return !join_tab->quick->check_if_keys_used(fields);
/* If scanning in clustered key */
if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
table->primary_key < MAX_KEY)
@@ -833,7 +931,7 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
multi_update::~multi_update()
{
TABLE_LIST *table;
- for (table= update_tables ; table; table= table->next)
+ for (table= update_tables ; table; table= table->next_local)
table->table->no_keyread= table->table->no_cache= 0;
if (tmp_tables)
@@ -860,7 +958,7 @@ bool multi_update::send_data(List<Item> &not_used_values)
TABLE_LIST *cur_table;
DBUG_ENTER("multi_update::send_data");
- for (cur_table= update_tables; cur_table ; cur_table= cur_table->next)
+ for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
TABLE *table= cur_table->table;
/*
@@ -974,7 +1072,7 @@ int multi_update::do_updates(bool from_send_error)
do_update= 0; // Don't retry this function
if (!found)
DBUG_RETURN(0);
- for (cur_table= update_tables; cur_table ; cur_table= cur_table->next)
+ for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local)
{
byte *ref_pos;
@@ -1105,7 +1203,6 @@ bool multi_update::send_eof()
if (updated && (local_error <= 0 || !trans_safe))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
if (local_error <= 0)
@@ -1137,8 +1234,9 @@ bool multi_update::send_eof()
sprintf(buff, ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated,
(ulong) thd->cuted_fields);
- ::send_ok(thd,
- (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
+ thd->row_count_func=
+ (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated;
+ ::send_ok(thd, (ulong) thd->row_count_func,
thd->insert_id_used ? thd->insert_id() : 0L,buff);
return 0;
}
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
new file mode 100644
index 00000000000..183b55ca12f
--- /dev/null
+++ b/sql/sql_view.cc
@@ -0,0 +1,946 @@
+/* Copyright (C) 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include "sql_select.h"
+#include "parse_file.h"
+#include "sp.h"
+
+static int mysql_register_view(THD *thd, TABLE_LIST *view,
+ enum_view_create_mode mode);
+
+const char *sql_updatable_view_key_names[]= { "NO", "YES", "LIMIT1", NullS };
+TYPELIB sql_updatable_view_key_typelib=
+{
+ array_elements(sql_updatable_view_key_names)-1, "",
+ sql_updatable_view_key_names
+};
+
+
+/*
+ Creating/altering VIEW procedure
+
+ SYNOPSIS
+ mysql_create_view()
+ thd - thread handler
+ mode - VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE
+
+ RETURN VALUE
+ 0 OK
+ -1 Error
+ 1 Error and error message given
+*/
+int mysql_create_view(THD *thd,
+ enum_view_create_mode mode)
+{
+ LEX *lex= thd->lex;
+ bool link_to_local;
+ /* first table in list is target VIEW name => cut off it */
+ TABLE_LIST *view= lex->unlink_first_table(&link_to_local);
+ TABLE_LIST *tables= lex->query_tables;
+ TABLE_LIST *tbl;
+ SELECT_LEX *select_lex= &lex->select_lex, *sl;
+ SELECT_LEX_UNIT *unit= &lex->unit;
+ int res= 0;
+ DBUG_ENTER("mysql_create_view");
+
+ if (lex->derived_tables || lex->proc_list.first ||
+ lex->variables_used || lex->param_list.elements)
+ {
+ my_error((lex->derived_tables ? ER_VIEW_SELECT_DERIVED :
+ (lex->proc_list.first ? ER_VIEW_SELECT_PROCEDURE :
+ ER_VIEW_SELECT_VARIABLE)), MYF(0));
+ res= -1;
+ goto err;
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (check_access(thd, CREATE_VIEW_ACL, view->db, &view->grant.privilege,
+ 0, 0) ||
+ grant_option && check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0))
+ DBUG_RETURN(1);
+ for (sl= select_lex; sl; sl= sl->next_select())
+ {
+ for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local)
+ {
+ /*
+ Ensure that we have some privilage on this table, more strict check
+ will be done on column level after preparation,
+ */
+ if (check_some_access(thd, VIEW_ANY_ACL, tbl))
+ {
+ my_printf_error(ER_TABLEACCESS_DENIED_ERROR,
+ ER(ER_TABLEACCESS_DENIED_ERROR),
+ MYF(0),
+ "ANY",
+ thd->priv_user,
+ thd->host_or_ip,
+ tbl->real_name);
+ DBUG_RETURN(-1);
+ }
+ /* mark this table as table which will be checked after preparation */
+ tbl->table_in_first_from_clause= 1;
+
+ /*
+ We need to check only SELECT_ACL for all normal fields, fields
+ where we need any privilege will be marked later
+ */
+ tbl->grant.want_privilege= SELECT_ACL;
+ /*
+ Make sure that all rights are loaded to table 'grant' field.
+
+ tbl->real_name will be correct name of table because VIEWs are
+ not opened yet.
+ */
+ fill_effective_table_privileges(thd, &tbl->grant, tbl->db,
+ tbl->real_name);
+ }
+ }
+
+ if (&lex->select_lex != lex->all_selects_list)
+ {
+ /* check tables of subqueries */
+ for (tbl= tables; tbl; tbl= tbl->next_global)
+ {
+ if (!tbl->table_in_first_from_clause)
+ {
+ if (check_access(thd, SELECT_ACL, tbl->db,
+ &tbl->grant.privilege, 0, 0) ||
+ grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 0))
+ {
+ res= 1;
+ goto err;
+ }
+ }
+ }
+ }
+ /*
+ Mark fields for special privilege check (any privilege)
+ */
+ for (sl= select_lex; sl; sl= sl->next_select())
+ {
+ List_iterator_fast<Item> it(sl->item_list);
+ Item *item;
+ while ((item= it++))
+ {
+ if (item->type() == Item::FIELD_ITEM)
+ ((Item_field *)item)->any_privileges= 1;
+ }
+ }
+#endif
+
+ if ((res= open_and_lock_tables(thd, tables)))
+ DBUG_RETURN(res);
+
+ /* check that tables are not temporary */
+ for (tbl= tables; tbl; tbl= tbl->next_global)
+ {
+ if (tbl->table->tmp_table != NO_TMP_TABLE && !tbl->view)
+ {
+ my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias);
+ res= -1;
+ goto err;
+ }
+
+ /*
+ Copy privileges of underlaying VIEWs which was filled by
+ fill_effective_table_privileges
+ (they was not copied in derived tables processing)
+ */
+ tbl->table->grant.privilege= tbl->grant.privilege;
+ }
+
+ // prepare select to resolve all fields
+ lex->view_prepare_mode= 1;
+ if (unit->prepare(thd, 0, 0))
+ {
+ /*
+ some errors from prepare are reported to user, if is not then
+ it will be checked after err: label
+ */
+ res= 1;
+ goto err;
+ }
+
+ /* view list (list of view fields names) */
+ if (lex->view_list.elements)
+ {
+ List_iterator_fast<Item> it(select_lex->item_list);
+ List_iterator_fast<LEX_STRING> nm(lex->view_list);
+ Item *item;
+ LEX_STRING *name;
+
+ if (lex->view_list.elements != select_lex->item_list.elements)
+ {
+ my_message(ER_VIEW_WRONG_LIST, ER(ER_VIEW_WRONG_LIST), MYF(0));
+ goto err;
+ }
+ while ((item= it++, name= nm++))
+ item->set_name(name->str, name->length, system_charset_info);
+ }
+
+ /* Test absence of duplicates names */
+ {
+ Item *item;
+ List_iterator_fast<Item> it(select_lex->item_list);
+ it++;
+ while ((item= it++))
+ {
+ Item *check;
+ List_iterator_fast<Item> itc(select_lex->item_list);
+ while ((check= itc++) && check != item)
+ {
+ if (strcmp(item->name, check->name) == 0)
+ {
+ my_error(ER_DUP_FIELDNAME, MYF(0), item->name);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /*
+ Compare/check grants on view with grants of underlaying tables
+ */
+ for (sl= select_lex; sl; sl= sl->next_select())
+ {
+ char *db= view->db ? view->db : thd->db;
+ List_iterator_fast<Item> it(sl->item_list);
+ Item *item;
+ fill_effective_table_privileges(thd, &view->grant, db,
+ view->real_name);
+ while ((item= it++))
+ {
+ uint priv= (get_column_grant(thd, &view->grant, db,
+ view->real_name, item->name) &
+ VIEW_ANY_ACL);
+ if (item->type() == Item::FIELD_ITEM)
+ {
+ Item_field *fld= (Item_field *)item;
+ /*
+ There are no any privileges on VIEW column or there are
+ some other privileges then we have for underlaying table
+ */
+ if (priv == 0 || (~fld->have_privileges & priv))
+ {
+ /* VIEW column has more privileges */
+ my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
+ ER(ER_COLUMNACCESS_DENIED_ERROR),
+ MYF(0),
+ "create view",
+ thd->priv_user,
+ thd->host_or_ip,
+ item->name,
+ view->real_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ else
+ {
+ if (!(priv & SELECT_ACL))
+ {
+ /* user have not privilege to SELECT expression */
+ my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
+ ER(ER_COLUMNACCESS_DENIED_ERROR),
+ MYF(0),
+ "select",
+ thd->priv_user,
+ thd->host_or_ip,
+ item->name,
+ view->real_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ }
+#endif
+
+ if (wait_if_global_read_lock(thd, 0))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+ VOID(pthread_mutex_lock(&LOCK_open));
+ res= mysql_register_view(thd, view, mode);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ start_waiting_global_read_lock(thd);
+ if (res)
+ goto err;
+
+ send_ok(thd);
+ lex->link_first_table_back(view, link_to_local);
+ return 0;
+
+err:
+ thd->proc_info= "end";
+ lex->link_first_table_back(view, link_to_local);
+ unit->cleanup();
+ if (thd->net.report_error)
+ res= -1;
+ DBUG_RETURN(res);
+}
+
+
+// index of revision number in following table
+static const int revision_number_position= 4;
+
+/*
+ table of VIEW .frm field descriptors
+
+ Note that one should NOT change the order for this, as it's used by
+ parse()
+*/
+
+static File_option view_parameters[]=
+{{{(char*) "query", 5}, offsetof(TABLE_LIST, query),
+ FILE_OPTIONS_STRING},
+ {{(char*) "md5", 3}, offsetof(TABLE_LIST, md5),
+ FILE_OPTIONS_STRING},
+ {{(char*) "updatable", 9}, offsetof(TABLE_LIST, updatable_view),
+ FILE_OPTIONS_ULONGLONG},
+ {{(char*) "algorithm", 9}, offsetof(TABLE_LIST, algorithm),
+ FILE_OPTIONS_ULONGLONG},
+ {{(char*) "revision", 8}, offsetof(TABLE_LIST, revision),
+ FILE_OPTIONS_REV},
+ {{(char*) "timestamp", 9}, offsetof(TABLE_LIST, timestamp),
+ FILE_OPTIONS_TIMESTAMP},
+ {{(char*)"create-version", 14},offsetof(TABLE_LIST, file_version),
+ FILE_OPTIONS_ULONGLONG},
+ {{(char*) "source", 6}, offsetof(TABLE_LIST, source),
+ FILE_OPTIONS_ESTRING},
+ {{NULL, 0}, 0,
+ FILE_OPTIONS_STRING}
+};
+
+static const uint required_view_parameters= 6;
+
+static LEX_STRING view_file_type[]= {{(char*)"VIEW", 4}};
+
+
+/*
+ Register VIEW (write .frm & process .frm's history backups)
+
+ SYNOPSIS
+ mysql_register_view()
+ thd - thread handler
+ view - view description
+ mode - VIEW_CREATE_NEW, VIEW_ALTER, VIEW_CREATE_OR_REPLACE
+
+ RETURN
+ 0 OK
+ -1 Error
+ 1 Error and error message given
+*/
+static int mysql_register_view(THD *thd, TABLE_LIST *view,
+ enum_view_create_mode mode)
+{
+ char buff[4096];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ char md5[33];
+ bool can_be_merged;
+ char dir_buff[FN_REFLEN], file_buff[FN_REFLEN];
+ LEX_STRING dir, file;
+ DBUG_ENTER("mysql_register_view");
+
+ // print query
+ str.length(0);
+ {
+ ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES;
+ thd->variables.sql_mode&= ~MODE_ANSI_QUOTES;
+ thd->lex->unit.print(&str);
+ thd->variables.sql_mode|= sql_mode;
+ }
+ str.append('\0');
+ DBUG_PRINT("VIEW", ("View: %s", str.ptr()));
+
+ // print file name
+ (void) my_snprintf(dir_buff, FN_REFLEN, "%s/%s/",
+ mysql_data_home, view->db);
+ unpack_filename(dir_buff, dir_buff);
+ dir.str= dir_buff;
+ dir.length= strlen(dir_buff);
+
+ file.str= file_buff;
+ file.length= (strxnmov(file_buff, FN_REFLEN, view->real_name, reg_ext,
+ NullS) - file_buff);
+ /* init timestamp */
+ if (!view->timestamp.str)
+ view->timestamp.str= view->timestamp_buffer;
+
+ // check old .frm
+ {
+ char path_buff[FN_REFLEN];
+ LEX_STRING path;
+ File_parser *parser;
+
+ path.str= path_buff;
+ fn_format(path_buff, file.str, dir.str, 0, MY_UNPACK_FILENAME);
+ path.length= strlen(path_buff);
+
+ if (!access(path.str, F_OK))
+ {
+ if (mode == VIEW_CREATE_NEW)
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), view->alias);
+ DBUG_RETURN(1);
+ }
+
+ if (!(parser= sql_parse_prepare(&path, &thd->mem_root, 0)))
+ DBUG_RETURN(1);
+
+ if (!parser->ok() ||
+ strncmp("VIEW", parser->type()->str, parser->type()->length))
+ {
+ my_error(ER_WRONG_OBJECT, MYF(0), (view->db ? view->db : thd->db),
+ view->real_name, "VIEW");
+ DBUG_RETURN(1);
+ }
+
+ /*
+ read revision number
+
+ TODO: read dependense list, too, to process cascade/restrict
+ TODO: special cascade/restrict procedure for alter?
+ */
+ if (parser->parse((gptr)view, &thd->mem_root,
+ view_parameters + revision_number_position, 1))
+ {
+ DBUG_RETURN(1);
+ }
+ }
+ else
+ {
+ if (mode == VIEW_ALTER)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), view->db, view->alias);
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ // fill structure
+ view->query.str= (char*)str.ptr();
+ view->query.length= str.length()-1; // we do not need last \0
+ view->source.str= thd->query;
+ view->source.length= thd->query_length;
+ view->file_version= 1;
+ view->calc_md5(md5);
+ view->md5.str= md5;
+ view->md5.length= 32;
+ can_be_merged= thd->lex->can_be_merged();
+ if (thd->lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
+ !thd->lex->can_be_merged())
+ {
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
+ ER(ER_WARN_VIEW_MERGE));
+ thd->lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED;
+ }
+ view->algorithm= thd->lex->create_view_algorithm;
+ if ((view->updatable_view= (can_be_merged &&
+ view->algorithm != VIEW_ALGORITHM_TMPTABLE)))
+ {
+ // TODO: change here when we will support UNIONs
+ for (TABLE_LIST *tbl= (TABLE_LIST *)thd->lex->select_lex.table_list.first;
+ tbl;
+ tbl= tbl->next_local)
+ {
+ if (tbl->view && !tbl->updatable_view)
+ {
+ view->updatable_view= 0;
+ break;
+ }
+ }
+ }
+ if (sql_create_definition_file(&dir, &file, view_file_type,
+ (gptr)view, view_parameters, 3))
+ {
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ read VIEW .frm and create structures
+
+ SYNOPSIS
+ mysql_make_view()
+ parser - parser object;
+ table - TABLE_LIST structure for filling
+
+ RETURN
+ 0 ok
+ 1 error
+
+*/
+
+my_bool
+mysql_make_view(File_parser *parser, TABLE_LIST *table)
+{
+ DBUG_ENTER("mysql_make_view");
+
+ if (table->view)
+ {
+ DBUG_PRINT("info",
+ ("VIEW %s.%s is already processed on previous PS/SP execution",
+ table->view_db.str, table->view_name.str));
+ DBUG_RETURN(0);
+ }
+
+ TABLE_LIST *old_next, *tbl_end, *tbl_next;
+ SELECT_LEX *end;
+ THD *thd= current_thd;
+ LEX *old_lex= thd->lex, *lex;
+ int res= 0;
+
+ /*
+ For now we assume that tables will not be changed during PS life (it
+ will be TRUE as far as we make new table cache).
+ */
+ Item_arena *arena= thd->current_arena, backup;
+ if (arena)
+ thd->set_n_backup_item_arena(arena, &backup);
+
+ /* init timestamp */
+ if (!table->timestamp.str)
+ table->timestamp.str= table->timestamp_buffer;
+ /*
+ TODO: when VIEWs will be stored in cache, table mem_root should
+ be used here
+ */
+ if (parser->parse((gptr)table, &thd->mem_root, view_parameters,
+ required_view_parameters))
+ goto err;
+
+ /*
+ Save VIEW parameters, which will be wiped out by derived table
+ processing
+ */
+ table->view_db.str= table->db;
+ table->view_db.length= table->db_length;
+ table->view_name.str= table->real_name;
+ table->view_name.length= table->real_name_length;
+
+ /*TODO: md5 test here and warning if it is differ */
+
+ /*
+ TODO: TABLE mem root should be used here when VIEW will be stored in
+ TABLE cache
+
+ now Lex placed in statement memory
+ */
+ table->view= lex= thd->lex= (LEX*) new(&thd->mem_root) st_lex_local;
+ mysql_init_query(thd, (uchar*)table->query.str, table->query.length, TRUE);
+ lex->select_lex.select_number= ++thd->select_number;
+ old_lex->derived_tables|= DERIVED_VIEW;
+ {
+ ulong options= thd->options;
+ /* switch off modes which can prevent normal parsing of VIEW
+ - MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing
+ + MODE_PIPES_AS_CONCAT affect expression parsing
+ + MODE_ANSI_QUOTES affect expression parsing
+ + MODE_IGNORE_SPACE affect expression parsing
+ - MODE_NOT_USED not used :)
+ * MODE_ONLY_FULL_GROUP_BY affect execution
+ * MODE_NO_UNSIGNED_SUBTRACTION affect execution
+ - MODE_NO_DIR_IN_CREATE affect table creation only
+ - MODE_POSTGRESQL compounded from other modes
+ - MODE_ORACLE compounded from other modes
+ - MODE_MSSQL compounded from other modes
+ - MODE_DB2 compounded from other modes
+ - MODE_MAXDB affect only CREATE TABLE parsing
+ - MODE_NO_KEY_OPTIONS affect only SHOW
+ - MODE_NO_TABLE_OPTIONS affect only SHOW
+ - MODE_NO_FIELD_OPTIONS affect only SHOW
+ - MODE_MYSQL323 affect only SHOW
+ - MODE_MYSQL40 affect only SHOW
+ - MODE_ANSI compounded from other modes
+ (+ transaction mode)
+ ? MODE_NO_AUTO_VALUE_ON_ZERO affect UPDATEs
+ + MODE_NO_BACKSLASH_ESCAPES affect expression parsing
+ */
+ thd->options&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES |
+ MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES);
+ res= yyparse((void *)thd);
+ thd->options= options;
+ }
+ if (!res && !thd->is_fatal_error)
+ {
+ TABLE_LIST *top_view= (table->belong_to_view ?
+ table->belong_to_view :
+ table);
+
+ /* move SP to main LEX */
+ sp_merge_funs(old_lex, lex);
+ if (lex->spfuns.array.buffer)
+ hash_free(&lex->spfuns);
+
+ old_next= table->next_global;
+ if ((table->next_global= lex->query_tables))
+ table->next_global->prev_global= &table->next_global;
+
+ /* mark to avoid temporary table using and put view reference*/
+ for (TABLE_LIST *tbl= table->next_global; tbl; tbl= tbl->next_global)
+ {
+ tbl->skip_temporary= 1;
+ tbl->belong_to_view= top_view;
+ }
+
+ /*
+ check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show
+ underlaying tables
+ */
+ if ((old_lex->sql_command == SQLCOM_SELECT && old_lex->describe) ||
+ old_lex->sql_command == SQLCOM_SHOW_CREATE)
+ {
+ if (check_table_access(thd, SELECT_ACL, table->next_global, 1) &&
+ check_table_access(thd, SHOW_VIEW_ACL, table->next_global, 1))
+ {
+ my_error(ER_VIEW_NO_EXPLAIN, MYF(0));
+ goto err;
+ }
+ }
+
+ /* move SQL_NO_CACHE & Co to whole query */
+ old_lex->safe_to_cache_query= (old_lex->safe_to_cache_query &&
+ lex->safe_to_cache_query);
+ /* move SQL_CACHE to whole query */
+ if (lex->select_lex.options & OPTION_TO_QUERY_CACHE)
+ old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
+
+ /*
+ check MERGE algorithm ability
+ - algorithm is not explicit TEMPORARY TABLE
+ - VIEW SELECT allow marging
+ - VIEW used in subquery or command support MERGE algorithm
+ */
+ if (table->algorithm != VIEW_ALGORITHM_TMPTABLE &&
+ lex->can_be_merged() &&
+ (table->select_lex->master_unit() != &old_lex->unit ||
+ old_lex->can_use_merged()) &&
+ !old_lex->can_not_use_merged())
+ {
+ /*
+ TODO: support multi tables substitutions
+
+ table->next_global should be the same as
+ (TABLE_LIST *)lex->select_lex.table_list.first;
+ */
+ TABLE_LIST *view_table= table->next_global;
+ /* lex should contain at least one table */
+ DBUG_ASSERT(view_table != 0);
+
+ table->effective_algorithm= VIEW_ALGORITHM_MERGE;
+ DBUG_PRINT("info", ("algorithm: MERGE"));
+ table->updatable= (table->updatable_view != 0);
+
+ if (old_next)
+ {
+ if ((view_table->next_global= old_next))
+ old_next->prev_global= &view_table->next_global;
+ }
+ table->ancestor= view_table;
+ // next table should include SELECT_LEX under this table SELECT_LEX
+ table->ancestor->select_lex= table->select_lex;
+ /*
+ move lock type (TODO: should we issue error in case of TMPTABLE
+ algorithm and non-read locking)?
+ */
+ view_table->lock_type= table->lock_type;
+
+ /* Store WHERE clause for postprocessing in setup_ancestor */
+ table->where= lex->select_lex.where;
+
+ /*
+ This SELECT_LEX will be linked in global SELECT_LEX list
+ to make it processed by mysql_handle_derived(),
+ but it will not be included to SELECT_LEX tree, because it
+ will not be executed
+ */
+ goto ok;
+ }
+
+ table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE;
+ DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE"));
+ lex->select_lex.linkage= DERIVED_TABLE_TYPE;
+ table->updatable= 0;
+
+ /* SELECT tree link */
+ lex->unit.include_down(table->select_lex);
+ lex->unit.slave= &lex->select_lex; // fix include_down initialisation
+
+ if (old_next)
+ {
+ if ((tbl_end= table->next_global))
+ {
+ for (; (tbl_next= tbl_end->next_global); tbl_end= tbl_next)
+ ;
+ if ((tbl_end->next_global= old_next))
+ tbl_end->next_global->prev_global= &tbl_end->next_global;
+ }
+ else
+ {
+ /* VIEW do not contain tables */
+ table->next_global= old_next;
+ }
+ }
+
+ table->derived= &lex->unit;
+ }
+ else
+ goto err;
+
+ok:
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ /* global SELECT list linking */
+ end= &lex->select_lex; // primary SELECT_LEX is always last
+ end->link_next= old_lex->all_selects_list;
+ old_lex->all_selects_list->link_prev= &end->link_next;
+ old_lex->all_selects_list= lex->all_selects_list;
+ lex->all_selects_list->link_prev=
+ (st_select_lex_node**)&old_lex->all_selects_list;
+
+ thd->lex= old_lex;
+ DBUG_RETURN(0);
+
+err:
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ table->view= 0; // now it is not VIEW placeholder
+ thd->lex= old_lex;
+ DBUG_RETURN(1);
+}
+
+
+/*
+ drop view
+
+ SYNOPSIS
+ mysql_drop_view()
+ thd - thread handler
+ views - views to delete
+ drop_mode - cascade/check
+
+ RETURN VALUE
+ 0 OK
+ -1 Error
+ 1 Error and error message given
+*/
+
+int mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
+{
+ DBUG_ENTER("mysql_drop_view");
+ char path[FN_REFLEN];
+ TABLE_LIST *view;
+ bool type= 0;
+
+ for (view= views; view; view= view->next_local)
+ {
+ strxnmov(path, FN_REFLEN, mysql_data_home, "/", view->db, "/",
+ view->real_name, reg_ext, NullS);
+ (void) unpack_filename(path, path);
+ VOID(pthread_mutex_lock(&LOCK_open));
+ if (access(path, F_OK) || (type= (mysql_frm_type(path) != FRMTYPE_VIEW)))
+ {
+ char name[FN_REFLEN];
+ my_snprintf(name, sizeof(name), "%s.%s", view->db, view->real_name);
+ if (thd->lex->drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
+ name);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ continue;
+ }
+ if (type)
+ my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->real_name, "VIEW");
+ else
+ my_error(ER_BAD_TABLE_ERROR, MYF(0), name);
+ goto err;
+ }
+ if (my_delete(path, MYF(MY_WME)))
+ goto err;
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ }
+ send_ok(thd);
+ DBUG_RETURN(0);
+
+err:
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(-1);
+
+}
+
+
+/*
+ Check type of .frm if we are not going to parse it
+
+ SYNOPSIS
+ mysql_frm_type()
+ path path to file
+
+ RETURN
+ FRMTYPE_ERROR error
+ FRMTYPE_TABLE table
+ FRMTYPE_VIEW view
+*/
+
+frm_type_enum mysql_frm_type(char *path)
+{
+ File file;
+ char header[10]; //"TYPE=VIEW\n" it is 10 characters
+ int length;
+ DBUG_ENTER("mysql_frm_type");
+
+ if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
+ {
+ DBUG_RETURN(FRMTYPE_ERROR);
+ }
+ length= my_read(file, (byte*) header, 10, MYF(MY_WME));
+ my_close(file, MYF(MY_WME));
+ if (length == (int) MY_FILE_ERROR)
+ DBUG_RETURN(FRMTYPE_ERROR);
+ if (!strncmp(header, "TYPE=VIEW\n", 10))
+ DBUG_RETURN(FRMTYPE_VIEW);
+ DBUG_RETURN(FRMTYPE_TABLE); // Is probably a .frm table
+}
+
+
+/*
+ check of key (primary or unique) presence in updatable view
+
+ SYNOPSIS
+ check_key_in_view()
+ thd thread handler
+ view view for check with opened table
+
+ RETURN
+ FALSE OK
+ TRUE view do not contain key or all fields
+*/
+
+bool check_key_in_view(THD *thd, TABLE_LIST *view)
+{
+ TABLE *table;
+ Item **trans;
+ KEY *key_info, *key_info_end;
+ uint i, elements_in_view;
+ DBUG_ENTER("check_key_in_view");
+
+ if (!view->view)
+ DBUG_RETURN(FALSE); /* it is normal table */
+ table= view->table;
+ trans= view->field_translation;
+ key_info_end= (key_info= table->key_info)+ table->keys;
+
+ elements_in_view= view->view->select_lex.item_list.elements;
+ DBUG_ASSERT(view->table != 0 && view->field_translation != 0);
+
+ /* Loop over all keys to see if a unique-not-null key is used */
+ for (;key_info != key_info_end ; key_info++)
+ {
+ if ((key_info->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
+ {
+ KEY_PART_INFO *key_part= key_info->key_part;
+ KEY_PART_INFO *key_part_end= key_part + key_info->key_parts;
+
+ /* check that all key parts are used */
+ for (;;)
+ {
+ uint k;
+ for (k= 0; k < elements_in_view; k++)
+ {
+ if (trans[k]->type() == Item::FIELD_ITEM &&
+ ((Item_field *)trans[k])->field == key_part->field)
+ break;
+ }
+ if (k == elements_in_view)
+ break; // Key is not possible
+ if (++key_part == key_part_end)
+ DBUG_RETURN(FALSE); // Found usable key
+ }
+ }
+ }
+
+ DBUG_PRINT("info", ("checking if all fields of table are used"));
+ /* check all fields presence */
+ {
+ Field **field_ptr;
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ for (i= 0; i < elements_in_view; i++)
+ {
+ if (trans[i]->type() == Item::FIELD_ITEM &&
+ ((Item_field *)trans[i])->field == *field_ptr)
+ break;
+ }
+ if (i == elements_in_view) // If field didn't exists
+ {
+ ulong mode= thd->variables.sql_updatable_view_key;
+ /*
+ 0 == NO ; Don't give any errors
+ 1 == YES ; Give always an error
+ 2 == LIMIT1 ; Give an error if this is used with LIMIT 1
+ This is used to protect against gui programs that
+ uses LIMIT 1 to update just the current row. This
+ doesn't work reliable if the view doesn't have a
+ unique key or if the view doesn't use all fields in
+ table.
+ */
+ if (mode == 1 ||
+ (mode == 2 &&
+ thd->lex->unit.global_parameters->select_limit == 1))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY));
+ DBUG_RETURN(FALSE);
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ insert fields from VIEW (MERGE algorithm) into given list
+
+ SYNOPSIS
+ insert_view_fields()
+ list list for insertion
+ view view for processing
+*/
+
+void insert_view_fields(List<Item> *list, TABLE_LIST *view)
+{
+ uint elements_in_view= view->view->select_lex.item_list.elements;
+ Item **trans;
+ DBUG_ENTER("insert_view_fields");
+
+ if (!(trans= view->field_translation))
+ DBUG_VOID_RETURN;
+
+ for (uint i= 0; i < elements_in_view; i++)
+ {
+ if (trans[i]->type() == Item::FIELD_ITEM)
+ list->push_back(trans[i]);
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/sql_view.h b/sql/sql_view.h
new file mode 100644
index 00000000000..431f82a5bb8
--- /dev/null
+++ b/sql/sql_view.h
@@ -0,0 +1,35 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+int mysql_create_view(THD *thd,
+ enum_view_create_mode mode);
+
+my_bool mysql_make_view(File_parser *parser, TABLE_LIST *table);
+
+int mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode);
+
+bool check_key_in_view(THD *thd, TABLE_LIST * view);
+
+void insert_view_fields(List<Item> *list, TABLE_LIST *view);
+
+frm_type_enum mysql_frm_type(char *path);
+
+extern TYPELIB sql_updatable_view_key_typelib;
+
+#define VIEW_ANY_ACL (SELECT_ACL | UPDATE_ACL | INSERT_ACL | DELETE_ACL)
+
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 2af122f27f7..19b6aa4b385 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -35,6 +35,10 @@
#include "sql_acl.h"
#include "lex_symbol.h"
#include "item_create.h"
+#include "sp_head.h"
+#include "sp_pcontext.h"
+#include "sp_rcontext.h"
+#include "sp.h"
#include <myisam.h>
#include <myisammrg.h>
@@ -83,10 +87,14 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B)
enum Item_udftype udf_type;
CHARSET_INFO *charset;
thr_lock_type lock_type;
- interval_type interval;
+ interval_type interval, interval_time_st;
timestamp_type date_time_type;
st_select_lex *select_lex;
chooser_compare_func_creator boolfunc2creator;
+ struct sp_cond_type *spcondtype;
+ struct { int vars, conds, hndlrs, curs; } spblock;
+ sp_name *spname;
+ struct st_lex *lex;
}
%{
@@ -127,6 +135,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token AVG_SYM
%token BEGIN_SYM
%token BINLOG_SYM
+%token CALL_SYM
%token CHANGE
%token CLIENT_SYM
%token COMMENT_SYM
@@ -135,7 +144,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CREATE
%token CROSS
%token CUBE_SYM
+%token DEFINER_SYM
%token DELETE_SYM
+%token DETERMINISTIC_SYM
%token DUAL_SYM
%token DO_SYM
%token DROP
@@ -165,6 +176,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SELECT_SYM
%token SHOW
%token SLAVE
+%token SQL_SYM
%token SQL_THREAD
%token START_SYM
%token STD_SYM
@@ -180,6 +192,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ACTION
%token AGGREGATE_SYM
+%token ALGORITHM_SYM
%token ALL
%token AND_SYM
%token AS
@@ -198,6 +211,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token BYTE_SYM
%token CACHE_SYM
%token CASCADE
+%token CASCADED
%token CAST_SYM
%token CHARSET
%token CHECKSUM_SYM
@@ -208,11 +222,15 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token COLUMNS
%token COLUMN_SYM
%token CONCURRENT
+%token CONDITION_SYM
+%token CONNECTION_SYM
%token CONSTRAINT
+%token CONTINUE_SYM
%token CONVERT_SYM
%token CURRENT_USER
%token DATABASES
%token DATA_SYM
+%token DECLARE_SYM
%token DEFAULT
%token DELAYED_SYM
%token DELAY_KEY_WRITE_SYM
@@ -230,14 +248,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token DIRECTORY_SYM
%token ESCAPE_SYM
%token EXISTS
+%token EXIT_SYM
%token EXTENDED_SYM
%token FALSE_SYM
+%token FETCH_SYM
%token FILE_SYM
%token FIRST_SYM
%token FIXED_SYM
%token FLOAT_NUM
%token FORCE_SYM
%token FOREIGN
+%token FOUND_SYM
%token FROM
%token FULL
%token FULLTEXT_SYM
@@ -260,8 +281,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token INFILE
%token INNER_SYM
%token INNOBASE_SYM
+%token INOUT_SYM
%token INTO
%token IN_SYM
+%token INVOKER_SYM
%token ISOLATION
%token JOIN_SYM
%token KEYS
@@ -271,14 +294,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token LEAVES
%token LEVEL_SYM
%token LEX_HOSTNAME
+%token LANGUAGE_SYM
%token LIKE
%token LINES
%token LOCAL_SYM
+%token LOCATOR_SYM
%token LOG_SYM
%token LOGS_SYM
%token LONG_NUM
%token LONG_SYM
%token LOW_PRIORITY
+%token MERGE_SYM
%token MASTER_HOST_SYM
%token MASTER_USER_SYM
%token MASTER_LOG_FILE_SYM
@@ -303,6 +329,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MEDIUM_SYM
%token MIN_ROWS
%token NAMES_SYM
+%token NAME_SYM
%token NATIONAL_SYM
%token NATURAL
%token NDBCLUSTER_SYM
@@ -323,6 +350,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token OR_SYM
%token OR_OR_CONCAT
%token ORDER_SYM
+%token OUT_SYM
%token OUTER
%token OUTFILE
%token DUMPFILE
@@ -354,6 +382,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ROW_FORMAT_SYM
%token ROW_SYM
%token RTREE_SYM
+%token SECURITY_SYM
%token SET
%token SEPARATOR_SYM
%token SERIAL_SYM
@@ -362,6 +391,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SIMPLE_SYM
%token SHUTDOWN
%token SPATIAL_SYM
+%token SPECIFIC_SYM
+%token SQLEXCEPTION_SYM
+%token SQLSTATE_SYM
+%token SQLWARNING_SYM
%token SSL_SYM
%token STARTING
%token STATUS_SYM
@@ -372,6 +405,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token TABLE_SYM
%token TABLESPACE
%token TEMPORARY
+%token TEMPTABLE_SYM
%token TERMINATED
%token TEXT_STRING
%token TO_SYM
@@ -384,11 +418,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token FUNC_ARG1
%token FUNC_ARG2
%token FUNC_ARG3
-%token UDF_RETURNS_SYM
+%token RETURN_SYM
+%token RETURNS_SYM
%token UDF_SONAME_SYM
-%token UDF_SYM
+%token FUNCTION_SYM
%token UNCOMMITTED_SYM
%token UNDERSCORE_CHARSET
+%token UNDO_SYM
%token UNICODE_SYM
%token UNION_SYM
%token UNIQUE_SYM
@@ -399,6 +435,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token VALUE_SYM
%token VALUES
%token VARIABLES
+%token VIEW_SYM
%token WHERE
%token WITH
%token WRITE_SYM
@@ -406,6 +443,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token X509_SYM
%token XOR
%token COMPRESSED_SYM
+%token ROW_COUNT_SYM
%token ERRORS
%token WARNINGS
@@ -442,6 +480,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token STRING_SYM
%token TEXT_SYM
%token TIMESTAMP
+%token TIMESTAMP_ADD
+%token TIMESTAMP_DIFF
%token TIME_SYM
%token TINYBLOB
%token TINYINT
@@ -488,6 +528,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token FIELD_FUNC
%token FORMAT_SYM
%token FOR_SYM
+%token FRAC_SECOND_SYM
%token FROM_UNIXTIME
%token GEOMCOLLFROMTEXT
%token GEOMFROMTEXT
@@ -533,6 +574,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token POLYGON
%token POSITION_SYM
%token PROCEDURE
+%token QUARTER_SYM
%token RAND
%token REPLACE
%token RIGHT
@@ -544,12 +586,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SUBSTRING
%token SUBSTRING_INDEX
%token TRIM
-%token UDA_CHAR_SUM
-%token UDA_FLOAT_SUM
-%token UDA_INT_SUM
-%token UDF_CHAR_FUNC
-%token UDF_FLOAT_FUNC
-%token UDF_INT_FUNC
%token UNIQUE_USERS
%token UNIX_TIMESTAMP
%token USER
@@ -573,6 +609,20 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SQL_SMALL_RESULT
%token SQL_BUFFER_RESULT
+%token CURSOR_SYM
+%token ELSEIF_SYM
+%token ITERATE_SYM
+%token GOTO_SYM
+%token LABEL_SYM
+%token LEAVE_SYM
+%token LOOP_SYM
+%token REPEAT_SYM
+%token UNTIL_SYM
+%token WHILE_SYM
+%token ASENSITIVE_SYM
+%token INSENSITIVE_SYM
+%token SENSITIVE_SYM
+
%token ISSUER_SYM
%token SUBJECT_SYM
%token CIPHER_SYM
@@ -598,6 +648,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
NCHAR_STRING opt_component key_cache_name
+ sp_opt_label
%type <lex_str_ptr>
opt_table_alias
@@ -631,17 +682,20 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <item>
literal text_literal insert_ident order_ident
simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
- table_wild no_in_expr expr_expr simple_expr no_and_expr
+ table_wild no_in_expr expr_expr simple_expr no_and_expr udf_expr
using_list expr_or_default set_expr_or_default interval_expr
param_marker singlerow_subselect singlerow_subselect_init
exists_subselect exists_subselect_init geometry_function
signed_literal now_or_signed_literal opt_escape
+ sp_opt_default
+ simple_ident_nospvar simple_ident_q
%type <item_num>
NUM_literal
%type <item_list>
- expr_list udf_expr_list when_list ident_list ident_list_arg
+ expr_list udf_expr_list udf_expr_list2 when_list
+ ident_list ident_list_arg
%type <key_type>
key_type opt_unique_or_fulltext constraint_key_type
@@ -657,14 +711,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <table_list>
join_table_list join_table
-
-%type <udf>
- UDF_CHAR_FUNC UDF_FLOAT_FUNC UDF_INT_FUNC
- UDA_CHAR_SUM UDA_FLOAT_SUM UDA_INT_SUM
+ table_factor table_ref
%type <date_time_type> date_time_type;
%type <interval> interval
+%type <interval_time_st> interval_time_st
+
%type <db_type> storage_engines
%type <row_type> row_types
@@ -708,7 +761,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
select_item_list select_item values_list no_braces
opt_limit_clause delete_limit_clause fields opt_values values
procedure_list procedure_list2 procedure_item
- when_list2 expr_list2 handler
+ when_list2 expr_list2 udf_expr_list3 handler
opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option
field_opt_list opt_binary table_lock_list table_lock
@@ -729,8 +782,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
subselect_end select_var_list select_var_list_init help opt_len
opt_extended_describe
prepare prepare_src execute deallocate
+ statement sp_suid opt_view_list view_list or_replace algorithm
+ sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic sp_a_chistic
END_OF_INPUT
+%type <NONE> call sp_proc_stmts sp_proc_stmt
+%type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
+%type <spcondtype> sp_cond sp_hcond
+%type <spblock> sp_decls sp_decl
+%type <lex> sp_cursor_stmt
+%type <spname> sp_name
+
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_CONCAT BETWEEN_SYM CASE_SYM
@@ -756,10 +818,16 @@ query:
| verb_clause END_OF_INPUT {};
verb_clause:
+ statement
+ | begin
+ ;
+
+/* Verb clauses, except begin */
+statement:
alter
| analyze
| backup
- | begin
+ | call
| change
| check
| checksum
@@ -1060,20 +1128,1161 @@ create:
lex->name=$4.str;
lex->create_info.options=$3;
}
- | CREATE udf_func_type UDF_SYM IDENT_sys
+ | CREATE udf_func_type FUNCTION_SYM sp_name
{
LEX *lex=Lex;
- lex->sql_command = SQLCOM_CREATE_FUNCTION;
- lex->udf.name = $4;
+ lex->spname= $4;
lex->udf.type= $2;
}
- UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
+ create_function_tail
+ {}
+ | CREATE PROCEDURE sp_name
+ {
+ LEX *lex= Lex;
+ sp_head *sp;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "PROCEDURE");
+ YYABORT;
+ }
+ /* Order is important here: new - reset - init */
+ sp= new sp_head();
+ sp->reset_thd_mem_root(YYTHD);
+ sp->init(lex);
+
+ sp->m_type= TYPE_ENUM_PROCEDURE;
+ lex->sphead= sp;
+ /*
+ * We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ * stored procedure, otherwise yylex will chop it into pieces
+ * at each ';'.
+ */
+ sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
+ YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
+ }
+ '('
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_param_begin= lex->tok_start+1;
+ }
+ sp_pdparam_list
+ ')'
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_param_end= lex->tok_start;
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ }
+ sp_c_chistics
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_chistics= &lex->sp_chistics;
+ lex->sphead->m_body_begin= lex->tok_start;
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ if (sp->check_backpatch(YYTHD))
+ YYABORT;
+ sp->init_strings(YYTHD, lex, $3);
+ lex->sql_command= SQLCOM_CREATE_PROCEDURE;
+ /* Restore flag if it was cleared above */
+ if (sp->m_old_cmq)
+ YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
+ sp->restore_thd_mem_root(YYTHD);
+ }
+ | CREATE or_replace algorithm VIEW_SYM table_ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->sql_command= SQLCOM_CREATE_VIEW;
+ lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
+ /* first table in list is target VIEW name */
+ if (!lex->select_lex.add_table_to_list(thd, $5, NULL, 0))
+ YYABORT;
+ }
+ opt_view_list AS select_init check_option
+ {}
+ ;
+
+sp_name:
+ IDENT_sys '.' IDENT_sys
+ {
+ $$= new sp_name($1, $3);
+ $$->init_qname(YYTHD);
+ }
+ | IDENT_sys
+ {
+ $$= sp_name_current_db_new(YYTHD, $1);
+ }
+ ;
+
+create_function_tail:
+ RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
{
LEX *lex=Lex;
- lex->udf.returns=(Item_result) $7;
- lex->udf.dl=$9.str;
+ lex->sql_command = SQLCOM_CREATE_FUNCTION;
+ lex->udf.name = lex->spname->m_name;
+ lex->udf.returns=(Item_result) $2;
+ lex->udf.dl=$4.str;
}
- ;
+ | '('
+ {
+ LEX *lex= Lex;
+ sp_head *sp;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "FUNCTION");
+ YYABORT;
+ }
+ /* Order is important here: new - reset - init */
+ sp= new sp_head();
+ sp->reset_thd_mem_root(YYTHD);
+ sp->init(lex);
+
+ sp->m_type= TYPE_ENUM_FUNCTION;
+ lex->sphead= sp;
+ /*
+ * We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ * stored procedure, otherwise yylex will chop it into pieces
+ * at each ';'.
+ */
+ sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
+ YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
+ lex->sphead->m_param_begin= lex->tok_start+1;
+ }
+ sp_fdparam_list ')'
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_param_end= lex->tok_start;
+ }
+ RETURNS_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ sp->m_returns_begin= lex->tok_start;
+ sp->m_returns_cs= lex->charset= NULL;
+ }
+ type
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ sp->m_returns_end= lex->tok_start;
+ sp->m_returns= (enum enum_field_types)$8;
+ sp->m_returns_cs= lex->charset;
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ }
+ sp_c_chistics
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_chistics= &lex->sp_chistics;
+ lex->sphead->m_body_begin= lex->tok_start;
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ if (sp->check_backpatch(YYTHD))
+ YYABORT;
+ lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
+ sp->init_strings(YYTHD, lex, lex->spname);
+ /* Restore flag if it was cleared above */
+ if (sp->m_old_cmq)
+ YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
+ sp->restore_thd_mem_root(YYTHD);
+ }
+ ;
+
+sp_a_chistics:
+ /* Empty */ {}
+ | sp_a_chistics sp_a_chistic {}
+ ;
+
+sp_c_chistics:
+ /* Empty */ {}
+ | sp_c_chistics sp_c_chistic {}
+ ;
+
+/* Characteristics for both create and alter */
+sp_chistic:
+ COMMENT_SYM TEXT_STRING_sys { Lex->sp_chistics.comment= $2; }
+ | sp_suid { }
+ ;
+
+/* Alter characteristics */
+sp_a_chistic:
+ sp_chistic { }
+ | NAME_SYM ident { Lex->name= $2.str; }
+ ;
+
+/* Create characteristics */
+sp_c_chistic:
+ sp_chistic { }
+ | LANGUAGE_SYM SQL_SYM { }
+ | DETERMINISTIC_SYM { Lex->sp_chistics.detistic= TRUE; }
+ | NOT DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; }
+ ;
+
+sp_suid:
+ SQL_SYM SECURITY_SYM DEFINER_SYM
+ {
+ Lex->sp_chistics.suid= IS_SUID;
+ }
+ | SQL_SYM SECURITY_SYM INVOKER_SYM
+ {
+ Lex->sp_chistics.suid= IS_NOT_SUID;
+ }
+ ;
+
+call:
+ CALL_SYM sp_name
+ {
+ LEX *lex = Lex;
+
+ lex->sql_command= SQLCOM_CALL;
+ lex->spname= $2;
+ lex->value_list.empty();
+ }
+ '(' sp_cparam_list ')' {}
+ ;
+
+/* CALL parameters */
+sp_cparam_list:
+ /* Empty */
+ | sp_cparams
+ ;
+
+sp_cparams:
+ sp_cparams ',' expr
+ {
+ Lex->value_list.push_back($3);
+ }
+ | expr
+ {
+ Lex->value_list.push_back($1);
+ }
+ ;
+
+/* Stored FUNCTION parameter declaration list */
+sp_fdparam_list:
+ /* Empty */
+ | sp_fdparams
+ ;
+
+sp_fdparams:
+ sp_fdparams ',' sp_fdparam
+ | sp_fdparam
+ ;
+
+sp_fdparam:
+ ident type
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_pvar(&$1, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_PARAM, $1.str);
+ YYABORT;
+ }
+ spc->push_pvar(&$1, (enum enum_field_types)$2, sp_param_in);
+ }
+ ;
+
+/* Stored PROCEDURE parameter declaration list */
+sp_pdparam_list:
+ /* Empty */
+ | sp_pdparams
+ ;
+
+sp_pdparams:
+ sp_pdparams ',' sp_pdparam
+ | sp_pdparam
+ ;
+
+sp_pdparam:
+ sp_opt_inout ident type
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_pvar(&$2, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_PARAM, $2.str);
+ YYABORT;
+ }
+ spc->push_pvar(&$2, (enum enum_field_types)$3,
+ (sp_param_mode_t)$1);
+ }
+ ;
+
+sp_opt_inout:
+ /* Empty */ { $$= sp_param_in; }
+ | IN_SYM { $$= sp_param_in; }
+ | OUT_SYM { $$= sp_param_out; }
+ | INOUT_SYM { $$= sp_param_inout; }
+ ;
+
+sp_proc_stmts:
+ /* Empty */ {}
+ | sp_proc_stmts { Lex->query_tables= 0; } sp_proc_stmt ';'
+
+ ;
+
+sp_decls:
+ /* Empty */
+ {
+ $$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
+ }
+ | sp_decls sp_decl ';'
+ {
+ /* We check for declarations out of (standard) order this way
+ because letting the grammar rules reflect it caused tricky
+ shift/reduce conflicts with the wrong result. (And we get
+ better error handling this way.) */
+ if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs))
+ { /* Variable or condition following cursor or handler */
+ send_error(YYTHD, ER_SP_VARCOND_AFTER_CURSHNDLR);
+ YYABORT;
+ }
+ if ($2.curs && $1.hndlrs)
+ { /* Cursor following handler */
+ send_error(YYTHD, ER_SP_CURSOR_AFTER_HANDLER);
+ YYABORT;
+ }
+ $$.vars= $1.vars + $2.vars;
+ $$.conds= $1.conds + $2.conds;
+ $$.hndlrs= $1.hndlrs + $2.hndlrs;
+ $$.curs= $1.curs + $2.curs;
+ }
+ ;
+
+sp_decl:
+ DECLARE_SYM sp_decl_idents type sp_opt_default
+ {
+ LEX *lex= Lex;
+ sp_pcontext *ctx= lex->spcont;
+ uint max= ctx->context_pvars();
+ enum enum_field_types type= (enum enum_field_types)$3;
+ Item *it= $4;
+
+ for (uint i = max-$2 ; i < max ; i++)
+ {
+ ctx->set_type(i, type);
+ if (! it)
+ ctx->set_isset(i, FALSE);
+ else
+ {
+ sp_instr_set *in= new sp_instr_set(lex->sphead->instructions(),
+ ctx,
+ ctx->pvar_context2index(i),
+ it, type);
+
+ in->tables= lex->query_tables;
+ lex->query_tables= 0;
+ lex->sphead->add_instr(in);
+ ctx->set_isset(i, TRUE);
+ ctx->set_default(i, it);
+ }
+ }
+ $$.vars= $2;
+ $$.conds= $$.hndlrs= $$.curs= 0;
+ }
+ | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_cond(&$2, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_COND, $2.str);
+ YYABORT;
+ }
+ YYTHD->lex->spcont->push_cond(&$2, $5);
+ $$.vars= $$.hndlrs= $$.curs= 0;
+ $$.conds= 1;
+ }
+ | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_instr_hpush_jump *i=
+ new sp_instr_hpush_jump(sp->instructions(), ctx, $2,
+ ctx->current_pvars());
+
+ sp->add_instr(i);
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ ctx->add_handler();
+ sp->m_in_handler= TRUE;
+ }
+ sp_hcond_list sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */
+
+ if ($2 == SP_HANDLER_CONTINUE)
+ sp->add_instr(new sp_instr_hreturn(sp->instructions(), ctx,
+ ctx->current_pvars()));
+ else
+ { /* EXIT or UNDO handler, just jump to the end of the block */
+ sp_instr_jump *i= new sp_instr_jump(sp->instructions(), ctx);
+
+ sp->add_instr(i);
+ sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */
+ }
+ lex->sphead->backpatch(hlab);
+ sp->m_in_handler= FALSE;
+ $$.vars= $$.conds= $$.curs= 0;
+ $$.hndlrs= $6;
+ }
+ | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint offp;
+ sp_instr_cpush *i;
+
+ if (ctx->find_cursor(&$2, &offp, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_CURS, $2.str);
+ delete $5;
+ YYABORT;
+ }
+ i= new sp_instr_cpush(sp->instructions(), ctx, $5);
+ sp->add_instr(i);
+ ctx->push_cursor(&$2);
+ $$.vars= $$.conds= $$.hndlrs= 0;
+ $$.curs= 1;
+ }
+ ;
+
+sp_cursor_stmt:
+ {
+ Lex->sphead->reset_lex(YYTHD);
+
+ /* We use statement here just be able to get a better
+ error message. Using 'select' works too, but will then
+ result in a generic "syntax error" if a non-select
+ statement is given. */
+ }
+ statement
+ {
+ LEX *lex= Lex;
+
+ if (lex->sql_command != SQLCOM_SELECT)
+ {
+ send_error(YYTHD, ER_SP_BAD_CURSOR_QUERY);
+ YYABORT;
+ }
+ if (lex->result)
+ {
+ send_error(YYTHD, ER_SP_BAD_CURSOR_SELECT);
+ YYABORT;
+ }
+ lex->sp_lex_in_use= TRUE;
+ $$= lex;
+ lex->sphead->restore_lex(YYTHD);
+ }
+ ;
+
+sp_handler_type:
+ EXIT_SYM { $$= SP_HANDLER_EXIT; }
+ | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
+/* | UNDO_SYM { QQ No yet } */
+ ;
+
+sp_hcond_list:
+ sp_hcond
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
+
+ i->add_condition($1);
+ $$= 1;
+ }
+ | sp_hcond_list ',' sp_hcond
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
+
+ i->add_condition($3);
+ $$= $1 + 1;
+ }
+ ;
+
+sp_cond:
+ ULONG_NUM
+ { /* mysql errno */
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::number;
+ $$->mysqlerr= $1;
+ }
+ | SQLSTATE_SYM opt_value TEXT_STRING_literal
+ { /* SQLSTATE */
+ uint len= ($3.length < sizeof($$->sqlstate)-1 ?
+ $3.length : sizeof($$->sqlstate)-1);
+
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::state;
+ memcpy($$->sqlstate, $3.str, len);
+ $$->sqlstate[len]= '\0';
+ }
+ ;
+
+opt_value:
+ /* Empty */ {}
+ | VALUE_SYM {}
+ ;
+
+sp_hcond:
+ sp_cond
+ {
+ $$= $1;
+ }
+ | ident /* CONDITION name */
+ {
+ $$= Lex->spcont->find_cond(&$1);
+ if ($$ == NULL)
+ {
+ net_printf(YYTHD, ER_SP_COND_MISMATCH, $1.str);
+ YYABORT;
+ }
+ }
+ | SQLWARNING_SYM /* SQLSTATEs 01??? */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::warning;
+ }
+ | NOT FOUND_SYM /* SQLSTATEs 02??? */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::notfound;
+ }
+ | SQLEXCEPTION_SYM /* All other SQLSTATEs */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::exception;
+ }
+ ;
+
+sp_decl_idents:
+ ident
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_pvar(&$1, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_VAR, $1.str);
+ YYABORT;
+ }
+ spc->push_pvar(&$1, (enum_field_types)0, sp_param_in);
+ $$= 1;
+ }
+ | sp_decl_idents ',' ident
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_pvar(&$3, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_VAR, $3.str);
+ YYABORT;
+ }
+ spc->push_pvar(&$3, (enum_field_types)0, sp_param_in);
+ $$= $1 + 1;
+ }
+ ;
+
+sp_opt_default:
+ /* Empty */ { $$ = NULL; }
+ | DEFAULT expr { $$ = $2; }
+ ;
+
+sp_proc_stmt:
+ {
+ Lex->sphead->reset_lex(YYTHD);
+ }
+ statement
+ {
+ LEX *lex= Lex;
+
+ if ((lex->sql_command == SQLCOM_SELECT && !lex->result) ||
+ sp_multi_results_command(lex->sql_command))
+ {
+ /* We maybe have one or more SELECT without INTO */
+ lex->sphead->m_multi_results= TRUE;
+ }
+ if (lex->sql_command == SQLCOM_CHANGE_DB)
+ { /* "USE db" doesn't work in a procedure */
+ send_error(YYTHD, ER_SP_NO_USE);
+ YYABORT;
+ }
+ /* Don't add an instruction for empty SET statements.
+ ** (This happens if the SET only contained local variables,
+ ** which get their set instructions generated separately.)
+ */
+ if (lex->sql_command != SQLCOM_SET_OPTION ||
+ ! lex->var_list.is_empty())
+ {
+ /* Currently we can't handle queries inside a FUNCTION,
+ ** because of the way table locking works.
+ ** This is unfortunate, and limits the usefulness of functions
+ ** a great deal, but it's nothing we can do about this at the
+ ** moment.
+ */
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
+ lex->sql_command != SQLCOM_SET_OPTION)
+ {
+ send_error(YYTHD, ER_SP_BADSTATEMENT);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_stmt *i=new sp_instr_stmt(lex->sphead->instructions(),
+ lex->spcont);
+
+ i->set_lex(lex);
+ lex->sphead->add_instr(i);
+ lex->sp_lex_in_use= TRUE;
+ }
+ }
+ lex->sphead->restore_lex(YYTHD);
+ }
+ | RETURN_SYM expr
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead->m_type == TYPE_ENUM_PROCEDURE)
+ {
+ send_error(YYTHD, ER_SP_BADRETURN);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_freturn *i;
+
+ if ($2->type() == Item::SUBSELECT_ITEM)
+ { /* QQ For now, just disallow subselects as values */
+ send_error(lex->thd, ER_SP_BADSTATEMENT);
+ YYABORT;
+ }
+ i= new sp_instr_freturn(lex->sphead->instructions(),
+ lex->spcont,
+ $2, lex->sphead->m_returns);
+ lex->sphead->add_instr(i);
+ lex->sphead->m_has_return= TRUE;
+ }
+ }
+ | IF sp_if END IF {}
+ | CASE_SYM WHEN_SYM
+ {
+ Lex->sphead->m_simple_case= FALSE;
+ }
+ sp_case END CASE_SYM {}
+ | CASE_SYM expr WHEN_SYM
+ {
+ /* We "fake" this by using an anonymous variable which we
+ set to the expression. Note that all WHENs are evaluate
+ at the same frame level, so we then know that it's the
+ top-most variable in the frame. */
+ LEX *lex= Lex;
+ uint offset= lex->spcont->current_pvars();
+ sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(),
+ lex->spcont,
+ offset, $2, MYSQL_TYPE_STRING);
+ LEX_STRING dummy;
+
+ dummy.str= (char *)"";
+ dummy.length= 0;
+ lex->spcont->push_pvar(&dummy, MYSQL_TYPE_STRING, sp_param_in);
+ i->tables= lex->query_tables;
+ lex->query_tables= 0;
+ lex->sphead->add_instr(i);
+ lex->sphead->m_simple_case= TRUE;
+ }
+ sp_case END CASE_SYM
+ {
+ Lex->spcont->pop_pvar();
+ }
+ | sp_labeled_control
+ {}
+ | { /* Unlabeled controls get a secret label. */
+ LEX *lex= Lex;
+
+ lex->spcont->push_label((char *)"", lex->sphead->instructions());
+ }
+ sp_unlabeled_control
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ | LEAVE_SYM IDENT
+ {
+ LEX *lex= Lex;
+ sp_head *sp = lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *lab= ctx->find_label($2.str);
+
+ if (! lab)
+ {
+ net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "LEAVE", $2.str);
+ YYABORT;
+ }
+ else
+ {
+ uint ip= sp->instructions();
+ sp_instr_jump *i;
+ sp_instr_hpop *ih;
+ sp_instr_cpop *ic;
+
+ ih= new sp_instr_hpop(ip++, ctx, 0);
+ sp->push_backpatch(ih, lab);
+ sp->add_instr(ih);
+ ic= new sp_instr_cpop(ip++, ctx, 0);
+ sp->push_backpatch(ic, lab);
+ sp->add_instr(ic);
+ i= new sp_instr_jump(ip, ctx);
+ sp->push_backpatch(i, lab); /* Jumping forward */
+ sp->add_instr(i);
+ }
+ }
+ | ITERATE_SYM IDENT
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *lab= ctx->find_label($2.str);
+
+ if (! lab || lab->type != SP_LAB_ITER)
+ {
+ net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "ITERATE", $2.str);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_jump *i;
+ uint ip= sp->instructions();
+ uint n;
+
+ n= ctx->diff_handlers(lab->ctx);
+ if (n)
+ sp->add_instr(new sp_instr_hpop(ip++, ctx, n));
+ n= ctx->diff_cursors(lab->ctx);
+ if (n)
+ sp->add_instr(new sp_instr_cpop(ip++, ctx, n));
+ i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */
+ sp->add_instr(i);
+ }
+ }
+ | LABEL_SYM IDENT
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *lab= ctx->find_label($2.str);
+
+ if (lab)
+ {
+ net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $2.str);
+ YYABORT;
+ }
+ else
+ {
+ lab= ctx->push_label($2.str, sp->instructions());
+ lab->type= SP_LAB_GOTO;
+ lab->ctx= ctx;
+ sp->backpatch(lab);
+ }
+ }
+ | GOTO_SYM IDENT
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab;
+ sp_instr_jump *i;
+ sp_instr_hpop *ih;
+ sp_instr_cpop *ic;
+
+ if (sp->m_in_handler)
+ {
+ send_error(lex->thd, ER_SP_GOTO_IN_HNDLR);
+ YYABORT;
+ }
+ lab= ctx->find_label($2.str);
+ if (! lab)
+ {
+ lab= (sp_label_t *)YYTHD->alloc(sizeof(sp_label_t));
+ lab->name= $2.str;
+ lab->ip= 0;
+ lab->type= SP_LAB_REF;
+ lab->ctx= ctx;
+
+ ih= new sp_instr_hpop(ip++, ctx, 0);
+ sp->push_backpatch(ih, lab);
+ sp->add_instr(ih);
+ ic= new sp_instr_cpop(ip++, ctx, 0);
+ sp->add_instr(ic);
+ sp->push_backpatch(ic, lab);
+ i= new sp_instr_jump(ip, ctx);
+ sp->push_backpatch(i, lab); /* Jumping forward */
+ sp->add_instr(i);
+ }
+ else
+ {
+ uint n;
+
+ n= ctx->diff_handlers(lab->ctx);
+ if (n)
+ {
+ ih= new sp_instr_hpop(ip++, ctx, n);
+ sp->add_instr(ih);
+ }
+ n= ctx->diff_cursors(lab->ctx);
+ if (n)
+ {
+ ic= new sp_instr_cpop(ip++, ctx, n);
+ sp->add_instr(ic);
+ }
+ i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */
+ sp->add_instr(i);
+ }
+ }
+ | OPEN_SYM ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_copen *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_copen(sp->instructions(), lex->spcont, offset);
+ sp->add_instr(i);
+ }
+ | FETCH_SYM ident INTO
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_cfetch *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset);
+ sp->add_instr(i);
+ }
+ sp_fetch_list
+ { }
+ | CLOSE_SYM ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_cclose *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset);
+ sp->add_instr(i);
+ }
+ ;
+
+sp_fetch_list:
+ ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$1)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $1.str);
+ YYABORT;
+ }
+ else
+ {
+ /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ spv->isset= TRUE;
+ }
+ }
+ |
+ sp_fetch_list ',' ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$3)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $3.str);
+ YYABORT;
+ }
+ else
+ {
+ /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ spv->isset= TRUE;
+ }
+ }
+ ;
+
+sp_if:
+ expr THEN_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx, $1);
+
+ i->tables= lex->query_tables;
+ lex->query_tables= 0;
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_instr(i);
+ }
+ sp_proc_stmts
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip, ctx);
+
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_elseifs
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_elseifs:
+ /* Empty */
+ | ELSEIF_SYM sp_if
+ | ELSE sp_proc_stmts
+ ;
+
+sp_case:
+ expr THEN_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i;
+
+ if (! sp->m_simple_case)
+ i= new sp_instr_jump_if_not(ip, ctx, $1);
+ else
+ { /* Simple case: <caseval> = <whenval> */
+ LEX_STRING ivar;
+
+ ivar.str= (char *)"_tmp_";
+ ivar.length= 5;
+ Item *var= (Item*) new Item_splocal(ivar,
+ ctx->current_pvars()-1);
+ Item *expr= new Item_func_eq(var, $1);
+
+ i= new sp_instr_jump_if_not(ip, ctx, expr);
+ lex->variables_used= 1;
+ }
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ i->tables= lex->query_tables;
+ lex->query_tables= 0;
+ sp->add_instr(i);
+ }
+ sp_proc_stmts
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip, ctx);
+
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_whens
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_whens:
+ /* Empty */
+ {
+ sp_head *sp= Lex->sphead;
+ uint ip= sp->instructions();
+ sp_instr_error *i= new sp_instr_error(ip, Lex->spcont,
+ ER_SP_CASE_NOT_FOUND);
+
+ sp->add_instr(i);
+ }
+ | ELSE sp_proc_stmts {}
+ | WHEN_SYM sp_case {}
+ ;
+
+sp_labeled_control:
+ IDENT ':'
+ {
+ LEX *lex= Lex;
+ sp_pcontext *ctx= lex->spcont;
+ sp_label_t *lab= ctx->find_label($1.str);
+
+ if (lab)
+ {
+ net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $1.str);
+ YYABORT;
+ }
+ else
+ {
+ lab= lex->spcont->push_label($1.str,
+ lex->sphead->instructions());
+ lab->type= SP_LAB_ITER;
+ }
+ }
+ sp_unlabeled_control sp_opt_label
+ {
+ LEX *lex= Lex;
+
+ if ($5.str)
+ {
+ sp_label_t *lab= lex->spcont->find_label($5.str);
+
+ if (!lab ||
+ my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
+ {
+ net_printf(YYTHD, ER_SP_LABEL_MISMATCH, $5.str);
+ YYABORT;
+ }
+ }
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_opt_label:
+ /* Empty */
+ { $$.str= NULL; $$.length= 0; }
+ | IDENT
+ { $$= $1; }
+ ;
+
+sp_unlabeled_control:
+ BEGIN_SYM
+ { /* QQ This is just a dummy for grouping declarations and statements
+ together. No [[NOT] ATOMIC] yet, and we need to figure out how
+ make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
+ LEX *lex= Lex;
+ sp_label_t *lab= lex->spcont->last_label();
+
+ lab->type= SP_LAB_BEGIN;
+ lex->spcont= lex->spcont->push_context();
+ }
+ sp_decls
+ sp_proc_stmts
+ END
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+
+ sp->backpatch(ctx->last_label()); /* We always have a label */
+ if ($3.hndlrs)
+ sp->add_instr(new sp_instr_hpop(sp->instructions(), ctx,
+ $3.hndlrs));
+ if ($3.curs)
+ sp->add_instr(new sp_instr_cpop(sp->instructions(), ctx,
+ $3.curs));
+ lex->spcont= ctx->pop_context();
+ }
+ | LOOP_SYM
+ sp_proc_stmts END LOOP_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
+
+ lex->sphead->add_instr(i);
+ }
+ | WHILE_SYM expr DO_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
+ $2);
+
+ /* Jumping forward */
+ sp->push_backpatch(i, lex->spcont->last_label());
+ i->tables= lex->query_tables;
+ lex->query_tables= 0;
+ sp->add_instr(i);
+ }
+ sp_proc_stmts END WHILE_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
+
+ lex->sphead->add_instr(i);
+ }
+ | REPEAT_SYM sp_proc_stmts UNTIL_SYM expr END REPEAT_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
+ $4, lab->ip);
+
+ i->tables= lex->query_tables;
+ lex->query_tables= 0;
+ lex->sphead->add_instr(i);
+ }
+ ;
create2:
'(' create2a {}
@@ -1114,6 +2323,10 @@ create_select:
lex->sql_command= SQLCOM_INSERT_SELECT;
else if (lex->sql_command == SQLCOM_REPLACE)
lex->sql_command= SQLCOM_REPLACE_SELECT;
+ /*
+ following work only with local list, global list is
+ created correctly in this case
+ */
lex->current_select->table_list.save_and_clear(&lex->save_list);
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
@@ -1123,7 +2336,13 @@ create_select:
Select->parsing_place= NO_MATTER;
}
opt_select_from
- { Lex->current_select->table_list.push_front(&Lex->save_list); }
+ {
+ /*
+ following work only with local list, global list is
+ created correctly in this case
+ */
+ Lex->current_select->table_list.push_front(&Lex->save_list);
+ }
;
opt_as:
@@ -1194,10 +2413,12 @@ create_table_option:
TABLE_LIST *table_list= lex->select_lex.get_table_list();
lex->create_info.merge_list= lex->select_lex.table_list;
lex->create_info.merge_list.elements--;
- lex->create_info.merge_list.first= (byte*) (table_list->next);
+ lex->create_info.merge_list.first=
+ (byte*) (table_list->next_local);
lex->select_lex.table_list.elements=1;
- lex->select_lex.table_list.next= (byte**) &(table_list->next);
- table_list->next=0;
+ lex->select_lex.table_list.next=
+ (byte**) &(table_list->next_local);
+ table_list->next_local= 0;
lex->create_info.used_fields|= HA_CREATE_USED_UNION;
}
| default_charset
@@ -1859,8 +3080,50 @@ alter:
LEX *lex=Lex;
lex->sql_command=SQLCOM_ALTER_DB;
lex->name=$3.str;
- };
+ }
+ | ALTER PROCEDURE sp_name
+ {
+ LEX *lex= Lex;
+
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->name= 0;
+ }
+ sp_a_chistics
+ {
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+
+ lex->sql_command= SQLCOM_ALTER_PROCEDURE;
+ lex->spname= $3;
+ }
+ | ALTER FUNCTION_SYM sp_name
+ {
+ LEX *lex= Lex;
+
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->name= 0;
+ }
+ sp_a_chistics
+ {
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_ALTER_FUNCTION;
+ lex->spname= $3;
+ }
+ | ALTER VIEW_SYM table_ident
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->sql_command= SQLCOM_CREATE_VIEW;
+ lex->create_view_mode= VIEW_ALTER;
+ lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
+ /* first table in list is target VIEW name */
+ lex->select_lex.add_table_to_list(thd, $3, NULL, 0);
+ }
+ opt_view_list AS select_init
+ {}
+ ;
alter_list:
| DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; }
@@ -2014,9 +3277,10 @@ opt_ignore:
| IGNORE_SYM { Lex->duplicates=DUP_IGNORE; };
opt_restrict:
- /* empty */ {}
- | RESTRICT {}
- | CASCADE {};
+ /* empty */ { Lex->drop_mode= DROP_DEFAULT; }
+ | RESTRICT { Lex->drop_mode= DROP_RESTRICT; }
+ | CASCADE { Lex->drop_mode= DROP_CASCADE; }
+ ;
opt_place:
/* empty */ {}
@@ -2505,8 +3769,12 @@ select_item:
YYABORT;
if ($4.str)
$2->set_name($4.str,$4.length,system_charset_info);
- else if (!$2->name)
- $2->set_name($1,(uint) ($3 - $1), YYTHD->charset());
+ else if (!$2->name) {
+ char *str = $1;
+ if (str[-1] == '`')
+ str--;
+ $2->set_name(str,(uint) ($3 - str), YYTHD->charset());
+ }
};
remember_name:
@@ -2730,12 +3998,16 @@ simple_expr:
| '@' ident_or_text SET_VAR expr
{
$$= new Item_func_set_user_var($2,$4);
- Lex->uncacheable(UNCACHEABLE_RAND);
+ LEX *lex= Lex;
+ lex->uncacheable(UNCACHEABLE_RAND);
+ lex->variables_used= 1;
}
| '@' ident_or_text
{
$$= new Item_func_get_user_var($2);
- Lex->uncacheable(UNCACHEABLE_RAND);
+ LEX *lex= Lex;
+ lex->uncacheable(UNCACHEABLE_RAND);
+ lex->variables_used= 1;
}
| '@' '@' opt_var_ident_type ident_or_text opt_component
{
@@ -2747,6 +4019,7 @@ simple_expr:
}
if (!($$= get_system_var(YYTHD, (enum_var_type) $3, $4, $5)))
YYABORT;
+ Lex->variables_used= 1;
}
| sum_expr
| '+' expr %prec NEG { $$= $2; }
@@ -2851,6 +4124,8 @@ simple_expr:
{ $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 0);}
| ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
{ $$= new Item_date_add_interval($3, $6, $7, 0); }
+ | REPEAT_SYM '(' expr ',' expr ')'
+ { $$= new Item_func_repeat($3,$5); }
| ATAN '(' expr ')'
{ $$= new Item_func_atan($3); }
| ATAN '(' expr ',' expr ')'
@@ -3022,6 +4297,8 @@ simple_expr:
{ $$= new Item_func_old_password($3); }
| POSITION_SYM '(' no_in_expr IN_SYM expr ')'
{ $$ = new Item_func_locate($5,$3); }
+ | QUARTER_SYM '(' expr ')'
+ { $$ = new Item_func_quarter($3); }
| RAND '(' expr ')'
{ $$= new Item_func_rand($3); Lex->uncacheable(UNCACHEABLE_RAND);}
| RAND '(' ')'
@@ -3033,6 +4310,11 @@ simple_expr:
| ROUND '(' expr ')'
{ $$= new Item_func_round($3, new Item_int((char*)"0",0,1),0); }
| ROUND '(' expr ',' expr ')' { $$= new Item_func_round($3,$5,0); }
+ | ROW_COUNT_SYM '(' ')'
+ {
+ $$= new Item_func_row_count();
+ Lex->safe_to_cache_query= 0;
+ }
| SUBDATE_SYM '(' expr ',' expr ')'
{ $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 1);}
| SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
@@ -3055,6 +4337,10 @@ simple_expr:
{ $$= new Item_datetime_typecast($3); }
| TIMESTAMP '(' expr ',' expr ')'
{ $$= new Item_func_add_time($3, $5, 1, 0); }
+ | TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')'
+ { $$= new Item_date_add_interval($7,$5,$3,0); }
+ | TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')'
+ { $$= new Item_func_timestamp_diff($5,$7,$3); }
| TRIM '(' expr ')'
{ $$= new Item_func_trim($3); }
| TRIM '(' LEADING expr FROM expr ')'
@@ -3075,48 +4361,90 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= new Item_int((char*) "TRUE",1,1); }
- | UDA_CHAR_SUM '(' udf_expr_list ')'
+ | ident '.' ident '(' udf_expr_list ')'
{
- if ($3 != NULL)
- $$ = new Item_sum_udf_str($1, *$3);
- else
- $$ = new Item_sum_udf_str($1);
- }
- | UDA_FLOAT_SUM '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_sum_udf_float($1, *$3);
- else
- $$ = new Item_sum_udf_float($1);
- }
- | UDA_INT_SUM '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_sum_udf_int($1, *$3);
- else
- $$ = new Item_sum_udf_int($1);
- }
- | UDF_CHAR_FUNC '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_func_udf_str($1, *$3);
- else
- $$ = new Item_func_udf_str($1);
- }
- | UDF_FLOAT_FUNC '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_func_udf_float($1, *$3);
- else
- $$ = new Item_func_udf_float($1);
- }
- | UDF_INT_FUNC '(' udf_expr_list ')'
- {
- if ($3 != NULL)
- $$ = new Item_func_udf_int($1, *$3);
+ LEX *lex= Lex;
+ sp_name *name= new sp_name($1, $3);
+
+ name->init_qname(YYTHD);
+ sp_add_fun_to_lex(Lex, name);
+ if ($5)
+ $$= new Item_func_sp(name, *$5);
else
- $$ = new Item_func_udf_int($1);
+ $$= new Item_func_sp(name);
}
+ | IDENT_sys '(' udf_expr_list ')'
+ {
+#ifdef HAVE_DLOPEN
+ udf_func *udf;
+
+ if (using_udf_functions && (udf=find_udf($1.str, $1.length)))
+ {
+ switch (udf->returns) {
+ case STRING_RESULT:
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if ($3 != NULL)
+ $$ = new Item_func_udf_str(udf, *$3);
+ else
+ $$ = new Item_func_udf_str(udf);
+ }
+ else
+ {
+ if ($3 != NULL)
+ $$ = new Item_sum_udf_str(udf, *$3);
+ else
+ $$ = new Item_sum_udf_str(udf);
+ }
+ break;
+ case REAL_RESULT:
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if ($3 != NULL)
+ $$ = new Item_func_udf_float(udf, *$3);
+ else
+ $$ = new Item_func_udf_float(udf);
+ }
+ else
+ {
+ if ($3 != NULL)
+ $$ = new Item_sum_udf_float(udf, *$3);
+ else
+ $$ = new Item_sum_udf_float(udf);
+ }
+ break;
+ case INT_RESULT:
+ if (udf->type == UDFTYPE_FUNCTION)
+ {
+ if ($3 != NULL)
+ $$ = new Item_func_udf_int(udf, *$3);
+ else
+ $$ = new Item_func_udf_int(udf);
+ }
+ else
+ {
+ if ($3 != NULL)
+ $$ = new Item_sum_udf_int(udf, *$3);
+ else
+ $$ = new Item_sum_udf_int(udf);
+ }
+ break;
+ default:
+ YYABORT;
+ }
+ }
+ else
+#endif /* HAVE_DLOPEN */
+ {
+ sp_name *name= sp_name_current_db_new(YYTHD, $1);
+
+ sp_add_fun_to_lex(Lex, name);
+ if ($3)
+ $$= new Item_func_sp(name, *$3);
+ else
+ $$= new Item_func_sp(name);
+ }
+ }
| UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')'
{
$$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9);
@@ -3224,8 +4552,37 @@ fulltext_options:
;
udf_expr_list:
- /* empty */ { $$= NULL; }
- | expr_list { $$= $1;};
+ /* empty */ { $$= NULL; }
+ | udf_expr_list2 { $$= $1;}
+ ;
+
+udf_expr_list2:
+ { Select->expr_list.push_front(new List<Item>); }
+ udf_expr_list3
+ { $$= Select->expr_list.pop(); }
+ ;
+
+udf_expr_list3:
+ udf_expr
+ {
+ Select->expr_list.head()->push_back($1);
+ }
+ | udf_expr_list3 ',' udf_expr
+ {
+ Select->expr_list.head()->push_back($3);
+ }
+ ;
+
+udf_expr:
+ remember_name expr remember_end select_alias
+ {
+ if ($4.str)
+ $2->set_name($4.str,$4.length,system_charset_info);
+ else
+ $2->set_name($1,(uint) ($3 - $1), YYTHD->charset());
+ $$= $2;
+ }
+ ;
sum_expr:
AVG_SYM '(' in_sum_expr ')'
@@ -3250,14 +4607,25 @@ sum_expr:
{ $$= new Item_sum_unique_users($3,atoi($5.str),atoi($7.str),$9); }
| MIN_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_min($3); }
+/*
+ According to ANSI SQL, DISTINCT is allowed and has
+ no sence inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...)
+ is processed like an ordinary MIN | MAX()
+ */
+ | MIN_SYM '(' DISTINCT in_sum_expr ')'
+ { $$=new Item_sum_min($4); }
| MAX_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_max($3); }
+ | MAX_SYM '(' DISTINCT in_sum_expr ')'
+ { $$=new Item_sum_max($4); }
| STD_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_std($3); }
| VARIANCE_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_variance($3); }
| SUM_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_sum($3); }
+ | SUM_SYM '(' DISTINCT in_sum_expr ')'
+ { $$=new Item_sum_sum_distinct($4); }
| GROUP_CONCAT_SYM '(' opt_distinct
{ Select->in_sum_expr++; }
expr_list opt_gorder_clause
@@ -3371,59 +4739,80 @@ when_list2:
sel->when_list.head()->push_back($5);
};
+table_ref:
+ table_factor { $$=$1; }
+ | join_table { $$=$1; }
+ {
+ LEX *lex= Lex;
+ if (!($$= lex->current_select->nest_last_join(lex->thd)))
+ YYABORT;
+ }
+ ;
+
join_table_list:
- '(' join_table_list ')' { $$=$2; }
- | join_table { $$=$1; }
- | join_table_list ',' join_table_list { $$=$3; }
- | join_table_list normal_join join_table_list { $$=$3; }
- | join_table_list STRAIGHT_JOIN join_table_list
- { $$=$3 ; $1->next->straight=1; }
- | join_table_list normal_join join_table_list ON expr
+ table_ref { $$=$1; }
+ | join_table_list ',' table_ref { $$=$3; }
+ ;
+
+join_table:
+ table_ref normal_join table_ref { $$=$3; }
+ | table_ref STRAIGHT_JOIN table_factor
+ { $3->straight=1; $$=$3 ; }
+ | table_ref normal_join table_ref ON expr
{ add_join_on($3,$5); $$=$3; }
- | join_table_list normal_join join_table_list
+ | table_ref normal_join table_ref
USING
{
SELECT_LEX *sel= Select;
- sel->db1=$1->db; sel->table1=$1->alias;
- sel->db2=$3->db; sel->table2=$3->alias;
+ sel->save_names_for_using_list($1, $3);
}
'(' using_list ')'
{ add_join_on($3,$7); $$=$3; }
- | join_table_list LEFT opt_outer JOIN_SYM join_table_list ON expr
+ | table_ref LEFT opt_outer JOIN_SYM table_ref ON expr
{ add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
- | join_table_list LEFT opt_outer JOIN_SYM join_table_list
+ | table_ref LEFT opt_outer JOIN_SYM table_factor
{
SELECT_LEX *sel= Select;
- sel->db1=$1->db; sel->table1=$1->alias;
- sel->db2=$5->db; sel->table2=$5->alias;
+ sel->save_names_for_using_list($1, $5);
}
USING '(' using_list ')'
{ add_join_on($5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
- | join_table_list NATURAL LEFT opt_outer JOIN_SYM join_table_list
+ | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
{
- add_join_natural($1,$1->next);
- $1->next->outer_join|=JOIN_TYPE_LEFT;
+ add_join_natural($1,$6);
+ $6->outer_join|=JOIN_TYPE_LEFT;
$$=$6;
}
- | join_table_list RIGHT opt_outer JOIN_SYM join_table_list ON expr
- { add_join_on($1,$7); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$5; }
- | join_table_list RIGHT opt_outer JOIN_SYM join_table_list
+ | table_ref RIGHT opt_outer JOIN_SYM table_ref ON expr
+ {
+ LEX *lex= Lex;
+ if (!($$= lex->current_select->convert_right_join()))
+ YYABORT;
+ add_join_on($$, $7);
+ }
+ | table_ref RIGHT opt_outer JOIN_SYM table_factor
{
SELECT_LEX *sel= Select;
- sel->db1=$1->db; sel->table1=$1->alias;
- sel->db2=$5->db; sel->table2=$5->alias;
+ sel->save_names_for_using_list($1, $5);
}
USING '(' using_list ')'
- { add_join_on($1,$9); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$5; }
- | join_table_list NATURAL RIGHT opt_outer JOIN_SYM join_table_list
+ {
+ LEX *lex= Lex;
+ if (!($$= lex->current_select->convert_right_join()))
+ YYABORT;
+ add_join_on($$, $9);
+ }
+ | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
{
- add_join_natural($1->next,$1);
- $1->outer_join|=JOIN_TYPE_RIGHT;
- $$=$6;
+ add_join_natural($6,$1);
+ LEX *lex= Lex;
+ if (!($$= lex->current_select->convert_right_join()))
+ YYABORT;
}
- | join_table_list NATURAL JOIN_SYM join_table_list
- { add_join_natural($1,$1->next); $$=$4; };
+ | table_ref NATURAL JOIN_SYM table_factor
+ { add_join_natural($1,$4); $$=$4; };
+
normal_join:
JOIN_SYM {}
@@ -3431,7 +4820,7 @@ normal_join:
| CROSS JOIN_SYM {}
;
-join_table:
+table_factor:
{
SELECT_LEX *sel= Select;
sel->use_index_ptr=sel->ignore_index_ptr=0;
@@ -3447,8 +4836,21 @@ join_table:
sel->get_use_index(),
sel->get_ignore_index())))
YYABORT;
+ sel->add_joined_table($$);
}
- | '{' ident join_table LEFT OUTER JOIN_SYM join_table ON expr '}'
+ | '('
+ {
+ LEX *lex= Lex;
+ if (lex->current_select->init_nested_join(lex->thd))
+ YYABORT;
+ }
+ join_table_list ')'
+ {
+ LEX *lex= Lex;
+ if (!($$= lex->current_select->end_nested_join(lex->thd)))
+ YYABORT;
+ }
+ | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref ON expr '}'
{ add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
| '(' SELECT_SYM select_derived ')' opt_table_alias
{
@@ -3461,12 +4863,13 @@ join_table:
(List<String> *)0)))
YYABORT;
+ lex->current_select->add_joined_table($$);
};
select_derived:
{
LEX *lex= Lex;
- lex->derived_tables= 1;
+ lex->derived_tables|= DERIVED_SUBQUERY;
if (((int)lex->sql_command >= (int)SQLCOM_HA_OPEN &&
lex->sql_command <= (int)SQLCOM_HA_READ) ||
lex->sql_command == (int)SQLCOM_KILL)
@@ -3557,23 +4960,29 @@ using_list:
};
interval:
- DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
+ interval_time_st {}
+ | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
| DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; }
| DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; }
| DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; }
- | DAY_SYM { $$=INTERVAL_DAY; }
| HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; }
| HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; }
| HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; }
- | HOUR_SYM { $$=INTERVAL_HOUR; }
| MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; }
| MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; }
| MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; }
+ | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
+ | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; };
+
+interval_time_st:
+ DAY_SYM { $$=INTERVAL_DAY; }
+ | WEEK_SYM { $$=INTERVAL_WEEK; }
+ | HOUR_SYM { $$=INTERVAL_HOUR; }
+ | FRAC_SECOND_SYM { $$=INTERVAL_MICROSECOND; }
| MINUTE_SYM { $$=INTERVAL_MINUTE; }
| MONTH_SYM { $$=INTERVAL_MONTH; }
- | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
+ | QUARTER_SYM { $$=INTERVAL_QUARTER; }
| SECOND_SYM { $$=INTERVAL_SECOND; }
- | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }
| YEAR_SYM { $$=INTERVAL_YEAR; }
;
@@ -3634,8 +5043,11 @@ having_clause:
opt_escape:
ESCAPE_SYM simple_expr { $$= $2; }
| /* empty */
- {
- $$= new Item_string("\\", 1, &my_charset_latin1);
+ {
+
+ $$= ((YYTHD->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
+ new Item_string("", 0, &my_charset_latin1) :
+ new Item_string("\\", 1, &my_charset_latin1));
}
;
@@ -3848,11 +5260,32 @@ select_var_list:
| select_var_ident {}
;
-select_var_ident: '@' ident_or_text
+select_var_ident:
+ '@' ident_or_text
+ {
+ LEX *lex=Lex;
+ if (lex->result)
+ ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0));
+ else
+ YYABORT;
+ }
+ | ident_or_text
{
LEX *lex=Lex;
- if (lex->result && ((select_dumpvar *)lex->result)->var_list.push_back((LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING))))
+ sp_pvar_t *t;
+
+ if (!lex->spcont || !(t=lex->spcont->find_pvar(&$1)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $1.str);
YYABORT;
+ }
+ if (! lex->result)
+ YYABORT;
+ else
+ {
+ ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($1,1,t->offset,t->type));
+ t->isset= TRUE;
+ }
}
;
@@ -3933,11 +5366,29 @@ drop:
lex->drop_if_exists=$3;
lex->name=$4.str;
}
- | DROP UDF_SYM IDENT_sys
+ | DROP FUNCTION_SYM if_exists sp_name opt_restrict
{
LEX *lex=Lex;
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_DROP_SP, "FUNCTION");
+ YYABORT;
+ }
lex->sql_command = SQLCOM_DROP_FUNCTION;
- lex->udf.name = $3;
+ lex->drop_if_exists= $3;
+ lex->spname= $4;
+ }
+ | DROP PROCEDURE if_exists sp_name opt_restrict
+ {
+ LEX *lex=Lex;
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_DROP_SP, "PROCEDURE");
+ YYABORT;
+ }
+ lex->sql_command = SQLCOM_DROP_PROCEDURE;
+ lex->drop_if_exists= $3;
+ lex->spname= $4;
}
| DROP USER
{
@@ -3947,9 +5398,15 @@ drop:
}
user_list
{}
+ | DROP VIEW_SYM if_exists table_list opt_restrict
+ {
+ THD *thd= YYTHD;
+ LEX *lex= thd->lex;
+ lex->sql_command= SQLCOM_DROP_VIEW;
+ lex->drop_if_exists= $3;
+ }
;
-
table_list:
table_name
| table_list ',' table_name;
@@ -4008,7 +5465,6 @@ replace:
}
insert_field_spec
{}
- {}
;
insert_lock_option:
@@ -4074,7 +5530,7 @@ ident_eq_list:
ident_eq_value;
ident_eq_value:
- simple_ident equal expr_or_default
+ simple_ident_nospvar equal expr_or_default
{
LEX *lex=Lex;
if (lex->field_list.push_back($1) ||
@@ -4169,12 +5625,12 @@ update:
;
update_list:
- update_list ',' simple_ident equal expr_or_default
+ update_list ',' simple_ident_nospvar equal expr_or_default
{
if (add_item_to_list(YYTHD, $3) || add_value_to_list(YYTHD, $5))
YYABORT;
}
- | simple_ident equal expr_or_default
+ | simple_ident_nospvar equal expr_or_default
{
if (add_item_to_list(YYTHD, $1) || add_value_to_list(YYTHD, $3))
YYABORT;
@@ -4427,9 +5883,19 @@ show_param:
}
| CREATE TABLE_SYM table_ident
{
- Lex->sql_command = SQLCOM_SHOW_CREATE;
- if (!Select->add_table_to_list(YYTHD, $3, NULL,0))
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE;
+ if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL,0))
+ YYABORT;
+ lex->only_view= 0;
+ }
+ | CREATE VIEW_SYM table_ident
+ {
+ LEX *lex= Lex;
+ lex->sql_command = SQLCOM_SHOW_CREATE;
+ if (!lex->select_lex.add_table_to_list(YYTHD, $3, NULL, 0))
YYABORT;
+ lex->only_view= 1;
}
| MASTER_SYM STATUS_SYM
{
@@ -4438,7 +5904,29 @@ show_param:
| SLAVE STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- };
+ }
+ | CREATE PROCEDURE sp_name
+ {
+ LEX *lex= Lex;
+
+ lex->sql_command = SQLCOM_SHOW_CREATE_PROC;
+ lex->spname= $3;
+ }
+ | CREATE FUNCTION_SYM sp_name
+ {
+ LEX *lex= Lex;
+
+ lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
+ lex->spname= $3;
+ }
+ | PROCEDURE STATUS_SYM wild
+ {
+ Lex->sql_command = SQLCOM_SHOW_STATUS_PROC;
+ }
+ | FUNCTION_SYM STATUS_SYM wild
+ {
+ Lex->sql_command = SQLCOM_SHOW_STATUS_FUNC;
+ };
show_engine_param:
STATUS_SYM
@@ -4628,18 +6116,23 @@ purge_option:
/* kill threads */
kill:
- KILL_SYM expr
+ KILL_SYM kill_option expr
{
LEX *lex=Lex;
- if ($2->fix_fields(lex->thd, 0, &$2) || $2->check_cols(1))
+ if ($3->fix_fields(lex->thd, 0, &$3) || $3->check_cols(1))
{
send_error(lex->thd, ER_SET_CONSTANTS_ONLY);
YYABORT;
}
lex->sql_command=SQLCOM_KILL;
- lex->thread_id= (ulong) $2->val_int();
+ lex->thread_id= (ulong) $3->val_int();
};
+kill_option:
+ /* empty */ { Lex->type= 0; }
+ | CONNECTION_SYM { Lex->type= 0; }
+ | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; };
+
/* change database */
use: USE_SYM ident
@@ -4835,7 +6328,7 @@ NUM_literal:
**********************************************************************/
insert_ident:
- simple_ident { $$=$1; }
+ simple_ident_nospvar { $$=$1; }
| table_wild { $$=$1; };
table_wild:
@@ -4859,13 +6352,47 @@ order_ident:
simple_ident:
ident
{
+ sp_pvar_t *spv;
+ LEX *lex = Lex;
+ sp_pcontext *spc = lex->spcont;
+
+ if (spc && (spv = spc->find_pvar(&$1)))
+ { /* We're compiling a stored procedure and found a variable */
+ if (lex->sql_command != SQLCOM_CALL && ! spv->isset)
+ {
+ push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SP_UNINIT_VAR, ER(ER_SP_UNINIT_VAR),
+ $1.str);
+ }
+ $$ = (Item*) new Item_splocal($1, spv->offset);
+ lex->variables_used= 1;
+ }
+ else
+ {
+ SELECT_LEX *sel=Select;
+ $$= (sel->parsing_place != IN_HAVING ||
+ sel->get_in_sum_expr() > 0) ?
+ (Item*) new Item_field(NullS,NullS,$1.str) :
+ (Item*) new Item_ref(0,0, NullS,NullS,$1.str);
+ }
+ }
+ | simple_ident_q { $$= $1; }
+ ;
+
+simple_ident_nospvar:
+ ident
+ {
SELECT_LEX *sel=Select;
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
(Item*) new Item_field(NullS,NullS,$1.str) :
- (Item*) new Item_ref(0,0, NullS,NullS,$1.str);
+ (Item*) new Item_ref(0,0,NullS,NullS,$1.str);
}
- | ident '.' ident
+ | simple_ident_q { $$= $1; }
+ ;
+
+simple_ident_q:
+ ident '.' ident
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
@@ -5043,6 +6570,7 @@ keyword:
| AFTER_SYM {}
| AGAINST {}
| AGGREGATE_SYM {}
+ | ALGORITHM_SYM {}
| ANY_SYM {}
| ASCII_SYM {}
| AUTO_INC {}
@@ -5058,6 +6586,7 @@ keyword:
| BYTE_SYM {}
| BTREE_SYM {}
| CACHE_SYM {}
+ | CASCADED {}
| CHANGED {}
| CHARSET {}
| CHECKSUM_SYM {}
@@ -5076,6 +6605,7 @@ keyword:
| DATE_SYM {}
| DAY_SYM {}
| DEALLOCATE_SYM {}
+ | DEFINER_SYM {}
| DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {}
| DIRECTORY_SYM {}
@@ -5113,6 +6643,7 @@ keyword:
| HOSTS_SYM {}
| HOUR_SYM {}
| IDENTIFIED_SYM {}
+ | INVOKER_SYM {}
| IMPORT {}
| INDEXES {}
| ISOLATION {}
@@ -5120,6 +6651,8 @@ keyword:
| INNOBASE_SYM {}
| INSERT_METHOD {}
| RELAY_THREAD {}
+ | LABEL_SYM {}
+ | LANGUAGE_SYM {}
| LAST_SYM {}
| LEAVES {}
| LEVEL_SYM {}
@@ -5147,6 +6680,7 @@ keyword:
| MAX_QUERIES_PER_HOUR {}
| MAX_UPDATES_PER_HOUR {}
| MEDIUM_SYM {}
+ | MERGE_SYM {}
| MICROSECOND_SYM {}
| MINUTE_SYM {}
| MIN_ROWS {}
@@ -5156,6 +6690,7 @@ keyword:
| MULTILINESTRING {}
| MULTIPOINT {}
| MULTIPOLYGON {}
+ | NAME_SYM {}
| NAMES_SYM {}
| NATIONAL_SYM {}
| NCHAR_SYM {}
@@ -5178,6 +6713,7 @@ keyword:
| PREV_SYM {}
| PROCESS {}
| PROCESSLIST_SYM {}
+ | QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
| RAID_0_SYM {}
@@ -5194,6 +6730,7 @@ keyword:
| RESET_SYM {}
| RESOURCES {}
| RESTORE_SYM {}
+ | RETURNS_SYM {}
| ROLLBACK_SYM {}
| ROLLUP_SYM {}
| ROWS_SYM {}
@@ -5202,6 +6739,7 @@ keyword:
| RTREE_SYM {}
| SAVEPOINT_SYM {}
| SECOND_SYM {}
+ | SECURITY_SYM {}
| SERIAL_SYM {}
| SERIALIZABLE_SYM {}
| SESSION_SYM {}
@@ -5225,22 +6763,27 @@ keyword:
| SUPER_SYM {}
| TABLESPACE {}
| TEMPORARY {}
+ | TEMPTABLE_SYM {}
| TEXT_SYM {}
| TRANSACTION_SYM {}
| TRUNCATE_SYM {}
| TIMESTAMP {}
+ | TIMESTAMP_ADD {}
+ | TIMESTAMP_DIFF {}
| TIME_SYM {}
| TYPE_SYM {}
| TYPES_SYM {}
- | UDF_SYM {}
+ | FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNICODE_SYM {}
| UNTIL_SYM {}
| USER {}
| USE_FRM {}
| VARIABLES {}
+ | VIEW_SYM {}
| VALUE_SYM {}
| WARNINGS {}
+ | WEEK_SYM {}
| WORK_SYM {}
| X509_SYM {}
| YEAR_SYM {}
@@ -5292,15 +6835,42 @@ opt_var_ident_type:
;
option_value:
- '@' ident_or_text equal expr
- {
- Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
- }
+ '@' ident_or_text equal expr
+ {
+ Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
+ }
| internal_variable_name equal set_expr_or_default
{
LEX *lex=Lex;
- lex->var_list.push_back(new set_var(lex->option_type, $1.var,
- &$1.base_name, $3));
+
+ if ($1.var)
+ { /* System variable */
+ lex->var_list.push_back(new set_var(lex->option_type, $1.var,
+ &$1.base_name, $3));
+ }
+ else
+ {
+ /* An SP local variable */
+ sp_pcontext *ctx= lex->spcont;
+ sp_pvar_t *spv;
+ sp_instr_set *i;
+ Item *it;
+
+ spv= ctx->find_pvar(&$1.base_name);
+
+ if ($3)
+ it= $3;
+ else if (spv->dflt)
+ it= spv->dflt;
+ else
+ it= new Item_null();
+ i= new sp_instr_set(lex->sphead->instructions(), ctx,
+ spv->offset, it, spv->type);
+ i->tables= lex->query_tables;
+ lex->query_tables= 0;
+ lex->sphead->add_instr(i);
+ spv->isset= TRUE;
+ }
}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
@@ -5358,18 +6928,33 @@ option_value:
internal_variable_name:
ident
{
- sys_var *tmp=find_sys_var($1.str, $1.length);
- if (!tmp)
- YYABORT;
- $$.var= tmp;
- $$.base_name.str=0;
- $$.base_name.length=0;
- /*
- If this is time_zone variable we should open time zone
- describing tables
- */
- if (tmp == &sys_time_zone)
- Lex->time_zone_tables_used= &fake_time_zone_tables_list;
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ /* We have to lookup here since local vars can shadow sysvars */
+ if (!spc || !(spv = spc->find_pvar(&$1)))
+ {
+ /* Not an SP local variable */
+ sys_var *tmp=find_sys_var($1.str, $1.length);
+ if (!tmp)
+ YYABORT;
+ $$.var= tmp;
+ $$.base_name.str=0;
+ $$.base_name.length=0;
+ /*
+ If this is time_zone variable we should open time zone
+ describing tables
+ */
+ if (tmp == &sys_time_zone)
+ Lex->time_zone_tables_used= &fake_time_zone_tables_list;
+ }
+ else
+ {
+ /* An SP local variable */
+ $$.var= NULL;
+ $$.base_name= $1;
+ }
}
| ident '.' ident
{
@@ -5614,8 +7199,10 @@ grant_privilege:
| SUPER_SYM { Lex->grant |= SUPER_ACL;}
| CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;}
| LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; }
- | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL;}
- | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL;}
+ | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; }
+ | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; }
+ | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; }
+ | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; }
;
@@ -6004,3 +7591,41 @@ subselect_end:
lex->current_select = lex->current_select->return_after_parsing();
};
+opt_view_list:
+ /* empty */ {}
+ | '(' view_list ')'
+ ;
+
+view_list:
+ ident
+ {
+ Lex->view_list.push_back((LEX_STRING*)
+ sql_memdup(&$1, sizeof(LEX_STRING)));
+ }
+ | view_list ',' ident
+ {
+ Lex->view_list.push_back((LEX_STRING*)
+ sql_memdup(&$3, sizeof(LEX_STRING)));
+ }
+ ;
+
+or_replace:
+ /* empty */ { Lex->create_view_mode= VIEW_CREATE_NEW; }
+ | OR_SYM REPLACE { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; }
+ ;
+
+algorithm:
+ /* empty */
+ { Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; }
+ | ALGORITHM_SYM EQ MERGE_SYM
+ { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; }
+ | ALGORITHM_SYM EQ TEMPTABLE_SYM
+ { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; }
+ ;
+check_option:
+ /* empty */ {}
+ | WITH CHECK_SYM OPTION {}
+ | WITH CASCADED CHECK_SYM OPTION {}
+ | WITH LOCAL_SYM CHECK_SYM OPTION {}
+ ;
+
diff --git a/sql/structs.h b/sql/structs.h
index c30d85f59cb..9f13ef54ce0 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -158,8 +158,8 @@ typedef struct st_known_date_time_format {
enum SHOW_TYPE
{
SHOW_UNDEF,
- SHOW_LONG, SHOW_LONGLONG, SHOW_INT, SHOW_CHAR, SHOW_CHAR_PTR, SHOW_BOOL,
- SHOW_MY_BOOL, SHOW_OPENTABLES, SHOW_STARTTIME, SHOW_QUESTION,
+ SHOW_LONG, SHOW_LONGLONG, SHOW_INT, SHOW_CHAR, SHOW_CHAR_PTR, SHOW_DOUBLE,
+ SHOW_BOOL, SHOW_MY_BOOL, SHOW_OPENTABLES, SHOW_STARTTIME, SHOW_QUESTION,
SHOW_LONG_CONST, SHOW_INT_CONST, SHOW_HAVE, SHOW_SYS, SHOW_HA_ROWS,
#ifdef HAVE_OPENSSL
SHOW_SSL_CTX_SESS_ACCEPT, SHOW_SSL_CTX_SESS_ACCEPT_GOOD,
diff --git a/sql/table.cc b/sql/table.cc
index 8b018d61e5a..5d0c60718d3 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -20,7 +20,8 @@
#include "mysql_priv.h"
#include <errno.h>
#include <m_ctype.h>
-
+#include "md5.h"
+#include "sql_acl.h"
/* Functions defined in this file */
@@ -57,6 +58,7 @@ static byte* get_field_name(Field **buff,uint *length,
2 Error (see frm_error)
3 Wrong data in .frm file
4 Error (see frm_error)
+ 5 It is new format of .frm file
*/
int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
@@ -81,17 +83,45 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
uchar *null_pos;
uint null_bit, new_frm_ver, field_pack_length;
SQL_CRYPT *crypted=0;
+ MEM_ROOT *old_root;
DBUG_ENTER("openfrm");
DBUG_PRINT("enter",("name: '%s' form: %lx",name,outparam));
+ error=1;
+ disk_buff=NULL;
+ old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+
+ if ((file=my_open(fn_format(index_file, name, "", reg_ext,
+ MY_UNPACK_FILENAME),
+ O_RDONLY | O_SHARE,
+ MYF(0)))
+ < 0)
+ {
+ goto err_w_init;
+ }
+
+ if (my_read(file,(byte*) head,64,MYF(MY_NABP)))
+ {
+ goto err_w_init;
+ }
+
+ if (memcmp(head, "TYPE=", 5) == 0)
+ {
+ // new .frm
+ my_close(file,MYF(MY_WME));
+
+ if (db_stat & NO_ERR_ON_NEW_FRM)
+ DBUG_RETURN(5);
+
+ // caller can't process new .frm
+ error= 4;
+ goto err_w_init;
+ }
+
bzero((char*) outparam,sizeof(*outparam));
outparam->blob_ptr_size=sizeof(char*);
- disk_buff=NULL; record= NULL; keynames=NullS;
outparam->db_stat = db_stat;
- error=1;
-
init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
- MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root);
outparam->real_name=strdup_root(&outparam->mem_root,
@@ -101,19 +131,11 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
if (!outparam->real_name || !outparam->table_name)
goto err_end;
- if ((file=my_open(fn_format(index_file,name,"",reg_ext,MY_UNPACK_FILENAME),
- O_RDONLY | O_SHARE,
- MYF(0)))
- < 0)
- {
- goto err_end; /* purecov: inspected */
- }
error=4;
if (!(outparam->path= strdup_root(&outparam->mem_root,name)))
goto err_not_open;
*fn_ext(outparam->path)='\0'; // Remove extension
- if (my_read(file,(byte*) head,64,MYF(MY_NABP))) goto err_not_open;
if (head[0] != (uchar) 254 || head[1] != 1 ||
(head[2] != FRM_VER && head[2] != FRM_VER+1 && head[2] != FRM_VER+3))
goto err_not_open; /* purecov: inspected */
@@ -718,6 +740,11 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
#endif
DBUG_RETURN (0);
+ err_w_init:
+ /* Avoid problem with uninitialized data */
+ bzero((char*) outparam,sizeof(*outparam));
+ outparam->real_name= (char*)name+dirname_length(name);
+
err_not_open:
x_free((gptr) disk_buff);
if (file > 0)
@@ -1421,6 +1448,227 @@ db_type get_table_type(const char *name)
}
+/*
+ calculate md5 of query
+
+ SYNOPSIS
+ st_table_list::calc_md5()
+ buffer buffer for md5 writing
+*/
+
+void st_table_list::calc_md5(char *buffer)
+{
+ my_MD5_CTX context;
+ uchar digest[16];
+ my_MD5Init(&context);
+ my_MD5Update(&context,(uchar *) query.str, query.length);
+ my_MD5Final(digest, &context);
+ sprintf((char *) buffer,
+ "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[0], digest[1], digest[2], digest[3],
+ digest[4], digest[5], digest[6], digest[7],
+ digest[8], digest[9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);
+}
+
+
+/*
+ set ancestor TABLE for table place holder of VIEW
+
+ SYNOPSIS
+ st_table_list::set_ancestor()
+*/
+
+void st_table_list::set_ancestor()
+{
+ if (ancestor->ancestor)
+ ancestor->set_ancestor();
+ table= ancestor->table;
+ ancestor->table->grant= grant;
+}
+
+
+/*
+ setup fields of placeholder of merged VIEW
+
+ SYNOPSIS
+ st_table_list::setup_ancestor()
+ thd - thread handler
+ conds - condition of this JOIN
+
+ RETURN
+ 0 - OK
+ 1 - error
+
+ TODO: for several substituted table last set up table (or maybe subtree,
+ it depends on future join implementation) will contain all fields of VIEW
+ (to be able call fix_fields() for them. All other will looks like empty
+ (without fields) for name resolving, but substituted expressions will
+ return correct used tables mask.
+*/
+
+bool st_table_list::setup_ancestor(THD *thd, Item **conds)
+{
+ Item **transl;
+ SELECT_LEX *select= &view->select_lex;
+ SELECT_LEX *current_select_save= thd->lex->current_select;
+ Item *item;
+ List_iterator_fast<Item> it(select->item_list);
+ uint i= 0;
+ bool save_set_query_id= thd->set_query_id;
+ bool save_wrapper= thd->lex->select_lex.no_wrap_view_item;
+ bool save_allow_sum_func= thd->allow_sum_func;
+ DBUG_ENTER("st_table_list::setup_ancestor");
+
+ if (ancestor->ancestor &&
+ ancestor->setup_ancestor(thd, conds))
+ DBUG_RETURN(1);
+
+ if (field_translation)
+ {
+ /* prevent look up in SELECTs tree */
+ thd->lex->current_select= &thd->lex->select_lex;
+ thd->set_query_id= 1;
+ /* this view was prepared already on previous PS/SP execution */
+ Item **end= field_translation + select->item_list.elements;
+ for (Item **item= field_translation; item < end; item++)
+ {
+ /* TODO: fix for several tables in VIEW */
+ uint want_privilege= ancestor->table->grant.want_privilege;
+ /* real rights will be checked in VIEW field */
+ ancestor->table->grant.want_privilege= 0;
+ /* aggregate function are allowed */
+ thd->allow_sum_func= 1;
+ if (!(*item)->fixed && (*item)->fix_fields(thd, ancestor, item))
+ goto err;
+ ancestor->table->grant.want_privilege= want_privilege;
+ }
+ goto ok;
+ }
+
+ /* view fields translation table */
+ if (!(transl=
+ (Item**)(thd->current_arena ?
+ thd->current_arena :
+ thd)->alloc(select->item_list.elements * sizeof(Item*))))
+ {
+ DBUG_RETURN(1);
+ }
+
+ /* prevent look up in SELECTs tree */
+ thd->lex->current_select= &thd->lex->select_lex;
+ thd->lex->select_lex.no_wrap_view_item= 1;
+
+ /*
+ Resolve all view items against ancestor table.
+
+ TODO: do it only for real used fields "on demand" to mark really
+ used fields correctly.
+ */
+ thd->set_query_id= 1;
+ while ((item= it++))
+ {
+ /* TODO: fix for several tables in VIEW */
+ uint want_privilege= ancestor->table->grant.want_privilege;
+ /* real rights will be checked in VIEW field */
+ ancestor->table->grant.want_privilege= 0;
+ /* aggregate function are allowed */
+ thd->allow_sum_func= 1;
+ if (!item->fixed && item->fix_fields(thd, ancestor, &item))
+ goto err;
+ ancestor->table->grant.want_privilege= want_privilege;
+ transl[i++]= item;
+ }
+ field_translation= transl;
+ /* TODO: sort this list? Use hash for big number of fields */
+
+ if (where)
+ {
+ Item_arena *arena= thd->current_arena, backup;
+ if (!where->fixed && where->fix_fields(thd, ancestor, &where))
+ goto err;
+
+ if (arena)
+ thd->set_n_backup_item_arena(arena, &backup);
+ if (outer_join)
+ {
+ /*
+ Store WHERE condition to ON expression for outer join, because we
+ can't use WHERE to correctly execute jeft joins on VIEWs and this
+ expression will not be moved to WHERE condition (i.e. will be clean
+ correctly for PS/SP)
+ */
+ on_expr= and_conds(on_expr, where);
+ }
+ else
+ {
+ /*
+ It is conds of JOIN, but it will be stored in st_select_lex::prep_where
+ for next reexecution
+ */
+ *conds= and_conds(*conds, where);
+ }
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ }
+
+ /* full text function moving to current select */
+ if (view->select_lex.ftfunc_list->elements)
+ {
+ Item_func_match *ifm;
+ List_iterator_fast<Item_func_match>
+ li(*(view->select_lex.ftfunc_list));
+ while ((ifm= li++))
+ current_select_save->ftfunc_list->push_front(ifm);
+ }
+
+ok:
+ thd->lex->select_lex.no_wrap_view_item= save_wrapper;
+ thd->lex->current_select= current_select_save;
+ thd->set_query_id= save_set_query_id;
+ thd->allow_sum_func= save_allow_sum_func;
+ DBUG_RETURN(0);
+
+err:
+ /* Hide "Unknown column" error */
+ if (thd->net.last_errno == ER_BAD_FIELD_ERROR)
+ {
+ thd->clear_error();
+ my_error(ER_VIEW_INVALID, MYF(0), view_db.str, view_name.str);
+ }
+ thd->lex->select_lex.no_wrap_view_item= save_wrapper;
+ thd->lex->current_select= current_select_save;
+ thd->set_query_id= save_set_query_id;
+ thd->allow_sum_func= save_allow_sum_func;
+ DBUG_RETURN(1);
+}
+
+
+void Field_iterator_view::set(TABLE_LIST *table)
+{
+ ptr= table->field_translation;
+ array_end= ptr + table->view->select_lex.item_list.elements;
+}
+
+
+const char *Field_iterator_table::name()
+{
+ return (*ptr)->field_name;
+}
+
+
+Item *Field_iterator_table::item(THD *thd)
+{
+ return new Item_field(thd, *ptr);
+}
+
+
+const char *Field_iterator_view::name()
+{
+ return (*ptr)->name;
+}
+
+
/*****************************************************************************
** Instansiate templates
*****************************************************************************/
diff --git a/sql/table.h b/sql/table.h
index f111377bc85..a1db271cc2b 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -20,6 +20,7 @@
class Item; /* Needed by ORDER */
class GRANT_TABLE;
class st_select_lex_unit;
+class st_select_lex;
/* Order clause list element */
@@ -27,6 +28,7 @@ typedef struct st_order {
struct st_order *next;
Item **item; /* Point at item in select fields */
Item *item_ptr; /* Storage for initial item */
+ Item **item_copy; /* For SPs; the original item ptr */
bool asc; /* true if ascending */
bool free_me; /* true if item isn't shared */
bool in_field_list; /* true if in select field list */
@@ -45,6 +47,13 @@ typedef struct st_grant_info
enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2};
+enum frm_type_enum
+{
+ FRMTYPE_ERROR= 0,
+ FRMTYPE_TABLE,
+ FRMTYPE_VIEW
+};
+
typedef struct st_filesort_info
{
IO_CACHE *io_cache; /* If sorted through filebyte */
@@ -69,7 +78,7 @@ struct st_table {
/* hash of field names (contains pointers to elements of field array) */
HASH name_hash;
byte *record[2]; /* Pointer to records */
- byte *default_values; /* Default values for INSERT */
+ byte *default_values; /* Default values for INSERT */
byte *insert_values; /* used by INSERT ... UPDATE */
uint fields; /* field count */
uint reclength; /* Recordlength */
@@ -157,12 +166,8 @@ struct st_table {
key_part_map const_key_parts[MAX_KEY];
ulong query_id;
uchar frm_version;
-
- union /* Temporary variables */
- {
- uint temp_pool_slot; /* Used by intern temp tables */
- struct st_table_list *pos_in_table_list;
- };
+ uint temp_pool_slot; /* Used by intern temp tables */
+ struct st_table_list *pos_in_table_list;/* Element referring to this table */
/* number of select if it is derived table */
uint derived_select_number;
THD *in_use; /* Which thread uses this */
@@ -173,32 +178,139 @@ struct st_table {
#define JOIN_TYPE_LEFT 1
#define JOIN_TYPE_RIGHT 2
+#define VIEW_ALGORITHM_UNDEFINED 0
+#define VIEW_ALGORITHM_TMPTABLE 1
+#define VIEW_ALGORITHM_MERGE 2
+
+struct st_lex;
+
typedef struct st_table_list
{
- struct st_table_list *next;
+ /* link in a local table list (used by SQL_LIST) */
+ struct st_table_list *next_local;
+ /* link in a global list of all queries tables */
+ struct st_table_list *next_global, **prev_global;
char *db, *alias, *real_name;
- char *option; /* Used by cache index */
+ char *option; /* Used by cache index */
Item *on_expr; /* Used with outer join */
struct st_table_list *natural_join; /* natural join on this table*/
/* ... join ... USE INDEX ... IGNORE INDEX */
- List<String> *use_index, *ignore_index;
- TABLE *table; /* opened table */
- st_table_list *table_list; /* pointer to node of list of all tables */
- class st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
- GRANT_INFO grant;
+ List<String> *use_index, *ignore_index;
+ TABLE *table; /* opened table */
+ /*
+ Reference from aux_tables to local list entry of main select of
+ multi-delete statement:
+ delete t1 from t2,t1 where t1.a<'B' and t2.b=t1.b;
+ here it will be reference of first occurrence of t1 to second (as you
+ can see this lists can't be merged)
+ */
+ st_table_list *correspondent_table;
+ st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
+ /* link to select_lex where this table was used */
+ st_select_lex *select_lex;
+ st_lex *view; /* link on VIEW lex for merging */
+ Item **field_translation; /* array of VIEW fields */
+ /* ancestor of this table (VIEW merge algorithm) */
+ st_table_list *ancestor;
+ /* most upper view this table belongs to */
+ st_table_list *belong_to_view;
+ Item *where; /* VIEW WHERE clause condition */
+ LEX_STRING query; /* text of (CRETE/SELECT) statement */
+ LEX_STRING md5; /* md5 of query tesxt */
+ LEX_STRING source; /* source of CREATE VIEW */
+ LEX_STRING view_db; /* save view database */
+ LEX_STRING view_name; /* save view name */
+ LEX_STRING timestamp; /* GMT time stamp of last operation */
+ ulonglong file_version; /* version of file's field set */
+ ulonglong updatable_view; /* VIEW can be updated */
+ ulonglong revision; /* revision control number */
+ ulonglong algorithm; /* 0 any, 1 tmp tables , 2 merging */
+ uint effective_algorithm; /* which algorithm was really used */
+ GRANT_INFO grant;
thr_lock_type lock_type;
uint outer_join; /* Which join type */
uint shared; /* Used in multi-upd */
uint32 db_length, real_name_length;
+ bool updatable; /* VIEW/TABLE can be updated now */
bool straight; /* optimize with prev table */
bool updating; /* for replicate-do/ignore table */
- bool force_index; /* Prefer index over table scan */
- bool ignore_leaves; /* Preload only non-leaf nodes */
+ bool force_index; /* prefer index over table scan */
+ bool ignore_leaves; /* preload only non-leaf nodes */
+ table_map dep_tables; /* tables the table depends on */
+ table_map on_expr_dep_tables; /* tables on expression depends on */
+ struct st_nested_join *nested_join; /* if the element is a nested join */
+ st_table_list *embedding; /* nested join containing the table */
+ List<struct st_table_list> *join_list;/* join list the table belongs to */
bool cacheable_table; /* stop PS caching */
- /* used in multi-upd privelege check */
- bool table_in_update_from_clause;
+ /* used in multi-upd/views privelege check */
+ bool table_in_first_from_clause;
+ bool skip_temporary; /* this table shouldn't be temporary */
+ bool setup_is_done; /* setup_tables() is done */
+ /* do view contain auto_increment field */
+ bool contain_auto_increment;
+ /* FRMTYPE_ERROR if any type is acceptable */
+ enum frm_type_enum required_type;
+ char timestamp_buffer[20]; /* buffer for timestamp (19+1) */
+
+ void calc_md5(char *buffer);
+ void set_ancestor();
+ bool setup_ancestor(THD *thd, Item **conds);
+ bool placeholder() {return derived || view; }
+ void print(THD *thd, String *str);
} TABLE_LIST;
+class Item;
+
+class Field_iterator: public Sql_alloc
+{
+public:
+ virtual ~Field_iterator() {}
+ virtual void set(TABLE_LIST *)= 0;
+ virtual void next()= 0;
+ virtual bool end_of_fields()= 0; /* Return 1 at end of list */
+ virtual const char *name()= 0;
+ virtual Item *item(THD *)= 0;
+ virtual Field *field()= 0;
+};
+
+
+class Field_iterator_table: public Field_iterator
+{
+ Field **ptr;
+public:
+ Field_iterator_table() :ptr(0) {}
+ void set(TABLE_LIST *table) { ptr= table->table->field; }
+ void set_table(TABLE *table) { ptr= table->field; }
+ void next() { ptr++; }
+ bool end_of_fields() { return *ptr == 0; }
+ const char *name();
+ Item *item(THD *thd);
+ Field *field() { return *ptr; }
+};
+
+
+class Field_iterator_view: public Field_iterator
+{
+ Item **ptr, **array_end;
+public:
+ Field_iterator_view() :ptr(0), array_end(0) {}
+ void set(TABLE_LIST *table);
+ void next() { ptr++; }
+ bool end_of_fields() { return ptr == array_end; }
+ const char *name();
+ Item *item(THD *thd) { return *ptr; }
+ Field *field() { return 0; }
+};
+
+typedef struct st_nested_join
+{
+ List<TABLE_LIST> join_list; /* list of elements in the nested join */
+ table_map used_tables; /* bitmap of tables in the nested join */
+ table_map not_null_tables; /* tables that rejects nulls */
+ struct st_join_table *first_nested;/* the first nested table in the plan */
+ uint counter; /* to count tables in the nested join */
+} NESTED_JOIN;
+
typedef struct st_changed_table_list
{
struct st_changed_table_list *next;
@@ -206,8 +318,7 @@ typedef struct st_changed_table_list
uint32 key_length;
} CHANGED_TABLE_LIST;
-typedef struct st_open_table_list
-{
+typedef struct st_open_table_list{
struct st_open_table_list *next;
char *db,*table;
uint32 in_use,locked;
diff --git a/sql/udf_example.cc b/sql/udf_example.cc
index 7e2ee9113b2..f0f33ed6fd7 100644
--- a/sql/udf_example.cc
+++ b/sql/udf_example.cc
@@ -56,7 +56,9 @@
**
** Function 'myfunc_int' returns summary length of all its arguments.
**
-** Function 'sequence' returns an sequence starting from a certain number
+** Function 'sequence' returns an sequence starting from a certain number.
+**
+** Function 'myfunc_argument_name' returns name of argument.
**
** On the end is a couple of functions that converts hostnames to ip and
** vice versa.
@@ -82,6 +84,7 @@
** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
+** CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so";
**
** After this the functions will work exactly like native MySQL functions.
** Functions should be created only once.
@@ -94,6 +97,7 @@
** DROP FUNCTION lookup;
** DROP FUNCTION reverse_lookup;
** DROP FUNCTION avgcost;
+** DROP FUNCTION myfunc_argument_name;
**
** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All
** Active function will be reloaded on every restart of server
@@ -984,4 +988,43 @@ avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error )
return data->totalprice/double(data->totalquantity);
}
+extern "C" {
+my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message);
+char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *null_value,
+ char *error);
+}
+
+my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message)
+{
+ if (args->arg_count != 1)
+ {
+ strmov(message,"myfunc_argument_name_init accepts only one argument");
+ return 1;
+ }
+ initid->max_length= args->attribute_lengths[0];
+ initid->maybe_null= 1;
+ initid->const_item= 1;
+ return 0;
+}
+
+char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *null_value,
+ char *error)
+{
+ if (!args->attributes[0])
+ {
+ null_value= 0;
+ return 0;
+ }
+ (*length)--; // space for ending \0 (for debugging purposes)
+ if (*length > args->attribute_lengths[0])
+ *length= args->attribute_lengths[0];
+ memcpy(result, args->attributes[0], *length);
+ result[*length]= 0;
+ return result;
+}
+
#endif /* HAVE_DLOPEN */
diff --git a/sql/uniques.cc b/sql/uniques.cc
index d060965aa66..b08727705e4 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -63,12 +63,255 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
comp_func_fixed_arg);
/* If the following fail's the next add will also fail */
my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16);
+ /*
+ If you change the following, change it in get_max_elements function, too.
+ */
max_elements= max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+size);
open_cached_file(&file, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE,
MYF(MY_WME));
}
+/*
+ Calculate log2(n!)
+
+ NOTES
+ Stirling's approximate formula is used:
+
+ n! ~= sqrt(2*M_PI*n) * (n/M_E)^n
+
+ Derivation of formula used for calculations is as follows:
+
+ log2(n!) = log(n!)/log(2) = log(sqrt(2*M_PI*n)*(n/M_E)^n) / log(2) =
+
+ = (log(2*M_PI*n)/2 + n*log(n/M_E)) / log(2).
+*/
+
+inline double log2_n_fact(double x)
+{
+ return (log(2*M_PI*x)/2 + x*log(x/M_E)) / M_LN2;
+}
+
+
+/*
+ Calculate cost of merge_buffers function call for given sequence of
+ input stream lengths and store the number of rows in result stream in *last.
+
+ SYNOPSIS
+ get_merge_buffers_cost()
+ buff_elems Array of #s of elements in buffers
+ elem_size Size of element stored in buffer
+ first Pointer to first merged element size
+ last Pointer to last merged element size
+
+ RETURN
+ Cost of merge_buffers operation in disk seeks.
+
+ NOTES
+ It is assumed that no rows are eliminated during merge.
+ The cost is calculated as
+
+ cost(read_and_write) + cost(merge_comparisons).
+
+ All bytes in the sequences is read and written back during merge so cost
+ of disk io is 2*elem_size*total_buf_elems/IO_SIZE (2 is for read + write)
+
+ For comparisons cost calculations we assume that all merged sequences have
+ the same length, so each of total_buf_size elements will be added to a sort
+ heap with (n_buffers-1) elements. This gives the comparison cost:
+
+ total_buf_elems* log2(n_buffers) / TIME_FOR_COMPARE_ROWID;
+*/
+
+static double get_merge_buffers_cost(uint *buff_elems, uint elem_size,
+ uint *first, uint *last)
+{
+ uint total_buf_elems= 0;
+ for (uint *pbuf= first; pbuf <= last; pbuf++)
+ total_buf_elems+= *pbuf;
+ *last= total_buf_elems;
+
+ int n_buffers= last - first + 1;
+
+ /* Using log2(n)=log(n)/log(2) formula */
+ return 2*((double)total_buf_elems*elem_size) / IO_SIZE +
+ total_buf_elems*log((double) n_buffers) / (TIME_FOR_COMPARE_ROWID * M_LN2);
+}
+
+
+/*
+ Calculate cost of merging buffers into one in Unique::get, i.e. calculate
+ how long (in terms of disk seeks) the two calls
+ merge_many_buffs(...);
+ merge_buffers(...);
+ will take.
+
+ SYNOPSIS
+ get_merge_many_buffs_cost()
+ buffer buffer space for temporary data, at least
+ Unique::get_cost_calc_buff_size bytes
+ maxbuffer # of full buffers
+ max_n_elems # of elements in first maxbuffer buffers
+ last_n_elems # of elements in last buffer
+ elem_size size of buffer element
+
+ NOTES
+ maxbuffer+1 buffers are merged, where first maxbuffer buffers contain
+ max_n_elems elements each and last buffer contains last_n_elems elements.
+
+ The current implementation does a dumb simulation of merge_many_buffs
+ function actions.
+
+ RETURN
+ Cost of merge in disk seeks.
+*/
+
+static double get_merge_many_buffs_cost(uint *buffer,
+ uint maxbuffer, uint max_n_elems,
+ uint last_n_elems, int elem_size)
+{
+ register int i;
+ double total_cost= 0.0;
+ uint *buff_elems= buffer; /* #s of elements in each of merged sequences */
+
+ /*
+ Set initial state: first maxbuffer sequences contain max_n_elems elements
+ each, last sequence contains last_n_elems elements.
+ */
+ for(i = 0; i < (int)maxbuffer; i++)
+ buff_elems[i]= max_n_elems;
+ buff_elems[maxbuffer]= last_n_elems;
+
+ /*
+ Do it exactly as merge_many_buff function does, calling
+ get_merge_buffers_cost to get cost of merge_buffers.
+ */
+ if (maxbuffer >= MERGEBUFF2)
+ {
+ while (maxbuffer >= MERGEBUFF2)
+ {
+ uint lastbuff= 0;
+ for (i = 0; i <= (int) maxbuffer - MERGEBUFF*3/2; i += MERGEBUFF)
+ {
+ total_cost+=get_merge_buffers_cost(buff_elems, elem_size,
+ buff_elems + i,
+ buff_elems + i + MERGEBUFF-1);
+ lastbuff++;
+ }
+ total_cost+=get_merge_buffers_cost(buff_elems, elem_size,
+ buff_elems + i,
+ buff_elems + maxbuffer);
+ maxbuffer= lastbuff;
+ }
+ }
+
+ /* Simulate final merge_buff call. */
+ total_cost += get_merge_buffers_cost(buff_elems, elem_size,
+ buff_elems, buff_elems + maxbuffer);
+ return total_cost;
+}
+
+
+/*
+ Calculate cost of using Unique for processing nkeys elements of size
+ key_size using max_in_memory_size memory.
+
+ SYNOPSIS
+ Unique::get_use_cost()
+ buffer space for temporary data, use Unique::get_cost_calc_buff_size
+ to get # bytes needed.
+ nkeys #of elements in Unique
+ key_size size of each elements in bytes
+ max_in_memory_size amount of memory Unique will be allowed to use
+
+ RETURN
+ Cost in disk seeks.
+
+ NOTES
+ cost(using_unqiue) =
+ cost(create_trees) + (see #1)
+ cost(merge) + (see #2)
+ cost(read_result) (see #3)
+
+ 1. Cost of trees creation
+ For each Unique::put operation there will be 2*log2(n+1) elements
+ comparisons, where n runs from 1 tree_size (we assume that all added
+ elements are different). Together this gives:
+
+ n_compares = 2*(log2(2) + log2(3) + ... + log2(N+1)) = 2*log2((N+1)!)
+
+ then cost(tree_creation) = n_compares*ROWID_COMPARE_COST;
+
+ Total cost of creating trees:
+ (n_trees - 1)*max_size_tree_cost + non_max_size_tree_cost.
+
+ Approximate value of log2(N!) is calculated by log2_n_fact function.
+
+ 2. Cost of merging.
+ If only one tree is created by Unique no merging will be necessary.
+ Otherwise, we model execution of merge_many_buff function and count
+ #of merges. (The reason behind this is that number of buffers is small,
+ while size of buffers is big and we don't want to loose precision with
+ O(x)-style formula)
+
+ 3. If only one tree is created by Unique no disk io will happen.
+ Otherwise, ceil(key_len*n_keys) disk seeks are necessary. We assume
+ these will be random seeks.
+*/
+
+double Unique::get_use_cost(uint *buffer, uint nkeys, uint key_size,
+ ulong max_in_memory_size)
+{
+ ulong max_elements_in_tree;
+ ulong last_tree_elems;
+ int n_full_trees; /* number of trees in unique - 1 */
+ double result;
+
+ max_elements_in_tree=
+ max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+key_size);
+
+ n_full_trees= nkeys / max_elements_in_tree;
+ last_tree_elems= nkeys % max_elements_in_tree;
+
+ /* Calculate cost of creating trees */
+ result= 2*log2_n_fact(last_tree_elems + 1.0);
+ if (n_full_trees)
+ result+= n_full_trees * log2_n_fact(max_elements_in_tree + 1.0);
+ result /= TIME_FOR_COMPARE_ROWID;
+
+ DBUG_PRINT("info",("unique trees sizes: %u=%u*%lu + %lu", nkeys,
+ n_full_trees, n_full_trees?max_elements_in_tree:0,
+ last_tree_elems));
+
+ if (!n_full_trees)
+ return result;
+
+ /*
+ There is more then one tree and merging is necessary.
+ First, add cost of writing all trees to disk, assuming that all disk
+ writes are sequential.
+ */
+ result += DISK_SEEK_BASE_COST * n_full_trees *
+ ceil(((double) key_size)*max_elements_in_tree / IO_SIZE);
+ result += DISK_SEEK_BASE_COST * ceil(((double) key_size)*last_tree_elems / IO_SIZE);
+
+ /* Cost of merge */
+ double merge_cost= get_merge_many_buffs_cost(buffer, n_full_trees,
+ max_elements_in_tree,
+ last_tree_elems, key_size);
+ if (merge_cost < 0.0)
+ return merge_cost;
+
+ result += merge_cost;
+ /*
+ Add cost of reading the resulting sequence, assuming there were no
+ duplicate elements.
+ */
+ result += ceil((double)key_size*nkeys/IO_SIZE);
+
+ return result;
+}
+
Unique::~Unique()
{
close_cached_file(&file);
@@ -84,6 +327,7 @@ bool Unique::flush()
elements+= tree.elements_in_tree;
file_ptr.count=tree.elements_in_tree;
file_ptr.file_pos=my_b_tell(&file);
+
if (tree_walk(&tree, (tree_walk_action) unique_write_to_file,
(void*) this, left_root_right) ||
insert_dynamic(&file_ptrs, (gptr) &file_ptr))
@@ -94,6 +338,237 @@ bool Unique::flush()
/*
+ Clear the tree and the file.
+ You must call reset() if you want to reuse Unique after walk().
+*/
+
+void
+Unique::reset()
+{
+ reset_tree(&tree);
+ /*
+ If elements != 0, some trees were stored in the file (see how
+ flush() works). Note, that we can not count on my_b_tell(&file) == 0
+ here, because it can return 0 right after walk(), and walk() does not
+ reset any Unique member.
+ */
+ if (elements)
+ {
+ reset_dynamic(&file_ptrs);
+ reinit_io_cache(&file, WRITE_CACHE, 0L, 0, 1);
+ }
+ elements= 0;
+}
+
+/*
+ The comparison function, passed to queue_init() in merge_walk() must
+ use comparison function of Uniques::tree, but compare members of struct
+ BUFFPEK.
+*/
+
+struct BUFFPEK_COMPARE_CONTEXT
+{
+ qsort_cmp2 key_compare;
+ void *key_compare_arg;
+};
+
+C_MODE_START
+
+static int buffpek_compare(void *arg, byte *key_ptr1, byte *key_ptr2)
+{
+ BUFFPEK_COMPARE_CONTEXT *ctx= (BUFFPEK_COMPARE_CONTEXT *) arg;
+ return ctx->key_compare(ctx->key_compare_arg,
+ *((byte **) key_ptr1), *((byte **)key_ptr2));
+}
+
+C_MODE_END
+
+
+/*
+ DESCRIPTION
+ Function is very similar to merge_buffers, but instead of writing sorted
+ unique keys to the output file, it invokes walk_action for each key.
+ This saves I/O if you need to pass through all unique keys only once.
+ SYNOPSIS
+ merge_walk()
+ All params are 'IN' (but see comment for begin, end):
+ merge_buffer buffer to perform cached piece-by-piece loading
+ of trees; initially the buffer is empty
+ merge_buffer_size size of merge_buffer. Must be aligned with
+ key_length
+ key_length size of tree element; key_length * (end - begin)
+ must be less or equal than merge_buffer_size.
+ begin pointer to BUFFPEK struct for the first tree.
+ end pointer to BUFFPEK struct for the last tree;
+ end > begin and [begin, end) form a consecutive
+ range. BUFFPEKs structs in that range are used and
+ overwritten in merge_walk().
+ walk_action element visitor. Action is called for each unique
+ key.
+ walk_action_arg argument to walk action. Passed to it on each call.
+ compare elements comparison function
+ compare_arg comparison function argument
+ file file with all trees dumped. Trees in the file
+ must contain sorted unique values. Cache must be
+ initialized in read mode.
+ RETURN VALUE
+ 0 ok
+ <> 0 error
+*/
+
+static bool merge_walk(uchar *merge_buffer, uint merge_buffer_size,
+ uint key_length, BUFFPEK *begin, BUFFPEK *end,
+ tree_walk_action walk_action, void *walk_action_arg,
+ qsort_cmp2 compare, void *compare_arg,
+ IO_CACHE *file)
+{
+ BUFFPEK_COMPARE_CONTEXT compare_context = { compare, compare_arg };
+ QUEUE queue;
+ if (end <= begin ||
+ merge_buffer_size < key_length * (end - begin + 1) ||
+ init_queue(&queue, end - begin, offsetof(BUFFPEK, key), 0,
+ buffpek_compare, &compare_context))
+ return 1;
+ /* we need space for one key when a piece of merge buffer is re-read */
+ merge_buffer_size-= key_length;
+ uchar *save_key_buff= merge_buffer + merge_buffer_size;
+ uint max_key_count_per_piece= merge_buffer_size/(end-begin)/key_length;
+ /* if piece_size is aligned reuse_freed_buffer will always hit */
+ uint piece_size= max_key_count_per_piece * key_length;
+ uint bytes_read; /* to hold return value of read_to_buffer */
+ BUFFPEK *top;
+ int res= 1;
+ /*
+ Invariant: queue must contain top element from each tree, until a tree
+ is not completely walked through.
+ Here we're forcing the invariant, inserting one element from each tree
+ to the queue.
+ */
+ for (top= begin; top != end; ++top)
+ {
+ top->base= merge_buffer + (top - begin) * piece_size;
+ top->max_keys= max_key_count_per_piece;
+ bytes_read= read_to_buffer(file, top, key_length);
+ if (bytes_read == (uint) (-1))
+ goto end;
+ DBUG_ASSERT(bytes_read);
+ queue_insert(&queue, (byte *) top);
+ }
+ top= (BUFFPEK *) queue_top(&queue);
+ while (queue.elements > 1)
+ {
+ /*
+ Every iteration one element is removed from the queue, and one is
+ inserted by the rules of the invariant. If two adjacent elements on
+ the top of the queue are not equal, biggest one is unique, because all
+ elements in each tree are unique. Action is applied only to unique
+ elements.
+ */
+ void *old_key= top->key;
+ /*
+ read next key from the cache or from the file and push it to the
+ queue; this gives new top.
+ */
+ top->key+= key_length;
+ if (--top->mem_count)
+ queue_replaced(&queue);
+ else /* next piece should be read */
+ {
+ /* save old_key not to overwrite it in read_to_buffer */
+ memcpy(save_key_buff, old_key, key_length);
+ old_key= save_key_buff;
+ bytes_read= read_to_buffer(file, top, key_length);
+ if (bytes_read == (uint) (-1))
+ goto end;
+ else if (bytes_read > 0) /* top->key, top->mem_count are reset */
+ queue_replaced(&queue); /* in read_to_buffer */
+ else
+ {
+ /*
+ Tree for old 'top' element is empty: remove it from the queue and
+ give all its memory to the nearest tree.
+ */
+ queue_remove(&queue, 0);
+ reuse_freed_buff(&queue, top, key_length);
+ }
+ }
+ top= (BUFFPEK *) queue_top(&queue);
+ /* new top has been obtained; if old top is unique, apply the action */
+ if (compare(compare_arg, old_key, top->key))
+ {
+ if (walk_action(old_key, 1, walk_action_arg))
+ goto end;
+ }
+ }
+ /*
+ Applying walk_action to the tail of the last tree: this is safe because
+ either we had only one tree in the beginning, either we work with the
+ last tree in the queue.
+ */
+ do
+ {
+ do
+ {
+ if (walk_action(top->key, 1, walk_action_arg))
+ goto end;
+ top->key+= key_length;
+ }
+ while (--top->mem_count);
+ bytes_read= read_to_buffer(file, top, key_length);
+ if (bytes_read == (uint) (-1))
+ goto end;
+ }
+ while (bytes_read);
+ res= 0;
+end:
+ delete_queue(&queue);
+ return res;
+}
+
+
+/*
+ DESCRIPTION
+ Walks consecutively through all unique elements:
+ if all elements are in memory, then it simply invokes 'tree_walk', else
+ all flushed trees are loaded to memory piece-by-piece, pieces are
+ sorted, and action is called for each unique value.
+ Note: so as merging resets file_ptrs state, this method can change
+ internal Unique state to undefined: if you want to reuse Unique after
+ walk() you must call reset() first!
+ SYNOPSIS
+ Unique:walk()
+ All params are 'IN':
+ action function-visitor, typed in include/my_tree.h
+ function is called for each unique element
+ arg argument for visitor, which is passed to it on each call
+ RETURN VALUE
+ 0 OK
+ <> 0 error
+ */
+
+bool Unique::walk(tree_walk_action action, void *walk_action_arg)
+{
+ if (elements == 0) /* the whole tree is in memory */
+ return tree_walk(&tree, action, walk_action_arg, left_root_right);
+
+ /* flush current tree to the file to have some memory for merge buffer */
+ if (flush())
+ return 1;
+ if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0))
+ return 1;
+ uchar *merge_buffer= (uchar *) my_malloc(max_in_memory_size, MYF(0));
+ if (merge_buffer == 0)
+ return 1;
+ int res= merge_walk(merge_buffer, max_in_memory_size, size,
+ (BUFFPEK *) file_ptrs.buffer,
+ (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements,
+ action, walk_action_arg,
+ tree.compare, tree.custom_arg, &file);
+ x_free(merge_buffer);
+ return res;
+}
+
+/*
Modify the TABLE element so that when one calls init_records()
the rows will be read in priority order.
*/
@@ -114,7 +589,7 @@ bool Unique::get(TABLE *table)
return 0;
}
}
- /* Not enough memory; Save the result to file */
+ /* Not enough memory; Save the result to file && free memory used by tree */
if (flush())
return 1;
diff --git a/sql/unireg.h b/sql/unireg.h
index 4ab2ba26b15..2879e30d861 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -139,6 +139,7 @@
#define DONT_GIVE_ERROR 256 /* Don't do frm_error on openfrm */
#define READ_SCREENS 1024 /* Read screens, info and helpfile */
#define DELAYED_OPEN 4096 /* Open table later */
+#define NO_ERR_ON_NEW_FRM 8192 /* stop error sending on new format */
#define SC_INFO_LENGTH 4 /* Form format constant */
#define TE_INFO_LENGTH 3
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5d0e4627b69..aafa16ec351 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -33,8 +33,10 @@ noinst_PROGRAMS = insert_test select_test thread_test client_test
#
INCLUDES = -I$(top_srcdir)/include $(openssl_includes)
LIBS = @CLIENT_LIBS@
-LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysql/libmysqlclient.la
-client_test_LDADD= $(LDADD) $(CXXLDFLAGS)
+LDADD = @CLIENT_EXTRA_LDFLAGS@ \
+ $(top_builddir)/libmysql/libmysqlclient.la
+client_test_LDADD= $(LDADD) $(CXXLDFLAGS) \
+ $(top_builddir)/mysys/libmysys.a
client_test_SOURCES= client_test.c
insert_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
select_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
diff --git a/tests/client_test.c b/tests/client_test.c
index b124fba3f59..73295050ceb 100644
--- a/tests/client_test.c
+++ b/tests/client_test.c
@@ -30,6 +30,7 @@
#define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */
+#define MAX_KEY 64
/* set default options */
static char *opt_db= 0;
@@ -693,6 +694,206 @@ static void client_use_result()
}
+/*
+ Accepts arbitrary number of queries and runs them against the database.
+ Used to fill tables for each test.
+*/
+
+void fill_tables(const char **query_list, unsigned query_count)
+{
+ int rc;
+ const char **query;
+ for (query= query_list; query < query_list + query_count;
+ ++query)
+ {
+ rc= mysql_query(mysql, *query);
+ myquery(rc);
+ }
+}
+
+/*
+ All state of fetch from one statement: statement handle, out buffers,
+ fetch position.
+ See fetch_n for for the only use case.
+*/
+
+enum { MAX_COLUMN_LENGTH= 255 };
+
+typedef struct st_stmt_fetch
+{
+ const char *query;
+ unsigned stmt_no;
+ MYSQL_STMT *handle;
+ my_bool is_open;
+ MYSQL_BIND *bind_array;
+ char **out_data;
+ unsigned long *out_data_length;
+ unsigned column_count;
+ unsigned row_count;
+} Stmt_fetch;
+
+
+/*
+ Create statement handle, prepare it with statement, execute and allocate
+ fetch buffers.
+*/
+
+void stmt_fetch_init(Stmt_fetch *fetch, unsigned stmt_no_arg,
+ const char *query_arg)
+{
+ unsigned long type= CURSOR_TYPE_READ_ONLY;
+ int rc;
+ unsigned i;
+ MYSQL_RES *metadata;
+
+ /* Save query and statement number for error messages */
+ fetch->stmt_no= stmt_no_arg;
+ fetch->query= query_arg;
+
+ fetch->handle= mysql_stmt_init(mysql);
+
+ rc= mysql_stmt_prepare(fetch->handle, fetch->query, strlen(fetch->query));
+ check_execute(fetch->handle, rc);
+
+ /*
+ The attribute is sent to server on execute and asks to open read-only
+ for result set
+ */
+ mysql_stmt_attr_set(fetch->handle, STMT_ATTR_CURSOR_TYPE,
+ (const void*) &type);
+
+ rc= mysql_stmt_execute(fetch->handle);
+ check_execute(fetch->handle, rc);
+
+ /* Find out total number of columns in result set */
+ metadata= mysql_stmt_result_metadata(fetch->handle);
+ fetch->column_count= mysql_num_fields(metadata);
+ mysql_free_result(metadata);
+
+ /*
+ Now allocate bind handles and buffers for output data:
+ calloc memory to reduce number of MYSQL_BIND members we need to
+ set up.
+ */
+
+ fetch->bind_array= (MYSQL_BIND *) calloc(1, sizeof(MYSQL_BIND) *
+ fetch->column_count);
+ fetch->out_data= (char**) calloc(1, sizeof(char*) * fetch->column_count);
+ fetch->out_data_length= (ulong*) calloc(1, sizeof(ulong) *
+ fetch->column_count);
+ for (i= 0; i < fetch->column_count; ++i)
+ {
+ fetch->out_data[i]= (char*) calloc(1, MAX_COLUMN_LENGTH);
+ fetch->bind_array[i].buffer_type= MYSQL_TYPE_STRING;
+ fetch->bind_array[i].buffer= fetch->out_data[i];
+ fetch->bind_array[i].buffer_length= MAX_COLUMN_LENGTH;
+ fetch->bind_array[i].length= fetch->out_data_length + i;
+ }
+
+ mysql_stmt_bind_result(fetch->handle, fetch->bind_array);
+
+ fetch->row_count= 0;
+ fetch->is_open= TRUE;
+
+ /* Ready for reading rows */
+}
+
+
+/* Fetch and print one row from cursor */
+
+int stmt_fetch_fetch_row(Stmt_fetch *fetch)
+{
+ int rc;
+ unsigned i;
+
+ if ((rc= mysql_stmt_fetch(fetch->handle)) == 0)
+ {
+ ++fetch->row_count;
+ printf("Stmt %d fetched row %d:\n", fetch->stmt_no, fetch->row_count);
+ for (i= 0; i < fetch->column_count; ++i)
+ {
+ fetch->out_data[i][fetch->out_data_length[i]]= '\0';
+ printf("column %d: %s\n", i+1, fetch->out_data[i]);
+ }
+ }
+ else
+ fetch->is_open= FALSE;
+ return rc;
+}
+
+
+void stmt_fetch_close(Stmt_fetch *fetch)
+{
+ unsigned i;
+
+ for (i= 0; i < fetch->column_count; ++i)
+ free(fetch->out_data[i]);
+ free(fetch->out_data);
+ free(fetch->out_data_length);
+ free(fetch->bind_array);
+ mysql_stmt_close(fetch->handle);
+}
+
+/*
+ For given array of queries, open query_count cursors and fetch
+ from them in simultaneous manner.
+ In case there was an error in one of the cursors, continue
+ reading from the rest.
+*/
+
+bool fetch_n(const char **query_list, unsigned query_count)
+{
+ unsigned open_statements= query_count;
+ int rc, error_count= 0;
+ Stmt_fetch *fetch_array= (Stmt_fetch*) calloc(1, sizeof(Stmt_fetch) *
+ query_count);
+ Stmt_fetch *fetch;
+
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ {
+ /* Init will exit(1) in case of error */
+ stmt_fetch_init(fetch, fetch - fetch_array,
+ query_list[fetch - fetch_array]);
+ }
+
+ while (open_statements)
+ {
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ {
+ if (fetch->is_open && (rc= stmt_fetch_fetch_row(fetch)))
+ {
+ --open_statements;
+ /*
+ We try to fetch from the rest of the statements in case of
+ error
+ */
+ if (rc != MYSQL_NO_DATA)
+ {
+ fprintf(stderr,
+ "Got error reading rows from statement %d,\n"
+ "query is: %s,\n"
+ "error message: %s", fetch - fetch_array, fetch->query,
+ mysql_stmt_error(fetch->handle));
+ ++error_count;
+ }
+ }
+ }
+ }
+ if (error_count)
+ fprintf(stderr, "Fetch FAILED");
+ else
+ {
+ unsigned total_row_count= 0;
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ total_row_count+= fetch->row_count;
+ printf("Success, total rows fetched: %d\n", total_row_count);
+ }
+ for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch)
+ stmt_fetch_close(fetch);
+ free(fetch_array);
+ return error_count != 0;
+}
+
/* Separate thread query to test some cases */
static my_bool thread_query(char *query)
@@ -5307,6 +5508,7 @@ static void test_subselect()
MYSQL_STMT *stmt;
int rc, id;
MYSQL_BIND bind[1];
+ DBUG_ENTER("test_subselect");
myheader("test_subselect");
@@ -5408,6 +5610,7 @@ static void test_subselect()
assert(rc == MYSQL_NO_DATA);
mysql_stmt_close(stmt);
+ DBUG_VOID_RETURN;
}
@@ -6818,13 +7021,13 @@ static void test_explain_bug()
"", "", "", 10, 0);
verify_prepare_field(result, 4, "possible_keys", "", MYSQL_TYPE_VAR_STRING,
- "", "", "", NAME_LEN*64, 0);
+ "", "", "", NAME_LEN*MAX_KEY, 0);
verify_prepare_field(result, 5, "key", "", MYSQL_TYPE_VAR_STRING,
"", "", "", NAME_LEN, 0);
- verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_LONGLONG,
- "", "", "", 3, 0);
+ verify_prepare_field(result, 6, "key_len", "", MYSQL_TYPE_VAR_STRING,
+ "", "", "", NAME_LEN*MAX_KEY, 0);
verify_prepare_field(result, 7, "ref", "", MYSQL_TYPE_VAR_STRING,
"", "", "", NAME_LEN*16, 0);
@@ -9537,12 +9740,12 @@ static void test_union_param()
/* bind parameters */
bind[0].buffer_type= MYSQL_TYPE_STRING;
- bind[0].buffer= (char*) &my_val;
+ bind[0].buffer= my_val;
bind[0].buffer_length= 4;
bind[0].length= &my_length;
bind[0].is_null= (char*)&my_null;
bind[1].buffer_type= MYSQL_TYPE_STRING;
- bind[1].buffer= (char*) &my_val;
+ bind[1].buffer= my_val;
bind[1].buffer_length= 4;
bind[1].length= &my_length;
bind[1].is_null= (char*)&my_null;
@@ -10043,6 +10246,358 @@ static void test_bug4030()
mysql_stmt_close(stmt);
}
+static void test_view()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ MYSQL_BIND bind[1];
+ char str_data[50];
+ ulong length = 0L;
+ long is_null = 0L;
+ const char *query=
+ "SELECT COUNT(*) FROM v1 WHERE `SERVERNAME`=?";
+
+ myheader("test_view");
+
+ rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,t2,t3,v1");
+ myquery(rc);
+
+ rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1,t2,t3");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE `t1` ( `SERVERGRP` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', `DBINSTANCE` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', PRIMARY KEY (`SERVERGRP`)) ENGINE=InnoDB DEFAULT CHARSET=latin1");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE `t2` ( `SERVERNAME` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', `SERVERGRP` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', PRIMARY KEY (`SERVERNAME`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE `t3` ( `SERVERGRP` varchar(20) character set latin1 collate latin1_bin NOT NULL default '', `TABNAME` varchar(30) character set latin1 collate latin1_bin NOT NULL default '', `MAPSTATE` char(1) character set latin1 collate latin1_bin NOT NULL default '', `ACTSTATE` char(1) character set latin1 collate latin1_bin NOT NULL default '', `LOCAL_NAME` varchar(30) character set latin1 collate latin1_bin NOT NULL default '', `CHG_DATE` varchar(8) character set latin1 collate latin1_bin NOT NULL default '00000000', `CHG_TIME` varchar(6) character set latin1 collate latin1_bin NOT NULL default '000000', `MXUSER` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', PRIMARY KEY (`SERVERGRP`,`TABNAME`,`MAPSTATE`,`ACTSTATE`,`LOCAL_NAME`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE VIEW v1 AS select sql_no_cache T0001.SERVERNAME AS `SERVERNAME`,T0003.TABNAME AS `TABNAME`,T0003.LOCAL_NAME AS `LOCAL_NAME`,T0002.DBINSTANCE AS `DBINSTANCE` from t2 T0001 join t1 T0002 join t3 T0003 where ((T0002.SERVERGRP = T0001.SERVERGRP) and (T0002.SERVERGRP = T0003.SERVERGRP) and (T0003.MAPSTATE = _latin1'A') and (T0003.ACTSTATE = _latin1' '))");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ strcpy(str_data, "TEST");
+ bind[0].buffer_type= FIELD_TYPE_STRING;
+ bind[0].buffer= (char *)&str_data;
+ bind[0].buffer_length= 50;
+ bind[0].length= &length;
+ length= 4;
+ bind[0].is_null= (char*)&is_null;
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt,rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ assert(1 == my_process_stmt_result(stmt));
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1,t2,t3");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+}
+
+
+static void test_view_where()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query=
+ "select v1.c,v2.c from v1, v2";
+
+ myheader("test_view_where");
+
+ rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1,v2");
+ myquery(rc);
+
+ rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,v2,t1");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE t1 (a int, b int)");
+ myquery(rc);
+ rc= mysql_query(mysql,"insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10)");
+ myquery(rc);
+ rc= mysql_query(mysql,"create view v1 (c) as select b from t1 where a<3");
+ myquery(rc);
+ rc= mysql_query(mysql,"create view v2 (c) as select b from t1 where a>=3");
+ myquery(rc);
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ assert(4 == my_process_stmt_result(stmt));
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW v1, v2");
+ myquery(rc);
+}
+
+
+static void test_view_2where()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ MYSQL_BIND bind[8];
+ char parms[8][100];
+ ulong length[8];
+ const char *query= "SELECT `RELID` ,`REPORT` ,`HANDLE` ,`LOG_GROUP` ,`USERNAME` ,`VARIANT` ,`TYPE` ,`VERSION` ,`ERFDAT` ,`ERFTIME` ,`ERFNAME` ,`AEDAT` ,`AETIME` ,`AENAME` ,`DEPENDVARS` ,`INACTIVE` FROM `V_LTDX` WHERE `MANDT` = ? AND `RELID` = ? AND `REPORT` = ? AND `HANDLE` = ? AND `LOG_GROUP` = ? AND `USERNAME` IN ( ? , ? ) AND `TYPE` = ?";
+
+ myheader("test_view_2where");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS LTDX");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS V_LTDX");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE `LTDX` ( `MANDT` char(3) character set latin1 collate latin1_bin NOT NULL default '000', `RELID` char(2) character set latin1 collate latin1_bin NOT NULL default '', `REPORT` varchar(40) character set latin1 collate latin1_bin NOT NULL default '', `HANDLE` varchar(4) character set latin1 collate latin1_bin NOT NULL default '', `LOG_GROUP` varchar(4) character set latin1 collate latin1_bin NOT NULL default '', `USERNAME` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', `VARIANT` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', `TYPE` char(1) character set latin1 collate latin1_bin NOT NULL default '', `SRTF2` int(11) NOT NULL default '0', `VERSION` varchar(6) character set latin1 collate latin1_bin NOT NULL default '000000', `ERFDAT` varchar(8) character set latin1 collate latin1_bin NOT NULL default '00000000', `ERFTIME` varchar(6) character set latin1 collate latin1_bin NOT NULL default '000000', `ERFNAME` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', `AEDAT` varchar(8) character set latin1 collate latin1_bin NOT NULL default '00000000', `AETIME` varchar(6) character set latin1 collate latin1_bin NOT NULL default '000000', `AENAME` varchar(12) character set latin1 collate latin1_bin NOT NULL default '', `DEPENDVARS` varchar(10) character set latin1 collate latin1_bin NOT NULL default '', `INACTIVE` char(1) character set latin1 collate latin1_bin NOT NULL default '', `CLUSTR` smallint(6) NOT NULL default '0', `CLUSTD` blob, PRIMARY KEY (`MANDT`,`RELID`,`REPORT`,`HANDLE`,`LOG_GROUP`,`USERNAME`,`VARIANT`,`TYPE`,`SRTF2`)) ENGINE=InnoDB DEFAULT CHARSET=latin1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE VIEW V_LTDX AS select T0001.MANDT AS `MANDT`,T0001.RELID AS `RELID`,T0001.REPORT AS `REPORT`,T0001.HANDLE AS `HANDLE`,T0001.LOG_GROUP AS `LOG_GROUP`,T0001.USERNAME AS `USERNAME`,T0001.VARIANT AS `VARIANT`,T0001.TYPE AS `TYPE`,T0001.VERSION AS `VERSION`,T0001.ERFDAT AS `ERFDAT`,T0001.ERFTIME AS `ERFTIME`,T0001.ERFNAME AS `ERFNAME`,T0001.AEDAT AS `AEDAT`,T0001.AETIME AS `AETIME`,T0001.AENAME AS `AENAME`,T0001.DEPENDVARS AS `DEPENDVARS`,T0001.INACTIVE AS `INACTIVE` from LTDX T0001 where (T0001.SRTF2 = 0)");
+ myquery(rc);
+ for (i=0; i < 8; i++) {
+ strcpy(parms[i], "1");
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].buffer = (char *)&parms[i];
+ bind[i].buffer_length = 100;
+ bind[i].is_null = 0;
+ bind[i].length = &length[i];
+ length[i] = 1;
+ }
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt,rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ assert(0 == my_process_stmt_result(stmt));
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP VIEW V_LTDX");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE LTDX");
+ myquery(rc);
+}
+
+
+static void test_view_star()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ MYSQL_BIND bind[8];
+ char parms[8][100];
+ ulong length[8];
+ const char *query= "SELECT * FROM vt1 WHERE a IN (?,?)";
+
+ myheader("test_view_star");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, vt1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, vt1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 (a int)");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE VIEW vt1 AS SELECT a FROM t1");
+ myquery(rc);
+ for (i= 0; i < 2; i++) {
+ sprintf((char *)&parms[i], "%d", i);
+ bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+ bind[i].buffer = (char *)&parms[i];
+ bind[i].buffer_length = 100;
+ bind[i].is_null = 0;
+ bind[i].length = &length[i];
+ length[i] = 1;
+ }
+
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt,rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ assert(0 == my_process_stmt_result(stmt));
+ }
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW vt1");
+ myquery(rc);
+}
+
+
+static void test_view_insert()
+{
+ MYSQL_STMT *insert_stmt, *select_stmt;
+ int rc, i;
+ MYSQL_BIND bind[1];
+ long my_val = 0L;
+ ulong my_length = 0L;
+ long my_null = 0L;
+ const char *query=
+ "insert into v1 values (?)";
+
+ myheader("test_view_insert");
+
+ rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1");
+ myquery(rc);
+ rc = mysql_query(mysql, "DROP VIEW IF EXISTS t1,v1");
+ myquery(rc);
+
+ rc= mysql_query(mysql,"create table t1 (a int, primary key (a))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "create view v1 as select a from t1 where a>=1");
+ myquery(rc);
+
+ insert_stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(insert_stmt, query, strlen(query));
+ check_execute(insert_stmt, rc);
+ query= "select * from t1";
+ select_stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(select_stmt, query, strlen(query));
+ check_execute(select_stmt, rc);
+
+ bind[0].buffer_type = FIELD_TYPE_LONG;
+ bind[0].buffer = (char *)&my_val;
+ bind[0].length = &my_length;
+ bind[0].is_null = (char*)&my_null;
+ rc= mysql_stmt_bind_param(insert_stmt, bind);
+ check_execute(insert_stmt, rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ my_val= i;
+
+ rc= mysql_stmt_execute(insert_stmt);
+ check_execute(insert_stmt, rc);
+
+ rc= mysql_stmt_execute(select_stmt);
+ check_execute(select_stmt, rc);
+ assert(i + 1 == (int) my_process_stmt_result(select_stmt));
+ }
+ mysql_stmt_close(insert_stmt);
+ mysql_stmt_close(select_stmt);
+
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_left_join_view()
+{
+ MYSQL_STMT *stmt;
+ int rc, i;
+ const char *query=
+ "select t1.a, v1.x from t1 left join v1 on (t1.a= v1.x);";
+
+ myheader("test_left_join_view");
+
+ rc = mysql_query(mysql, "DROP TABLE IF EXISTS t1,v1");
+ myquery(rc);
+
+ rc = mysql_query(mysql, "DROP VIEW IF EXISTS v1,t1");
+ myquery(rc);
+ rc= mysql_query(mysql,"CREATE TABLE t1 (a int)");
+ myquery(rc);
+ rc= mysql_query(mysql,"insert into t1 values (1), (2), (3)");
+ myquery(rc);
+ rc= mysql_query(mysql,"create view v1 (x) as select a from t1 where a > 1");
+ myquery(rc);
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+
+ for (i= 0; i < 3; i++)
+ {
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ assert(3 == my_process_stmt_result(stmt));
+ }
+ mysql_stmt_close(stmt);
+
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+}
+
+
+static void test_view_insert_fields()
+{
+ MYSQL_STMT *stmt;
+ char parm[11][1000];
+ ulong l[11];
+ int rc, i;
+ MYSQL_BIND bind[11];
+ const char *query= "INSERT INTO `v1` ( `K1C4` ,`K2C4` ,`K3C4` ,`K4N4` ,`F1C4` ,`F2I4` ,`F3N5` ,`F7F8` ,`F6N4` ,`F5C8` ,`F9D8` ) VALUES( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )";
+
+ myheader("test_view_insert_fields");
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1, v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP VIEW IF EXISTS t1, v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE TABLE t1 ( K1C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K2C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K3C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', K4N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', F1C4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '', F2I4 int(11) NOT NULL default '0', F3N5 varchar(5) character set latin1 collate latin1_bin NOT NULL default '00000', F4I4 int(11) NOT NULL default '0', F5C8 varchar(8) character set latin1 collate latin1_bin NOT NULL default '', F6N4 varchar(4) character set latin1 collate latin1_bin NOT NULL default '0000', F7F8 double NOT NULL default '0', F8F8 double NOT NULL default '0', F9D8 decimal(8,2) NOT NULL default '0.00', PRIMARY KEY (K1C4,K2C4,K3C4,K4N4)) ENGINE=InnoDB DEFAULT CHARSET=latin1");
+ myquery(rc);
+ rc= mysql_query(mysql, "CREATE VIEW v1 AS select sql_no_cache K1C4 AS `K1C4`,K2C4 AS `K2C4`,K3C4 AS `K3C4`,K4N4 AS `K4N4`,F1C4 AS `F1C4`,F2I4 AS `F2I4`,F3N5 AS `F3N5`,F7F8 AS `F7F8`,F6N4 AS `F6N4`,F5C8 AS `F5C8`,F9D8 AS `F9D8` from t1 T0001");
+
+ for (i= 0; i < 11; i++)
+ {
+ l[i]= 20;
+ bind[i].buffer_type= MYSQL_TYPE_STRING;
+ bind[i].is_null= 0;
+ bind[i].buffer= (char *)&parm[i];
+
+ strcpy(parm[i], "1");
+ bind[i].buffer_length= 2;
+ bind[i].length= &l[i];
+ }
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_bind_param(stmt, bind);
+ check_execute(stmt, rc);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ mysql_stmt_close(stmt);
+
+ query= "select * from t1";
+ stmt= mysql_stmt_init(mysql);
+ rc= mysql_stmt_prepare(stmt, query, strlen(query));
+ check_execute(stmt, rc);
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+ assert(1 == my_process_stmt_result(stmt));
+
+ mysql_stmt_close(stmt);
+ rc= mysql_query(mysql, "DROP VIEW v1");
+ myquery(rc);
+ rc= mysql_query(mysql, "DROP TABLE t1");
+ myquery(rc);
+
+}
static void test_bug5126()
{
@@ -10163,6 +10718,60 @@ static void test_bug4231()
myquery(rc);
}
+
+static void test_basic_cursors()
+{
+ const char *basic_tables[]=
+ {
+ "DROP TABLE IF EXISTS t1, t2",
+
+ "CREATE TABLE t1 "
+ "(id INTEGER NOT NULL PRIMARY KEY, "
+ " name VARCHAR(20) NOT NULL)",
+
+ "INSERT INTO t1 (id, name) VALUES "
+ " (2, 'Ja'), (3, 'Ede'), "
+ " (4, 'Haag'), (5, 'Kabul'), "
+ " (6, 'Almere'), (7, 'Utrecht'), "
+ " (8, 'Qandahar'), (9, 'Amsterdam'), "
+ " (10, 'Amersfoort'), (11, 'Constantine')",
+
+ "CREATE TABLE t2 "
+ "(id INTEGER NOT NULL PRIMARY KEY, "
+ " name VARCHAR(20) NOT NULL)",
+
+ "INSERT INTO t2 (id, name) VALUES "
+ " (4, 'Guam'), (5, 'Aruba'), "
+ " (6, 'Angola'), (7, 'Albania'), "
+ " (8, 'Anguilla'), (9, 'Argentina'), "
+ " (10, 'Azerbaijan'), (11, 'Afghanistan'), "
+ " (12, 'Burkina Faso'), (13, 'Faroe Islands')"
+ };
+ const char *queries[]=
+ {
+ "SELECT * FROM t1",
+ "SELECT * FROM t2"
+ };
+
+ myheader("test_basic_cursors");
+
+ fill_tables(basic_tables, sizeof(basic_tables)/sizeof(*basic_tables));
+
+ fetch_n(queries, sizeof(queries)/sizeof(*queries));
+}
+
+
+static void test_cursors_with_union()
+{
+ const char *queries[]=
+ {
+ "SELECT t1.name FROM t1 UNION SELECT t2.name FROM t2",
+ "SELECT t1.id FROM t1 WHERE t1.id < 5"
+ };
+ myheader("test_cursors_with_union");
+ fetch_n(queries, sizeof(queries)/sizeof(*queries));
+}
+
/*
Read and parse arguments and MySQL options from my.cnf
*/
@@ -10463,6 +11072,15 @@ int main(int argc, char **argv)
test_bug5126(); /* support for mediumint type in libmysql */
test_bug4231(); /* proper handling of all-zero times and
dates in the server */
+ test_view(); /* Test of VIEWS with prepared statements */
+ test_view_where(); /* VIEW with WHERE clause & merge algorithm */
+ test_view_2where(); /* VIEW with WHERE * SELECt with WHERE */
+ test_view_star(); /* using query with * from VIEW */
+ test_view_insert(); /* inserting in VIEW without field list */
+ test_left_join_view(); /* left join on VIEW with WHERE condition */
+ test_view_insert_fields(); /* insert into VIOEW with fields list */
+ test_basic_cursors();
+ test_cursors_with_union();
/*
XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.
diff --git a/tests/udf_test b/tests/udf_test
index 4621a7b34a5..15ad640f984 100644
--- a/tests/udf_test
+++ b/tests/udf_test
@@ -9,6 +9,7 @@ CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
+CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so";
select metaphon("hello");
select myfunc_double("hello","world");
@@ -20,6 +21,7 @@ create temporary table t1 (a int,b double);
insert into t1 values (1,5),(1,4),(2,8),(3,9),(4,11);
select avgcost(a,b) from t1;
select avgcost(a,b) from t1 group by a;
+select a, myfunc_argument_name(a), myfunc_argument_name(a as b) from t1;
drop table t1;
DROP FUNCTION metaphon;
@@ -28,3 +30,4 @@ DROP FUNCTION myfunc_int;
DROP FUNCTION lookup;
DROP FUNCTION reverse_lookup;
DROP FUNCTION avgcost;
+DROP FUNCTION myfunc_argument_name;
diff --git a/tests/udf_test.res b/tests/udf_test.res
index 66634e13616..de9e9969f3a 100644
--- a/tests/udf_test.res
+++ b/tests/udf_test.res
@@ -35,6 +35,12 @@ CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"
Query OK, 0 rows affected
--------------
+CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so"
+--------------
+
+Query OK, 0 rows affected
+
+--------------
select metaphon("hello")
--------------
@@ -107,6 +113,18 @@ avgcost(a,b)
4 rows in set
--------------
+select a, myfunc_argument_name(a) from t1;
+--------------
+
+a myfunc_argument_name(a) myfunc_argument_name(a as b)
+1 a b
+1 a b
+2 a b
+3 a b
+4 a b
+5 rows in set
+
+--------------
drop table t1
--------------
@@ -148,4 +166,10 @@ DROP FUNCTION avgcost
Query OK, 0 rows affected
+--------------
+DROP FUNCTION myfunc_argument_name;
+--------------
+
+Query OK, 0 rows affected
+
Bye