summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2017-03-08 19:44:22 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2017-03-09 08:53:08 +0200
commitad0c218a440575fa6fb6634aca7a08448a4360e0 (patch)
treeb52811847ce51c92eabdeed3104df8b0168943df
parentbb4ef470c24cdbcedba3dd3dcda3b3d88b6fb491 (diff)
parent9fe92a9770a801c4cd36390620486be4cb06752b (diff)
downloadmariadb-git-ad0c218a440575fa6fb6634aca7a08448a4360e0.tar.gz
Merge 10.0 into 10.1
Also, implement MDEV-11027 a little differently from 5.5 and 10.0: recv_apply_hashed_log_recs(): Change the return type back to void (DB_SUCCESS was always returned). Report progress also via systemd using sd_notifyf().
-rw-r--r--CMakeLists.txt3
-rw-r--r--client/mysqldump.c5
-rw-r--r--config.h.cmake1
-rw-r--r--include/my_sys.h12
-rw-r--r--include/mysql/psi/mysql_file.h39
-rw-r--r--include/mysql/psi/psi.h2
-rw-r--r--mysql-test/include/gap_lock_error_all.inc27
-rw-r--r--mysql-test/include/gap_lock_error_cleanup.inc1
-rw-r--r--mysql-test/include/gap_lock_error_init.inc24
-rw-r--r--mysql-test/include/gap_lock_error_select.inc89
-rw-r--r--mysql-test/include/gap_lock_error_update.inc91
-rw-r--r--mysql-test/r/derived.result23
-rw-r--r--mysql-test/r/grant.result70
-rw-r--r--mysql-test/r/join_nested.result95
-rw-r--r--mysql-test/r/join_nested_jcl6.result95
-rw-r--r--mysql-test/r/mysqldump.result1
-rw-r--r--mysql-test/r/partition_innodb.result91
-rw-r--r--mysql-test/r/partition_myisam.result16
-rw-r--r--mysql-test/r/ps.result72
-rw-r--r--mysql-test/r/range_vs_index_merge.result46
-rw-r--r--mysql-test/r/range_vs_index_merge_innodb.result48
-rw-r--r--mysql-test/r/repair_symlink-5543.result9
-rw-r--r--mysql-test/r/subselect4.result34
-rw-r--r--mysql-test/r/subselect_innodb.result15
-rw-r--r--mysql-test/r/symlink-aria-11902.result39
-rw-r--r--mysql-test/r/symlink-myisam-11902.result38
-rw-r--r--mysql-test/r/table_elim.result5
-rw-r--r--mysql-test/r/view.result83
-rw-r--r--mysql-test/suite/federated/federated_bug_35333.test1
-rw-r--r--mysql-test/suite/innodb/r/log_file_size.result12
-rw-r--r--mysql-test/suite/innodb/t/log_file_size.test16
-rw-r--r--mysql-test/suite/parts/r/partition_bigint_innodb.result121
-rw-r--r--mysql-test/suite/parts/r/partition_bigint_myisam.result121
-rw-r--r--mysql-test/suite/parts/r/partition_double_innodb.result82
-rw-r--r--mysql-test/suite/parts/r/partition_double_myisam.result82
-rw-r--r--mysql-test/suite/parts/r/partition_float_innodb.result82
-rw-r--r--mysql-test/suite/parts/r/partition_float_myisam.result82
-rw-r--r--mysql-test/suite/parts/r/partition_int_innodb.result448
-rw-r--r--mysql-test/suite/parts/r/partition_int_myisam.result448
-rw-r--r--mysql-test/suite/parts/r/partition_mediumint_innodb.result109
-rw-r--r--mysql-test/suite/parts/r/partition_mediumint_myisam.result109
-rw-r--r--mysql-test/suite/parts/r/partition_smallint_innodb.result109
-rw-r--r--mysql-test/suite/parts/r/partition_smallint_myisam.result109
-rw-r--r--mysql-test/suite/parts/r/partition_tinyint_innodb.result109
-rw-r--r--mysql-test/suite/parts/r/partition_tinyint_myisam.result109
-rw-r--r--mysql-test/suite/parts/t/partition_bigint_innodb.test46
-rw-r--r--mysql-test/suite/parts/t/partition_bigint_myisam.test46
-rw-r--r--mysql-test/suite/parts/t/partition_double_innodb.test46
-rw-r--r--mysql-test/suite/parts/t/partition_double_myisam.test46
-rw-r--r--mysql-test/suite/parts/t/partition_float_innodb.test7
-rw-r--r--mysql-test/suite/parts/t/partition_float_myisam.test7
-rw-r--r--mysql-test/suite/parts/t/partition_int_innodb.test10
-rw-r--r--mysql-test/suite/parts/t/partition_int_myisam.test10
-rw-r--r--mysql-test/suite/parts/t/partition_mediumint_innodb.test46
-rw-r--r--mysql-test/suite/parts/t/partition_mediumint_myisam.test46
-rw-r--r--mysql-test/suite/parts/t/partition_smallint_innodb.test46
-rw-r--r--mysql-test/suite/parts/t/partition_smallint_myisam.test46
-rw-r--r--mysql-test/suite/parts/t/partition_tinyint_innodb.test46
-rw-r--r--mysql-test/suite/parts/t/partition_tinyint_myisam.test46
-rw-r--r--mysql-test/suite/rpl/r/rpl_heartbeat_basic.result3
-rw-r--r--mysql-test/suite/rpl/t/rpl_heartbeat_basic.test46
-rw-r--r--mysql-test/suite/sys_vars/r/innodb_stats_include_delete_marked_basic.result25
-rw-r--r--mysql-test/suite/sys_vars/r/sysvars_innodb,32bit,xtradb.rdiff17
-rw-r--r--mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff9
-rw-r--r--mysql-test/suite/sys_vars/t/innodb_stats_include_delete_marked_basic.test53
-rw-r--r--mysql-test/suite/sys_vars/t/secure_file_priv.test3
-rw-r--r--mysql-test/suite/vcol/r/vcol_select_myisam.result109
-rw-r--r--mysql-test/suite/vcol/t/vcol_select_myisam.test32
-rw-r--r--mysql-test/t/derived.test23
-rw-r--r--mysql-test/t/grant.test85
-rw-r--r--mysql-test/t/join_nested.test71
-rw-r--r--mysql-test/t/mysqldump.test9
-rw-r--r--mysql-test/t/partition_innodb.test98
-rw-r--r--mysql-test/t/partition_myisam.test22
-rw-r--r--mysql-test/t/ps.test33
-rw-r--r--mysql-test/t/range_vs_index_merge.test41
-rw-r--r--mysql-test/t/repair_symlink-5543.test4
-rw-r--r--mysql-test/t/subselect4.test19
-rw-r--r--mysql-test/t/subselect_innodb.test21
-rw-r--r--mysql-test/t/symlink-aria-11902.test6
-rw-r--r--mysql-test/t/symlink-myisam-11902.test60
-rw-r--r--mysql-test/t/table_elim.test4
-rw-r--r--mysql-test/t/view.test60
-rw-r--r--mysql-test/unstable-tests121
-rw-r--r--mysql-test/valgrind.supp11
-rw-r--r--mysys/mf_format.c7
-rw-r--r--mysys/my_context.c3
-rw-r--r--mysys/my_create.c20
-rw-r--r--mysys/my_delete.c11
-rw-r--r--mysys/my_div.c2
-rw-r--r--mysys/my_fopen.c18
-rw-r--r--mysys/my_init.c2
-rw-r--r--mysys/my_open.c41
-rw-r--r--mysys/my_symlink.c94
-rw-r--r--mysys/my_symlink2.c49
-rw-r--r--mysys/my_sync.c2
-rw-r--r--mysys/my_thr_init.c13
-rw-r--r--mysys/mysys_priv.h28
-rw-r--r--pcre/AUTHORS6
-rw-r--r--pcre/CMakeLists.txt1
-rw-r--r--pcre/ChangeLog47
-rw-r--r--pcre/LICENCE6
-rw-r--r--pcre/NEWS6
-rw-r--r--pcre/configure.ac10
-rw-r--r--pcre/doc/html/pcrecompat.html2
-rw-r--r--pcre/doc/html/pcrepattern.html37
-rw-r--r--pcre/doc/pcre.txt2109
-rw-r--r--pcre/doc/pcrecompat.32
-rw-r--r--pcre/doc/pcrepattern.337
-rw-r--r--pcre/pcre_compile.c80
-rw-r--r--pcre/pcre_jit_compile.c4
-rw-r--r--pcre/pcre_jit_test.c1
-rw-r--r--pcre/pcregrep.c6
-rw-r--r--pcre/pcretest.c20
-rw-r--r--pcre/testdata/testinput16
-rw-r--r--pcre/testdata/testinput1626
-rw-r--r--pcre/testdata/testinput1917
-rw-r--r--pcre/testdata/testinput26
-rw-r--r--pcre/testdata/testinput66
-rw-r--r--pcre/testdata/testinput79
-rw-r--r--pcre/testdata/testinput84
-rw-r--r--pcre/testdata/testoutput18
-rw-r--r--pcre/testdata/testoutput1652
-rw-r--r--pcre/testdata/testoutput1926
-rw-r--r--pcre/testdata/testoutput237
-rw-r--r--pcre/testdata/testoutput68
-rw-r--r--pcre/testdata/testoutput726
-rw-r--r--pcre/testdata/testoutput810
-rw-r--r--plugin/server_audit/server_audit.c36
-rw-r--r--sql-common/client.c11
-rw-r--r--sql/ha_partition.cc1
-rw-r--r--sql/handler.cc5
-rw-r--r--sql/item_cmpfunc.cc12
-rw-r--r--sql/item_cmpfunc.h7
-rw-r--r--sql/item_subselect.cc9
-rw-r--r--sql/item_subselect.h9
-rw-r--r--sql/log_slow.h33
-rw-r--r--sql/mysqld.cc4
-rw-r--r--sql/opt_range.cc14
-rw-r--r--sql/partition_info.cc2
-rw-r--r--sql/sql_acl.cc6
-rw-r--r--sql/sql_db.cc9
-rw-r--r--sql/sql_derived.cc3
-rw-r--r--sql/sql_join_cache.cc5
-rw-r--r--sql/sql_parse.cc66
-rw-r--r--sql/sql_parse.h3
-rw-r--r--sql/sql_select.cc79
-rw-r--r--sql/table.cc2
-rw-r--r--storage/connect/CMakeLists.txt10
-rw-r--r--storage/connect/array.cpp108
-rw-r--r--storage/connect/array.h6
-rw-r--r--storage/connect/colblk.cpp16
-rw-r--r--storage/connect/connect.cc77
-rw-r--r--storage/connect/domdoc.cpp11
-rw-r--r--storage/connect/domdoc.h1
-rw-r--r--storage/connect/filamap.cpp12
-rw-r--r--storage/connect/filamdbf.cpp27
-rw-r--r--storage/connect/filamgz.cpp12
-rw-r--r--storage/connect/filamgz.h12
-rw-r--r--storage/connect/filamzip.cpp561
-rw-r--r--storage/connect/filamzip.h164
-rw-r--r--storage/connect/ha_connect.cc343
-rw-r--r--storage/connect/jdbconn.cpp24
-rw-r--r--storage/connect/json.cpp80
-rw-r--r--storage/connect/jsonudf.cpp109
-rw-r--r--storage/connect/mycat.cc15
-rw-r--r--storage/connect/myconn.cpp20
-rw-r--r--storage/connect/mysql-test/connect/r/xml_zip.result98
-rw-r--r--storage/connect/mysql-test/connect/r/zip.result240
-rw-r--r--storage/connect/mysql-test/connect/std_data/bios.json273
-rw-r--r--storage/connect/mysql-test/connect/std_data/xsample2.xml47
-rw-r--r--storage/connect/mysql-test/connect/t/have_zip.inc19
-rw-r--r--storage/connect/mysql-test/connect/t/xml_zip.test41
-rw-r--r--storage/connect/mysql-test/connect/t/zip.test136
-rw-r--r--storage/connect/odbconn.cpp14
-rw-r--r--storage/connect/plgdbsem.h26
-rw-r--r--storage/connect/plgdbutl.cpp14
-rw-r--r--storage/connect/plgxml.cpp4
-rw-r--r--storage/connect/plgxml.h2
-rw-r--r--storage/connect/plugutil.c3
-rw-r--r--storage/connect/reldef.cpp6
-rw-r--r--storage/connect/reldef.h18
-rw-r--r--storage/connect/tabdos.cpp40
-rw-r--r--storage/connect/tabdos.h8
-rw-r--r--storage/connect/tabext.cpp640
-rw-r--r--storage/connect/tabext.h200
-rw-r--r--storage/connect/tabfix.cpp4
-rw-r--r--storage/connect/tabfix.h2
-rw-r--r--storage/connect/tabfmt.cpp75
-rw-r--r--storage/connect/tabfmt.h5
-rw-r--r--storage/connect/tabjdbc.cpp555
-rw-r--r--storage/connect/tabjdbc.h129
-rw-r--r--storage/connect/tabjson.cpp31
-rw-r--r--storage/connect/tabjson.h4
-rw-r--r--storage/connect/table.cpp249
-rw-r--r--storage/connect/tabmac.cpp2
-rw-r--r--storage/connect/tabmac.h2
-rw-r--r--storage/connect/tabmul.cpp26
-rw-r--r--storage/connect/tabmul.h18
-rw-r--r--storage/connect/tabmysql.cpp183
-rw-r--r--storage/connect/tabmysql.h75
-rw-r--r--storage/connect/taboccur.cpp9
-rw-r--r--storage/connect/tabodbc.cpp321
-rw-r--r--storage/connect/tabodbc.h133
-rw-r--r--storage/connect/tabpivot.cpp7
-rw-r--r--storage/connect/tabpivot.h2
-rw-r--r--storage/connect/tabsys.cpp8
-rw-r--r--storage/connect/tabsys.h4
-rw-r--r--storage/connect/tabtbl.cpp23
-rw-r--r--storage/connect/tabutil.cpp23
-rw-r--r--storage/connect/tabutil.h8
-rw-r--r--storage/connect/tabvct.cpp6
-rw-r--r--storage/connect/tabvct.h2
-rw-r--r--storage/connect/tabvir.cpp2
-rw-r--r--storage/connect/tabwmi.cpp21
-rw-r--r--storage/connect/tabxcl.cpp8
-rw-r--r--storage/connect/tabxcl.h2
-rw-r--r--storage/connect/tabxml.cpp13
-rw-r--r--storage/connect/tabxml.h2
-rw-r--r--storage/connect/tabzip.cpp16
-rw-r--r--storage/connect/tabzip.h2
-rw-r--r--storage/connect/value.h2
-rwxr-xr-xstorage/connect/xindex.cpp8
-rw-r--r--storage/connect/xindex.h4
-rw-r--r--storage/connect/xobject.h3
-rw-r--r--storage/connect/xtable.h211
-rw-r--r--storage/innobase/btr/btr0cur.cc7
-rw-r--r--storage/innobase/dict/dict0dict.cc1
-rw-r--r--storage/innobase/dyn/dyn0dyn.cc1
-rw-r--r--storage/innobase/fsp/fsp0fsp.cc6
-rw-r--r--storage/innobase/include/dict0dict.ic7
-rw-r--r--storage/innobase/include/dyn0dyn.ic8
-rw-r--r--storage/innobase/include/log0recv.h35
-rw-r--r--storage/innobase/include/mach0data.ic13
-rw-r--r--storage/innobase/include/page0page.ic1
-rw-r--r--storage/innobase/log/log0log.cc17
-rw-r--r--storage/innobase/log/log0recv.cc218
-rw-r--r--storage/innobase/mtr/mtr0mtr.cc1
-rw-r--r--storage/innobase/page/page0page.cc2
-rw-r--r--storage/innobase/page/page0zip.cc2
-rw-r--r--storage/innobase/row/row0merge.cc6
-rw-r--r--storage/innobase/row/row0upd.cc4
-rw-r--r--storage/innobase/srv/srv0start.cc10
-rw-r--r--storage/innobase/sync/sync0sync.cc2
-rw-r--r--storage/maria/ha_maria.cc234
-rw-r--r--storage/maria/ma_check.c6
-rw-r--r--storage/maria/ma_create.c57
-rw-r--r--storage/maria/ma_delete_table.c20
-rw-r--r--storage/maria/ma_open.c71
-rw-r--r--storage/maria/ma_static.c6
-rw-r--r--storage/maria/maria_chk.c2
-rw-r--r--storage/maria/maria_def.h3
-rw-r--r--storage/myisam/ha_myisam.cc223
-rw-r--r--storage/myisam/mi_check.c17
-rw-r--r--storage/myisam/mi_create.c53
-rw-r--r--storage/myisam/mi_delete_table.c37
-rw-r--r--storage/myisam/mi_open.c71
-rw-r--r--storage/myisam/mi_static.c8
-rw-r--r--storage/myisam/myisamchk.c2
-rw-r--r--storage/myisam/myisamdef.h3
-rw-r--r--storage/tokudb/CMakeLists.txt2
-rw-r--r--storage/tokudb/PerconaFT/ft/ft-ops.cc23
-rw-r--r--storage/tokudb/PerconaFT/ft/ft-ops.h5
-rw-r--r--storage/tokudb/PerconaFT/ft/logger/recover.cc3
-rw-r--r--storage/tokudb/PerconaFT/ft/node.cc18
-rw-r--r--storage/tokudb/PerconaFT/ft/node.h54
-rw-r--r--storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc3
-rw-r--r--storage/tokudb/PerconaFT/ft/txn/roll.cc3
-rw-r--r--storage/tokudb/PerconaFT/util/dmt.h5
-rw-r--r--storage/tokudb/PerconaFT/util/omt.h2
-rw-r--r--storage/tokudb/ha_tokudb.cc81
-rw-r--r--storage/tokudb/ha_tokudb.h2
-rw-r--r--storage/tokudb/mysql-test/tokudb/r/dir_per_db_rename_to_nonexisting_schema.result46
-rw-r--r--storage/tokudb/mysql-test/tokudb/r/gap_lock_error.result469
-rw-r--r--storage/tokudb/mysql-test/tokudb/r/percona_kill_idle_trx_tokudb.result43
-rw-r--r--storage/tokudb/mysql-test/tokudb/t/dir_per_db_rename_to_nonexisting_schema.test64
-rw-r--r--storage/tokudb/mysql-test/tokudb/t/gap_lock_error.test5
-rw-r--r--storage/tokudb/mysql-test/tokudb/t/percona_kill_idle_trx_tokudb.test5
-rw-r--r--storage/tokudb/mysql-test/tokudb_backup/t/suite.opt2
-rw-r--r--storage/xtradb/btr/btr0btr.cc2
-rw-r--r--storage/xtradb/btr/btr0cur.cc116
-rw-r--r--storage/xtradb/buf/buf0buddy.cc1
-rw-r--r--storage/xtradb/buf/buf0buf.cc9
-rw-r--r--storage/xtradb/buf/buf0lru.cc142
-rw-r--r--storage/xtradb/dict/dict0dict.cc1
-rw-r--r--storage/xtradb/dict/dict0stats.cc13
-rw-r--r--storage/xtradb/dyn/dyn0dyn.cc1
-rw-r--r--storage/xtradb/fsp/fsp0fsp.cc12
-rw-r--r--storage/xtradb/fts/fts0opt.cc7
-rw-r--r--storage/xtradb/handler/ha_innodb.cc103
-rw-r--r--storage/xtradb/handler/ha_innodb.h2
-rw-r--r--storage/xtradb/handler/handler0alter.cc79
-rw-r--r--storage/xtradb/handler/i_s.cc24
-rw-r--r--storage/xtradb/include/btr0cur.h18
-rw-r--r--storage/xtradb/include/btr0sea.h7
-rw-r--r--storage/xtradb/include/btr0sea.ic4
-rw-r--r--storage/xtradb/include/buf0buddy.ic2
-rw-r--r--storage/xtradb/include/buf0buf.h5
-rw-r--r--storage/xtradb/include/dict0dict.h19
-rw-r--r--storage/xtradb/include/dict0dict.ic7
-rw-r--r--storage/xtradb/include/dyn0dyn.h18
-rw-r--r--storage/xtradb/include/dyn0dyn.ic10
-rw-r--r--storage/xtradb/include/log0online.h20
-rw-r--r--storage/xtradb/include/log0recv.h35
-rw-r--r--storage/xtradb/include/mach0data.h16
-rw-r--r--storage/xtradb/include/mach0data.ic13
-rw-r--r--storage/xtradb/include/mtr0mtr.h5
-rw-r--r--storage/xtradb/include/os0file.h5
-rw-r--r--storage/xtradb/include/os0thread.h8
-rw-r--r--storage/xtradb/include/page0page.h20
-rw-r--r--storage/xtradb/include/page0page.ic1
-rw-r--r--storage/xtradb/include/page0zip.h7
-rw-r--r--storage/xtradb/include/rem0rec.h3
-rw-r--r--storage/xtradb/include/row0upd.h5
-rw-r--r--storage/xtradb/include/srv0srv.h9
-rw-r--r--storage/xtradb/include/trx0trx.h2
-rw-r--r--storage/xtradb/include/univ.i4
-rw-r--r--storage/xtradb/log/log0log.cc17
-rw-r--r--storage/xtradb/log/log0online.cc177
-rw-r--r--storage/xtradb/log/log0recv.cc214
-rw-r--r--storage/xtradb/mach/mach0data.cc44
-rw-r--r--storage/xtradb/mtr/mtr0mtr.cc1
-rw-r--r--storage/xtradb/os/os0file.cc53
-rw-r--r--storage/xtradb/os/os0thread.cc28
-rw-r--r--storage/xtradb/page/page0page.cc2
-rw-r--r--storage/xtradb/page/page0zip.cc2
-rw-r--r--storage/xtradb/rem/rem0rec.cc2
-rw-r--r--storage/xtradb/row/row0merge.cc10
-rw-r--r--storage/xtradb/row/row0mysql.cc2
-rw-r--r--storage/xtradb/row/row0purge.cc2
-rw-r--r--storage/xtradb/row/row0upd.cc4
-rw-r--r--storage/xtradb/srv/srv0srv.cc96
-rw-r--r--storage/xtradb/srv/srv0start.cc40
-rw-r--r--storage/xtradb/sync/sync0sync.cc2
-rw-r--r--storage/xtradb/trx/trx0sys.cc66
-rwxr-xr-xsupport-files/build-tags4
-rw-r--r--support-files/rpm/server-postin.sh33
-rw-r--r--tests/mysql_client_test.c50
338 files changed, 11297 insertions, 5856 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5b256f37a38..b083e09cef1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -328,6 +328,9 @@ SET(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON)
# Common defines and includes
ADD_DEFINITIONS(-DHAVE_CONFIG_H)
+IF(_FILE_OFFSET_BITS)
+ ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS})
+ENDIF()
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include)
# Add bundled or system zlib.
diff --git a/client/mysqldump.c b/client/mysqldump.c
index ced65f78986..42c3b39cf5c 100644
--- a/client/mysqldump.c
+++ b/client/mysqldump.c
@@ -700,8 +700,9 @@ static void write_header(FILE *sql_file, char *db_name)
"-- MySQL dump %s Distrib %s, for %s (%s)\n--\n",
DUMP_VERSION, MYSQL_SERVER_VERSION, SYSTEM_TYPE,
MACHINE_TYPE);
- print_comment(sql_file, 0, "-- Host: %s Database: %s\n",
- fix_for_comment(current_host ? current_host : "localhost"),
+ print_comment(sql_file, 0, "-- Host: %s ",
+ fix_for_comment(current_host ? current_host : "localhost"));
+ print_comment(sql_file, 0, "Database: %s\n",
fix_for_comment(db_name ? db_name : ""));
print_comment(sql_file, 0,
"-- ------------------------------------------------------\n"
diff --git a/config.h.cmake b/config.h.cmake
index 493f90edc8c..5e48085a61a 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -492,7 +492,6 @@
#cmakedefine _LARGE_FILES 1
#cmakedefine _LARGEFILE_SOURCE 1
#cmakedefine _LARGEFILE64_SOURCE 1
-#cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@
#cmakedefine TIME_WITH_SYS_TIME 1
diff --git a/include/my_sys.h b/include/my_sys.h
index 2b1698ca2bc..d30c3ee78d3 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -66,9 +66,10 @@ typedef struct my_aio_result {
#define MY_FAE 8 /* Fatal if any error */
#define MY_WME 16 /* Write message on error */
#define MY_WAIT_IF_FULL 32 /* Wait and try again if disk full error */
-#define MY_IGNORE_BADFD 32 /* my_sync: ignore 'bad descriptor' errors */
+#define MY_IGNORE_BADFD 32 /* my_sync(): ignore 'bad descriptor' errors */
#define MY_ENCRYPT 64 /* Encrypt IO_CACHE temporary files */
-#define MY_FULL_IO 512 /* For my_read - loop intil I/O is complete */
+#define MY_NOSYMLINKS 512 /* my_open(): don't follow symlinks */
+#define MY_FULL_IO 512 /* my_read(): loop intil I/O is complete */
#define MY_DONT_CHECK_FILESIZE 128 /* Option to init_io_cache() */
#define MY_LINK_WARNING 32 /* my_redel() gives warning if links */
#define MY_COPYTIME 64 /* my_redel() copys time */
@@ -261,7 +262,7 @@ extern ulong my_file_opened,my_stream_opened, my_tmp_file_created;
extern ulong my_file_total_opened;
extern ulong my_sync_count;
extern uint mysys_usage_id;
-extern my_bool my_init_done;
+extern my_bool my_init_done, my_thr_key_mysys_exists;
extern my_bool my_assert_on_error;
extern myf my_global_flags; /* Set to MY_WME for more error messages */
/* Point to current my_message() */
@@ -615,6 +616,7 @@ int my_b_pread(IO_CACHE *info, uchar *Buffer, size_t Count, my_off_t pos);
typedef uint32 ha_checksum;
+extern int (*mysys_test_invalid_symlink)(const char *filename);
#include <my_alloc.h>
/* Prototypes for mysys and my_func functions */
@@ -642,9 +644,11 @@ extern int my_realpath(char *to, const char *filename, myf MyFlags);
extern File my_create_with_symlink(const char *linkname, const char *filename,
int createflags, int access_flags,
myf MyFlags);
-extern int my_delete_with_symlink(const char *name, myf MyFlags);
extern int my_rename_with_symlink(const char *from,const char *to,myf MyFlags);
extern int my_symlink(const char *content, const char *linkname, myf MyFlags);
+extern int my_handler_delete_with_symlink(PSI_file_key key, const char *name,
+ const char *ext, myf sync_dir);
+
extern size_t my_read(File Filedes,uchar *Buffer,size_t Count,myf MyFlags);
extern size_t my_pread(File Filedes,uchar *Buffer,size_t Count,my_off_t offset,
myf MyFlags);
diff --git a/include/mysql/psi/mysql_file.h b/include/mysql/psi/mysql_file.h
index c839b2b019b..aca66bd4974 100644
--- a/include/mysql/psi/mysql_file.h
+++ b/include/mysql/psi/mysql_file.h
@@ -442,20 +442,6 @@
#endif
/**
- @def mysql_file_delete_with_symlink(K, P1, P2)
- Instrumented delete with symbolic link.
- @c mysql_file_delete_with_symlink is a replacement
- for @c my_delete_with_symlink.
-*/
-#ifdef HAVE_PSI_FILE_INTERFACE
- #define mysql_file_delete_with_symlink(K, P1, P2) \
- inline_mysql_file_delete_with_symlink(K, __FILE__, __LINE__, P1, P2)
-#else
- #define mysql_file_delete_with_symlink(K, P1, P2) \
- inline_mysql_file_delete_with_symlink(P1, P2)
-#endif
-
-/**
@def mysql_file_rename_with_symlink(K, P1, P2, P3)
Instrumented rename with symbolic link.
@c mysql_file_rename_with_symlink is a replacement
@@ -1337,31 +1323,6 @@ inline_mysql_file_create_with_symlink(
return file;
}
-static inline int
-inline_mysql_file_delete_with_symlink(
-#ifdef HAVE_PSI_FILE_INTERFACE
- PSI_file_key key, const char *src_file, uint src_line,
-#endif
- const char *name, myf flags)
-{
- int result;
-#ifdef HAVE_PSI_FILE_INTERFACE
- struct PSI_file_locker *locker;
- PSI_file_locker_state state;
- locker= PSI_FILE_CALL(get_thread_file_name_locker)
- (&state, key, PSI_FILE_DELETE, name, &locker);
- if (likely(locker != NULL))
- {
- PSI_FILE_CALL(start_file_close_wait)(locker, src_file, src_line);
- result= my_delete_with_symlink(name, flags);
- PSI_FILE_CALL(end_file_close_wait)(locker, result);
- return result;
- }
-#endif
-
- result= my_delete_with_symlink(name, flags);
- return result;
-}
static inline int
inline_mysql_file_rename_with_symlink(
diff --git a/include/mysql/psi/psi.h b/include/mysql/psi/psi.h
index 7fcff89c8b6..3f43445e08a 100644
--- a/include/mysql/psi/psi.h
+++ b/include/mysql/psi/psi.h
@@ -417,7 +417,7 @@ enum PSI_file_operation
PSI_FILE_FSTAT= 12,
/** File chsize, as in @c my_chsize(). */
PSI_FILE_CHSIZE= 13,
- /** File delete, such as @c my_delete() or @c my_delete_with_symlink(). */
+ /** File delete, such as @c my_delete() or @c my_handler_delete_with_symlink(). */
PSI_FILE_DELETE= 14,
/** File rename, such as @c my_rename() or @c my_rename_with_symlink(). */
PSI_FILE_RENAME= 15,
diff --git a/mysql-test/include/gap_lock_error_all.inc b/mysql-test/include/gap_lock_error_all.inc
new file mode 100644
index 00000000000..fc69dfb56f0
--- /dev/null
+++ b/mysql-test/include/gap_lock_error_all.inc
@@ -0,0 +1,27 @@
+--source include/have_partition.inc
+--source include/gap_lock_error_init.inc
+
+let $select_lock=for update;
+let $autocommit = 0;
+--source include/gap_lock_error_select.inc
+let $autocommit = 1;
+--source include/gap_lock_error_select.inc
+
+let $select_lock=lock in share mode;
+let $autocommit = 0;
+--source include/gap_lock_error_select.inc
+let $autocommit = 1;
+--source include/gap_lock_error_select.inc
+
+let $select_lock=;
+let $autocommit = 0;
+--source include/gap_lock_error_select.inc
+let $autocommit = 1;
+--source include/gap_lock_error_select.inc
+
+let $autocommit = 0;
+--source include/gap_lock_error_update.inc
+let $autocommit = 1;
+--source include/gap_lock_error_update.inc
+
+--source include/gap_lock_error_cleanup.inc
diff --git a/mysql-test/include/gap_lock_error_cleanup.inc b/mysql-test/include/gap_lock_error_cleanup.inc
new file mode 100644
index 00000000000..8558b5a528f
--- /dev/null
+++ b/mysql-test/include/gap_lock_error_cleanup.inc
@@ -0,0 +1 @@
+drop table gap1, gap2, gap3, gap4;
diff --git a/mysql-test/include/gap_lock_error_init.inc b/mysql-test/include/gap_lock_error_init.inc
new file mode 100644
index 00000000000..26aa852a398
--- /dev/null
+++ b/mysql-test/include/gap_lock_error_init.inc
@@ -0,0 +1,24 @@
+eval CREATE TABLE gap1 (id1 INT, id2 INT, id3 INT, c1 INT, value INT,
+ PRIMARY KEY (id1, id2, id3),
+ INDEX i (c1)) ENGINE=$engine;
+CREATE TABLE gap2 like gap1;
+eval CREATE TABLE gap3 (id INT, value INT,
+ PRIMARY KEY (id),
+ UNIQUE KEY ui(value)) ENGINE=$engine;
+eval CREATE TABLE gap4 (id INT, value INT,
+ PRIMARY KEY (id)) ENGINE=$engine
+ PARTITION BY HASH(id) PARTITIONS 2;
+--disable_query_log
+let $max = 1000;
+let $i = 1;
+while ($i <= $max) {
+ eval INSERT INTO gap1 (id1, id2, id3, c1, value)
+ VALUES ($i div 2, $i div 10, $i, $i, $i);
+ eval INSERT INTO gap2 (id1, id2, id3, c1, value)
+ VALUES ($i div 2, $i div 10, $i, $i, $i);
+ inc $i;
+}
+--enable_query_log
+
+insert into gap3 values (1,1), (2,2),(3,3),(4,4),(5,5);
+insert into gap4 values (1,1), (2,2),(3,3),(4,4),(5,5);
diff --git a/mysql-test/include/gap_lock_error_select.inc b/mysql-test/include/gap_lock_error_select.inc
new file mode 100644
index 00000000000..91db9bed68a
--- /dev/null
+++ b/mysql-test/include/gap_lock_error_select.inc
@@ -0,0 +1,89 @@
+eval set session autocommit=$autocommit;
+let $is_gaplock_target = `SELECT @@autocommit = 0 && '$select_lock' != '' && '$expect_gap_lock_errors' = 1`;
+
+if ($is_gaplock_target)
+{
+# rnd_init
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 limit 1 $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 where value != 100 limit 1 $select_lock;
+# index_read_map
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 where id1=1 $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 where id1=1 and id2= 1 $select_lock;
+# read_range_first
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 where id1=1 and id2= 1 and id3 != 1 $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 where id1=1 and id2= 1 and id3
+ between 1 and 3 $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 where id1=1 and id2= 1 order by id3 asc
+ limit 1 $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 where id1=1 and id2= 1 order by id3 desc
+ limit 1 $select_lock;
+# index_first
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 order by id1 asc limit 1 $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 $select_lock;
+# index_last
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 order by id1 desc limit 1 $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 order by id1 desc, id2 desc, id3 desc
+ limit 1 $select_lock;
+# secondary index lookup
+--error ER_UNKNOWN_ERROR
+eval select * from gap1 force index(i) where c1=1 $select_lock;
+# unique index lookup, ensure no gap lock errors as this is effectively a
+# single point select that does not lock ranges or gaps of keys
+eval select * from gap3 force index(ui) where value=1 $select_lock;
+# primary key lookup, ensure no gap lock errors as these are effectively
+# single point selects that do not lock ranges or gaps of keys
+eval select * from gap1 where id1=1 and id2=1 and id3=1 $select_lock;
+eval select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) $select_lock;
+eval select * from gap1 where id1=1 and id2=1 and id3=1 and value=1
+ order by c1 $select_lock;
+eval select * from gap3 where id=1 $select_lock;
+eval select * from gap4 where id=1 $select_lock;
+eval select * from gap4 where id in (1, 2, 3) $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap4 $select_lock;
+--error ER_UNKNOWN_ERROR
+eval select * from gap4 where id between 3 and 7 $select_lock;
+}
+
+if (!$is_gaplock_target)
+{
+eval select * from gap1 limit 1 $select_lock;
+eval select * from gap1 where value != 100 limit 1 $select_lock;
+eval select * from gap1 where id1=1 $select_lock;
+eval select * from gap1 where id1=1 and id2= 1 $select_lock;
+eval select * from gap1 where id1=1 and id2= 1 and id3 != 1 $select_lock;
+eval select * from gap1 where id1=1 and id2= 1 and id3
+ between 1 and 3 $select_lock;
+eval select * from gap1 where id1=1 and id2= 1 order by id3 asc
+ limit 1 $select_lock;
+eval select * from gap1 where id1=1 and id2= 1 order by id3 desc
+ limit 1 $select_lock;
+eval select * from gap1 order by id1 asc limit 1 $select_lock;
+eval select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 $select_lock;
+eval select * from gap1 order by id1 desc limit 1 $select_lock;
+eval select * from gap1 order by id1 desc, id2 desc, id3 desc
+ limit 1 $select_lock;
+eval select * from gap1 force index(i) where c1=1 $select_lock;
+eval select * from gap3 force index(ui) where value=1 $select_lock;
+eval select * from gap1 where id1=1 and id2=1 and id3=1 $select_lock;
+eval select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) $select_lock;
+eval select * from gap1 where id1=1 and id2=1 and id3=1 and value=1
+ order by c1 $select_lock;
+eval select * from gap3 where id=1 $select_lock;
+eval select * from gap4 where id=1 $select_lock;
+eval select * from gap4 where id in (1, 2, 3) $select_lock;
+eval select * from gap4 $select_lock;
+eval select * from gap4 where id between 3 and 7 $select_lock;
+}
diff --git a/mysql-test/include/gap_lock_error_update.inc b/mysql-test/include/gap_lock_error_update.inc
new file mode 100644
index 00000000000..d456cf81588
--- /dev/null
+++ b/mysql-test/include/gap_lock_error_update.inc
@@ -0,0 +1,91 @@
+eval set session autocommit=$autocommit;
+let $is_gaplock_target = `SELECT @@autocommit = 0 && '$expect_gap_lock_errors' = 1`;
+
+if ($is_gaplock_target)
+{
+## single-table insert,update,delete
+insert into gap1 (id1, id2, id3) values (-1,-1,-1);
+insert into gap1 (id1, id2, id3) values (-1,-1,-1)
+ on duplicate key update value=100;
+--error ER_UNKNOWN_ERROR
+update gap1 set value=100 where id1=1;
+update gap1 set value=100 where id1=1 and id2=1 and id3=1;
+--error ER_UNKNOWN_ERROR
+delete from gap1 where id1=2;
+delete from gap1 where id1=-1 and id2=-1 and id3=-1;
+commit;
+
+## multi-table statements (preventing all gap locks with autocommit)
+# insert into select
+--error ER_UNKNOWN_ERROR
+insert into gap2 select * from gap1;
+--error ER_UNKNOWN_ERROR
+insert into gap2 select * from gap1 where id1=1;
+insert into gap2 select * from gap1 where id1=1 and id2=1 and id3=1;
+
+# create table select
+create table t4 select * from gap1 where id1=1 and id2=1 and id3=1;
+drop table t4;
+--error ER_UNKNOWN_ERROR
+create table t4 select * from gap1;
+--error ER_UNKNOWN_ERROR
+create table t4 select * from gap1 where id1=1;
+
+# update join
+update gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 set gap1.value=100 where gap2.id1=3
+ and gap2.id2=3 and gap2.id3=3;
+--error ER_UNKNOWN_ERROR
+update gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 set gap1.value=100 where gap2.id1=3;
+--error ER_UNKNOWN_ERROR
+update gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 join gap3 on gap1.id1=gap3.id
+ set gap1.value=100 where gap2.id1=3;
+--error ER_UNKNOWN_ERROR
+update gap1 set gap1.value= (select count(*) from gap2);
+
+# delete join
+delete gap1 from gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 where gap2.id1=3
+ and gap2.id2=3 and gap2.id3=3;
+--error ER_UNKNOWN_ERROR
+delete gap1 from gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 where gap2.id1=3;
+
+# select join / self join
+--error ER_UNKNOWN_ERROR
+select * from gap1, gap2 limit 1 for update;
+--error ER_UNKNOWN_ERROR
+select * from gap1 a, gap1 b limit 1 for update;
+
+# unique secondary key
+create table u1(
+ c1 int,
+ c2 int,
+ c3 int,
+ c4 int,
+ primary key (c1, c2, c3),
+ unique key (c3, c1)
+);
+begin;
+insert into u1 values (1,1,1,1);
+commit;
+begin;
+insert into u1 values (1,2,1,1) on duplicate key update c4=10;
+commit;
+begin;
+select * from u1 where c3=1 and c1 = 1 for update;
+--error ER_UNKNOWN_ERROR
+select * from u1 where c3=1 for update;
+commit;
+drop table u1;
+}
+
+if (!$is_gaplock_target)
+{
+# autocommit doesn't prevent single table operations
+insert into gap1 (id1, id2, id3) values (-1,-1,-1);
+insert into gap1 (id1, id2, id3) values (-1,-1,-1)
+ on duplicate key update value=100;
+update gap1 set value=100 where id1=1;
+update gap1 set value=100 where id1=1 and id2=1 and id3=1;
+delete from gap1 where id1=2;
+delete from gap1 where id1=-1 and id2=-1 and id3=-1;
+commit;
+}
diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result
index 2f6662e0132..c8736b557de 100644
--- a/mysql-test/r/derived.result
+++ b/mysql-test/r/derived.result
@@ -1013,6 +1013,29 @@ David Yes 210
Edward Yes 150
DROP TABLE example1463;
set sql_mode= @save_sql_mode;
+#
+# MDEV-9028: SELECT DISTINCT constant column of derived table
+# used as the second operand of LEFT JOIN
+#
+create table t1 (id int, data varchar(255));
+insert into t1 values (1,'yes'),(2,'yes');
+select distinct t1.id, tt.id, tt.data
+from t1
+left join
+(select t1.id, 'yes' as data from t1) as tt
+on t1.id = tt.id;
+id id data
+1 1 yes
+2 2 yes
+select distinct t1.id, tt.id, tt.data
+from t1
+left join
+(select t1.id, 'yes' as data from t1 where id > 1) as tt
+on t1.id = tt.id;
+id id data
+2 2 yes
+1 NULL NULL
+drop table t1;
# end of 5.5
#
# Start of 10.1 tests
diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result
index 3020c7deba7..826d1f6c6b2 100644
--- a/mysql-test/r/grant.result
+++ b/mysql-test/r/grant.result
@@ -2535,6 +2535,54 @@ DROP USER mysqltest_u1@localhost;
# End of Bug#38347.
#
+# BUG#11759114 - '51401: GRANT TREATS NONEXISTENT FUNCTIONS/PRIVILEGES
+# DIFFERENTLY'.
+#
+drop database if exists mysqltest_db1;
+create database mysqltest_db1;
+create user mysqltest_u1;
+# Both GRANT statements below should fail with the same error.
+grant execute on function mysqltest_db1.f1 to mysqltest_u1;
+ERROR 42000: FUNCTION or PROCEDURE f1 does not exist
+grant execute on procedure mysqltest_db1.p1 to mysqltest_u1;
+ERROR 42000: FUNCTION or PROCEDURE p1 does not exist
+# Let us show that GRANT behaviour for routines is consistent
+# with GRANT behaviour for tables. Attempt to grant privilege
+# on non-existent table also results in an error.
+grant select on mysqltest_db1.t1 to mysqltest_u1;
+ERROR 42S02: Table 'mysqltest_db1.t1' doesn't exist
+show grants for mysqltest_u1;
+Grants for mysqltest_u1@%
+GRANT USAGE ON *.* TO 'mysqltest_u1'@'%'
+drop database mysqltest_db1;
+drop user mysqltest_u1;
+#
+# Bug#12766319 - 61865: RENAME USER DOES NOT WORK CORRECTLY -
+# REQUIRES FLUSH PRIVILEGES
+#
+CREATE USER foo@'127.0.0.1';
+GRANT ALL ON *.* TO foo@'127.0.0.1';
+# First attempt, should connect successfully
+SELECT user(), current_user();
+user() current_user()
+foo@localhost foo@127.0.0.1
+# Rename the user
+RENAME USER foo@'127.0.0.1' to foo@'127.0.0.0/255.0.0.0';
+# Second attempt, should connect successfully as its valid mask
+# This was failing without fix
+SELECT user(), current_user();
+user() current_user()
+foo@localhost foo@127.0.0.0/255.0.0.0
+# Rename the user back to original
+RENAME USER foo@'127.0.0.0/255.0.0.0' to foo@'127.0.0.1';
+# Third attempt, should connect successfully
+SELECT user(), current_user();
+user() current_user()
+foo@localhost foo@127.0.0.1
+# Clean-up
+DROP USER foo@'127.0.0.1';
+# End of Bug#12766319
+#
# Bug#11756966 - 48958: STORED PROCEDURES CAN BE LEVERAGED TO BYPASS
# DATABASE SECURITY
#
@@ -2560,26 +2608,4 @@ ERROR 42000: Access denied for user 'untrusted'@'localhost' to database 'secret'
# Connection default
DROP USER untrusted@localhost;
DROP DATABASE secret;
-#
-# BUG#11759114 - '51401: GRANT TREATS NONEXISTENT FUNCTIONS/PRIVILEGES
-# DIFFERENTLY'.
-#
-drop database if exists mysqltest_db1;
-create database mysqltest_db1;
-create user mysqltest_u1;
-# Both GRANT statements below should fail with the same error.
-grant execute on function mysqltest_db1.f1 to mysqltest_u1;
-ERROR 42000: FUNCTION or PROCEDURE f1 does not exist
-grant execute on procedure mysqltest_db1.p1 to mysqltest_u1;
-ERROR 42000: FUNCTION or PROCEDURE p1 does not exist
-# Let us show that GRANT behaviour for routines is consistent
-# with GRANT behaviour for tables. Attempt to grant privilege
-# on non-existent table also results in an error.
-grant select on mysqltest_db1.t1 to mysqltest_u1;
-ERROR 42S02: Table 'mysqltest_db1.t1' doesn't exist
-show grants for mysqltest_u1;
-Grants for mysqltest_u1@%
-GRANT USAGE ON *.* TO 'mysqltest_u1'@'%'
-drop database mysqltest_db1;
-drop user mysqltest_u1;
set GLOBAL sql_mode=default;
diff --git a/mysql-test/r/join_nested.result b/mysql-test/r/join_nested.result
index 84b6ff640e9..6ddd39cbfec 100644
--- a/mysql-test/r/join_nested.result
+++ b/mysql-test/r/join_nested.result
@@ -1870,4 +1870,99 @@ f4
NULL
NULL
DROP TABLE t1,t2,t3,t4,t5;
+#
+# MDEV-7992: Nested left joins + 'not exists' optimization
+#
+CREATE TABLE t1(
+K1 INT PRIMARY KEY,
+Name VARCHAR(15)
+);
+INSERT INTO t1 VALUES
+(1,'T1Row1'), (2,'T1Row2');
+CREATE TABLE t2(
+K2 INT PRIMARY KEY,
+K1r INT,
+rowTimestamp DATETIME,
+Event VARCHAR(15)
+);
+INSERT INTO t2 VALUES
+(1, 1, '2015-04-13 10:42:11' ,'T1Row1Event1'),
+(2, 1, '2015-04-13 10:42:12' ,'T1Row1Event2'),
+(3, 1, '2015-04-13 10:42:12' ,'T1Row1Event3');
+SELECT t1a.*, t2a.*,
+t2i.K2 AS K2B, t2i.K1r AS K1rB,
+t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB
+FROM
+t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1
+LEFT JOIN
+( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1)
+ON (t1i.K1 = 1) AND
+(((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR
+(t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2))
+OR (t2i.K2 IS NULL))
+WHERE
+t2a.K1r = 1 AND t2i.K2 IS NULL;
+K1 Name K2 K1r rowTimestamp Event K2B K1rB rowTimestampB EventB
+1 T1Row1 3 1 2015-04-13 10:42:12 T1Row1Event3 NULL NULL NULL NULL
+EXPLAIN EXTENDED SELECT t1a.*, t2a.*,
+t2i.K2 AS K2B, t2i.K1r AS K1rB,
+t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB
+FROM
+t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1
+LEFT JOIN
+( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1)
+ON (t1i.K1 = 1) AND
+(((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR
+(t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2))
+OR (t2i.K2 IS NULL))
+WHERE
+t2a.K1r = 1 AND t2i.K2 IS NULL;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1a const PRIMARY PRIMARY 4 const 1 100.00
+1 SIMPLE t2a ALL NULL NULL NULL NULL 3 100.00 Using where
+1 SIMPLE t1i const PRIMARY PRIMARY 4 const 1 100.00 Using index
+1 SIMPLE t2i ALL NULL NULL NULL NULL 3 100.00 Using where; Not exists
+Warnings:
+Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`test`.`t2a`.`K2` AS `K2`,`test`.`t2a`.`K1r` AS `K1r`,`test`.`t2a`.`rowTimestamp` AS `rowTimestamp`,`test`.`t2a`.`Event` AS `Event`,`test`.`t2i`.`K2` AS `K2B`,`test`.`t2i`.`K1r` AS `K1rB`,`test`.`t2i`.`rowTimestamp` AS `rowTimestampB`,`test`.`t2i`.`Event` AS `EventB` from `test`.`t1` `t1a` join `test`.`t2` `t2a` left join (`test`.`t1` `t1i` left join `test`.`t2` `t2i` on((`test`.`t2i`.`K1r` = 1))) on(((`test`.`t1i`.`K1` = 1) and (((`test`.`t2i`.`K1r` = 1) and (`test`.`t2i`.`rowTimestamp` > `test`.`t2a`.`rowTimestamp`)) or ((`test`.`t2i`.`rowTimestamp` = `test`.`t2a`.`rowTimestamp`) and (`test`.`t2i`.`K2` > `test`.`t2a`.`K2`)) or isnull(`test`.`t2i`.`K2`)))) where ((`test`.`t2a`.`K1r` = 1) and isnull(`test`.`t2i`.`K2`))
+CREATE VIEW v1 AS
+SELECT t2i.*
+FROM t1 as t1i LEFT JOIN t2 as t2i ON t2i.K1r = t1i.K1
+WHERE t1i.K1 = 1 ;
+SELECT
+t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB,
+t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB
+FROM
+t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1
+LEFT JOIN
+v1 as t2b
+ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR
+(t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2))
+OR (t2b.K2 IS NULL)
+WHERE
+t1a.K1 = 1 AND
+t2b.K2 IS NULL;
+K1 Name K2 K1r rowTimestamp Event K2B K1rB rowTimestampB EventB
+1 T1Row1 3 1 2015-04-13 10:42:12 T1Row1Event3 NULL NULL NULL NULL
+EXPLAIN EXTENDED SELECT
+t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB,
+t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB
+FROM
+t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1
+LEFT JOIN
+v1 as t2b
+ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR
+(t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2))
+OR (t2b.K2 IS NULL)
+WHERE
+t1a.K1 = 1 AND
+t2b.K2 IS NULL;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1a const PRIMARY PRIMARY 4 const 1 100.00
+1 SIMPLE t2a ALL NULL NULL NULL NULL 3 100.00 Using where
+1 SIMPLE t1i const PRIMARY PRIMARY 4 const 1 100.00 Using index
+1 SIMPLE t2i ALL NULL NULL NULL NULL 3 100.00 Using where; Not exists
+Warnings:
+Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`t2a`.`K2` AS `K2`,`t2a`.`K1r` AS `K1r`,`t2a`.`rowTimestamp` AS `rowTimestamp`,`t2a`.`Event` AS `Event`,`test`.`t2i`.`K2` AS `K2B`,`test`.`t2i`.`K1r` AS `K1rB`,`test`.`t2i`.`rowTimestamp` AS `rowTimestampB`,`test`.`t2i`.`Event` AS `EventB` from `test`.`t1` `t1a` join `test`.`t2` `t2a` left join (`test`.`t1` `t1i` left join `test`.`t2` `t2i` on((`test`.`t2i`.`K1r` = 1))) on(((`test`.`t1i`.`K1` = 1) and (((`test`.`t2i`.`K1r` = 1) and (`test`.`t2i`.`rowTimestamp` > `t2a`.`rowTimestamp`)) or ((`test`.`t2i`.`rowTimestamp` = `t2a`.`rowTimestamp`) and (`test`.`t2i`.`K2` > `t2a`.`K2`)) or isnull(`test`.`t2i`.`K2`)))) where ((`t2a`.`K1r` = 1) and isnull(`test`.`t2i`.`K2`))
+DROP VIEW v1;
+DROP TABLE t1,t2;
set optimizer_search_depth= @tmp_mdev621;
diff --git a/mysql-test/r/join_nested_jcl6.result b/mysql-test/r/join_nested_jcl6.result
index 3b47645ca79..bac8e1cb7db 100644
--- a/mysql-test/r/join_nested_jcl6.result
+++ b/mysql-test/r/join_nested_jcl6.result
@@ -1881,6 +1881,101 @@ f4
NULL
NULL
DROP TABLE t1,t2,t3,t4,t5;
+#
+# MDEV-7992: Nested left joins + 'not exists' optimization
+#
+CREATE TABLE t1(
+K1 INT PRIMARY KEY,
+Name VARCHAR(15)
+);
+INSERT INTO t1 VALUES
+(1,'T1Row1'), (2,'T1Row2');
+CREATE TABLE t2(
+K2 INT PRIMARY KEY,
+K1r INT,
+rowTimestamp DATETIME,
+Event VARCHAR(15)
+);
+INSERT INTO t2 VALUES
+(1, 1, '2015-04-13 10:42:11' ,'T1Row1Event1'),
+(2, 1, '2015-04-13 10:42:12' ,'T1Row1Event2'),
+(3, 1, '2015-04-13 10:42:12' ,'T1Row1Event3');
+SELECT t1a.*, t2a.*,
+t2i.K2 AS K2B, t2i.K1r AS K1rB,
+t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB
+FROM
+t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1
+LEFT JOIN
+( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1)
+ON (t1i.K1 = 1) AND
+(((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR
+(t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2))
+OR (t2i.K2 IS NULL))
+WHERE
+t2a.K1r = 1 AND t2i.K2 IS NULL;
+K1 Name K2 K1r rowTimestamp Event K2B K1rB rowTimestampB EventB
+1 T1Row1 3 1 2015-04-13 10:42:12 T1Row1Event3 NULL NULL NULL NULL
+EXPLAIN EXTENDED SELECT t1a.*, t2a.*,
+t2i.K2 AS K2B, t2i.K1r AS K1rB,
+t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB
+FROM
+t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1
+LEFT JOIN
+( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1)
+ON (t1i.K1 = 1) AND
+(((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR
+(t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2))
+OR (t2i.K2 IS NULL))
+WHERE
+t2a.K1r = 1 AND t2i.K2 IS NULL;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1a const PRIMARY PRIMARY 4 const 1 100.00
+1 SIMPLE t2a ALL NULL NULL NULL NULL 3 100.00 Using where
+1 SIMPLE t1i const PRIMARY PRIMARY 4 const 1 100.00 Using index
+1 SIMPLE t2i ALL NULL NULL NULL NULL 3 100.00 Using where; Not exists
+Warnings:
+Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`test`.`t2a`.`K2` AS `K2`,`test`.`t2a`.`K1r` AS `K1r`,`test`.`t2a`.`rowTimestamp` AS `rowTimestamp`,`test`.`t2a`.`Event` AS `Event`,`test`.`t2i`.`K2` AS `K2B`,`test`.`t2i`.`K1r` AS `K1rB`,`test`.`t2i`.`rowTimestamp` AS `rowTimestampB`,`test`.`t2i`.`Event` AS `EventB` from `test`.`t1` `t1a` join `test`.`t2` `t2a` left join (`test`.`t1` `t1i` left join `test`.`t2` `t2i` on((`test`.`t2i`.`K1r` = 1))) on(((`test`.`t1i`.`K1` = 1) and (((`test`.`t2i`.`K1r` = 1) and (`test`.`t2i`.`rowTimestamp` > `test`.`t2a`.`rowTimestamp`)) or ((`test`.`t2i`.`rowTimestamp` = `test`.`t2a`.`rowTimestamp`) and (`test`.`t2i`.`K2` > `test`.`t2a`.`K2`)) or isnull(`test`.`t2i`.`K2`)))) where ((`test`.`t2a`.`K1r` = 1) and isnull(`test`.`t2i`.`K2`))
+CREATE VIEW v1 AS
+SELECT t2i.*
+FROM t1 as t1i LEFT JOIN t2 as t2i ON t2i.K1r = t1i.K1
+WHERE t1i.K1 = 1 ;
+SELECT
+t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB,
+t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB
+FROM
+t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1
+LEFT JOIN
+v1 as t2b
+ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR
+(t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2))
+OR (t2b.K2 IS NULL)
+WHERE
+t1a.K1 = 1 AND
+t2b.K2 IS NULL;
+K1 Name K2 K1r rowTimestamp Event K2B K1rB rowTimestampB EventB
+1 T1Row1 3 1 2015-04-13 10:42:12 T1Row1Event3 NULL NULL NULL NULL
+EXPLAIN EXTENDED SELECT
+t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB,
+t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB
+FROM
+t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1
+LEFT JOIN
+v1 as t2b
+ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR
+(t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2))
+OR (t2b.K2 IS NULL)
+WHERE
+t1a.K1 = 1 AND
+t2b.K2 IS NULL;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1a const PRIMARY PRIMARY 4 const 1 100.00
+1 SIMPLE t2a ALL NULL NULL NULL NULL 3 100.00 Using where
+1 SIMPLE t1i const PRIMARY PRIMARY 4 const 1 100.00 Using index
+1 SIMPLE t2i ALL NULL NULL NULL NULL 3 100.00 Using where; Not exists
+Warnings:
+Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`t2a`.`K2` AS `K2`,`t2a`.`K1r` AS `K1r`,`t2a`.`rowTimestamp` AS `rowTimestamp`,`t2a`.`Event` AS `Event`,`test`.`t2i`.`K2` AS `K2B`,`test`.`t2i`.`K1r` AS `K1rB`,`test`.`t2i`.`rowTimestamp` AS `rowTimestampB`,`test`.`t2i`.`Event` AS `EventB` from `test`.`t1` `t1a` join `test`.`t2` `t2a` left join (`test`.`t1` `t1i` left join `test`.`t2` `t2i` on((`test`.`t2i`.`K1r` = 1))) on(((`test`.`t1i`.`K1` = 1) and (((`test`.`t2i`.`K1r` = 1) and (`test`.`t2i`.`rowTimestamp` > `t2a`.`rowTimestamp`)) or ((`test`.`t2i`.`rowTimestamp` = `t2a`.`rowTimestamp`) and (`test`.`t2i`.`K2` > `t2a`.`K2`)) or isnull(`test`.`t2i`.`K2`)))) where ((`t2a`.`K1r` = 1) and isnull(`test`.`t2i`.`K2`))
+DROP VIEW v1;
+DROP TABLE t1,t2;
set optimizer_search_depth= @tmp_mdev621;
CREATE TABLE t5 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b));
CREATE TABLE t6 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b));
diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result
index ce786c2ede8..f46fecb69cd 100644
--- a/mysql-test/r/mysqldump.result
+++ b/mysql-test/r/mysqldump.result
@@ -5515,3 +5515,4 @@ USE `db1`;
DROP DATABASE db1;
DROP DATABASE db2;
+FOUND /Database: mysql/ in bug11505.sql
diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result
index 850e443be5d..b322d63a7e1 100644
--- a/mysql-test/r/partition_innodb.result
+++ b/mysql-test/r/partition_innodb.result
@@ -734,6 +734,97 @@ SELECT * FROM t1 WHERE d = '1991-01-01';
d
1991-01-01
DROP TABLE t1;
+set global default_storage_engine=default;
+#
+# MDEV-9455: [ERROR] mysqld got signal 11
+#
+CREATE TABLE `t1` (
+`DIARY_TOTAL_DAY_SEQ` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+`IMORY_ID` bigint(20) NOT NULL,
+`NAME` varchar(75) DEFAULT NULL,
+`DATETIME` varchar(10) NOT NULL DEFAULT '',
+`DAILY_CALL_CNT` int(11) DEFAULT NULL,
+`DAILY_SMS_CNT` int(11) DEFAULT NULL,
+`NUMBER` varchar(64) DEFAULT NULL,
+`DURATION` varchar(16) DEFAULT NULL,
+PRIMARY KEY (`DIARY_TOTAL_DAY_SEQ`,`DATETIME`),
+KEY `IDX_t1_01` (`IMORY_ID`,`DATETIME`)
+) AUTO_INCREMENT=328702514 DEFAULT CHARSET=utf8mb4
+PARTITION BY RANGE COLUMNS(`DATETIME`)
+(PARTITION p0 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB,
+PARTITION p1 VALUES LESS THAN ('2015-11-01') ENGINE = InnoDB,
+PARTITION p2 VALUES LESS THAN ('2015-12-01') ENGINE = InnoDB,
+PARTITION p3 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB,
+PARTITION p4 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB,
+PARTITION p5 VALUES LESS THAN ('2016-03-01') ENGINE = InnoDB,
+PARTITION p6 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB,
+PARTITION p7 VALUES LESS THAN ('2016-05-01') ENGINE = InnoDB,
+PARTITION p8 VALUES LESS THAN ('2016-06-01') ENGINE = InnoDB,
+PARTITION p9 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB,
+PARTITION p10 VALUES LESS THAN ('2016-08-01') ENGINE = InnoDB)
+;
+CREATE TABLE `t2` (
+`DIARY_SEQ` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+`IMORY_ID` bigint(20) NOT NULL,
+`CALL_TYPE` varchar(1) DEFAULT NULL,
+`DATA_TYPE` varchar(1) DEFAULT NULL,
+`FEATURES` varchar(1) DEFAULT NULL,
+`NAME` varchar(75) DEFAULT NULL,
+`NUMBER` varchar(64) DEFAULT NULL,
+`DATETIME` datetime NOT NULL,
+`REG_DATE` datetime NOT NULL,
+`TITLE` varchar(50) DEFAULT NULL,
+`BODY` varchar(4200) DEFAULT NULL,
+`MIME_TYPE` varchar(32) DEFAULT NULL,
+`DURATION` varchar(16) DEFAULT NULL,
+`DEVICE_ID` varchar(64) DEFAULT NULL,
+`DEVICE_NAME` varchar(32) DEFAULT NULL,
+PRIMARY KEY (`DIARY_SEQ`,`DATETIME`,`REG_DATE`),
+KEY `IDX_TB_DIARY_01` (`IMORY_ID`,`DATETIME`,`CALL_TYPE`,`NUMBER`),
+KEY `IDX_TB_DIARY_02` (`REG_DATE`)
+) AUTO_INCREMENT=688799006 DEFAULT CHARSET=utf8mb4
+PARTITION BY RANGE COLUMNS(REG_DATE)
+(PARTITION p0 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB,
+PARTITION p1 VALUES LESS THAN ('2015-11-01') ENGINE = InnoDB,
+PARTITION p2 VALUES LESS THAN ('2015-12-01') ENGINE = InnoDB,
+PARTITION p3 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB,
+PARTITION p4 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB,
+PARTITION p5 VALUES LESS THAN ('2016-03-01') ENGINE = InnoDB,
+PARTITION p6 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB,
+PARTITION p7 VALUES LESS THAN ('2016-05-01') ENGINE = InnoDB,
+PARTITION p8 VALUES LESS THAN ('2016-06-01') ENGINE = InnoDB,
+PARTITION p9 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB,
+PARTITION p10 VALUES LESS THAN ('2016-08-01') ENGINE = InnoDB)
+;
+SELECT
+A.IMORY_ID,
+A.NUMBER,
+A.NAME,
+DATE_FORMAT(A.DATETIME, '%Y-%m-%d') AS TARGET_DATE,
+SUM( CASE WHEN A.DATA_TYPE='1' THEN 1 ELSE 0 END) AS CALL_CNT,
+SUM( CASE WHEN A.DATA_TYPE IN ('2', '3') THEN 1 ELSE 0 END) AS SMS_CNT,
+SUM(CAST(A.DURATION AS INT)) AS DURATION,
+( SELECT COUNT(*)
+FROM t1
+WHERE IMORY_ID=A.IMORY_ID
+AND NUMBER=A.NUMBER
+AND NAME=A.NAME
+AND DATETIME = DATE_FORMAT(A.DATETIME, '%Y-%m-%d')
+) STATS_COUNT
+FROM t2 A
+WHERE A.IMORY_ID = 55094102
+AND A.DATETIME LIKE (
+SELECT CONCAT (DATE_FORMAT(DATETIME, '%Y-%m-%d') ,'%')
+FROM t2
+WHERE IMORY_ID=55094102
+AND DIARY_SEQ IN ( 608351221, 608351225, 608351229 )
+group by DATE_FORMAT(DATETIME, '%Y-%m-%d')
+)
+GROUP BY A.IMORY_ID, A.NUMBER, A.NAME, DATE_FORMAT(A.DATETIME, '%Y-%m-%d')
+;
+IMORY_ID NUMBER NAME TARGET_DATE CALL_CNT SMS_CNT DURATION STATS_COUNT
+drop table t2, t1;
+set global default_storage_engine='innodb';
#
# MDEV-5963: InnoDB: Assertion failure in file row0sel.cc line 2503,
# Failing assertion: 0 with "key ptr now exceeds key end by 762 bytes"
diff --git a/mysql-test/r/partition_myisam.result b/mysql-test/r/partition_myisam.result
index bb1a7b19a9d..08a5bf72f64 100644
--- a/mysql-test/r/partition_myisam.result
+++ b/mysql-test/r/partition_myisam.result
@@ -230,6 +230,22 @@ PARTITION pMax VALUES LESS THAN MAXVALUE);
INSERT INTO t1 VALUES (1, "Partition p1, first row");
DROP TABLE t1;
#
+# MDEV-10418 Assertion `m_extra_cache' failed
+# in ha_partition::late_extra_cache(uint)
+#
+CREATE TABLE t1 (f1 INT) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1),(2);
+CREATE TABLE t2 (f2 INT) ENGINE=MyISAM PARTITION BY RANGE(f2) (PARTITION pmax VALUES LESS THAN MAXVALUE);
+INSERT INTO t2 VALUES (8);
+CREATE ALGORITHM = MERGE VIEW v AS SELECT f2 FROM t2, t1;
+UPDATE v SET f2 = 1;
+SELECT * FROM t2;
+f2
+1
+DROP VIEW v;
+DROP TABLE t2;
+DROP TABLE t1;
+#
# bug#11760213-52599: ALTER TABLE REMOVE PARTITIONING ON NON-PARTITIONED
# TABLE CORRUPTS MYISAM
DROP TABLE if exists `t1`;
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index 6c21f4225a0..1e63ac57768 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -4108,4 +4108,76 @@ NULL
NULL
deallocate prepare stmt;
drop table t1,t2,t3,t4;
+#
+# MDEV-11859: the plans for the first and the second executions
+# of PS are not the same
+#
+create table t1 (id int, c varchar(3), key idx(c))engine=myisam;
+insert into t1 values (3,'bar'), (1,'xxx'), (2,'foo'), (5,'yyy');
+prepare stmt1 from
+"explain extended
+ select * from t1 where (1, 2) in ( select 3, 4 ) or c = 'foo'";
+execute stmt1;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ref idx idx 6 const 1 100.00 Using index condition
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`c` AS `c` from `test`.`t1` where (`test`.`t1`.`c` = 'foo')
+execute stmt1;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ref idx idx 6 const 1 100.00 Using index condition
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`c` AS `c` from `test`.`t1` where (`test`.`t1`.`c` = 'foo')
+deallocate prepare stmt1;
+prepare stmt1 from
+"select * from t1 where (1, 2) in ( select 3, 4 ) or c = 'foo'";
+flush status;
+execute stmt1;
+id c
+2 foo
+show status like '%Handler_read%';
+Variable_name Value
+Handler_read_first 0
+Handler_read_key 1
+Handler_read_last 0
+Handler_read_next 1
+Handler_read_prev 0
+Handler_read_retry 0
+Handler_read_rnd 0
+Handler_read_rnd_deleted 0
+Handler_read_rnd_next 0
+flush status;
+execute stmt1;
+id c
+2 foo
+show status like '%Handler_read%';
+Variable_name Value
+Handler_read_first 0
+Handler_read_key 1
+Handler_read_last 0
+Handler_read_next 1
+Handler_read_prev 0
+Handler_read_retry 0
+Handler_read_rnd 0
+Handler_read_rnd_deleted 0
+Handler_read_rnd_next 0
+deallocate prepare stmt1;
+prepare stmt2 from
+"explain extended
+ select * from t1 where (1, 2) in ( select 3, 4 )";
+execute stmt2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`c` AS `c` from `test`.`t1` where 0
+execute stmt2;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used
+Warnings:
+Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`c` AS `c` from `test`.`t1` where 0
+deallocate prepare stmt2;
+drop table t1;
# End of 5.5 tests
diff --git a/mysql-test/r/range_vs_index_merge.result b/mysql-test/r/range_vs_index_merge.result
index 9af359a55f3..6813c40a5cf 100644
--- a/mysql-test/r/range_vs_index_merge.result
+++ b/mysql-test/r/range_vs_index_merge.result
@@ -60,11 +60,11 @@ id select_type table type possible_keys key key_len ref rows Extra
EXPLAIN
SELECT * FROM City
WHERE Population > 100000 AND Name LIKE 'Aba%' OR
-Country IN ('CAN', 'ARG') AND ID < 3800 OR
-Country < 'U' AND Name LIKE 'Zhu%' OR
-ID BETWEEN 3800 AND 3810;
+Country IN ('CAN', 'ARG') AND ID BETWEEN 120 AND 130 OR
+Country <= 'ALB' AND Name LIKE 'L%' OR
+ID BETWEEN 3807 AND 3810;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Country,PRIMARY 35,3,4 NULL 132 Using sort_union(Name,Country,PRIMARY); Using where
+1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,PRIMARY,Country 35,4,3 NULL 31 Using sort_union(Name,PRIMARY,Country); Using where
EXPLAIN
SELECT * FROM City
WHERE (Population > 101000 AND Population < 115000);
@@ -1769,4 +1769,42 @@ a b
167 9999
168 10000
DROP TABLE t1;
+#
+# MDEV-8603: Wrong result OR/AND condition over index fields
+#
+CREATE TABLE t1 (
+id INT NOT NULL,
+state VARCHAR(64),
+capital VARCHAR(64),
+UNIQUE KEY (id),
+KEY state (state,id),
+KEY capital (capital, id)
+);
+INSERT INTO t1 VALUES
+(1,'Arizona','Phoenix'),
+(2,'Hawaii','Honolulu'),
+(3,'Georgia','Atlanta'),
+(4,'Florida','Tallahassee'),
+(5,'Alaska','Juneau'),
+(6,'Michigan','Lansing'),
+(7,'Pennsylvania','Harrisburg'),
+(8,'Virginia','Richmond')
+;
+EXPLAIN
+SELECT * FROM t1 FORCE KEY (state,capital)
+WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9
+OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas';
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range state,capital state 71 NULL 12 Using index condition; Using where
+SELECT * FROM t1 FORCE KEY (state,capital)
+WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9
+OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas';
+id state capital
+4 Florida Tallahassee
+3 Georgia Atlanta
+2 Hawaii Honolulu
+6 Michigan Lansing
+7 Pennsylvania Harrisburg
+8 Virginia Richmond
+DROP TABLE t1;
set session optimizer_switch='index_merge_sort_intersection=default';
diff --git a/mysql-test/r/range_vs_index_merge_innodb.result b/mysql-test/r/range_vs_index_merge_innodb.result
index 601ae9b7613..13fbc0ac3ef 100644
--- a/mysql-test/r/range_vs_index_merge_innodb.result
+++ b/mysql-test/r/range_vs_index_merge_innodb.result
@@ -61,11 +61,11 @@ id select_type table type possible_keys key key_len ref rows Extra
EXPLAIN
SELECT * FROM City
WHERE Population > 100000 AND Name LIKE 'Aba%' OR
-Country IN ('CAN', 'ARG') AND ID < 3800 OR
-Country < 'U' AND Name LIKE 'Zhu%' OR
-ID BETWEEN 3800 AND 3810;
+Country IN ('CAN', 'ARG') AND ID BETWEEN 120 AND 130 OR
+Country <= 'ALB' AND Name LIKE 'L%' OR
+ID BETWEEN 3807 AND 3810;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Country,PRIMARY 35,7,4 NULL 123 Using sort_union(Name,Country,PRIMARY); Using where
+1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Country,PRIMARY 35,3,4 NULL 33 Using sort_union(Name,Country,PRIMARY); Using where
EXPLAIN
SELECT * FROM City
WHERE (Population > 101000 AND Population < 115000);
@@ -369,7 +369,7 @@ WHERE ((ID < 200) AND (Name LIKE 'Ha%' OR (Country > 'A' AND Country < 'ARG')))
OR ((ID BETWEEN 100 AND 200) AND
(Name LIKE 'Pa%' OR (Population > 103000 AND Population < 104000)));
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE City range PRIMARY,Population,Country,Name PRIMARY 4 NULL 200 Using where
+1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Population,PRIMARY 39,4,4 NULL 305 Using sort_union(Name,Population,PRIMARY); Using where
SELECT * FROM City USE INDEX ()
WHERE ((ID < 10) AND (Name LIKE 'H%' OR (Country > 'A' AND Country < 'ARG')))
OR ((ID BETWEEN 100 AND 110) AND
@@ -1770,5 +1770,43 @@ a b
167 9999
168 10000
DROP TABLE t1;
+#
+# MDEV-8603: Wrong result OR/AND condition over index fields
+#
+CREATE TABLE t1 (
+id INT NOT NULL,
+state VARCHAR(64),
+capital VARCHAR(64),
+UNIQUE KEY (id),
+KEY state (state,id),
+KEY capital (capital, id)
+);
+INSERT INTO t1 VALUES
+(1,'Arizona','Phoenix'),
+(2,'Hawaii','Honolulu'),
+(3,'Georgia','Atlanta'),
+(4,'Florida','Tallahassee'),
+(5,'Alaska','Juneau'),
+(6,'Michigan','Lansing'),
+(7,'Pennsylvania','Harrisburg'),
+(8,'Virginia','Richmond')
+;
+EXPLAIN
+SELECT * FROM t1 FORCE KEY (state,capital)
+WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9
+OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas';
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 range state,capital state 71 NULL 10 Using index condition; Using where
+SELECT * FROM t1 FORCE KEY (state,capital)
+WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9
+OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas';
+id state capital
+4 Florida Tallahassee
+3 Georgia Atlanta
+2 Hawaii Honolulu
+6 Michigan Lansing
+7 Pennsylvania Harrisburg
+8 Virginia Richmond
+DROP TABLE t1;
set session optimizer_switch='index_merge_sort_intersection=default';
SET SESSION STORAGE_ENGINE=DEFAULT;
diff --git a/mysql-test/r/repair_symlink-5543.result b/mysql-test/r/repair_symlink-5543.result
index 98ded32686e..e827c7b7f73 100644
--- a/mysql-test/r/repair_symlink-5543.result
+++ b/mysql-test/r/repair_symlink-5543.result
@@ -2,12 +2,15 @@ create table t1 (a int) engine=myisam data directory='MYSQL_TMP_DIR';
insert t1 values (1);
repair table t1;
Table Op Msg_type Msg_text
-test.t1 repair status OK
+test.t1 repair error 40 for record at pos 0
+test.t1 repair Error File 'MYSQL_TMP_DIR/t1.MYD' not found (Errcode: 40 "Too many levels of symbolic links")
+test.t1 repair status Operation failed
drop table t1;
create table t2 (a int) engine=aria data directory='MYSQL_TMP_DIR';
insert t2 values (1);
repair table t2;
Table Op Msg_type Msg_text
-test.t2 repair status OK
+test.t2 repair error 40 for record at pos 256
+test.t2 repair Error File 'MYSQL_TMP_DIR/t2.MAD' not found (Errcode: 40 "Too many levels of symbolic links")
+test.t2 repair status Operation failed
drop table t2;
-foobar5543
diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result
index 3783ba12db2..b1c1855a789 100644
--- a/mysql-test/r/subselect4.result
+++ b/mysql-test/r/subselect4.result
@@ -2440,6 +2440,40 @@ EXECUTE stmt;
i
6
drop table t1, t2, t3;
+#
+# MDEV-11078: NULL NOT IN (non-empty subquery) should never return results
+#
+create table t1(a int,b int);
+create table t2(a int,b int);
+insert into t1 value (1,2);
+select (NULL) in (select 1 from t1);
+(NULL) in (select 1 from t1)
+NULL
+select (null) in (select 1 from t2);
+(null) in (select 1 from t2)
+0
+select 1 in (select 1 from t1);
+1 in (select 1 from t1)
+1
+select 1 in (select 1 from t2);
+1 in (select 1 from t2)
+0
+select 1 from dual where null in (select 1 from t1);
+1
+select 1 from dual where null in (select 1 from t2);
+1
+select (null,null) in (select * from t1);
+(null,null) in (select * from t1)
+NULL
+select (null,null) in (select * from t2);
+(null,null) in (select * from t2)
+0
+select 1 from dual where null not in (select 1 from t1);
+1
+select 1 from dual where null not in (select 1 from t2);
+1
+1
+drop table t1,t2;
SET optimizer_switch= @@global.optimizer_switch;
set @@tmp_table_size= @@global.tmp_table_size;
#
diff --git a/mysql-test/r/subselect_innodb.result b/mysql-test/r/subselect_innodb.result
index cfbe5d41418..11221c797ff 100644
--- a/mysql-test/r/subselect_innodb.result
+++ b/mysql-test/r/subselect_innodb.result
@@ -494,6 +494,21 @@ HAVING SQ2_alias1 . col_int_key >= 7
drop table t1;
set optimizer_switch=@subselect_innodb_tmp;
#
+# MDEV-9635:Server crashes in part_of_refkey or assertion
+# `!created && key_to_save < (int)s->keys' failed in
+# TABLE::use_index(int) or with join_cache_level>2
+#
+SET join_cache_level=3;
+CREATE TABLE t1 (f1 VARCHAR(1024)) ENGINE=InnoDB;
+CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1;
+CREATE TABLE t2 (f2 VARCHAR(4)) ENGINE=InnoDB;
+INSERT INTO t2 VALUES ('foo'),('bar');
+SELECT * FROM v1, t2 WHERE ( f1, f2 ) IN ( SELECT f1, f1 FROM t1 );
+f1 f2
+set join_cache_level = default;
+drop view v1;
+drop table t1,t2;
+#
# MDEV-6041: ORDER BY+subqueries: subquery_table.key=outer_table.col is not recongized as binding
#
create table t1(a int) engine=innodb;
diff --git a/mysql-test/r/symlink-aria-11902.result b/mysql-test/r/symlink-aria-11902.result
new file mode 100644
index 00000000000..66405b1c25f
--- /dev/null
+++ b/mysql-test/r/symlink-aria-11902.result
@@ -0,0 +1,39 @@
+set default_storage_engine=Aria;
+call mtr.add_suppression("File.*t1.* not found");
+create table mysql.t1 (a int, b char(16), index(a));
+insert mysql.t1 values (100, 'test'),(101,'test');
+create table t1 (a int, b char(16), index(a))
+data directory="MYSQLTEST_VARDIR/tmp/foo";
+insert t1 values (200, 'some'),(201,'some');
+select * from t1;
+a b
+200 some
+201 some
+flush tables;
+set debug_sync='mi_open_datafile SIGNAL ok WAIT_FOR go';
+select * from t1;
+set debug_sync='now WAIT_FOR ok';
+set debug_sync='now SIGNAL go';
+ERROR HY000: File 'MYSQLTEST_VARDIR/tmp/foo/t1.MAD' not found (Errcode: 20 "Not a directory")
+flush tables;
+drop table if exists t1;
+create table t1 (a int, b char(16), index (a))
+index directory="MYSQLTEST_VARDIR/tmp/foo";
+insert t1 values (200, 'some'),(201,'some');
+explain select a from t1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 2 Using index
+select a from t1;
+a
+200
+201
+flush tables;
+set debug_sync='mi_open_kfile SIGNAL waiting WAIT_FOR run';
+select a from t1;
+set debug_sync='now WAIT_FOR waiting';
+set debug_sync='now SIGNAL run';
+ERROR HY000: Can't find file: './test/t1.MAI' (errno: 20 "Not a directory")
+flush tables;
+drop table if exists t1;
+drop table mysql.t1;
+set debug_sync='RESET';
diff --git a/mysql-test/r/symlink-myisam-11902.result b/mysql-test/r/symlink-myisam-11902.result
new file mode 100644
index 00000000000..4b07aa3f4a7
--- /dev/null
+++ b/mysql-test/r/symlink-myisam-11902.result
@@ -0,0 +1,38 @@
+call mtr.add_suppression("File.*t1.* not found");
+create table mysql.t1 (a int, b char(16), index(a));
+insert mysql.t1 values (100, 'test'),(101,'test');
+create table t1 (a int, b char(16), index(a))
+data directory="MYSQLTEST_VARDIR/tmp/foo";
+insert t1 values (200, 'some'),(201,'some');
+select * from t1;
+a b
+200 some
+201 some
+flush tables;
+set debug_sync='mi_open_datafile SIGNAL ok WAIT_FOR go';
+select * from t1;
+set debug_sync='now WAIT_FOR ok';
+set debug_sync='now SIGNAL go';
+ERROR HY000: File 'MYSQLTEST_VARDIR/tmp/foo/t1.MYD' not found (Errcode: 20 "Not a directory")
+flush tables;
+drop table if exists t1;
+create table t1 (a int, b char(16), index (a))
+index directory="MYSQLTEST_VARDIR/tmp/foo";
+insert t1 values (200, 'some'),(201,'some');
+explain select a from t1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 index NULL a 5 NULL 2 Using index
+select a from t1;
+a
+200
+201
+flush tables;
+set debug_sync='mi_open_kfile SIGNAL waiting WAIT_FOR run';
+select a from t1;
+set debug_sync='now WAIT_FOR waiting';
+set debug_sync='now SIGNAL run';
+ERROR HY000: Can't find file: './test/t1.MYI' (errno: 20 "Not a directory")
+flush tables;
+drop table if exists t1;
+drop table mysql.t1;
+set debug_sync='RESET';
diff --git a/mysql-test/r/table_elim.result b/mysql-test/r/table_elim.result
index 3e0cc480aec..3e66880ae84 100644
--- a/mysql-test/r/table_elim.result
+++ b/mysql-test/r/table_elim.result
@@ -597,7 +597,8 @@ CREATE TABLE t1 (a int(11), b varchar(1)) ;
INSERT IGNORE INTO t1 VALUES (0,'g');
CREATE TABLE t3 ( a varchar(1)) ;
INSERT IGNORE INTO t3 VALUES ('g');
-CREATE TABLE t2 ( a int(11) NOT NULL, PRIMARY KEY (a)) ;
+CREATE TABLE t2 ( a int(11) NOT NULL, PRIMARY KEY (a));
+INSERT INTO t2 VALUES (9), (10);
create view v1 as SELECT t1.* FROM t1 LEFT JOIN t2 ON ( t1.a = t2.a ) WHERE t2.a <> 0;
SELECT alias1.* FROM t3 LEFT JOIN v1 as alias1 ON ( t3.a = alias1.b );
a b
@@ -606,7 +607,7 @@ EXPLAIN SELECT alias1.* FROM t3 LEFT JOIN v1 as alias1 ON ( t3.a = alias1.b );
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t3 system NULL NULL NULL NULL 1
1 SIMPLE t1 ALL NULL NULL NULL NULL 1 Using where
-1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using where; Using index
+1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using index
drop view v1;
DROP TABLE t1,t2,t3;
#
diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index 0335381ee44..0e967ee0b3c 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -5568,6 +5568,89 @@ Warnings:
Warning 1356 View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
drop view v1;
drop table t1,t2;
+#
+# MDEV-12099: usage of mergeable view with LEFT JOIN
+# that can be converted to INNER JOIN
+#
+create table t1 (a int, b int, key(a)) engine=myisam;
+insert into t1 values
+(3,20), (7,10), (2,10), (4,30), (8,70),
+(7,70), (9,100), (9,60), (8,80), (7,60);
+create table t2 (c int, d int, key (c)) engine=myisam;
+insert into t2 values
+(50,100), (20, 200), (10,300),
+(150,100), (120, 200), (110,300),
+(250,100), (220, 200), (210,300);
+create table t3(e int, f int not null, key(e), unique (f)) engine=myisam;
+insert into t3 values
+(100, 3), (300, 5), (400, 4), (300,7),
+(300,2), (600, 13), (800, 15), (700, 14),
+(600, 23), (800, 25), (700, 24);
+create view v1 as
+select * from t2 left join t3 on t3.e=t2.d where t3.f is not null;
+select *
+from t1 left join v1 on v1.c=t1.b
+where t1.a < 5;
+a b c d e f
+2 10 10 300 300 5
+2 10 10 300 300 7
+2 10 10 300 300 2
+3 20 NULL NULL NULL NULL
+4 30 NULL NULL NULL NULL
+select *
+from t1 left join ( t2 left join t3 on t3.e=t2.d )
+on t2.c=t1.b and t3.f is not null
+where t1.a < 5;
+a b c d e f
+2 10 10 300 300 5
+2 10 10 300 300 7
+2 10 10 300 300 2
+3 20 NULL NULL NULL NULL
+4 30 NULL NULL NULL NULL
+explain extended
+select *
+from t1 left join v1 on v1.c=t1.b
+where t1.a < 5;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 range a a 5 NULL 3 100.00 Using index condition
+1 SIMPLE t2 ref c c 5 test.t1.b 2 100.00 Using where
+1 SIMPLE t3 ref f,e e 5 test.t2.d 2 100.00 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d`,`test`.`t3`.`e` AS `e`,`test`.`t3`.`f` AS `f` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t2`.`c` = `test`.`t1`.`b`) and (`test`.`t3`.`e` = `test`.`t2`.`d`) and (`test`.`t3`.`f` is not null) and (`test`.`t1`.`b` is not null) and (`test`.`t2`.`d` is not null))) where (`test`.`t1`.`a` < 5)
+explain extended
+select *
+from t1 left join ( t2 left join t3 on t3.e=t2.d )
+on t2.c=t1.b and t3.f is not null
+where t1.a < 5;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 range a a 5 NULL 3 100.00 Using index condition
+1 SIMPLE t2 ref c c 5 test.t1.b 2 100.00 Using where
+1 SIMPLE t3 ref f,e e 5 test.t2.d 2 100.00 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d`,`test`.`t3`.`e` AS `e`,`test`.`t3`.`f` AS `f` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t2`.`c` = `test`.`t1`.`b`) and (`test`.`t3`.`e` = `test`.`t2`.`d`) and (`test`.`t3`.`f` is not null) and (`test`.`t1`.`b` is not null) and (`test`.`t2`.`d` is not null))) where (`test`.`t1`.`a` < 5)
+explain extended
+select *
+from t1 left join v1 on v1.c=t1.b and v1.f=t1.a
+where t1.a < 5;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 range a a 5 NULL 3 100.00 Using index condition
+1 SIMPLE t3 eq_ref f,e f 4 test.t1.a 1 100.00 Using where
+1 SIMPLE t2 ref c c 5 test.t1.b 2 100.00 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d`,`test`.`t3`.`e` AS `e`,`test`.`t3`.`f` AS `f` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t2`.`c` = `test`.`t1`.`b`) and (`test`.`t3`.`f` = `test`.`t1`.`a`) and (`test`.`t2`.`d` = `test`.`t3`.`e`) and (`test`.`t1`.`a` is not null) and (`test`.`t1`.`a` is not null) and (`test`.`t1`.`b` is not null))) where (`test`.`t1`.`a` < 5)
+explain extended
+select *
+from t1 left join ( t2 left join t3 on t3.e=t2.d )
+on t2.c=t1.b and t3.f=t1.a and t3.f is not null
+where t1.a < 5;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 SIMPLE t1 range a a 5 NULL 3 100.00 Using index condition
+1 SIMPLE t3 eq_ref f,e f 4 test.t1.a 1 100.00 Using where
+1 SIMPLE t2 ref c c 5 test.t1.b 2 100.00 Using where
+Warnings:
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d`,`test`.`t3`.`e` AS `e`,`test`.`t3`.`f` AS `f` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t2`.`c` = `test`.`t1`.`b`) and (`test`.`t3`.`f` = `test`.`t1`.`a`) and (`test`.`t2`.`d` = `test`.`t3`.`e`) and (`test`.`t1`.`a` is not null) and (`test`.`t1`.`a` is not null) and (`test`.`t1`.`b` is not null))) where (`test`.`t1`.`a` < 5)
+drop view v1;
+drop table t1,t2,t3;
# -----------------------------------------------------------------
# -- End of 5.5 tests.
# -----------------------------------------------------------------
diff --git a/mysql-test/suite/federated/federated_bug_35333.test b/mysql-test/suite/federated/federated_bug_35333.test
index 47feefd75a1..d43f4e930f8 100644
--- a/mysql-test/suite/federated/federated_bug_35333.test
+++ b/mysql-test/suite/federated/federated_bug_35333.test
@@ -61,6 +61,7 @@ let $MYSQLD_DATADIR= `SELECT @@datadir`;
--echo #
--echo # Trigger a MyISAM system error during an INFORMATION_SCHEMA.TABLES query
--echo #
+--replace_result 20 2
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1';
diff --git a/mysql-test/suite/innodb/r/log_file_size.result b/mysql-test/suite/innodb/r/log_file_size.result
index d0b389379e7..67794f293aa 100644
--- a/mysql-test/suite/innodb/r/log_file_size.result
+++ b/mysql-test/suite/innodb/r/log_file_size.result
@@ -20,28 +20,24 @@ ERROR 42000: Unknown storage engine 'InnoDB'
FOUND /syntax error in innodb_log_group_home_dir/ in mysqld.1.err
SELECT * FROM t1;
ERROR 42000: Unknown storage engine 'InnoDB'
-FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err
+FOUND /InnoDB: Starting crash recovery from checkpoint LSN=/ in mysqld.1.err
SELECT * FROM t1;
ERROR 42000: Unknown storage engine 'InnoDB'
-FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err
SELECT * FROM t1;
ERROR 42000: Unknown storage engine 'InnoDB'
FOUND /InnoDB: innodb_read_only prevents crash recovery/ in mysqld.1.err
SELECT * FROM t1;
ERROR 42000: Unknown storage engine 'InnoDB'
-FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err
-FOUND /InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err
+FOUND /redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err
SELECT * FROM t1;
ERROR 42000: Unknown storage engine 'InnoDB'
-FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err
-FOUND /InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err
+FOUND /redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err
SELECT * FROM t1;
ERROR 42000: Unknown storage engine 'InnoDB'
FOUND /InnoDB: innodb_read_only prevents crash recovery/ in mysqld.1.err
SELECT * FROM t1;
ERROR 42000: Unknown storage engine 'InnoDB'
-FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err
-FOUND /InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err
+FOUND /redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err
SELECT * FROM t1;
ERROR 42000: Unknown storage engine 'InnoDB'
SELECT * FROM t1;
diff --git a/mysql-test/suite/innodb/t/log_file_size.test b/mysql-test/suite/innodb/t/log_file_size.test
index ae63ee6e133..bf307123734 100644
--- a/mysql-test/suite/innodb/t/log_file_size.test
+++ b/mysql-test/suite/innodb/t/log_file_size.test
@@ -76,15 +76,13 @@ let SEARCH_PATTERN= syntax error in innodb_log_group_home_dir;
--source include/restart_mysqld.inc
--error ER_UNKNOWN_STORAGE_ENGINE
SELECT * FROM t1;
-let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records;
+let SEARCH_PATTERN= InnoDB: Starting crash recovery from checkpoint LSN=;
--source include/search_pattern_in_file.inc
--let $restart_parameters= --debug=d,innodb_log_abort_3
--source include/restart_mysqld.inc
--error ER_UNKNOWN_STORAGE_ENGINE
SELECT * FROM t1;
-let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records;
---source include/search_pattern_in_file.inc
--let $restart_parameters= --innodb-read-only
--source include/restart_mysqld.inc
@@ -98,18 +96,14 @@ let SEARCH_PATTERN= InnoDB: innodb_read_only prevents crash recovery;
--source include/restart_mysqld.inc
--error ER_UNKNOWN_STORAGE_ENGINE
SELECT * FROM t1;
-let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records;
---source include/search_pattern_in_file.inc
-let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
+let SEARCH_PATTERN= redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
--source include/search_pattern_in_file.inc
--let $restart_parameters= --debug=d,innodb_log_abort_5
--source include/restart_mysqld.inc
--error ER_UNKNOWN_STORAGE_ENGINE
SELECT * FROM t1;
-let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records;
---source include/search_pattern_in_file.inc
-let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
+let SEARCH_PATTERN= redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
--source include/search_pattern_in_file.inc
--let $restart_parameters= --innodb-read-only
@@ -124,9 +118,7 @@ let SEARCH_PATTERN= InnoDB: innodb_read_only prevents crash recovery;
--error ER_UNKNOWN_STORAGE_ENGINE
SELECT * FROM t1;
-let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records;
---source include/search_pattern_in_file.inc
-let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
+let SEARCH_PATTERN= redo log from 3\*[0-9]+ to 2\*[0-9]+ pages;
--source include/search_pattern_in_file.inc
--let $restart_parameters= --debug=d,innodb_log_abort_7
diff --git a/mysql-test/suite/parts/r/partition_bigint_innodb.result b/mysql-test/suite/parts/r/partition_bigint_innodb.result
new file mode 100644
index 00000000000..bb0f08d9356
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_bigint_innodb.result
@@ -0,0 +1,121 @@
+create table t1 (a bigint unsigned not null, primary key(a)) engine='InnoDB'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` bigint(20) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
+insert into t1 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612), (1), (2), (65535);
+select * from t1;
+a
+1
+18446744073709551612
+18446744073709551613
+18446744073709551614
+18446744073709551615
+2
+65535
+select * from t1 where a=-2;
+a
+delete from t1 where a=-2;
+select * from t1;
+a
+1
+18446744073709551612
+18446744073709551613
+18446744073709551614
+18446744073709551615
+2
+65535
+select * from t1 where a=18446744073709551615;
+a
+18446744073709551615
+delete from t1 where a=18446744073709551615;
+select * from t1;
+a
+1
+18446744073709551612
+18446744073709551613
+18446744073709551614
+2
+65535
+drop table t1;
+create table t2 (a bigint unsigned not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 8;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` bigint(20) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 8 */
+insert into t2 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612);
+select * from t2;
+a
+18446744073709551612
+18446744073709551613
+18446744073709551614
+18446744073709551615
+select * from t2 where a=18446744073709551615;
+a
+18446744073709551615
+delete from t2 where a=18446744073709551615;
+select * from t2;
+a
+18446744073709551612
+18446744073709551613
+18446744073709551614
+delete from t2;
+1024 inserts;
+select count(*) from t2;
+count(*)
+1024
+drop table t2;
+create table t3 (a bigint not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 7;
+show create table t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `a` bigint(20) NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 7 */
+insert into t3 values (9223372036854775807), (9223372036854775806), (9223372036854775805), (9223372036854775804), (-9223372036854775808), (-9223372036854775807), (1), (-1), (0);
+select * from t3;
+a
+-1
+-9223372036854775807
+-9223372036854775808
+0
+1
+9223372036854775804
+9223372036854775805
+9223372036854775806
+9223372036854775807
+select * from t3 where a=9223372036854775806;
+a
+9223372036854775806
+delete from t3 where a=9223372036854775806;
+select * from t3;
+a
+-1
+-9223372036854775807
+-9223372036854775808
+0
+1
+9223372036854775804
+9223372036854775805
+9223372036854775807
+drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_bigint_myisam.result b/mysql-test/suite/parts/r/partition_bigint_myisam.result
new file mode 100644
index 00000000000..5938bcaf7d8
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_bigint_myisam.result
@@ -0,0 +1,121 @@
+create table t1 (a bigint unsigned not null, primary key(a)) engine='MYISAM'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` bigint(20) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
+insert into t1 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612), (1), (2), (65535);
+select * from t1;
+a
+1
+18446744073709551612
+18446744073709551613
+18446744073709551614
+18446744073709551615
+2
+65535
+select * from t1 where a=-2;
+a
+delete from t1 where a=-2;
+select * from t1;
+a
+1
+18446744073709551612
+18446744073709551613
+18446744073709551614
+18446744073709551615
+2
+65535
+select * from t1 where a=18446744073709551615;
+a
+18446744073709551615
+delete from t1 where a=18446744073709551615;
+select * from t1;
+a
+1
+18446744073709551612
+18446744073709551613
+18446744073709551614
+2
+65535
+drop table t1;
+create table t2 (a bigint unsigned not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 8;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` bigint(20) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 8 */
+insert into t2 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612);
+select * from t2;
+a
+18446744073709551612
+18446744073709551613
+18446744073709551614
+18446744073709551615
+select * from t2 where a=18446744073709551615;
+a
+18446744073709551615
+delete from t2 where a=18446744073709551615;
+select * from t2;
+a
+18446744073709551612
+18446744073709551613
+18446744073709551614
+delete from t2;
+65535 inserts;
+select count(*) from t2;
+count(*)
+65535
+drop table t2;
+create table t3 (a bigint not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 7;
+show create table t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `a` bigint(20) NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 7 */
+insert into t3 values (9223372036854775807), (9223372036854775806), (9223372036854775805), (9223372036854775804), (-9223372036854775808), (-9223372036854775807), (1), (-1), (0);
+select * from t3;
+a
+-1
+-9223372036854775807
+-9223372036854775808
+0
+1
+9223372036854775804
+9223372036854775805
+9223372036854775806
+9223372036854775807
+select * from t3 where a=9223372036854775806;
+a
+9223372036854775806
+delete from t3 where a=9223372036854775806;
+select * from t3;
+a
+-1
+-9223372036854775807
+-9223372036854775808
+0
+1
+9223372036854775804
+9223372036854775805
+9223372036854775807
+drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_double_innodb.result b/mysql-test/suite/parts/r/partition_double_innodb.result
new file mode 100644
index 00000000000..8c0daf929dd
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_double_innodb.result
@@ -0,0 +1,82 @@
+create table t1 (a double not null, primary key(a)) engine='InnoDB'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` double NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
+insert into t1 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208);
+select * from t1;
+a
+-2.2250738585072016e208
+-1.5
+-1
+-2.2250738585072014e-208
+0
+1.5
+1234.567
+2.2250738585072016e208
+select * from t1 where a=1.5;
+a
+1.5
+delete from t1 where a=1.5;
+select * from t1;
+a
+-2.2250738585072016e208
+-1.5
+-1
+-2.2250738585072014e-208
+0
+1234.567
+2.2250738585072016e208
+drop table t1;
+create table t2 (a double not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 10;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` double NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 10 */
+insert into t2 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208);
+select * from t2;
+a
+-2.2250738585072016e208
+-1.5
+-1
+-2.2250738585072014e-208
+0
+1.5
+1234.567
+2.2250738585072016e208
+select * from t2 where a=1234.567;
+a
+1234.567
+delete from t2 where a=1234.567;
+select * from t2;
+a
+-2.2250738585072016e208
+-1.5
+-1
+-2.2250738585072014e-208
+0
+1.5
+2.2250738585072016e208
+delete from t2;
+1024*3 inserts;
+select count(*) from t2;
+count(*)
+3072
+drop table t2;
diff --git a/mysql-test/suite/parts/r/partition_double_myisam.result b/mysql-test/suite/parts/r/partition_double_myisam.result
new file mode 100644
index 00000000000..045763e5ef9
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_double_myisam.result
@@ -0,0 +1,82 @@
+create table t1 (a double not null, primary key(a)) engine='MYISAM'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` double NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
+insert into t1 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208);
+select * from t1;
+a
+-2.2250738585072016e208
+-1.5
+-1
+-2.2250738585072014e-208
+0
+1.5
+1234.567
+2.2250738585072016e208
+select * from t1 where a=1.5;
+a
+1.5
+delete from t1 where a=1.5;
+select * from t1;
+a
+-2.2250738585072016e208
+-1.5
+-1
+-2.2250738585072014e-208
+0
+1234.567
+2.2250738585072016e208
+drop table t1;
+create table t2 (a double not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 10;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` double NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 10 */
+insert into t2 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208);
+select * from t2;
+a
+-2.2250738585072016e208
+-1.5
+-1
+-2.2250738585072014e-208
+0
+1.5
+1234.567
+2.2250738585072016e208
+select * from t2 where a=1234.567;
+a
+1234.567
+delete from t2 where a=1234.567;
+select * from t2;
+a
+-2.2250738585072016e208
+-1.5
+-1
+-2.2250738585072014e-208
+0
+1.5
+2.2250738585072016e208
+delete from t2;
+16384*3 inserts;
+select count(*) from t2;
+count(*)
+49152
+drop table t2;
diff --git a/mysql-test/suite/parts/r/partition_float_innodb.result b/mysql-test/suite/parts/r/partition_float_innodb.result
index d2f04a68629..b0870992c41 100644
--- a/mysql-test/suite/parts/r/partition_float_innodb.result
+++ b/mysql-test/suite/parts/r/partition_float_innodb.result
@@ -88,85 +88,3 @@ select count(*) from t2;
count(*)
3072
drop table t2;
-create table t1 (a double not null, primary key(a)) engine='InnoDB'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` double NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
-insert into t1 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208);
-select * from t1;
-a
--2.2250738585072016e208
--1.5
--1
--2.2250738585072014e-208
-0
-1.5
-1234.567
-2.2250738585072016e208
-select * from t1 where a=1.5;
-a
-1.5
-delete from t1 where a=1.5;
-select * from t1;
-a
--2.2250738585072016e208
--1.5
--1
--2.2250738585072014e-208
-0
-1234.567
-2.2250738585072016e208
-drop table t1;
-create table t2 (a double not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 10;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` double NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 10 */
-insert into t2 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208);
-select * from t2;
-a
--2.2250738585072016e208
--1.5
--1
--2.2250738585072014e-208
-0
-1.5
-1234.567
-2.2250738585072016e208
-select * from t2 where a=1234.567;
-a
-1234.567
-delete from t2 where a=1234.567;
-select * from t2;
-a
--2.2250738585072016e208
--1.5
--1
--2.2250738585072014e-208
-0
-1.5
-2.2250738585072016e208
-delete from t2;
-1024*3 inserts;
-select count(*) from t2;
-count(*)
-3072
-drop table t2;
diff --git a/mysql-test/suite/parts/r/partition_float_myisam.result b/mysql-test/suite/parts/r/partition_float_myisam.result
index 2d52d095989..931c4ef0394 100644
--- a/mysql-test/suite/parts/r/partition_float_myisam.result
+++ b/mysql-test/suite/parts/r/partition_float_myisam.result
@@ -88,85 +88,3 @@ select count(*) from t2;
count(*)
49152
drop table t2;
-create table t1 (a double not null, primary key(a)) engine='MYISAM'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` double NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
-insert into t1 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208);
-select * from t1;
-a
--2.2250738585072016e208
--1.5
--1
--2.2250738585072014e-208
-0
-1.5
-1234.567
-2.2250738585072016e208
-select * from t1 where a=1.5;
-a
-1.5
-delete from t1 where a=1.5;
-select * from t1;
-a
--2.2250738585072016e208
--1.5
--1
--2.2250738585072014e-208
-0
-1234.567
-2.2250738585072016e208
-drop table t1;
-create table t2 (a double not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 10;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` double NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 10 */
-insert into t2 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208);
-select * from t2;
-a
--2.2250738585072016e208
--1.5
--1
--2.2250738585072014e-208
-0
-1.5
-1234.567
-2.2250738585072016e208
-select * from t2 where a=1234.567;
-a
-1234.567
-delete from t2 where a=1234.567;
-select * from t2;
-a
--2.2250738585072016e208
--1.5
--1
--2.2250738585072014e-208
-0
-1.5
-2.2250738585072016e208
-delete from t2;
-16384*3 inserts;
-select count(*) from t2;
-count(*)
-49152
-drop table t2;
diff --git a/mysql-test/suite/parts/r/partition_int_innodb.result b/mysql-test/suite/parts/r/partition_int_innodb.result
index 7a51b80d5d7..c1798e5f711 100644
--- a/mysql-test/suite/parts/r/partition_int_innodb.result
+++ b/mysql-test/suite/parts/r/partition_int_innodb.result
@@ -1,221 +1,3 @@
-create table t1 (a tinyint unsigned not null, primary key(a)) engine='InnoDB'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` tinyint(3) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
-insert into t1 values (255), (254), (253), (252), (1), (2), (128);
-select * from t1;
-a
-1
-128
-2
-252
-253
-254
-255
-select * from t1 where a=253;
-a
-253
-delete from t1 where a=253;
-select * from t1;
-a
-1
-128
-2
-252
-254
-255
-drop table t1;
-create table t2 (a tinyint unsigned not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 8;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` tinyint(3) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 8 */
-insert into t2 values (255), (254), (253), (252);
-select * from t2;
-a
-252
-253
-254
-255
-select * from t2 where a=253;
-a
-253
-delete from t2 where a=253;
-select * from t2;
-a
-252
-254
-255
-delete from t2;
-255 inserts;
-select count(*) from t2;
-count(*)
-255
-drop table t2;
-create table t3 (a tinyint not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 7;
-show create table t3;
-Table Create Table
-t3 CREATE TABLE `t3` (
- `a` tinyint(4) NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 7 */
-insert into t3 values (127), (126), (125), (124), (-128), (-127), (1), (-1), (0);
-select * from t3;
-a
--1
--127
--128
-0
-1
-124
-125
-126
-127
-select * from t3 where a=125;
-a
-125
-delete from t3 where a=125;
-select * from t3;
-a
--1
--127
--128
-0
-1
-124
-126
-127
-drop table t3;
-create table t1 (a smallint unsigned not null, primary key(a)) engine='InnoDB'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
-insert into t1 values (65535), (65534), (65533), (65532), (1), (2), (256);
-select * from t1;
-a
-1
-2
-256
-65532
-65533
-65534
-65535
-select * from t1 where a=65533;
-a
-65533
-delete from t1 where a=65533;
-select * from t1;
-a
-1
-2
-256
-65532
-65534
-65535
-drop table t1;
-create table t2 (a smallint unsigned not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 8;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 8 */
-insert into t2 values (65535), (65534), (65533), (65532);
-select * from t2;
-a
-65532
-65533
-65534
-65535
-select * from t2 where a=65533;
-a
-65533
-delete from t2 where a=65533;
-select * from t2;
-a
-65532
-65534
-65535
-delete from t2;
-1024 inserts;
-select count(*) from t2;
-count(*)
-1024
-drop table t2;
-create table t3 (a smallint not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 7;
-show create table t3;
-Table Create Table
-t3 CREATE TABLE `t3` (
- `a` smallint(6) NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 7 */
-insert into t3 values (32767), (32766), (32765), (32764), (-32768), (-32767), (1), (-1), (0);
-select * from t3;
-a
--1
--32767
--32768
-0
-1
-32764
-32765
-32766
-32767
-select * from t3 where a=32765;
-a
-32765
-delete from t3 where a=32765;
-select * from t3;
-a
--1
--32767
--32768
-0
-1
-32764
-32766
-32767
-drop table t3;
create table t1 (a int unsigned not null, primary key(a)) engine='InnoDB'
partition by key (a) (
partition pa1 max_rows=20 min_rows=2,
@@ -325,233 +107,3 @@ a
2147483646
2147483647
drop table t3;
-create table t1 (a mediumint unsigned not null, primary key(a)) engine='InnoDB'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` mediumint(8) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
-insert into t1 values (16777215), (16777214), (16777213), (16777212), (1), (2), (65535);
-select * from t1;
-a
-1
-16777212
-16777213
-16777214
-16777215
-2
-65535
-select * from t1 where a=16777213;
-a
-16777213
-delete from t1 where a=16777213;
-select * from t1;
-a
-1
-16777212
-16777214
-16777215
-2
-65535
-drop table t1;
-create table t2 (a mediumint unsigned not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 8;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` mediumint(8) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 8 */
-insert into t2 values (16777215), (16777214), (16777213), (16777212);
-select * from t2;
-a
-16777212
-16777213
-16777214
-16777215
-select * from t2 where a=16777213;
-a
-16777213
-delete from t2 where a=16777213;
-select * from t2;
-a
-16777212
-16777214
-16777215
-delete from t2;
-1024 inserts;
-select count(*) from t2;
-count(*)
-1024
-drop table t2;
-create table t3 (a mediumint not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 7;
-show create table t3;
-Table Create Table
-t3 CREATE TABLE `t3` (
- `a` mediumint(9) NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 7 */
-insert into t3 values (8388607), (8388606), (8388605), (8388604), (-8388608), (-8388607), (1), (-1), (0);
-select * from t3;
-a
--1
--8388607
--8388608
-0
-1
-8388604
-8388605
-8388606
-8388607
-select * from t3 where a=8388605;
-a
-8388605
-delete from t3 where a=8388605;
-select * from t3;
-a
--1
--8388607
--8388608
-0
-1
-8388604
-8388606
-8388607
-drop table t3;
-create table t1 (a bigint unsigned not null, primary key(a)) engine='InnoDB'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` bigint(20) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
-insert into t1 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612), (1), (2), (65535);
-select * from t1;
-a
-1
-18446744073709551612
-18446744073709551613
-18446744073709551614
-18446744073709551615
-2
-65535
-select * from t1 where a=-2;
-a
-delete from t1 where a=-2;
-select * from t1;
-a
-1
-18446744073709551612
-18446744073709551613
-18446744073709551614
-18446744073709551615
-2
-65535
-select * from t1 where a=18446744073709551615;
-a
-18446744073709551615
-delete from t1 where a=18446744073709551615;
-select * from t1;
-a
-1
-18446744073709551612
-18446744073709551613
-18446744073709551614
-2
-65535
-drop table t1;
-create table t2 (a bigint unsigned not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 8;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` bigint(20) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 8 */
-insert into t2 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612);
-select * from t2;
-a
-18446744073709551612
-18446744073709551613
-18446744073709551614
-18446744073709551615
-select * from t2 where a=18446744073709551615;
-a
-18446744073709551615
-delete from t2 where a=18446744073709551615;
-select * from t2;
-a
-18446744073709551612
-18446744073709551613
-18446744073709551614
-delete from t2;
-1024 inserts;
-select count(*) from t2;
-count(*)
-1024
-drop table t2;
-create table t3 (a bigint not null, primary key(a)) engine='InnoDB'
-partition by key (a) partitions 7;
-show create table t3;
-Table Create Table
-t3 CREATE TABLE `t3` (
- `a` bigint(20) NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 7 */
-insert into t3 values (9223372036854775807), (9223372036854775806), (9223372036854775805), (9223372036854775804), (-9223372036854775808), (-9223372036854775807), (1), (-1), (0);
-select * from t3;
-a
--1
--9223372036854775807
--9223372036854775808
-0
-1
-9223372036854775804
-9223372036854775805
-9223372036854775806
-9223372036854775807
-select * from t3 where a=9223372036854775806;
-a
-9223372036854775806
-delete from t3 where a=9223372036854775806;
-select * from t3;
-a
--1
--9223372036854775807
--9223372036854775808
-0
-1
-9223372036854775804
-9223372036854775805
-9223372036854775807
-drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_int_myisam.result b/mysql-test/suite/parts/r/partition_int_myisam.result
index 4387bbfdd78..8b8352ebc38 100644
--- a/mysql-test/suite/parts/r/partition_int_myisam.result
+++ b/mysql-test/suite/parts/r/partition_int_myisam.result
@@ -1,221 +1,3 @@
-create table t1 (a tinyint unsigned not null, primary key(a)) engine='MYISAM'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` tinyint(3) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
-insert into t1 values (255), (254), (253), (252), (1), (2), (128);
-select * from t1;
-a
-1
-128
-2
-252
-253
-254
-255
-select * from t1 where a=253;
-a
-253
-delete from t1 where a=253;
-select * from t1;
-a
-1
-128
-2
-252
-254
-255
-drop table t1;
-create table t2 (a tinyint unsigned not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 8;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` tinyint(3) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 8 */
-insert into t2 values (255), (254), (253), (252);
-select * from t2;
-a
-252
-253
-254
-255
-select * from t2 where a=253;
-a
-253
-delete from t2 where a=253;
-select * from t2;
-a
-252
-254
-255
-delete from t2;
-255 inserts;
-select count(*) from t2;
-count(*)
-255
-drop table t2;
-create table t3 (a tinyint not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 7;
-show create table t3;
-Table Create Table
-t3 CREATE TABLE `t3` (
- `a` tinyint(4) NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 7 */
-insert into t3 values (127), (126), (125), (124), (-128), (-127), (1), (-1), (0);
-select * from t3;
-a
--1
--127
--128
-0
-1
-124
-125
-126
-127
-select * from t3 where a=125;
-a
-125
-delete from t3 where a=125;
-select * from t3;
-a
--1
--127
--128
-0
-1
-124
-126
-127
-drop table t3;
-create table t1 (a smallint unsigned not null, primary key(a)) engine='MYISAM'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
-insert into t1 values (65535), (65534), (65533), (65532), (1), (2), (256);
-select * from t1;
-a
-1
-2
-256
-65532
-65533
-65534
-65535
-select * from t1 where a=65533;
-a
-65533
-delete from t1 where a=65533;
-select * from t1;
-a
-1
-2
-256
-65532
-65534
-65535
-drop table t1;
-create table t2 (a smallint unsigned not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 8;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 8 */
-insert into t2 values (65535), (65534), (65533), (65532);
-select * from t2;
-a
-65532
-65533
-65534
-65535
-select * from t2 where a=65533;
-a
-65533
-delete from t2 where a=65533;
-select * from t2;
-a
-65532
-65534
-65535
-delete from t2;
-65535 inserts;
-select count(*) from t2;
-count(*)
-65535
-drop table t2;
-create table t3 (a smallint not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 7;
-show create table t3;
-Table Create Table
-t3 CREATE TABLE `t3` (
- `a` smallint(6) NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 7 */
-insert into t3 values (32767), (32766), (32765), (32764), (-32768), (-32767), (1), (-1), (0);
-select * from t3;
-a
--1
--32767
--32768
-0
-1
-32764
-32765
-32766
-32767
-select * from t3 where a=32765;
-a
-32765
-delete from t3 where a=32765;
-select * from t3;
-a
--1
--32767
--32768
-0
-1
-32764
-32766
-32767
-drop table t3;
create table t1 (a int unsigned not null, primary key(a)) engine='MYISAM'
partition by key (a) (
partition pa1 max_rows=20 min_rows=2,
@@ -325,233 +107,3 @@ a
2147483646
2147483647
drop table t3;
-create table t1 (a mediumint unsigned not null, primary key(a)) engine='MYISAM'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` mediumint(8) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
-insert into t1 values (16777215), (16777214), (16777213), (16777212), (1), (2), (65535);
-select * from t1;
-a
-1
-16777212
-16777213
-16777214
-16777215
-2
-65535
-select * from t1 where a=16777213;
-a
-16777213
-delete from t1 where a=16777213;
-select * from t1;
-a
-1
-16777212
-16777214
-16777215
-2
-65535
-drop table t1;
-create table t2 (a mediumint unsigned not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 8;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` mediumint(8) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 8 */
-insert into t2 values (16777215), (16777214), (16777213), (16777212);
-select * from t2;
-a
-16777212
-16777213
-16777214
-16777215
-select * from t2 where a=16777213;
-a
-16777213
-delete from t2 where a=16777213;
-select * from t2;
-a
-16777212
-16777214
-16777215
-delete from t2;
-65535 inserts;
-select count(*) from t2;
-count(*)
-65535
-drop table t2;
-create table t3 (a mediumint not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 7;
-show create table t3;
-Table Create Table
-t3 CREATE TABLE `t3` (
- `a` mediumint(9) NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 7 */
-insert into t3 values (8388607), (8388606), (8388605), (8388604), (-8388608), (-8388607), (1), (-1), (0);
-select * from t3;
-a
--1
--8388607
--8388608
-0
-1
-8388604
-8388605
-8388606
-8388607
-select * from t3 where a=8388605;
-a
-8388605
-delete from t3 where a=8388605;
-select * from t3;
-a
--1
--8388607
--8388608
-0
-1
-8388604
-8388606
-8388607
-drop table t3;
-create table t1 (a bigint unsigned not null, primary key(a)) engine='MYISAM'
-partition by key (a) (
-partition pa1 max_rows=20 min_rows=2,
-partition pa2 max_rows=30 min_rows=3,
-partition pa3 max_rows=30 min_rows=4,
-partition pa4 max_rows=40 min_rows=2);
-show create table t1;
-Table Create Table
-t1 CREATE TABLE `t1` (
- `a` bigint(20) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
- PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
- PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
- PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
-insert into t1 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612), (1), (2), (65535);
-select * from t1;
-a
-1
-18446744073709551612
-18446744073709551613
-18446744073709551614
-18446744073709551615
-2
-65535
-select * from t1 where a=-2;
-a
-delete from t1 where a=-2;
-select * from t1;
-a
-1
-18446744073709551612
-18446744073709551613
-18446744073709551614
-18446744073709551615
-2
-65535
-select * from t1 where a=18446744073709551615;
-a
-18446744073709551615
-delete from t1 where a=18446744073709551615;
-select * from t1;
-a
-1
-18446744073709551612
-18446744073709551613
-18446744073709551614
-2
-65535
-drop table t1;
-create table t2 (a bigint unsigned not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 8;
-show create table t2;
-Table Create Table
-t2 CREATE TABLE `t2` (
- `a` bigint(20) unsigned NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 8 */
-insert into t2 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612);
-select * from t2;
-a
-18446744073709551612
-18446744073709551613
-18446744073709551614
-18446744073709551615
-select * from t2 where a=18446744073709551615;
-a
-18446744073709551615
-delete from t2 where a=18446744073709551615;
-select * from t2;
-a
-18446744073709551612
-18446744073709551613
-18446744073709551614
-delete from t2;
-65535 inserts;
-select count(*) from t2;
-count(*)
-65535
-drop table t2;
-create table t3 (a bigint not null, primary key(a)) engine='MYISAM'
-partition by key (a) partitions 7;
-show create table t3;
-Table Create Table
-t3 CREATE TABLE `t3` (
- `a` bigint(20) NOT NULL,
- PRIMARY KEY (`a`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1
-/*!50100 PARTITION BY KEY (a)
-PARTITIONS 7 */
-insert into t3 values (9223372036854775807), (9223372036854775806), (9223372036854775805), (9223372036854775804), (-9223372036854775808), (-9223372036854775807), (1), (-1), (0);
-select * from t3;
-a
--1
--9223372036854775807
--9223372036854775808
-0
-1
-9223372036854775804
-9223372036854775805
-9223372036854775806
-9223372036854775807
-select * from t3 where a=9223372036854775806;
-a
-9223372036854775806
-delete from t3 where a=9223372036854775806;
-select * from t3;
-a
--1
--9223372036854775807
--9223372036854775808
-0
-1
-9223372036854775804
-9223372036854775805
-9223372036854775807
-drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_mediumint_innodb.result b/mysql-test/suite/parts/r/partition_mediumint_innodb.result
new file mode 100644
index 00000000000..8e3e5543ddc
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_mediumint_innodb.result
@@ -0,0 +1,109 @@
+create table t1 (a mediumint unsigned not null, primary key(a)) engine='InnoDB'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` mediumint(8) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
+insert into t1 values (16777215), (16777214), (16777213), (16777212), (1), (2), (65535);
+select * from t1;
+a
+1
+16777212
+16777213
+16777214
+16777215
+2
+65535
+select * from t1 where a=16777213;
+a
+16777213
+delete from t1 where a=16777213;
+select * from t1;
+a
+1
+16777212
+16777214
+16777215
+2
+65535
+drop table t1;
+create table t2 (a mediumint unsigned not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 8;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` mediumint(8) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 8 */
+insert into t2 values (16777215), (16777214), (16777213), (16777212);
+select * from t2;
+a
+16777212
+16777213
+16777214
+16777215
+select * from t2 where a=16777213;
+a
+16777213
+delete from t2 where a=16777213;
+select * from t2;
+a
+16777212
+16777214
+16777215
+delete from t2;
+1024 inserts;
+select count(*) from t2;
+count(*)
+1024
+drop table t2;
+create table t3 (a mediumint not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 7;
+show create table t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `a` mediumint(9) NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 7 */
+insert into t3 values (8388607), (8388606), (8388605), (8388604), (-8388608), (-8388607), (1), (-1), (0);
+select * from t3;
+a
+-1
+-8388607
+-8388608
+0
+1
+8388604
+8388605
+8388606
+8388607
+select * from t3 where a=8388605;
+a
+8388605
+delete from t3 where a=8388605;
+select * from t3;
+a
+-1
+-8388607
+-8388608
+0
+1
+8388604
+8388606
+8388607
+drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_mediumint_myisam.result b/mysql-test/suite/parts/r/partition_mediumint_myisam.result
new file mode 100644
index 00000000000..4853680610b
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_mediumint_myisam.result
@@ -0,0 +1,109 @@
+create table t1 (a mediumint unsigned not null, primary key(a)) engine='MYISAM'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` mediumint(8) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
+insert into t1 values (16777215), (16777214), (16777213), (16777212), (1), (2), (65535);
+select * from t1;
+a
+1
+16777212
+16777213
+16777214
+16777215
+2
+65535
+select * from t1 where a=16777213;
+a
+16777213
+delete from t1 where a=16777213;
+select * from t1;
+a
+1
+16777212
+16777214
+16777215
+2
+65535
+drop table t1;
+create table t2 (a mediumint unsigned not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 8;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` mediumint(8) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 8 */
+insert into t2 values (16777215), (16777214), (16777213), (16777212);
+select * from t2;
+a
+16777212
+16777213
+16777214
+16777215
+select * from t2 where a=16777213;
+a
+16777213
+delete from t2 where a=16777213;
+select * from t2;
+a
+16777212
+16777214
+16777215
+delete from t2;
+65535 inserts;
+select count(*) from t2;
+count(*)
+65535
+drop table t2;
+create table t3 (a mediumint not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 7;
+show create table t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `a` mediumint(9) NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 7 */
+insert into t3 values (8388607), (8388606), (8388605), (8388604), (-8388608), (-8388607), (1), (-1), (0);
+select * from t3;
+a
+-1
+-8388607
+-8388608
+0
+1
+8388604
+8388605
+8388606
+8388607
+select * from t3 where a=8388605;
+a
+8388605
+delete from t3 where a=8388605;
+select * from t3;
+a
+-1
+-8388607
+-8388608
+0
+1
+8388604
+8388606
+8388607
+drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_smallint_innodb.result b/mysql-test/suite/parts/r/partition_smallint_innodb.result
new file mode 100644
index 00000000000..fbf23fe582c
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_smallint_innodb.result
@@ -0,0 +1,109 @@
+create table t1 (a smallint unsigned not null, primary key(a)) engine='InnoDB'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
+insert into t1 values (65535), (65534), (65533), (65532), (1), (2), (256);
+select * from t1;
+a
+1
+2
+256
+65532
+65533
+65534
+65535
+select * from t1 where a=65533;
+a
+65533
+delete from t1 where a=65533;
+select * from t1;
+a
+1
+2
+256
+65532
+65534
+65535
+drop table t1;
+create table t2 (a smallint unsigned not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 8;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 8 */
+insert into t2 values (65535), (65534), (65533), (65532);
+select * from t2;
+a
+65532
+65533
+65534
+65535
+select * from t2 where a=65533;
+a
+65533
+delete from t2 where a=65533;
+select * from t2;
+a
+65532
+65534
+65535
+delete from t2;
+1024 inserts;
+select count(*) from t2;
+count(*)
+1024
+drop table t2;
+create table t3 (a smallint not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 7;
+show create table t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `a` smallint(6) NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 7 */
+insert into t3 values (32767), (32766), (32765), (32764), (-32768), (-32767), (1), (-1), (0);
+select * from t3;
+a
+-1
+-32767
+-32768
+0
+1
+32764
+32765
+32766
+32767
+select * from t3 where a=32765;
+a
+32765
+delete from t3 where a=32765;
+select * from t3;
+a
+-1
+-32767
+-32768
+0
+1
+32764
+32766
+32767
+drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_smallint_myisam.result b/mysql-test/suite/parts/r/partition_smallint_myisam.result
new file mode 100644
index 00000000000..a405d025919
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_smallint_myisam.result
@@ -0,0 +1,109 @@
+create table t1 (a smallint unsigned not null, primary key(a)) engine='MYISAM'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
+insert into t1 values (65535), (65534), (65533), (65532), (1), (2), (256);
+select * from t1;
+a
+1
+2
+256
+65532
+65533
+65534
+65535
+select * from t1 where a=65533;
+a
+65533
+delete from t1 where a=65533;
+select * from t1;
+a
+1
+2
+256
+65532
+65534
+65535
+drop table t1;
+create table t2 (a smallint unsigned not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 8;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 8 */
+insert into t2 values (65535), (65534), (65533), (65532);
+select * from t2;
+a
+65532
+65533
+65534
+65535
+select * from t2 where a=65533;
+a
+65533
+delete from t2 where a=65533;
+select * from t2;
+a
+65532
+65534
+65535
+delete from t2;
+65535 inserts;
+select count(*) from t2;
+count(*)
+65535
+drop table t2;
+create table t3 (a smallint not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 7;
+show create table t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `a` smallint(6) NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 7 */
+insert into t3 values (32767), (32766), (32765), (32764), (-32768), (-32767), (1), (-1), (0);
+select * from t3;
+a
+-1
+-32767
+-32768
+0
+1
+32764
+32765
+32766
+32767
+select * from t3 where a=32765;
+a
+32765
+delete from t3 where a=32765;
+select * from t3;
+a
+-1
+-32767
+-32768
+0
+1
+32764
+32766
+32767
+drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_tinyint_innodb.result b/mysql-test/suite/parts/r/partition_tinyint_innodb.result
new file mode 100644
index 00000000000..d7138539b78
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_tinyint_innodb.result
@@ -0,0 +1,109 @@
+create table t1 (a tinyint unsigned not null, primary key(a)) engine='InnoDB'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */
+insert into t1 values (255), (254), (253), (252), (1), (2), (128);
+select * from t1;
+a
+1
+128
+2
+252
+253
+254
+255
+select * from t1 where a=253;
+a
+253
+delete from t1 where a=253;
+select * from t1;
+a
+1
+128
+2
+252
+254
+255
+drop table t1;
+create table t2 (a tinyint unsigned not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 8;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 8 */
+insert into t2 values (255), (254), (253), (252);
+select * from t2;
+a
+252
+253
+254
+255
+select * from t2 where a=253;
+a
+253
+delete from t2 where a=253;
+select * from t2;
+a
+252
+254
+255
+delete from t2;
+255 inserts;
+select count(*) from t2;
+count(*)
+255
+drop table t2;
+create table t3 (a tinyint not null, primary key(a)) engine='InnoDB'
+partition by key (a) partitions 7;
+show create table t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `a` tinyint(4) NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 7 */
+insert into t3 values (127), (126), (125), (124), (-128), (-127), (1), (-1), (0);
+select * from t3;
+a
+-1
+-127
+-128
+0
+1
+124
+125
+126
+127
+select * from t3 where a=125;
+a
+125
+delete from t3 where a=125;
+select * from t3;
+a
+-1
+-127
+-128
+0
+1
+124
+126
+127
+drop table t3;
diff --git a/mysql-test/suite/parts/r/partition_tinyint_myisam.result b/mysql-test/suite/parts/r/partition_tinyint_myisam.result
new file mode 100644
index 00000000000..08a688e8f36
--- /dev/null
+++ b/mysql-test/suite/parts/r/partition_tinyint_myisam.result
@@ -0,0 +1,109 @@
+create table t1 (a tinyint unsigned not null, primary key(a)) engine='MYISAM'
+partition by key (a) (
+partition pa1 max_rows=20 min_rows=2,
+partition pa2 max_rows=30 min_rows=3,
+partition pa3 max_rows=30 min_rows=4,
+partition pa4 max_rows=40 min_rows=2);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `a` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM,
+ PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM,
+ PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM,
+ PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */
+insert into t1 values (255), (254), (253), (252), (1), (2), (128);
+select * from t1;
+a
+1
+128
+2
+252
+253
+254
+255
+select * from t1 where a=253;
+a
+253
+delete from t1 where a=253;
+select * from t1;
+a
+1
+128
+2
+252
+254
+255
+drop table t1;
+create table t2 (a tinyint unsigned not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 8;
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `a` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 8 */
+insert into t2 values (255), (254), (253), (252);
+select * from t2;
+a
+252
+253
+254
+255
+select * from t2 where a=253;
+a
+253
+delete from t2 where a=253;
+select * from t2;
+a
+252
+254
+255
+delete from t2;
+255 inserts;
+select count(*) from t2;
+count(*)
+255
+drop table t2;
+create table t3 (a tinyint not null, primary key(a)) engine='MYISAM'
+partition by key (a) partitions 7;
+show create table t3;
+Table Create Table
+t3 CREATE TABLE `t3` (
+ `a` tinyint(4) NOT NULL,
+ PRIMARY KEY (`a`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+/*!50100 PARTITION BY KEY (a)
+PARTITIONS 7 */
+insert into t3 values (127), (126), (125), (124), (-128), (-127), (1), (-1), (0);
+select * from t3;
+a
+-1
+-127
+-128
+0
+1
+124
+125
+126
+127
+select * from t3 where a=125;
+a
+125
+delete from t3 where a=125;
+select * from t3;
+a
+-1
+-127
+-128
+0
+1
+124
+126
+127
+drop table t3;
diff --git a/mysql-test/suite/parts/t/partition_bigint_innodb.test b/mysql-test/suite/parts/t/partition_bigint_innodb.test
new file mode 100644
index 00000000000..348ee0add05
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_bigint_innodb.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_bigint_innodb.test #
+# #
+# Purpose: #
+# Tests around integer type #
+# INNODB branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_int_innodb #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+--source include/have_innodb.inc
+let $engine= 'InnoDB';
+
+##### max rows to be inserted
+let $maxrows=1024;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_bigint.inc
diff --git a/mysql-test/suite/parts/t/partition_bigint_myisam.test b/mysql-test/suite/parts/t/partition_bigint_myisam.test
new file mode 100644
index 00000000000..f427ffce08d
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_bigint_myisam.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_bigint_myisam.test #
+# #
+# Purpose: #
+# Tests around integer type #
+# MyISAM branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_int_myisam #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+--source include/long_test.inc
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+let $engine= 'MYISAM';
+##### number of rows to be inserted
+let $maxrows=65535;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_bigint.inc
diff --git a/mysql-test/suite/parts/t/partition_double_innodb.test b/mysql-test/suite/parts/t/partition_double_innodb.test
new file mode 100644
index 00000000000..e31f7049502
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_double_innodb.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_double_innodb.test #
+# #
+# Purpose: #
+# Tests around float type #
+# INNODB branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_float_innodb #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+--source include/have_innodb.inc
+let $engine= 'InnoDB';
+
+##### Number of row to be inserted.
+let $maxrows=1024;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_double.inc
diff --git a/mysql-test/suite/parts/t/partition_double_myisam.test b/mysql-test/suite/parts/t/partition_double_myisam.test
new file mode 100644
index 00000000000..6228d657c48
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_double_myisam.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_double_myisam.test #
+# #
+# Purpose: #
+# Tests around float type #
+# MyISAM branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_float_myisam #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+--source include/long_test.inc
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+let $engine= 'MYISAM';
+##### Number of row to be inserted.
+let $maxrows=16384;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_double.inc
diff --git a/mysql-test/suite/parts/t/partition_float_innodb.test b/mysql-test/suite/parts/t/partition_float_innodb.test
index 2f1fe723dad..8d96eafbb06 100644
--- a/mysql-test/suite/parts/t/partition_float_innodb.test
+++ b/mysql-test/suite/parts/t/partition_float_innodb.test
@@ -8,9 +8,9 @@
#------------------------------------------------------------------------------#
# Original Author: HH #
# Original Date: 2006-08-01 #
-# Change Author: #
-# Change Date: #
-# Change: #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test for double type has been spawned into a separate test file #
################################################################################
#
@@ -44,4 +44,3 @@ let $maxrows=1024;
#------------------------------------------------------------------------------#
# Execute the tests to be applied to all storage engines
--source suite/parts/inc/partition_float.inc
---source suite/parts/inc/partition_double.inc
diff --git a/mysql-test/suite/parts/t/partition_float_myisam.test b/mysql-test/suite/parts/t/partition_float_myisam.test
index f15e6ad3636..bdc0edd41bb 100644
--- a/mysql-test/suite/parts/t/partition_float_myisam.test
+++ b/mysql-test/suite/parts/t/partition_float_myisam.test
@@ -8,9 +8,9 @@
#------------------------------------------------------------------------------#
# Original Author: HH #
# Original Date: 2006-08-01 #
-# Change Author: #
-# Change Date: #
-# Change: #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test for double type has been spawned into a separate test file #
################################################################################
#
@@ -44,4 +44,3 @@ let $maxrows=16384;
#------------------------------------------------------------------------------#
# Execute the tests to be applied to all storage engines
--source suite/parts/inc/partition_float.inc
---source suite/parts/inc/partition_double.inc
diff --git a/mysql-test/suite/parts/t/partition_int_innodb.test b/mysql-test/suite/parts/t/partition_int_innodb.test
index 698a2c93c22..fd00e6a0d80 100644
--- a/mysql-test/suite/parts/t/partition_int_innodb.test
+++ b/mysql-test/suite/parts/t/partition_int_innodb.test
@@ -8,9 +8,9 @@
#------------------------------------------------------------------------------#
# Original Author: HH #
# Original Date: 2006-08-01 #
-# Change Author: #
-# Change Date: #
-# Change: #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: Int subtypes (tinyint etc.) have been spawned into separate tests #
################################################################################
#
@@ -43,8 +43,4 @@ let $maxrows=1024;
#------------------------------------------------------------------------------#
# Execute the tests to be applied to all storage engines
---source suite/parts/inc/partition_tinyint.inc
---source suite/parts/inc/partition_smallint.inc
--source suite/parts/inc/partition_int.inc
---source suite/parts/inc/partition_mediumint.inc
---source suite/parts/inc/partition_bigint.inc
diff --git a/mysql-test/suite/parts/t/partition_int_myisam.test b/mysql-test/suite/parts/t/partition_int_myisam.test
index 5f29b575244..e8de09f1bf3 100644
--- a/mysql-test/suite/parts/t/partition_int_myisam.test
+++ b/mysql-test/suite/parts/t/partition_int_myisam.test
@@ -8,9 +8,9 @@
#------------------------------------------------------------------------------#
# Original Author: HH #
# Original Date: 2006-08-01 #
-# Change Author: #
-# Change Date: #
-# Change: #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: Int subtypes (tinyint etc.) have been spawned into separate tests #
################################################################################
#
@@ -43,8 +43,4 @@ let $maxrows=65535;
#------------------------------------------------------------------------------#
# Execute the tests to be applied to all storage engines
---source suite/parts/inc/partition_tinyint.inc
---source suite/parts/inc/partition_smallint.inc
--source suite/parts/inc/partition_int.inc
---source suite/parts/inc/partition_mediumint.inc
---source suite/parts/inc/partition_bigint.inc
diff --git a/mysql-test/suite/parts/t/partition_mediumint_innodb.test b/mysql-test/suite/parts/t/partition_mediumint_innodb.test
new file mode 100644
index 00000000000..9218b55fa78
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_mediumint_innodb.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_mediumint_innodb.test #
+# #
+# Purpose: #
+# Tests around integer type #
+# INNODB branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_int_innodb #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+--source include/have_innodb.inc
+let $engine= 'InnoDB';
+
+##### max rows to be inserted
+let $maxrows=1024;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_mediumint.inc
diff --git a/mysql-test/suite/parts/t/partition_mediumint_myisam.test b/mysql-test/suite/parts/t/partition_mediumint_myisam.test
new file mode 100644
index 00000000000..bbf1775ba97
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_mediumint_myisam.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_mediumint_myisam.test #
+# #
+# Purpose: #
+# Tests around integer type #
+# MyISAM branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_int_myisam #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+--source include/long_test.inc
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+let $engine= 'MYISAM';
+##### number of rows to be inserted
+let $maxrows=65535;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_mediumint.inc
diff --git a/mysql-test/suite/parts/t/partition_smallint_innodb.test b/mysql-test/suite/parts/t/partition_smallint_innodb.test
new file mode 100644
index 00000000000..22d16cf9d55
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_smallint_innodb.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_smallint_innodb.test #
+# #
+# Purpose: #
+# Tests around integer type #
+# INNODB branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_int_innodb #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+--source include/have_innodb.inc
+let $engine= 'InnoDB';
+
+##### max rows to be inserted
+let $maxrows=1024;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_smallint.inc
diff --git a/mysql-test/suite/parts/t/partition_smallint_myisam.test b/mysql-test/suite/parts/t/partition_smallint_myisam.test
new file mode 100644
index 00000000000..f473a6772f8
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_smallint_myisam.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_smallint_myisam.test #
+# #
+# Purpose: #
+# Tests around integer type #
+# MyISAM branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_int_myisam #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+--source include/long_test.inc
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+let $engine= 'MYISAM';
+##### number of rows to be inserted
+let $maxrows=65535;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_smallint.inc
diff --git a/mysql-test/suite/parts/t/partition_tinyint_innodb.test b/mysql-test/suite/parts/t/partition_tinyint_innodb.test
new file mode 100644
index 00000000000..aec10c1aea5
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_tinyint_innodb.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_tinyint_innodb.test #
+# #
+# Purpose: #
+# Tests around integer type #
+# INNODB branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_int_innodb #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+--source include/have_innodb.inc
+let $engine= 'InnoDB';
+
+##### max rows to be inserted
+let $maxrows=1024;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_tinyint.inc
diff --git a/mysql-test/suite/parts/t/partition_tinyint_myisam.test b/mysql-test/suite/parts/t/partition_tinyint_myisam.test
new file mode 100644
index 00000000000..9807bffb1da
--- /dev/null
+++ b/mysql-test/suite/parts/t/partition_tinyint_myisam.test
@@ -0,0 +1,46 @@
+################################################################################
+# t/partition_tinyint_myisam.test #
+# #
+# Purpose: #
+# Tests around integer type #
+# MyISAM branch #
+# #
+#------------------------------------------------------------------------------#
+# Original Author: HH #
+# Original Date: 2006-08-01 #
+# Change Author: Elena Stepanova #
+# Change Date: 2017-02-18 #
+# Change: The test file is spawned from the mega-test partition_int_myisam #
+################################################################################
+
+#
+# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE !
+# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN
+# THE SOURCED FILES ONLY.
+#
+# Please read the README at the end of inc/partition.pre before changing
+# any of the variables.
+#
+
+--source include/long_test.inc
+
+#------------------------------------------------------------------------------#
+# General not engine specific settings and requirements
+
+##### Options, for debugging support #####
+let $debug= 0;
+
+# The server must support partitioning.
+--source include/have_partition.inc
+
+#------------------------------------------------------------------------------#
+# Engine specific settings and requirements
+
+##### Storage engine to be tested
+let $engine= 'MYISAM';
+##### number of rows to be inserted
+let $maxrows=65535;
+
+#------------------------------------------------------------------------------#
+# Execute the tests to be applied to all storage engines
+--source suite/parts/inc/partition_tinyint.inc
diff --git a/mysql-test/suite/rpl/r/rpl_heartbeat_basic.result b/mysql-test/suite/rpl/r/rpl_heartbeat_basic.result
index 91ed6d7a0cb..0cb1aed9905 100644
--- a/mysql-test/suite/rpl/r/rpl_heartbeat_basic.result
+++ b/mysql-test/suite/rpl/r/rpl_heartbeat_basic.result
@@ -7,7 +7,6 @@ RESET SLAVE;
SET @restore_slave_net_timeout=@@global.slave_net_timeout;
RESET MASTER;
SET @restore_slave_net_timeout=@@global.slave_net_timeout;
-SET @restore_event_scheduler=@@global.event_scheduler;
*** Default value ***
CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=MASTER_PORT, MASTER_USER='root';
@@ -221,7 +220,7 @@ RESET SLAVE;
CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=MASTER_PORT, MASTER_USER='root', MASTER_CONNECT_RETRY=20, MASTER_HEARTBEAT_PERIOD=5;
include/start_slave.inc
SET @@global.event_scheduler=1;
-Number of received heartbeat events: 0
+Received heartbeats meet expectations: TRUE
DELETE FROM t1;
DROP EVENT e1;
diff --git a/mysql-test/suite/rpl/t/rpl_heartbeat_basic.test b/mysql-test/suite/rpl/t/rpl_heartbeat_basic.test
index 5b55f11da85..4c8d3a1fedb 100644
--- a/mysql-test/suite/rpl/t/rpl_heartbeat_basic.test
+++ b/mysql-test/suite/rpl/t/rpl_heartbeat_basic.test
@@ -34,7 +34,6 @@ eval SET @restore_slave_heartbeat_timeout=$slave_heartbeat_timeout;
--connection master
RESET MASTER;
SET @restore_slave_net_timeout=@@global.slave_net_timeout;
-SET @restore_event_scheduler=@@global.event_scheduler;
--echo
#
@@ -352,21 +351,54 @@ eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$MASTER_MYPORT, MASTE
--connection master
# Enable scheduler
SET @@global.event_scheduler=1;
+
--sync_slave_with_master
let $rcvd_heartbeats_before= query_get_value(SHOW STATUS LIKE 'slave_received_heartbeats', Value, 1);
-# Wait some updates for table t1 from master
-let $wait_condition= SELECT COUNT(*)=1 FROM t1 WHERE a > 5;
---source include/wait_condition.inc
+
+--connection master
+
+# Whether or not to send a heartbeat is decided on the master, based on
+# whether the binlog was updated during the period or not.
+# Even with the 1-second event, we cannot make the master to write binary
+# logs (or execute SQL) in a timely manner. We can only check that they
+# were executed in a timely manner, and if they were not, neutralize the
+# heartbeat check on the slave.
+# We will wait for 5 events, and keep checking 'Binlog_commits' on master.
+# Time interval between consequent events will be measured.
+# We can only expect that no heartbeats have been sent if the interval
+# between events never exceeded MASTER_HEARTBEAT_PERIOD.
+# If it has exceeded the value at least once, the slave can legitimately
+# receive a heartbeat (but we cannot require it, because the delay
+# could have occurred somewhere else, e.g. upon checking the status).
+# So, if the delay is detected, we will signal slave to ignore possible
+# heartbeats.
+
+let $possible_heartbeats= 0;
+let $commits_to_wait= 5;
+while ($commits_to_wait)
+{
+ let $tm= `SELECT UNIX_TIMESTAMP(NOW(3))`;
+ let $binlog_commits= query_get_value(SHOW STATUS LIKE 'Binlog_commits', Value, 1);
+ let $wait_condition= SELECT VARIABLE_VALUE > $binlog_commits FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME= 'BINLOG_COMMITS';
+ --source include/wait_condition.inc
+ dec $commits_to_wait;
+ if (`SELECT UNIX_TIMESTAMP(NOW(3)) > $tm + 5`)
+ {
+ let $possible_heartbeats= 1;
+ let $commits_to_wait= 0;
+ }
+}
+
+--connection slave
let $rcvd_heartbeats_after= query_get_value(SHOW STATUS LIKE 'slave_received_heartbeats', Value, 1);
-let $result= query_get_value(SELECT ($rcvd_heartbeats_after - $rcvd_heartbeats_before) > 0 AS Result, Result, 1);
---echo Number of received heartbeat events: $result
+let $result= `SELECT CASE WHEN $possible_heartbeats THEN 'TRUE' WHEN $rcvd_heartbeats_after - $rcvd_heartbeats_before > 0 THEN 'FALSE' ELSE 'TRUE' END`;
+--echo Received heartbeats meet expectations: $result
--connection master
DELETE FROM t1;
DROP EVENT e1;
--sync_slave_with_master
--echo
-
# Check received heartbeat events while logs flushed on slave
--echo *** Flush logs on slave ***
STOP SLAVE;
diff --git a/mysql-test/suite/sys_vars/r/innodb_stats_include_delete_marked_basic.result b/mysql-test/suite/sys_vars/r/innodb_stats_include_delete_marked_basic.result
new file mode 100644
index 00000000000..ffd208e7927
--- /dev/null
+++ b/mysql-test/suite/sys_vars/r/innodb_stats_include_delete_marked_basic.result
@@ -0,0 +1,25 @@
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+0
+SET GLOBAL innodb_stats_include_delete_marked=1;
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+1
+SET SESSION innodb_stats_include_delete_marked=1;
+ERROR HY000: Variable 'innodb_stats_include_delete_marked' is a GLOBAL variable and should be set with SET GLOBAL
+SET GLOBAL innodb_stats_include_delete_marked=100;
+ERROR 42000: Variable 'innodb_stats_include_delete_marked' can't be set to the value of '100'
+SET GLOBAL innodb_stats_include_delete_marked=foo;
+ERROR 42000: Variable 'innodb_stats_include_delete_marked' can't be set to the value of 'foo'
+SET GLOBAL innodb_stats_include_delete_marked=OFF ;
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+0
+SET GLOBAL innodb_stats_include_delete_marked=ON ;
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+1
+SET GLOBAL innodb_stats_include_delete_marked=Default ;
+SELECT @@innodb_stats_include_delete_marked;
+@@innodb_stats_include_delete_marked
+0
diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit,xtradb.rdiff b/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit,xtradb.rdiff
index 285caafb3d0..858df585a7b 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit,xtradb.rdiff
+++ b/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit,xtradb.rdiff
@@ -1,7 +1,7 @@
--- suite/sys_vars/r/sysvars_innodb.result 2016-05-06 14:03:16.000000000 +0300
+++ suite/sys_vars/r/sysvars_innodb,32bit.reject 2016-05-08 13:28:44.312418574 +0300
@@ -47,13 +47,27 @@
- ENUM_VALUE_LIST NULL
+ ENUM_VALUE_LIST OFF,ON
READ_ONLY NO
COMMAND_LINE_ARGUMENT OPTIONAL
+VARIABLE_NAME INNODB_ADAPTIVE_HASH_INDEX_PARTITIONS
@@ -97,7 +97,7 @@
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 64
@@ -355,6 +369,20 @@
- ENUM_VALUE_LIST NULL
+ ENUM_VALUE_LIST OFF,ON
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
+VARIABLE_NAME INNODB_BUFFER_POOL_POPULATE
@@ -418,15 +418,6 @@
VARIABLE_COMMENT Helps to save your data in case the disk image of the database becomes corrupt.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 6
-@@ -1047,7 +1215,7 @@
- GLOBAL_VALUE_ORIGIN COMPILE-TIME
- DEFAULT_VALUE 0
- VARIABLE_SCOPE GLOBAL
--VARIABLE_TYPE BIGINT UNSIGNED
-+VARIABLE_TYPE INT UNSIGNED
- VARIABLE_COMMENT Kills the server during crash recovery.
- NUMERIC_MIN_VALUE 0
- NUMERIC_MAX_VALUE 10
@@ -1055,6 +1223,20 @@
ENUM_VALUE_LIST NULL
READ_ONLY YES
@@ -1227,8 +1218,8 @@
COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME INNODB_VERSION
SESSION_VALUE NULL
--GLOBAL_VALUE 5.6.33
-+GLOBAL_VALUE 5.6.34-79.1
+-GLOBAL_VALUE 5.6.35
++GLOBAL_VALUE 5.6.35-80.0
GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE NULL
VARIABLE_SCOPE GLOBAL
diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff b/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff
index e1db778832f..ceb5cdaa46e 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff
+++ b/mysql-test/suite/sys_vars/r/sysvars_innodb,32bit.rdiff
@@ -197,15 +197,6 @@
VARIABLE_COMMENT Helps to save your data in case the disk image of the database becomes corrupt.
NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 6
-@@ -1047,7 +1047,7 @@
- GLOBAL_VALUE_ORIGIN COMPILE-TIME
- DEFAULT_VALUE 0
- VARIABLE_SCOPE GLOBAL
--VARIABLE_TYPE BIGINT UNSIGNED
-+VARIABLE_TYPE INT UNSIGNED
- VARIABLE_COMMENT Kills the server during crash recovery.
- NUMERIC_MIN_VALUE 0
- NUMERIC_MAX_VALUE 10
@@ -1075,7 +1075,7 @@
GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE 8000000
diff --git a/mysql-test/suite/sys_vars/t/innodb_stats_include_delete_marked_basic.test b/mysql-test/suite/sys_vars/t/innodb_stats_include_delete_marked_basic.test
new file mode 100644
index 00000000000..2a3a0f9b44e
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/innodb_stats_include_delete_marked_basic.test
@@ -0,0 +1,53 @@
+###############################################################################
+# #
+# Variable Name: innodb_stats_include_delete_marked #
+# Scope: Global #
+# Access Type: Dynamic #
+# Data Type: numeric #
+# #
+# #
+# Creation Date: 2016-08-29 #
+# Author : Aditya #
+# #
+# #
+# Description: #
+# * Value check #
+# * Scope check #
+# #
+###############################################################################
+
+--source include/have_innodb.inc
+
+####################################################################
+# Display default value #
+####################################################################
+SELECT @@innodb_stats_include_delete_marked;
+
+SET GLOBAL innodb_stats_include_delete_marked=1;
+
+SELECT @@innodb_stats_include_delete_marked;
+
+# check error
+--error ER_GLOBAL_VARIABLE
+SET SESSION innodb_stats_include_delete_marked=1;
+
+# check error
+--error ER_WRONG_VALUE_FOR_VAR
+SET GLOBAL innodb_stats_include_delete_marked=100;
+
+# check error
+--error ER_WRONG_VALUE_FOR_VAR
+SET GLOBAL innodb_stats_include_delete_marked=foo;
+
+SET GLOBAL innodb_stats_include_delete_marked=OFF ;
+
+SELECT @@innodb_stats_include_delete_marked;
+
+SET GLOBAL innodb_stats_include_delete_marked=ON ;
+
+SELECT @@innodb_stats_include_delete_marked;
+
+# Check with default setting
+SET GLOBAL innodb_stats_include_delete_marked=Default ;
+
+SELECT @@innodb_stats_include_delete_marked;
diff --git a/mysql-test/suite/sys_vars/t/secure_file_priv.test b/mysql-test/suite/sys_vars/t/secure_file_priv.test
index 5c53da58275..a5a465d8c98 100644
--- a/mysql-test/suite/sys_vars/t/secure_file_priv.test
+++ b/mysql-test/suite/sys_vars/t/secure_file_priv.test
@@ -21,6 +21,9 @@ SHOW VARIABLES LIKE 'secure_file_priv';
--perl
use File::Basename;
my $protected_file= dirname($ENV{MYSQLTEST_VARDIR}).'/bug50373.txt';
+# Ensure bug50373.txt does not exist (e.g. leftover from previous
+# test runs).
+unlink $protected_file;
open(FILE, ">", "$ENV{MYSQL_TMP_DIR}/bug50373.inc") or die;
print FILE "SELECT * FROM t1 INTO OUTFILE '".$protected_file."';\n";
print FILE "DELETE FROM t1;\n";
diff --git a/mysql-test/suite/vcol/r/vcol_select_myisam.result b/mysql-test/suite/vcol/r/vcol_select_myisam.result
index 934d047f6bf..6dee132b3e5 100644
--- a/mysql-test/suite/vcol/r/vcol_select_myisam.result
+++ b/mysql-test/suite/vcol/r/vcol_select_myisam.result
@@ -295,3 +295,112 @@ Note 1003 select `test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1`
SELECT * FROM t1 NATURAL JOIN t2;
b a
DROP TABLE t1,t2;
+create table t1 (
+pk integer auto_increment,
+bi integer not null,
+vi integer generated always as (bi) persistent,
+bc varchar(1) not null,
+vc varchar(2) generated always as (concat(bc, bc)) persistent,
+primary key (pk),
+key (vi, vc));
+insert t1 (bi, bc) values (0, 'x'), (0, 'n'), (1, 'w'), (7, 's'), (0, 'a'), (4, 'd'), (1, 'w'), (1, 'j'), (1, 'm'), (4, 'k'), (7, 't'), (4, 'k'), (2, 'e'), (0, 'i'), (1, 't'), (6, 'z'), (3, 'c'), (6, 'i'), (8, 'v');
+create table t2 (
+pk integer auto_increment,
+bi integer not null,
+vi integer generated always as (bi) persistent,
+bc varchar(257) not null,
+vc varchar(2) generated always as (concat(bc, bc)) persistent,
+primary key (pk),
+key (vi, vc));
+insert t2 (bi, bc) values (1, 'c'), (8, 'm'), (9, 'd'), (6, 'y'), (1, 't'), (6, 'd'), (2, 's'), (4, 'r'), (8, 'm'), (4, 'b'), (4, 'x'), (7, 'g'), (4, 'p'), (1, 'q'), (9, 'w'), (4, 'd'), (8, 'e'), (4, 'b'), (8, 'y');
+explain # should be using join buffer
+select t2.vi from (t2 as t3 right join (t2 left join t1 on (t1.bi = t2.vi)) on (t1.vc = t2.vc));
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 index NULL vi 10 NULL 19 Using index
+1 SIMPLE t1 ALL NULL NULL NULL NULL 19 Using where; Using join buffer (flat, BNL join)
+1 SIMPLE t3 index NULL PRIMARY 4 NULL 19 Using where; Using index; Using join buffer (incremental, BNL join)
+select t2.vi from (t2 as t3 right join (t2 left join t1 on (t1.bi = t2.vi)) on (t1.vc = t2.vc));
+vi
+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
+2
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+4
+6
+6
+6
+6
+7
+7
+8
+8
+8
+8
+9
+9
+drop table t2,t1;
diff --git a/mysql-test/suite/vcol/t/vcol_select_myisam.test b/mysql-test/suite/vcol/t/vcol_select_myisam.test
index c14faba576d..b392b74c2d9 100644
--- a/mysql-test/suite/vcol/t/vcol_select_myisam.test
+++ b/mysql-test/suite/vcol/t/vcol_select_myisam.test
@@ -68,3 +68,35 @@ SELECT * FROM t1 NATURAL JOIN t2;
SELECT * FROM t1 NATURAL JOIN t2;
DROP TABLE t1,t2;
+
+#
+# MDEV-11525 Assertion `cp + len <= buff + buff_size' failed in JOIN_CACHE::write_record_data
+#
+
+create table t1 (
+ pk integer auto_increment,
+ bi integer not null,
+ vi integer generated always as (bi) persistent,
+ bc varchar(1) not null,
+ vc varchar(2) generated always as (concat(bc, bc)) persistent,
+ primary key (pk),
+ key (vi, vc));
+insert t1 (bi, bc) values (0, 'x'), (0, 'n'), (1, 'w'), (7, 's'), (0, 'a'), (4, 'd'), (1, 'w'), (1, 'j'), (1, 'm'), (4, 'k'), (7, 't'), (4, 'k'), (2, 'e'), (0, 'i'), (1, 't'), (6, 'z'), (3, 'c'), (6, 'i'), (8, 'v');
+create table t2 (
+ pk integer auto_increment,
+ bi integer not null,
+ vi integer generated always as (bi) persistent,
+ bc varchar(257) not null,
+ vc varchar(2) generated always as (concat(bc, bc)) persistent,
+ primary key (pk),
+ key (vi, vc));
+insert t2 (bi, bc) values (1, 'c'), (8, 'm'), (9, 'd'), (6, 'y'), (1, 't'), (6, 'd'), (2, 's'), (4, 'r'), (8, 'm'), (4, 'b'), (4, 'x'), (7, 'g'), (4, 'p'), (1, 'q'), (9, 'w'), (4, 'd'), (8, 'e'), (4, 'b'), (8, 'y');
+explain # should be using join buffer
+select t2.vi from (t2 as t3 right join (t2 left join t1 on (t1.bi = t2.vi)) on (t1.vc = t2.vc));
+--sorted_result
+select t2.vi from (t2 as t3 right join (t2 left join t1 on (t1.bi = t2.vi)) on (t1.vc = t2.vc));
+drop table t2,t1;
+
+#
+# End of 5.5 tests
+#
diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test
index 28781ad6fdb..d881430a060 100644
--- a/mysql-test/t/derived.test
+++ b/mysql-test/t/derived.test
@@ -875,6 +875,29 @@ SELECT Customer, Success, SUM(OrderSize)
DROP TABLE example1463;
set sql_mode= @save_sql_mode;
+--echo #
+--echo # MDEV-9028: SELECT DISTINCT constant column of derived table
+--echo # used as the second operand of LEFT JOIN
+--echo #
+
+create table t1 (id int, data varchar(255));
+insert into t1 values (1,'yes'),(2,'yes');
+
+select distinct t1.id, tt.id, tt.data
+ from t1
+ left join
+ (select t1.id, 'yes' as data from t1) as tt
+ on t1.id = tt.id;
+
+select distinct t1.id, tt.id, tt.data
+ from t1
+ left join
+ (select t1.id, 'yes' as data from t1 where id > 1) as tt
+ on t1.id = tt.id;
+
+drop table t1;
+
+
--echo # end of 5.5
--echo #
diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test
index 1d828cd8693..a8ddf350b73 100644
--- a/mysql-test/t/grant.test
+++ b/mysql-test/t/grant.test
@@ -2178,6 +2178,68 @@ DROP USER mysqltest_u1@localhost;
--echo # End of Bug#38347.
--echo
+
+--echo #
+--echo # BUG#11759114 - '51401: GRANT TREATS NONEXISTENT FUNCTIONS/PRIVILEGES
+--echo # DIFFERENTLY'.
+--echo #
+--disable_warnings
+drop database if exists mysqltest_db1;
+--enable_warnings
+create database mysqltest_db1;
+create user mysqltest_u1;
+--echo # Both GRANT statements below should fail with the same error.
+--error ER_SP_DOES_NOT_EXIST
+grant execute on function mysqltest_db1.f1 to mysqltest_u1;
+--error ER_SP_DOES_NOT_EXIST
+grant execute on procedure mysqltest_db1.p1 to mysqltest_u1;
+--echo # Let us show that GRANT behaviour for routines is consistent
+--echo # with GRANT behaviour for tables. Attempt to grant privilege
+--echo # on non-existent table also results in an error.
+--error ER_NO_SUCH_TABLE
+grant select on mysqltest_db1.t1 to mysqltest_u1;
+show grants for mysqltest_u1;
+drop database mysqltest_db1;
+drop user mysqltest_u1;
+
+
+--echo #
+--echo # Bug#12766319 - 61865: RENAME USER DOES NOT WORK CORRECTLY -
+--echo # REQUIRES FLUSH PRIVILEGES
+--echo #
+
+CREATE USER foo@'127.0.0.1';
+GRANT ALL ON *.* TO foo@'127.0.0.1';
+
+--echo # First attempt, should connect successfully
+connect (conn1, '127.0.0.1', foo,,test);
+SELECT user(), current_user();
+
+--echo # Rename the user
+RENAME USER foo@'127.0.0.1' to foo@'127.0.0.0/255.0.0.0';
+
+--echo # Second attempt, should connect successfully as its valid mask
+--echo # This was failing without fix
+connect (conn2, '127.0.0.1', foo,,test);
+SELECT user(), current_user();
+
+--echo # Rename the user back to original
+RENAME USER foo@'127.0.0.0/255.0.0.0' to foo@'127.0.0.1';
+
+--echo # Third attempt, should connect successfully
+connect (conn3, '127.0.0.1', foo,,test);
+SELECT user(), current_user();
+
+--echo # Clean-up
+connection default;
+disconnect conn1;
+disconnect conn2;
+disconnect conn3;
+DROP USER foo@'127.0.0.1';
+
+--echo # End of Bug#12766319
+
+
--echo #
--echo # Bug#11756966 - 48958: STORED PROCEDURES CAN BE LEVERAGED TO BYPASS
--echo # DATABASE SECURITY
@@ -2210,29 +2272,6 @@ disconnect con1;
DROP USER untrusted@localhost;
DROP DATABASE secret;
---echo #
---echo # BUG#11759114 - '51401: GRANT TREATS NONEXISTENT FUNCTIONS/PRIVILEGES
---echo # DIFFERENTLY'.
---echo #
---disable_warnings
-drop database if exists mysqltest_db1;
---enable_warnings
-create database mysqltest_db1;
-create user mysqltest_u1;
---echo # Both GRANT statements below should fail with the same error.
---error ER_SP_DOES_NOT_EXIST
-grant execute on function mysqltest_db1.f1 to mysqltest_u1;
---error ER_SP_DOES_NOT_EXIST
-grant execute on procedure mysqltest_db1.p1 to mysqltest_u1;
---echo # Let us show that GRANT behaviour for routines is consistent
---echo # with GRANT behaviour for tables. Attempt to grant privilege
---echo # on non-existent table also results in an error.
---error ER_NO_SUCH_TABLE
-grant select on mysqltest_db1.t1 to mysqltest_u1;
-show grants for mysqltest_u1;
-drop database mysqltest_db1;
-drop user mysqltest_u1;
-
set GLOBAL sql_mode=default;
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
diff --git a/mysql-test/t/join_nested.test b/mysql-test/t/join_nested.test
index 7b7d9236835..e60b7827f75 100644
--- a/mysql-test/t/join_nested.test
+++ b/mysql-test/t/join_nested.test
@@ -1309,5 +1309,74 @@ LEFT JOIN t4 AS alias5
JOIN t5 ON alias5.f5
ON alias2.f3 ON alias1.f2;
DROP TABLE t1,t2,t3,t4,t5;
-set optimizer_search_depth= @tmp_mdev621;
+--echo #
+--echo # MDEV-7992: Nested left joins + 'not exists' optimization
+--echo #
+
+CREATE TABLE t1(
+ K1 INT PRIMARY KEY,
+ Name VARCHAR(15)
+);
+
+INSERT INTO t1 VALUES
+ (1,'T1Row1'), (2,'T1Row2');
+
+
+CREATE TABLE t2(
+ K2 INT PRIMARY KEY,
+ K1r INT,
+ rowTimestamp DATETIME,
+ Event VARCHAR(15)
+);
+
+INSERT INTO t2 VALUES
+ (1, 1, '2015-04-13 10:42:11' ,'T1Row1Event1'),
+ (2, 1, '2015-04-13 10:42:12' ,'T1Row1Event2'),
+ (3, 1, '2015-04-13 10:42:12' ,'T1Row1Event3');
+
+let $q1=
+SELECT t1a.*, t2a.*,
+ t2i.K2 AS K2B, t2i.K1r AS K1rB,
+ t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB
+FROM
+ t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1
+ LEFT JOIN
+ ( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1)
+ ON (t1i.K1 = 1) AND
+ (((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR
+ (t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2))
+ OR (t2i.K2 IS NULL))
+WHERE
+t2a.K1r = 1 AND t2i.K2 IS NULL;
+
+eval $q1;
+eval EXPLAIN EXTENDED $q1;
+
+CREATE VIEW v1 AS
+ SELECT t2i.*
+ FROM t1 as t1i LEFT JOIN t2 as t2i ON t2i.K1r = t1i.K1
+ WHERE t1i.K1 = 1 ;
+
+let $q2=
+SELECT
+ t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB,
+ t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB
+FROM
+ t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1
+ LEFT JOIN
+ v1 as t2b
+ ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR
+ (t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2))
+ OR (t2b.K2 IS NULL)
+WHERE
+ t1a.K1 = 1 AND
+ t2b.K2 IS NULL;
+
+eval $q2;
+eval EXPLAIN EXTENDED $q2;
+
+DROP VIEW v1;
+DROP TABLE t1,t2;
+
+set optimizer_search_depth= @tmp_mdev621;
diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test
index 9cde1f93a4a..87091d549d1 100644
--- a/mysql-test/t/mysqldump.test
+++ b/mysql-test/t/mysqldump.test
@@ -2513,6 +2513,7 @@ if (`select convert(@@version_compile_os using latin1) IN ("Win32","Win64","Wind
}
--exec $MYSQL_DUMP --routines --compact $shell_ready_db_name
DROP DATABASE `a\"'``b`;
+
#"
use test;
@@ -2568,3 +2569,11 @@ CREATE VIEW nonunique_table_view_name AS SELECT 1;
DROP DATABASE db1;
DROP DATABASE db2;
+
+#"
+# MDEV-11505 wrong databasename in mysqldump comment
+#
+let SEARCH_FILE=$MYSQLTEST_VARDIR/tmp/bug11505.sql;
+let SEARCH_PATTERN=Database: mysql;
+exec $MYSQL_DUMP mysql func > $SEARCH_FILE;
+source include/search_pattern_in_file.inc;
diff --git a/mysql-test/t/partition_innodb.test b/mysql-test/t/partition_innodb.test
index d7f683aa9e9..00af34f9d26 100644
--- a/mysql-test/t/partition_innodb.test
+++ b/mysql-test/t/partition_innodb.test
@@ -822,6 +822,104 @@ INSERT INTO t1 (d) VALUES ('1991-01-01');
SELECT * FROM t1 WHERE d = '1991-01-01';
DROP TABLE t1;
+set global default_storage_engine=default;
+
+--echo #
+--echo # MDEV-9455: [ERROR] mysqld got signal 11
+--echo #
+
+CREATE TABLE `t1` (
+ `DIARY_TOTAL_DAY_SEQ` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `IMORY_ID` bigint(20) NOT NULL,
+ `NAME` varchar(75) DEFAULT NULL,
+ `DATETIME` varchar(10) NOT NULL DEFAULT '',
+ `DAILY_CALL_CNT` int(11) DEFAULT NULL,
+ `DAILY_SMS_CNT` int(11) DEFAULT NULL,
+ `NUMBER` varchar(64) DEFAULT NULL,
+ `DURATION` varchar(16) DEFAULT NULL,
+ PRIMARY KEY (`DIARY_TOTAL_DAY_SEQ`,`DATETIME`),
+ KEY `IDX_t1_01` (`IMORY_ID`,`DATETIME`)
+) AUTO_INCREMENT=328702514 DEFAULT CHARSET=utf8mb4
+PARTITION BY RANGE COLUMNS(`DATETIME`)
+(PARTITION p0 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB,
+ PARTITION p1 VALUES LESS THAN ('2015-11-01') ENGINE = InnoDB,
+ PARTITION p2 VALUES LESS THAN ('2015-12-01') ENGINE = InnoDB,
+ PARTITION p3 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB,
+ PARTITION p4 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB,
+ PARTITION p5 VALUES LESS THAN ('2016-03-01') ENGINE = InnoDB,
+ PARTITION p6 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB,
+ PARTITION p7 VALUES LESS THAN ('2016-05-01') ENGINE = InnoDB,
+ PARTITION p8 VALUES LESS THAN ('2016-06-01') ENGINE = InnoDB,
+ PARTITION p9 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB,
+ PARTITION p10 VALUES LESS THAN ('2016-08-01') ENGINE = InnoDB)
+;
+
+CREATE TABLE `t2` (
+ `DIARY_SEQ` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ `IMORY_ID` bigint(20) NOT NULL,
+ `CALL_TYPE` varchar(1) DEFAULT NULL,
+ `DATA_TYPE` varchar(1) DEFAULT NULL,
+ `FEATURES` varchar(1) DEFAULT NULL,
+ `NAME` varchar(75) DEFAULT NULL,
+ `NUMBER` varchar(64) DEFAULT NULL,
+ `DATETIME` datetime NOT NULL,
+ `REG_DATE` datetime NOT NULL,
+ `TITLE` varchar(50) DEFAULT NULL,
+ `BODY` varchar(4200) DEFAULT NULL,
+ `MIME_TYPE` varchar(32) DEFAULT NULL,
+ `DURATION` varchar(16) DEFAULT NULL,
+ `DEVICE_ID` varchar(64) DEFAULT NULL,
+ `DEVICE_NAME` varchar(32) DEFAULT NULL,
+ PRIMARY KEY (`DIARY_SEQ`,`DATETIME`,`REG_DATE`),
+ KEY `IDX_TB_DIARY_01` (`IMORY_ID`,`DATETIME`,`CALL_TYPE`,`NUMBER`),
+ KEY `IDX_TB_DIARY_02` (`REG_DATE`)
+) AUTO_INCREMENT=688799006 DEFAULT CHARSET=utf8mb4
+PARTITION BY RANGE COLUMNS(REG_DATE)
+(PARTITION p0 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB,
+ PARTITION p1 VALUES LESS THAN ('2015-11-01') ENGINE = InnoDB,
+ PARTITION p2 VALUES LESS THAN ('2015-12-01') ENGINE = InnoDB,
+ PARTITION p3 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB,
+ PARTITION p4 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB,
+ PARTITION p5 VALUES LESS THAN ('2016-03-01') ENGINE = InnoDB,
+ PARTITION p6 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB,
+ PARTITION p7 VALUES LESS THAN ('2016-05-01') ENGINE = InnoDB,
+ PARTITION p8 VALUES LESS THAN ('2016-06-01') ENGINE = InnoDB,
+ PARTITION p9 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB,
+ PARTITION p10 VALUES LESS THAN ('2016-08-01') ENGINE = InnoDB)
+;
+
+SELECT
+ A.IMORY_ID,
+ A.NUMBER,
+ A.NAME,
+ DATE_FORMAT(A.DATETIME, '%Y-%m-%d') AS TARGET_DATE,
+ SUM( CASE WHEN A.DATA_TYPE='1' THEN 1 ELSE 0 END) AS CALL_CNT,
+ SUM( CASE WHEN A.DATA_TYPE IN ('2', '3') THEN 1 ELSE 0 END) AS SMS_CNT,
+ SUM(CAST(A.DURATION AS INT)) AS DURATION,
+ ( SELECT COUNT(*)
+ FROM t1
+ WHERE IMORY_ID=A.IMORY_ID
+ AND NUMBER=A.NUMBER
+ AND NAME=A.NAME
+ AND DATETIME = DATE_FORMAT(A.DATETIME, '%Y-%m-%d')
+ ) STATS_COUNT
+FROM t2 A
+WHERE A.IMORY_ID = 55094102
+ AND A.DATETIME LIKE (
+ SELECT CONCAT (DATE_FORMAT(DATETIME, '%Y-%m-%d') ,'%')
+ FROM t2
+ WHERE IMORY_ID=55094102
+ AND DIARY_SEQ IN ( 608351221, 608351225, 608351229 )
+ group by DATE_FORMAT(DATETIME, '%Y-%m-%d')
+ )
+GROUP BY A.IMORY_ID, A.NUMBER, A.NAME, DATE_FORMAT(A.DATETIME, '%Y-%m-%d')
+;
+
+drop table t2, t1;
+
+
+set global default_storage_engine='innodb';
+
--echo #
--echo # MDEV-5963: InnoDB: Assertion failure in file row0sel.cc line 2503,
--echo # Failing assertion: 0 with "key ptr now exceeds key end by 762 bytes"
diff --git a/mysql-test/t/partition_myisam.test b/mysql-test/t/partition_myisam.test
index d07637057e0..4d083c37b68 100644
--- a/mysql-test/t/partition_myisam.test
+++ b/mysql-test/t/partition_myisam.test
@@ -216,6 +216,28 @@ PARTITION BY RANGE (a)
PARTITION pMax VALUES LESS THAN MAXVALUE);
INSERT INTO t1 VALUES (1, "Partition p1, first row");
DROP TABLE t1;
+
+--echo #
+--echo # MDEV-10418 Assertion `m_extra_cache' failed
+--echo # in ha_partition::late_extra_cache(uint)
+--echo #
+
+CREATE TABLE t1 (f1 INT) ENGINE=MyISAM;
+INSERT INTO t1 VALUES (1),(2);
+
+CREATE TABLE t2 (f2 INT) ENGINE=MyISAM PARTITION BY RANGE(f2) (PARTITION pmax VALUES LESS THAN MAXVALUE);
+INSERT INTO t2 VALUES (8);
+
+CREATE ALGORITHM = MERGE VIEW v AS SELECT f2 FROM t2, t1;
+
+UPDATE v SET f2 = 1;
+
+SELECT * FROM t2;
+
+DROP VIEW v;
+DROP TABLE t2;
+DROP TABLE t1;
+
--echo #
--echo # bug#11760213-52599: ALTER TABLE REMOVE PARTITIONING ON NON-PARTITIONED
--echo # TABLE CORRUPTS MYISAM
diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test
index 67f6f021434..66cd173d512 100644
--- a/mysql-test/t/ps.test
+++ b/mysql-test/t/ps.test
@@ -3697,5 +3697,38 @@ EXECUTE stmt;
deallocate prepare stmt;
drop table t1,t2,t3,t4;
+--echo #
+--echo # MDEV-11859: the plans for the first and the second executions
+--echo # of PS are not the same
+--echo #
+
+create table t1 (id int, c varchar(3), key idx(c))engine=myisam;
+insert into t1 values (3,'bar'), (1,'xxx'), (2,'foo'), (5,'yyy');
+
+prepare stmt1 from
+"explain extended
+ select * from t1 where (1, 2) in ( select 3, 4 ) or c = 'foo'";
+execute stmt1;
+execute stmt1;
+deallocate prepare stmt1;
+
+prepare stmt1 from
+"select * from t1 where (1, 2) in ( select 3, 4 ) or c = 'foo'";
+flush status;
+execute stmt1;
+show status like '%Handler_read%';
+flush status;
+execute stmt1;
+show status like '%Handler_read%';
+deallocate prepare stmt1;
+
+prepare stmt2 from
+"explain extended
+ select * from t1 where (1, 2) in ( select 3, 4 )";
+execute stmt2;
+execute stmt2;
+deallocate prepare stmt2;
+
+drop table t1;
--echo # End of 5.5 tests
diff --git a/mysql-test/t/range_vs_index_merge.test b/mysql-test/t/range_vs_index_merge.test
index 7ecca454f4c..5d12d46c9e9 100644
--- a/mysql-test/t/range_vs_index_merge.test
+++ b/mysql-test/t/range_vs_index_merge.test
@@ -57,9 +57,9 @@ SELECT * FROM City
EXPLAIN
SELECT * FROM City
WHERE Population > 100000 AND Name LIKE 'Aba%' OR
- Country IN ('CAN', 'ARG') AND ID < 3800 OR
- Country < 'U' AND Name LIKE 'Zhu%' OR
- ID BETWEEN 3800 AND 3810;
+ Country IN ('CAN', 'ARG') AND ID BETWEEN 120 AND 130 OR
+ Country <= 'ALB' AND Name LIKE 'L%' OR
+ ID BETWEEN 3807 AND 3810;
# The output of the next 3 commands tells us about selectivities
# of the conditions utilized in 2 queries following after them
@@ -1206,6 +1206,41 @@ SELECT * FROM t1
DROP TABLE t1;
+
+--echo #
+--echo # MDEV-8603: Wrong result OR/AND condition over index fields
+--echo #
+
+CREATE TABLE t1 (
+ id INT NOT NULL,
+ state VARCHAR(64),
+ capital VARCHAR(64),
+ UNIQUE KEY (id),
+ KEY state (state,id),
+ KEY capital (capital, id)
+);
+
+INSERT INTO t1 VALUES
+ (1,'Arizona','Phoenix'),
+ (2,'Hawaii','Honolulu'),
+ (3,'Georgia','Atlanta'),
+ (4,'Florida','Tallahassee'),
+ (5,'Alaska','Juneau'),
+ (6,'Michigan','Lansing'),
+ (7,'Pennsylvania','Harrisburg'),
+ (8,'Virginia','Richmond')
+;
+
+EXPLAIN
+SELECT * FROM t1 FORCE KEY (state,capital)
+WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9
+ OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas';
+SELECT * FROM t1 FORCE KEY (state,capital)
+WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9
+ OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas';
+
+DROP TABLE t1;
+
#the following command must be the last one in the file
set session optimizer_switch='index_merge_sort_intersection=default';
diff --git a/mysql-test/t/repair_symlink-5543.test b/mysql-test/t/repair_symlink-5543.test
index 58cc810b1a9..b83c1aab1f6 100644
--- a/mysql-test/t/repair_symlink-5543.test
+++ b/mysql-test/t/repair_symlink-5543.test
@@ -9,7 +9,7 @@
eval create table t1 (a int) engine=myisam data directory='$MYSQL_TMP_DIR';
insert t1 values (1);
--system ln -s $MYSQL_TMP_DIR/foobar5543 $MYSQL_TMP_DIR/t1.TMD
---replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR
+--replace_regex / '.*\/t1/ 'MYSQL_TMP_DIR\/t1/
repair table t1;
drop table t1;
@@ -17,7 +17,7 @@ drop table t1;
eval create table t2 (a int) engine=aria data directory='$MYSQL_TMP_DIR';
insert t2 values (1);
--system ln -s $MYSQL_TMP_DIR/foobar5543 $MYSQL_TMP_DIR/t2.TMD
---replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR
+--replace_regex / '.*\/t2/ 'MYSQL_TMP_DIR\/t2/
repair table t2;
drop table t2;
diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test
index 253160c46ac..e75a22a08f4 100644
--- a/mysql-test/t/subselect4.test
+++ b/mysql-test/t/subselect4.test
@@ -1997,6 +1997,25 @@ EXECUTE stmt;
drop table t1, t2, t3;
+--echo #
+--echo # MDEV-11078: NULL NOT IN (non-empty subquery) should never return results
+--echo #
+
+create table t1(a int,b int);
+create table t2(a int,b int);
+insert into t1 value (1,2);
+select (NULL) in (select 1 from t1);
+select (null) in (select 1 from t2);
+select 1 in (select 1 from t1);
+select 1 in (select 1 from t2);
+select 1 from dual where null in (select 1 from t1);
+select 1 from dual where null in (select 1 from t2);
+select (null,null) in (select * from t1);
+select (null,null) in (select * from t2);
+select 1 from dual where null not in (select 1 from t1);
+select 1 from dual where null not in (select 1 from t2);
+drop table t1,t2;
+
SET optimizer_switch= @@global.optimizer_switch;
set @@tmp_table_size= @@global.tmp_table_size;
diff --git a/mysql-test/t/subselect_innodb.test b/mysql-test/t/subselect_innodb.test
index af6ec90ba74..b26c5036f3f 100644
--- a/mysql-test/t/subselect_innodb.test
+++ b/mysql-test/t/subselect_innodb.test
@@ -483,6 +483,26 @@ drop table t1;
set optimizer_switch=@subselect_innodb_tmp;
--echo #
+--echo # MDEV-9635:Server crashes in part_of_refkey or assertion
+--echo # `!created && key_to_save < (int)s->keys' failed in
+--echo # TABLE::use_index(int) or with join_cache_level>2
+--echo #
+
+SET join_cache_level=3;
+
+CREATE TABLE t1 (f1 VARCHAR(1024)) ENGINE=InnoDB;
+CREATE ALGORITHM=TEMPTABLE VIEW v1 AS SELECT * FROM t1;
+
+CREATE TABLE t2 (f2 VARCHAR(4)) ENGINE=InnoDB;
+INSERT INTO t2 VALUES ('foo'),('bar');
+
+SELECT * FROM v1, t2 WHERE ( f1, f2 ) IN ( SELECT f1, f1 FROM t1 );
+
+set join_cache_level = default;
+drop view v1;
+drop table t1,t2;
+
+--echo #
--echo # MDEV-6041: ORDER BY+subqueries: subquery_table.key=outer_table.col is not recongized as binding
--echo #
create table t1(a int) engine=innodb;
@@ -535,4 +555,3 @@ from
t1;
drop table t1,t2;
-
diff --git a/mysql-test/t/symlink-aria-11902.test b/mysql-test/t/symlink-aria-11902.test
new file mode 100644
index 00000000000..a2a266cbb25
--- /dev/null
+++ b/mysql-test/t/symlink-aria-11902.test
@@ -0,0 +1,6 @@
+#
+# MDEV-11902 mi_open race condition
+#
+source include/have_maria.inc;
+set default_storage_engine=Aria;
+source symlink-myisam-11902.test;
diff --git a/mysql-test/t/symlink-myisam-11902.test b/mysql-test/t/symlink-myisam-11902.test
new file mode 100644
index 00000000000..426f8e61edc
--- /dev/null
+++ b/mysql-test/t/symlink-myisam-11902.test
@@ -0,0 +1,60 @@
+#
+# MDEV-11902 mi_open race condition
+#
+source include/have_debug_sync.inc;
+source include/have_symlink.inc;
+source include/not_windows.inc;
+call mtr.add_suppression("File.*t1.* not found");
+
+create table mysql.t1 (a int, b char(16), index(a));
+insert mysql.t1 values (100, 'test'),(101,'test');
+let $datadir=`select @@datadir`;
+
+exec mkdir $MYSQLTEST_VARDIR/tmp/foo;
+replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR;
+eval create table t1 (a int, b char(16), index(a))
+ data directory="$MYSQLTEST_VARDIR/tmp/foo";
+insert t1 values (200, 'some'),(201,'some');
+select * from t1;
+flush tables;
+set debug_sync='mi_open_datafile SIGNAL ok WAIT_FOR go';
+send select * from t1;
+connect con1, localhost, root;
+set debug_sync='now WAIT_FOR ok';
+exec rm -r $MYSQLTEST_VARDIR/tmp/foo;
+exec ln -s $datadir/mysql $MYSQLTEST_VARDIR/tmp/foo;
+set debug_sync='now SIGNAL go';
+connection default;
+replace_regex / '.*\/tmp\// 'MYSQLTEST_VARDIR\/tmp\// /31/20/;
+error 29;
+reap;
+flush tables;
+drop table if exists t1;
+exec rm -r $MYSQLTEST_VARDIR/tmp/foo;
+
+# same with INDEX DIRECTORY
+exec mkdir $MYSQLTEST_VARDIR/tmp/foo;
+replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR;
+eval create table t1 (a int, b char(16), index (a))
+ index directory="$MYSQLTEST_VARDIR/tmp/foo";
+insert t1 values (200, 'some'),(201,'some');
+explain select a from t1;
+select a from t1;
+flush tables;
+set debug_sync='mi_open_kfile SIGNAL waiting WAIT_FOR run';
+send select a from t1;
+connection con1;
+set debug_sync='now WAIT_FOR waiting';
+exec rm -r $MYSQLTEST_VARDIR/tmp/foo;
+exec ln -s $datadir/mysql $MYSQLTEST_VARDIR/tmp/foo;
+set debug_sync='now SIGNAL run';
+connection default;
+replace_regex / '.*\/test\// '.\/test\// /31/20/;
+error ER_FILE_NOT_FOUND;
+reap;
+flush tables;
+drop table if exists t1;
+exec rm -r $MYSQLTEST_VARDIR/tmp/foo;
+
+drop table mysql.t1;
+set debug_sync='RESET';
diff --git a/mysql-test/t/table_elim.test b/mysql-test/t/table_elim.test
index 52857f9169f..8de4743b9fd 100644
--- a/mysql-test/t/table_elim.test
+++ b/mysql-test/t/table_elim.test
@@ -534,12 +534,12 @@ INSERT IGNORE INTO t1 VALUES (0,'g');
CREATE TABLE t3 ( a varchar(1)) ;
INSERT IGNORE INTO t3 VALUES ('g');
-CREATE TABLE t2 ( a int(11) NOT NULL, PRIMARY KEY (a)) ;
+CREATE TABLE t2 ( a int(11) NOT NULL, PRIMARY KEY (a));
+INSERT INTO t2 VALUES (9), (10);
create view v1 as SELECT t1.* FROM t1 LEFT JOIN t2 ON ( t1.a = t2.a ) WHERE t2.a <> 0;
SELECT alias1.* FROM t3 LEFT JOIN v1 as alias1 ON ( t3.a = alias1.b );
EXPLAIN SELECT alias1.* FROM t3 LEFT JOIN v1 as alias1 ON ( t3.a = alias1.b );
-
drop view v1;
DROP TABLE t1,t2,t3;
diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test
index e9d091202d5..fa784a7b946 100644
--- a/mysql-test/t/view.test
+++ b/mysql-test/t/view.test
@@ -5517,6 +5517,66 @@ SHOW CREATE VIEW v1;
drop view v1;
drop table t1,t2;
+
+--echo #
+--echo # MDEV-12099: usage of mergeable view with LEFT JOIN
+--echo # that can be converted to INNER JOIN
+--echo #
+
+create table t1 (a int, b int, key(a)) engine=myisam;
+insert into t1 values
+ (3,20), (7,10), (2,10), (4,30), (8,70),
+ (7,70), (9,100), (9,60), (8,80), (7,60);
+
+create table t2 (c int, d int, key (c)) engine=myisam;
+insert into t2 values
+ (50,100), (20, 200), (10,300),
+ (150,100), (120, 200), (110,300),
+ (250,100), (220, 200), (210,300);
+
+create table t3(e int, f int not null, key(e), unique (f)) engine=myisam;
+insert into t3 values
+ (100, 3), (300, 5), (400, 4), (300,7),
+ (300,2), (600, 13), (800, 15), (700, 14),
+ (600, 23), (800, 25), (700, 24);
+
+create view v1 as
+ select * from t2 left join t3 on t3.e=t2.d where t3.f is not null;
+
+select *
+ from t1 left join v1 on v1.c=t1.b
+ where t1.a < 5;
+
+select *
+ from t1 left join ( t2 left join t3 on t3.e=t2.d )
+ on t2.c=t1.b and t3.f is not null
+ where t1.a < 5;
+
+explain extended
+select *
+ from t1 left join v1 on v1.c=t1.b
+ where t1.a < 5;
+
+explain extended
+select *
+ from t1 left join ( t2 left join t3 on t3.e=t2.d )
+ on t2.c=t1.b and t3.f is not null
+ where t1.a < 5;
+
+explain extended
+select *
+ from t1 left join v1 on v1.c=t1.b and v1.f=t1.a
+ where t1.a < 5;
+
+explain extended
+select *
+ from t1 left join ( t2 left join t3 on t3.e=t2.d )
+ on t2.c=t1.b and t3.f=t1.a and t3.f is not null
+ where t1.a < 5;
+
+drop view v1;
+drop table t1,t2,t3;
+
--echo # -----------------------------------------------------------------
--echo # -- End of 5.5 tests.
--echo # -----------------------------------------------------------------
diff --git a/mysql-test/unstable-tests b/mysql-test/unstable-tests
index 42985e1d66d..8030c99877c 100644
--- a/mysql-test/unstable-tests
+++ b/mysql-test/unstable-tests
@@ -27,6 +27,7 @@ main.alter_table : Modified in 10.1.21
main.alter_table_trans : MDEV-11805 - timeout
main.analyze_stmt_slow_query_log : Modified in 10.1.21
main.cast : Modified in 10.1.21
+main.count_distinct2 : MDEV-11768 - timeout
main.create : Modified in 10.1.21
main.create_delayed : MDEV-10605 - failed with timeout
main.create_drop_binlog : Uses binlog_start_pos.inc modified in 10.1.20
@@ -40,12 +41,15 @@ main.ctype_utf32 : Modified in 10.1.21
main.ctype_utf8 : Modified in 10.1.20
main.ctype_utf8mb4 : Modified in 10.1.20
main.default : Modified in 10.1.20
+main.debug_sync : MDEV-10607 - internal error
main.derived : Modified in 10.1.20
+main.derived_opt : MDEV-11768 - timeout
main.derived_view : Modified in 10.1.20
-main.events_restart : MDEV-11221: assertion failure
+main.events_restart : MDEV-11221 - assertion failure
main.events_slowlog : Added in 10.1.21
main.fulltext_charsets : Added in 10.1.20
main.func_time : Modified in 10.1.21
+main.grant : Modified in 10.1.22
main.group_by : Modified in 10.1.20
main.group_by_innodb : Modified in 10.1.20
main.host_cache_size_functionality : MDEV-10606 - sporadic failure on shutdown
@@ -54,6 +58,7 @@ main.index_merge_innodb : MDEV-7142 - Wrong execution plan, also modi
main.information_schema_part : Modified in 10.1.21
main.innodb_mysql_lock : MDEV-7861 - sporadic lock detection failure
main.join_cache : Modified in 10.1.21
+main.join_nested : Modified in 10.1.22
main.kill_processlist-6619 : MDEV-10793 - wrong result in processlist
main.loaddata : Modified in 10.1.21
main.log_slow : Modified in 10.1.21
@@ -62,19 +67,26 @@ main.mdev-504 : MDEV-10607 - sporadic "can't connect"
main.mdev375 : MDEV-10607 - sporadic "can't connect"
main.merge : MDEV-10607 - sporadic "can't connect"
main.mysqlbinlog : Uses binlog_start_pos.inc modified in 10.1.20
+main.mysqldump : Modified in 10.1.22
main.mysqldump-max : Uses binlog_start_pos.inc modified in 10.1.20
main.mysqlslap : MDEV-11801 - timeout
+main.mysqlhotcopy_myisam : MDEV-10995 - test hangs on debug build
main.mysqltest : MDEV-9269 - fails on Alpha
main.named_pipe : Uses search_pattern_in_file.inc modified in 10.1.20
main.order_by : Modified in 10.1.21
main.order_by_optimizer_innodb : MDEV-10683 - wrong execution plan
main.parser : Modified in 10.1.20
+main.partition_innodb : Modified in 10.1.22
+main.partition_myisam : Modified in 10.1.22
main.pool_of_threads : Modified in 10.1.21
main.ps : MDEV-11017 - sporadic wrong Prepared_stmt_count
+main.range_vs_index_merge : Modified in 10.1.22
+main.repair_symlink-5543 : Modified in 10.1.22
main.selectivity : Modified in 10.1.20
-main.show_explain : MDEV-10674 - sporadic failure
-main.signal_demo3 : MDEV-11720 - Thread stack overrun on labrador
+main.show_explain : MDEV-10674 - wrong result
+main.signal_demo3 : MDEV-11720 - Thread stack overrun on Solaris
main.sp : Modified in 10.1.21
+main.sp_notembedded : MDEV-10607 - internal error
main.sp-prelocking : Modified in 10.1.21
main.sp-security : MDEV-10607 - sporadic "can't connect"
main.stat_tables_par_innodb : MDEV-10515 - sporadic wrong results
@@ -89,17 +101,24 @@ main.subselect_no_mat : Uses subselect.test modified in 10.1.21
main.subselect_no_opts : Uses subselect.test modified in 10.1.21
main.subselect_no_scache : Uses subselect.test modified in 10.1.21
main.subselect_no_semijoin : Uses subselect.test modified in 10.1.21
+main.symlink-aria-11902 : Added in 10.0.30
+main.symlink-myisam-11902 : Added in 10.0.30
+main.table_elim : Modified in 10.1.22
main.trigger_null-8605 : Modified in 10.1.21
main.type_datetime_hires : MDEV-10687 - timeout
main.type_decimal : Modified in 10.1.20
main.union : Modified in 10.1.21
main.view : Modified in 10.1.21
main.wait_timeout_not_windows : Uses search_pattern_in_file.inc modified in 10.1.20
+main.xa : MDEV-11769 - lock wait timeout
#----------------------------------------------------------------
-archive.archive-big : MDEV-10615 - table is marked as crashed
-archive.discover : MDEV-10510 - table is marked as crashed; modified in 10.1.21
+archive.archive-big : MDEV-10615 - table is marked as crashed
+archive.archive_bitfield : MDEV-11771 - table is marked as crashed
+archive.archive_symlink : MDEV-12170 - unexpected error on rmdir
+archive.discover : MDEV-10510 - table is marked as crashed
+archive.mysqlhotcopy_archive : MDEV-10995 - test hangs on debug build
#----------------------------------------------------------------
@@ -107,6 +126,7 @@ binlog.binlog_commit_wait : MDEV-10150 - Error: too much time el
binlog.binlog_incident : Uses binlog_start_pos.inc modified in 10.1.20
binlog.binlog_killed : Uses binlog_start_pos.inc modified in 10.1.20
binlog.binlog_killed_simulate : Uses binlog_start_pos.inc modified in 10.1.20
+binlog.binlog_max_binlog_stmt_cache_size : Added in 10.0.30
binlog.binlog_mysqlbinlog2 : Uses binlog_start_pos.inc modified in 10.1.20
binlog.mix_innodb_myisam_binlog : Uses binlog_start_pos.inc modified in 10.1.20
binlog.binlog_row_annotate : Uses binlog_start_pos.inc modified in 10.1.20
@@ -118,9 +138,10 @@ binlog_encryption.* : Added in 10.1.20
#----------------------------------------------------------------
-connect.jdbc : Modified in 10.1.21
-connect.jdbc_new : Modified in 10.1.21
connect.tbl : MDEV-9844, MDEV-10179 - sporadic crashes, valgrind warnings, wrong results
+connect.xml : Uses xsample2.xml modified in 10.0.30
+connect.xml_zip : Added in 10.0.30
+connect.zip : Added in 10.0.30
#----------------------------------------------------------------
@@ -159,6 +180,7 @@ engines/funcs.* : Not maintained in timely manner
#----------------------------------------------------------------
+federated_bug_35333 : Modified in 10.0.30
federated.federatedx : MDEV-10617 - Wrong checksum, timeouts
federated.federated_innodb : MDEV-10617, MDEV-10417 - Wrong checksum, timeouts, fails on Mips
federated.federated_partition : MDEV-10417 - Fails on Mips
@@ -166,7 +188,11 @@ federated.federated_transactions : MDEV-10617, MDEV-10417 - Wrong checksum, time
#----------------------------------------------------------------
+funcs_1.memory_views : MDEV-11773 - timeout
funcs_1.processlist_val_no_prot : MDEV-11223 - Wrong result
+funcs_1.processlist_val_ps : MDEV-12175 - Wrong result
+
+funcs_2/charset.* : MDEV-10999 - test not maintained
funcs_2.innodb_charset : Modified in 10.1.21
funcs_2.memory_charset : MDEV-10290 - Timeout
@@ -185,20 +211,24 @@ galera_3nodes.* : MDEV-11490 - Warnings not suppressed
#----------------------------------------------------------------
innodb.101_compatibility : Added in 10.1.21
+innodb.alter_key_block_size-11757 : Added in 10.0.30
innodb.binlog_consistent : MDEV-10618 - Server fails to start; also uses binlog_start_pos.inc modified in 10.1.20
innodb.doublewrite : Added in 10.1.21
innodb.group_commit_binlog_pos : Uses binlog_start_pos.inc modified in 10.1.20
innodb.group_commit_binlog_pos_no_optimize_thread : Uses binlog_start_pos.inc modified in 10.1.20
innodb.group_commit_crash : Modified in 10.1.21
-innodb.group_commit_crash_no_optimize_thread : Modified in 10.1.21
+innodb.group_commit_crash_no_optimize_thread : MDEV-11770 - checksum mismatch
innodb.innodb-alter-table : MDEV-10619 - Testcase timeout
+innodb.innodb_blob_unrecoverable_crash : Modified in 10.0.30
innodb.innodb-bug-14068765 : MDEV-9105 - valgrind warnings, assertion failures
innodb.innodb-bug-14084530 : MDEV-9105 - valgrind warnings, assertion failures
innodb.innodb_bug14147491 : MDEV-11808, also modified in 10.1.21
innodb.innodb_bug14676111 : MDEV-11802 - wrong result
innodb.innodb_bug30423 : MDEV-7311 - Wrong number of rows in the plan
+innodb.innodb_bug59641 : Modified in 10.0.30
innodb.innodb-change-buffer-recovery : Modified in 10.1.21
innodb.innodb_defragment_fill_factor : Modified in 10.1.20
+innodb.innodb-get-fk : Modified in 10.0.30
innodb.innodb-lock-schedule-algorithm : Modified in 10.1.20
innodb.innodb-page_compression_zip : MDEV-10641 - mutex problem
innodb.innodb_stats : MDEV-10682 - wrong result
@@ -206,7 +236,11 @@ innodb.innodb_sys_semaphore_waits : MDEV-10331 - wrong result
innodb.innodb-wl5522 : MDEV-9105 - valgrind warnings, assertion failures
innodb.innodb-wl5522-1 : MDEV-9105 - valgrind warnings, assertion failures
innodb.innodb-wl5522-debug-zip : Modified in 10.1.21
+innodb.innodb_monitor : MDEV-10939 - Testcase timeout
+innodb.read_only_recovery : Added in 10.0.30
+innodb.xa_recovery : Modified in 10.0.30
innodb.log_data_file_size : Added in 10.1.21
+innodb.log_file_size : Added in 10.0.30
innodb.table_index_statistics : Added in 10.1.20
innodb.trigger : Modified in 10.1.20
innodb.xa_recovery : Modified in 10.1.21
@@ -214,6 +248,7 @@ innodb.xa_recovery : Modified in 10.1.21
#----------------------------------------------------------------
innodb_fts.create : Added in 10.1.20
+innodb_fts.innodb_fts_stopword_charset : MDEV-12052 - Crash on shutdown
#----------------------------------------------------------------
@@ -233,26 +268,47 @@ mroonga/storage.repair_table_no_index_file : MDEV-9364 -
#----------------------------------------------------------------
-multi_source.gtid : MDEV-10620, MDEV-10417 - Timeout in wait condition, fails on Mips
+multi_source.gtid : MDEV-10417 - Fails on Mips
multi_source.info_logs : MDEV-10042 - wrong result
multi_source.multisource : MDEV-10417 - Fails on Mips; also uses binlog_start_pos.inc modified in 10.1.20
multi_source.reset_slave : MDEV-10690 - wrong result; also uses binlog_start_pos.inc modified in 10.1.20
multi_source.simple : MDEV-4633 - Wrong slave status output; also uses binlog_start_pos.inc modified in 10.1.20
multi_source.status_vars : MDEV-4632 - failed while waiting for Slave_received_heartbeats
+
+#----------------------------------------------------------------
+
+oqgraph.regression_mdev6282 : Modified in 10.0.30
+oqgraph.regression_mdev6345 : Modified in 10.0.30
+
#----------------------------------------------------------------
-parts.partition_float_myisam : MDEV-10621 - Testcase timeout
-parts.partition_int_myisam : MDEV-10621 - Testcase timeout
+parts.partition_bigint_innodb : Added in 10.0.30
+parts.partition_bigint_myisam : Added in 10.0.30
+parts.partition_double_innodb : Added in 10.0.30
+parts.partition_double_myisam : Added in 10.0.30
+parts.partition_exch_qa_10 : MDEV-11765 - wrong result
+parts.partition_float_innodb : Modified in 10.0.30
+parts.partition_float_myisam : Modified in 10.0.30
+parts.partition_int_innodb : Modified in 10.0.30
+parts.partition_int_myisam : Modified in 10.0.30
+parts.partition_mediumint_innodb : Added in 10.0.30
+parts.partition_mediumint_myisam : Added in 10.0.30
+parts.partition_smallint_innodb : Added in 10.0.30
+parts.partition_smallint_myisam : Added in 10.0.30
+parts.partition_tinyint_innodb : Added in 10.0.30
+parts.partition_tinyint_myisam : Added in 10.0.30
#----------------------------------------------------------------
perfschema.func_file_io : MDEV-5708 - fails for s390x
perfschema.func_mutex : MDEV-5708 - fails for s390x
+perfschema.hostcache_ipv6_ssl : MDEV-10696 - crash on shutdown
perfschema.setup_actors : MDEV-10679 - rare crash
perfschema.socket_summary_by_event_name_func : MDEV-10622 - Socket summary tables do not match
perfschema.stage_mdl_global : MDEV-11803 - wrong result on slow builders
-perfschema.threads_mysql : MDEV-10677 - sporadic wrong result
+perfschema.stage_mdl_procedure : MDEV-11545 - Wrong result
+perfschema.threads_mysql : MDEV-10677, MDEV-12177 - Wrong result
#----------------------------------------------------------------
@@ -264,38 +320,32 @@ plugins.two_password_validations : MDEV-11650 - valgrind warnings
#----------------------------------------------------------------
-roles.role_case_sensitive-10744 : Added in 10.1.20
-roles.create_and_drop_role : Modified in 10.1.20
#----------------------------------------------------------------
rpl.last_insert_id : MDEV-10625 - warnings in error log
-rpl.rpl_alter_extra_persistent : Added in 10.1.21
rpl.rpl_auto_increment : MDEV-10417 - Fails on Mips
rpl.rpl_auto_increment_bug45679 : MDEV-10417 - Fails on Mips
rpl.rpl_auto_increment_update_failure : MDEV-10625 - warnings in error log
rpl.rpl_binlog_index : MDEV-9501 - Warning: failed registering on master
rpl.rpl_checksum : Uses search_pattern_in_file.inc modified in 10.1.20
-rpl.rpl_checksum_cache : MDEV-10626 - Testcase timeout
-rpl.rpl_circular_for_4_hosts : MDEV-10627 - Testcase timeout
+rpl.rpl_checksum_cache : MDEV-12173 - InnoDB error
rpl.rpl_ddl : MDEV-10417 - Fails on Mips
rpl.rpl_domain_id_filter_restart : MDEV-10684 - Wrong result
rpl.rpl_gtid_basic : MDEV-10681 - server startup problem
rpl.rpl_gtid_crash : MDEV-9501 - Warning: failed registering on master
rpl.rpl_gtid_errorlog : Uses search_pattern_in_file.inc modified in 10.1.20
-rpl.rpl_gtid_master_promote : MDEV-10628 - Timeout in sync_with_master
rpl.rpl_gtid_mdev9033 : MDEV-10680 - warnings
rpl.rpl_gtid_stop_start : MDEV-10629 - Crash on shutdown
rpl.rpl_gtid_until : MDEV-10625 - warnings in error log
-rpl.rpl_heartbeat_basic : MDEV-11668 - wrong result
+rpl.rpl_heartbeat_basic : Modified in 10.0.30
rpl.rpl_innodb_bug30888 : MDEV-10417 - Fails on Mips
rpl.rpl_insert : MDEV-9329 - Fails on Ubuntu/s390x
rpl.rpl_insert_delayed : MDEV-9329 - Fails on Ubuntu/s390x
rpl.rpl_invoked_features : MDEV-10417 - Fails on Mips
rpl.rpl_mariadb_slave_capability : MDEV-11018 - sporadic wrong events in binlog
-rpl.rpl_mdev10863 : Added in 10.1.20
rpl.rpl_mdev6020 : MDEV-10630, MDEV-10417 - Timeouts, fails on Mips
-rpl.rpl_mdev6386 : MDEV-10631 - Wrong result on slave
+rpl.rpl_mdev6386 : Modified in 10.0.30
rpl.rpl_parallel : MDEV-10632, MDEV-10653 - Failures to sync, timeouts
rpl.rpl_parallel_optimistic : MDEV-10511 - timeout
rpl.rpl_parallel_retry : MDEV-11119 - Server crash
@@ -303,24 +353,21 @@ rpl.rpl_parallel_temptable : MDEV-10356 - Crash in close_thread_table
rpl.rpl_partition_innodb : MDEV-10417 - Fails on Mips
rpl.rpl_row_annotate : Uses binlog_start_pos.inc modified in 10.1.20
rpl.rpl_password_boundaries : MDEV-11534 - Slave IO warnings
-rpl.rpl_row_drop_create_temp_table : MDEV-10626 - Testcase timeout
+rpl.rpl_row_basic_11bugs : MDEV-12171 - Server failed to start
rpl.rpl_row_flsh_tbls : Uses binlog_start_pos.inc modified in 10.1.20
rpl.rpl_row_log_innodb : MDEV-10688 - Wrong result
-rpl.rpl_row_mysqlbinlog : Modified in 10.1.21
rpl.rpl_row_sp001 : MDEV-9329 - Fails on Ubuntu/s390x
rpl.rpl_semi_sync : MDEV-11220 - Wrong result
rpl.rpl_semi_sync_event_after_sync : MDEV-11806 - warnings
rpl.rpl_semi_sync_uninstall_plugin : MDEV-7140 - Wrong plugin status
rpl.rpl_semi_sync_wait_point : MDEV-11807 - timeout in wait condition
-rpl.rpl_show_slave_hosts : MDEV-10681 - server startup problem
+rpl.rpl_show_slave_hosts : MDEV-10681, MDEV-12171 - server startup problem
rpl.rpl_skip_replication : MDEV-9268 - Fails with timeout in sync_slave_with_master on Alpha
rpl.rpl_slave_grp_exec : MDEV-10514 - Unexpected deadlock
-rpl.rpl_special_charset : Modified in 10.1.21
rpl.rpl_stm_flsh_tbls : Uses binlog_start_pos.inc modified in 10.1.20
rpl.rpl_stop_slave_error : Uses search_pattern_in_file.inc modified in 10.1.20
rpl.rpl_sync : MDEV-10633 - Database page corruption
rpl.rpl_temporary_error2 : MDEV-10634 - Wrong number of retries
-rpl.sec_behind_master-5114 : Modified in 10.1.21
#----------------------------------------------------------------
@@ -353,16 +400,15 @@ sys_vars.autocommit_func2 : MDEV-9329 - Fails on Ubuntu/s39
sys_vars.keep_files_on_create_basic : MDEV-10676 - timeout
sys_vars.innodb_buffer_pool_dump_pct_basic : MDEV-10651 - sporadic failure on file_exists
sys_vars.innodb_fatal_semaphore_wait_threshold : MDEV-10513 - crashes
-sys_vars.replicate_do_db_basic : Modified in 10.1.20
-sys_vars.replicate_do_table_basic : Modified in 10.1.20
-sys_vars.replicate_ignore_db_basic : Modified in 10.1.20
-sys_vars.replicate_ignore_table_basic : Modified in 10.1.20
-sys_vars.replicate_wild_do_table_basic : Modified in 10.1.20
-sys_vars.replicate_wild_ignore_table_basic : Modified in 10.1.20
+sys_vars.innodb_force_recovery_crash_basic : Modified in 10.0.30
+sys_vars.innodb_stats_include_delete_marked_basic : Added in 10.0.30
+sys_vars.innodb_status_output_basic : MDEV-12174 - Timeout
sys_vars.rpl_init_slave_func : MDEV-10149 - wrong results
sys_vars.sysvars_innodb : MDEV-6958 - error-prone rdiffs
sys_vars.sysvars_server_embedded : MDEV-6958 - error-prone rdiffs
sys_vars.table_open_cache_instances_basic : Modified in 10.1.20
+sys_vars.secure_file_priv : Modified in 10.0.30
+sys_vars.thread_cache_size_func : MDEV-11775 - wrong result
#----------------------------------------------------------------
@@ -370,6 +416,10 @@ tokudb.cluster_filter : MDEV-10678 - Wrong execution plan
tokudb.cluster_filter_hidden : MDEV-10678 - Wrong execution plan
tokudb.cluster_filter_unpack_varchar : MDEV-10636 - Wrong execution plan
tokudb.dir_per_db : MDEV-11537 - Wrong result
+tokudb.dir_per_db_rename_to_nonexisting_schema : Added in 10.0.30
+tokudb.gap_lock_error : Added in 10.0.30
+tokudb.locks-select-update-3 : Modified in 10.0.30
+tokudb.percona_kill_idle_trx_tokudb : Added in 10.0.30
tokudb.table_index_statistics : Added in 10.1.20
tokudb_bugs.checkpoint_lock : MDEV-10637 - Wrong processlist output
@@ -379,6 +429,8 @@ tokudb_bugs.xa : MDEV-11804 - Lock wait timeout
tokudb_rpl.rpl_parallel_optimistic : Added in 10.1.20
tokudb_rpl.rpl_tokudb_rfr_partition_table : Added in 10.1.20
+rpl-tokudb.rpl_extra_col_slave_tokudb : Result file modified in 10.0.30
+
#----------------------------------------------------------------
unit.ma_test_loghandler : MDEV-10638 - record read not ok
@@ -387,6 +439,11 @@ unit.ma_test_loghandler : MDEV-10638 - record read not ok
vcol.not_supported : MDEV-10639 - Testcase timeout
vcol.vcol_keys_innodb : MDEV-10639 - Testcase timeout
+vcol.vcol_misc : Modified in 10.0.30
+vcol.vcol_select_myisam : Modified in 10.0.30
+vcol.vcol_trigger_sp_innodb : Include file modified in 10.0.30
+vcol.vcol_trigger_sp_myisam : Include file modified in 10.0.30
+vcol.wrong_arena : Added in 10.0.30
#----------------------------------------------------------------
diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp
index db44e49482a..08086033fc9 100644
--- a/mysql-test/valgrind.supp
+++ b/mysql-test/valgrind.supp
@@ -1129,6 +1129,17 @@
...
fun:pthread_create*
}
+<<<<<<< HEAD
+
+{
+ Memory Leak in loader and valgrind malloc
+ Memcheck:Leak
+ match-leak-kinds:reachable
+ obj:*/vgpreload_memcheck*.so
+ ...
+ obj:*/ld-*.so
+ ...
+}
{
ConnectSE: unixODBC SQLAllocEnv leaves some "still reachable" pointers
diff --git a/mysys/mf_format.c b/mysys/mf_format.c
index 91354db0b64..6672a4386e4 100644
--- a/mysys/mf_format.c
+++ b/mysys/mf_format.c
@@ -97,13 +97,8 @@ char * fn_format(char * to, const char *name, const char *dir,
pos=strmake(strmov(to,dev),name,length);
(void) strmov(pos,ext); /* Don't convert extension */
}
- /*
- If MY_RETURN_REAL_PATH and MY_RESOLVE_SYMLINK is given, only do
- realpath if the file is a symbolic link
- */
if (flag & MY_RETURN_REAL_PATH)
- (void) my_realpath(to, to, MYF(flag & MY_RESOLVE_SYMLINKS ?
- MY_RESOLVE_LINK: 0));
+ (void) my_realpath(to, to, MYF(0));
else if (flag & MY_RESOLVE_SYMLINKS)
{
strmov(buff,to);
diff --git a/mysys/my_context.c b/mysys/my_context.c
index 017cebdc110..29ba9f79c72 100644
--- a/mysys/my_context.c
+++ b/mysys/my_context.c
@@ -206,7 +206,8 @@ my_context_spawn(struct my_context *c, void (*f)(void *), void *d)
(
"movq %%rsp, (%[save])\n\t"
"movq %[stack], %%rsp\n\t"
-#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) && !defined(__INTEL_COMPILER)
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __clang__) && \
+ !defined(__INTEL_COMPILER)
/*
This emits a DWARF DW_CFA_undefined directive to make the return address
undefined. This indicates that this is the top of the stack frame, and
diff --git a/mysys/my_create.c b/mysys/my_create.c
index 6a3bcd63557..014b65c4e14 100644
--- a/mysys/my_create.c
+++ b/mysys/my_create.c
@@ -36,7 +36,7 @@
File my_create(const char *FileName, int CreateFlags, int access_flags,
myf MyFlags)
{
- int fd, rc;
+ int fd;
DBUG_ENTER("my_create");
DBUG_PRINT("my",("Name: '%s' CreateFlags: %d AccessFlags: %d MyFlags: %lu",
FileName, CreateFlags, access_flags, MyFlags));
@@ -54,21 +54,7 @@ File my_create(const char *FileName, int CreateFlags, int access_flags,
fd= -1;
}
- rc= my_register_filename(fd, FileName, FILE_BY_CREATE,
+ fd= my_register_filename(fd, FileName, FILE_BY_CREATE,
EE_CANTCREATEFILE, MyFlags);
- /*
- my_register_filename() may fail on some platforms even if the call to
- *open() above succeeds. In this case, don't leave the stale file because
- callers assume the file to not exist if my_create() fails, so they don't
- do any cleanups.
- */
- if (unlikely(fd >= 0 && rc < 0))
- {
- int tmp= my_errno;
- my_close(fd, MyFlags);
- my_delete(FileName, MyFlags);
- my_errno= tmp;
- }
-
- DBUG_RETURN(rc);
+ DBUG_RETURN(fd);
} /* my_create */
diff --git a/mysys/my_delete.c b/mysys/my_delete.c
index 3dfe290dabe..0faf6079d98 100644
--- a/mysys/my_delete.c
+++ b/mysys/my_delete.c
@@ -21,6 +21,12 @@
static int my_win_unlink(const char *name);
#endif
+CREATE_NOSYMLINK_FUNCTION(
+ unlink_nosymlinks(const char *pathname),
+ unlinkat(dfd, filename, 0),
+ unlink(pathname)
+);
+
int my_delete(const char *name, myf MyFlags)
{
int err;
@@ -30,7 +36,10 @@ int my_delete(const char *name, myf MyFlags)
#ifdef _WIN32
err = my_win_unlink(name);
#else
- err = unlink(name);
+ if (MyFlags & MY_NOSYMLINKS)
+ err= unlink_nosymlinks(name);
+ else
+ err= unlink(name);
#endif
if(err)
diff --git a/mysys/my_div.c b/mysys/my_div.c
index 660b87e5ab4..44eb5392421 100644
--- a/mysys/my_div.c
+++ b/mysys/my_div.c
@@ -27,7 +27,7 @@
char * my_filename(File fd)
{
DBUG_ENTER("my_filename");
- if ((uint) fd >= (uint) my_file_limit)
+ if ((uint) fd >= (uint) my_file_limit || !my_file_info[fd].name)
DBUG_RETURN((char*) "UNKNOWN");
if (fd >= 0 && my_file_info[fd].type != UNOPEN)
{
diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c
index 2ac033c8264..0bb431dd560 100644
--- a/mysys/my_fopen.c
+++ b/mysys/my_fopen.c
@@ -69,19 +69,13 @@ FILE *my_fopen(const char *filename, int flags, myf MyFlags)
DBUG_RETURN(fd); /* safeguard */
}
mysql_mutex_lock(&THR_LOCK_open);
- if ((my_file_info[filedesc].name= (char*)
- my_strdup(filename,MyFlags)))
- {
- my_stream_opened++;
- my_file_total_opened++;
- my_file_info[filedesc].type= STREAM_BY_FOPEN;
- mysql_mutex_unlock(&THR_LOCK_open);
- DBUG_PRINT("exit",("stream: 0x%lx", (long) fd));
- DBUG_RETURN(fd);
- }
+ my_file_info[filedesc].name= (char*) my_strdup(filename,MyFlags);
+ my_stream_opened++;
+ my_file_total_opened++;
+ my_file_info[filedesc].type= STREAM_BY_FOPEN;
mysql_mutex_unlock(&THR_LOCK_open);
- (void) my_fclose(fd,MyFlags);
- my_errno=ENOMEM;
+ DBUG_PRINT("exit",("stream: 0x%lx", (long) fd));
+ DBUG_RETURN(fd);
}
else
my_errno=errno;
diff --git a/mysys/my_init.c b/mysys/my_init.c
index 6ec9552c528..7f0f7a8cc44 100644
--- a/mysys/my_init.c
+++ b/mysys/my_init.c
@@ -227,7 +227,7 @@ Voluntary context switches %ld, Involuntary context switches %ld\n",
/* At very last, delete mysys key, it is used everywhere including DBUG */
pthread_key_delete(THR_KEY_mysys);
- my_init_done=0;
+ my_init_done= my_thr_key_mysys_exists= 0;
} /* my_end */
#ifndef DBUG_OFF
diff --git a/mysys/my_open.c b/mysys/my_open.c
index b6d8f08bfc1..3999810eb2e 100644
--- a/mysys/my_open.c
+++ b/mysys/my_open.c
@@ -15,9 +15,14 @@
#include "mysys_priv.h"
#include "mysys_err.h"
-#include <my_dir.h>
+#include <m_string.h>
#include <errno.h>
+CREATE_NOSYMLINK_FUNCTION(
+ open_nosymlinks(const char *pathname, int flags, int mode),
+ openat(dfd, filename, O_NOFOLLOW | flags, mode),
+ open(pathname, O_NOFOLLOW | flags, mode)
+);
/*
Open a file
@@ -45,10 +50,11 @@ File my_open(const char *FileName, int Flags, myf MyFlags)
MyFlags|= my_global_flags;
#if defined(_WIN32)
fd= my_win_open(FileName, Flags);
-#elif !defined(NO_OPEN_3)
- fd = open(FileName, Flags | O_CLOEXEC, my_umask); /* Normal unix */
#else
- fd = open((char *) FileName, Flags | O_CLOEXEC);
+ if (MyFlags & MY_NOSYMLINKS)
+ fd = open_nosymlinks(FileName, Flags | O_CLOEXEC, my_umask);
+ else
+ fd = open(FileName, Flags | O_CLOEXEC, my_umask);
#endif
fd= my_register_filename(fd, FileName, FILE_BY_OPEN,
@@ -131,25 +137,16 @@ File my_register_filename(File fd, const char *FileName, enum file_type
thread_safe_increment(my_file_opened,&THR_LOCK_open);
DBUG_RETURN(fd); /* safeguard */
}
- else
- {
- mysql_mutex_lock(&THR_LOCK_open);
- if ((my_file_info[fd].name = (char*) my_strdup(FileName,MyFlags)))
- {
- my_file_opened++;
- my_file_total_opened++;
- my_file_info[fd].type = type_of_file;
- mysql_mutex_unlock(&THR_LOCK_open);
- DBUG_PRINT("exit",("fd: %d",fd));
- DBUG_RETURN(fd);
- }
- mysql_mutex_unlock(&THR_LOCK_open);
- my_errno= ENOMEM;
- }
- (void) my_close(fd, MyFlags);
+ mysql_mutex_lock(&THR_LOCK_open);
+ my_file_info[fd].name = (char*) my_strdup(FileName, MyFlags);
+ my_file_opened++;
+ my_file_total_opened++;
+ my_file_info[fd].type = type_of_file;
+ mysql_mutex_unlock(&THR_LOCK_open);
+ DBUG_PRINT("exit",("fd: %d",fd));
+ DBUG_RETURN(fd);
}
- else
- my_errno= errno;
+ my_errno= errno;
DBUG_PRINT("error",("Got error %d on open", my_errno));
if (MyFlags & (MY_FFNF | MY_FAE | MY_WME))
diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c
index b0e910f7ba0..72648d4c9a8 100644
--- a/mysys/my_symlink.c
+++ b/mysys/my_symlink.c
@@ -1,5 +1,6 @@
/*
Copyright (c) 2001, 2011, Oracle and/or its affiliates
+ Copyright (c) 2010, 2017, MariaDB
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
@@ -23,6 +24,14 @@
#include <sys/stat.h>
#endif
+static int always_valid(const char *filename __attribute__((unused)))
+{
+ return 0;
+}
+
+int (*mysys_test_invalid_symlink)(const char *filename)= always_valid;
+
+
/*
Reads the content of a symbolic link
If the file is not a symbolic link, return the original file name in to.
@@ -120,6 +129,11 @@ int my_is_symlink(const char *filename __attribute__((unused)))
to is guaranteed to never set to a string longer than FN_REFLEN
(including the end \0)
+
+ On error returns -1, unless error is file not found, in which case it
+ is 1.
+
+ Sets my_errno to specific error number.
*/
int my_realpath(char *to, const char *filename, myf MyFlags)
@@ -145,7 +159,10 @@ int my_realpath(char *to, const char *filename, myf MyFlags)
if (MyFlags & MY_WME)
my_error(EE_REALPATH, MYF(0), filename, my_errno);
my_load_path(to, filename, NullS);
- result= -1;
+ if (my_errno == ENOENT)
+ result= 1;
+ else
+ result= -1;
}
DBUG_RETURN(result);
#elif defined(_WIN32)
@@ -168,3 +185,78 @@ int my_realpath(char *to, const char *filename, myf MyFlags)
#endif
return 0;
}
+
+#ifdef HAVE_OPEN_PARENT_DIR_NOSYMLINKS
+/** opens the parent dir. walks the path, and does not resolve symlinks
+
+ returns the pointer to the file name (basename) within the pathname
+ or NULL in case of an error
+
+ stores the parent dir (dirname) file descriptor in pdfd.
+ It can be -1 even if there was no error!
+
+ This is used for symlinked tables for DATA/INDEX DIRECTORY.
+ The paths there have been realpath()-ed. So, we can assume here that
+
+ * `pathname` is an absolute path
+ * no '.', '..', and '//' in the path
+ * file exists
+*/
+
+const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd)
+{
+ char buf[PATH_MAX+1];
+ char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf));
+ int fd, dfd= -1;
+
+ if (*end)
+ {
+ errno= ENAMETOOLONG;
+ return NULL;
+ }
+
+ if (*s != '/') /* not an absolute path */
+ {
+ errno= ENOENT;
+ return NULL;
+ }
+
+ for (;;)
+ {
+ if (*e == '/') /* '//' in the path */
+ {
+ errno= ENOENT;
+ goto err;
+ }
+ while (*e && *e != '/')
+ e++;
+ *e= 0;
+
+ if (!memcmp(s, ".", 2) || !memcmp(s, "..", 3))
+ {
+ errno= ENOENT;
+ goto err;
+ }
+
+ if (++e >= end)
+ {
+ *pdfd= dfd;
+ return pathname + (s - buf);
+ }
+
+ fd = openat(dfd, s, O_NOFOLLOW | O_PATH);
+ if (fd < 0)
+ goto err;
+
+ if (dfd >= 0)
+ close(dfd);
+
+ dfd= fd;
+ s= e;
+ }
+err:
+ if (dfd >= 0)
+ close(dfd);
+ return NULL;
+}
+#endif
diff --git a/mysys/my_symlink2.c b/mysys/my_symlink2.c
index e2cc641ebec..a2f7b6a0741 100644
--- a/mysys/my_symlink2.c
+++ b/mysys/my_symlink2.c
@@ -92,27 +92,6 @@ File my_create_with_symlink(const char *linkname, const char *filename,
}
/*
- If the file was a symlink, delete both symlink and the file which the
- symlink pointed to.
-*/
-
-int my_delete_with_symlink(const char *name, myf MyFlags)
-{
- char link_name[FN_REFLEN];
- int was_symlink= (!my_disable_symlinks &&
- !my_readlink(link_name, name, MYF(0)));
- int result;
- DBUG_ENTER("my_delete_with_symlink");
-
- if (!(result=my_delete(name, MyFlags)))
- {
- if (was_symlink)
- result=my_delete(link_name, MyFlags);
- }
- DBUG_RETURN(result);
-}
-
-/*
If the file is a normal file, just rename it.
If the file is a symlink:
- Create a new file with the name 'to' that points at
@@ -182,3 +161,31 @@ int my_rename_with_symlink(const char *from, const char *to, myf MyFlags)
DBUG_RETURN(result);
#endif /* HAVE_READLINK */
}
+
+/** delete a - possibly symlinked - table file
+
+ This is used to delete a file that is part of a table (e.g. MYI or MYD
+ file of MyISAM) when dropping a table. A file might be a symlink -
+ if the table was created with DATA DIRECTORY or INDEX DIRECTORY -
+ in this case both the symlink and the symlinked file are deleted,
+ but only if the symlinked file is not in the datadir.
+*/
+int my_handler_delete_with_symlink(PSI_file_key key, const char *name,
+ const char *ext, myf sync_dir)
+{
+ char orig[FN_REFLEN], real[FN_REFLEN];
+ int res= 0;
+ DBUG_ENTER("my_handler_delete_with_symlink");
+
+ fn_format(orig, name, "", ext, MY_UNPACK_FILENAME | MY_APPEND_EXT);
+ if (my_is_symlink(orig))
+ {
+ /*
+ Delete the symlinked file only if the symlink is not
+ pointing into datadir.
+ */
+ if (!(my_realpath(real, orig, MYF(0)) || mysys_test_invalid_symlink(real)))
+ res= mysql_file_delete(key, real, MYF(MY_NOSYMLINKS | sync_dir));
+ }
+ DBUG_RETURN(mysql_file_delete(key, orig, MYF(sync_dir)) || res);
+}
diff --git a/mysys/my_sync.c b/mysys/my_sync.c
index c0afd587ada..d1e239692f1 100644
--- a/mysys/my_sync.c
+++ b/mysys/my_sync.c
@@ -196,7 +196,7 @@ int my_sync_dir_by_file(const char *file_name __attribute__((unused)),
char dir_name[FN_REFLEN];
size_t dir_name_length;
dirname_part(dir_name, file_name, &dir_name_length);
- return my_sync_dir(dir_name, my_flags);
+ return my_sync_dir(dir_name, my_flags & ~MY_NOSYMLINKS);
#else
return 0;
#endif
diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c
index 5234010ac38..1d3c3059bd5 100644
--- a/mysys/my_thr_init.c
+++ b/mysys/my_thr_init.c
@@ -44,6 +44,8 @@ static uint get_thread_lib(void);
/** True if @c my_thread_global_init() has been called. */
static my_bool my_thread_global_init_done= 0;
+/* True if THR_KEY_mysys is created */
+my_bool my_thr_key_mysys_exists= 0;
/*
@@ -167,11 +169,20 @@ my_bool my_thread_global_init(void)
return 0;
my_thread_global_init_done= 1;
- if ((pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0)
+ /*
+ THR_KEY_mysys is deleted in my_end() as DBUG libraries are using it even
+ after my_thread_global_end() is called.
+ my_thr_key_mysys_exist is used to protect against application like QT
+ that calls my_thread_global_init() + my_thread_global_end() multiple times
+ without calling my_init() + my_end().
+ */
+ if (!my_thr_key_mysys_exists &&
+ (pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0)
{
fprintf(stderr, "Can't initialize threads: error %d\n", pth_ret);
return 1;
}
+ my_thr_key_mysys_exists= 1;
/* Mutex used by my_thread_init() and after my_thread_destroy_mutex() */
my_thread_init_internal_mutex();
diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h
index d080aca7404..471862a6d0b 100644
--- a/mysys/mysys_priv.h
+++ b/mysys/mysys_priv.h
@@ -108,6 +108,34 @@ size_t sf_malloc_usable_size(void *ptr, my_bool *is_thread_specific);
void my_error_unregister_all(void);
+#if !defined(O_PATH) && defined(O_EXEC) /* FreeBSD */
+#define O_PATH O_EXEC
+#endif
+
+#ifdef O_PATH
+#define HAVE_OPEN_PARENT_DIR_NOSYMLINKS
+const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd);
+#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \
+ int dfd, res; \
+ const char *filename= my_open_parent_dir_nosymlinks(pathname, &dfd); \
+ if (filename == NULL) return -1; \
+ res= AT; \
+ if (dfd >= 0) close(dfd); \
+ return res;
+#elif defined(HAVE_REALPATH)
+#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \
+ char buf[PATH_MAX+1]; \
+ if (realpath(pathname, buf) == NULL) return -1; \
+ if (strcmp(pathname, buf)) { errno= ENOTDIR; return -1; } \
+ return NOAT;
+#else
+#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \
+ return NOAT;
+#endif
+
+#define CREATE_NOSYMLINK_FUNCTION(PROTO,AT,NOAT) \
+static int PROTO { NOSYMLINK_FUNCTION_BODY(AT,NOAT) }
+
#ifdef _WIN32
#include <sys/stat.h>
/* my_winfile.c exports, should not be used outside mysys */
diff --git a/pcre/AUTHORS b/pcre/AUTHORS
index 342417a8a19..291657caef1 100644
--- a/pcre/AUTHORS
+++ b/pcre/AUTHORS
@@ -8,7 +8,7 @@ Email domain: cam.ac.uk
University of Cambridge Computing Service,
Cambridge, England.
-Copyright (c) 1997-2016 University of Cambridge
+Copyright (c) 1997-2017 University of Cambridge
All rights reserved
@@ -19,7 +19,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2010-2016 Zoltan Herczeg
+Copyright(c) 2010-2017 Zoltan Herczeg
All rights reserved.
@@ -30,7 +30,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2009-2016 Zoltan Herczeg
+Copyright(c) 2009-2017 Zoltan Herczeg
All rights reserved.
diff --git a/pcre/CMakeLists.txt b/pcre/CMakeLists.txt
index 91e2e781fc2..30b06a46fef 100644
--- a/pcre/CMakeLists.txt
+++ b/pcre/CMakeLists.txt
@@ -66,6 +66,7 @@
# 2013-10-08 PH got rid of the "source" command, which is a bash-ism (use ".")
# 2013-11-05 PH added support for PARENS_NEST_LIMIT
# 2016-03-01 PH applied Chris Wilson's patch for MSVC static build
+# 2016-06-24 PH applied Chris Wilson's revised patch (adds a separate option)
PROJECT(PCRE C CXX)
diff --git a/pcre/ChangeLog b/pcre/ChangeLog
index a34f845f8a1..01511b1327c 100644
--- a/pcre/ChangeLog
+++ b/pcre/ChangeLog
@@ -4,6 +4,53 @@ ChangeLog for PCRE
Note that the PCRE 8.xx series (PCRE1) is now in a bugfix-only state. All
development is happening in the PCRE2 10.xx series.
+Version 8.40 11-January-2017
+----------------------------
+
+1. Using -o with -M in pcregrep could cause unnecessary repeated output when
+ the match extended over a line boundary.
+
+2. Applied Chris Wilson's second patch (Bugzilla #1681) to CMakeLists.txt for
+ MSVC static compilation, putting the first patch under a new option.
+
+3. Fix register overwite in JIT when SSE2 acceleration is enabled.
+
+4. Ignore "show all captures" (/=) for DFA matching.
+
+5. Fix JIT unaligned accesses on x86. Patch by Marc Mutz.
+
+6. In any wide-character mode (8-bit UTF or any 16-bit or 32-bit mode),
+ without PCRE_UCP set, a negative character type such as \D in a positive
+ class should cause all characters greater than 255 to match, whatever else
+ is in the class. There was a bug that caused this not to happen if a
+ Unicode property item was added to such a class, for example [\D\P{Nd}] or
+ [\W\pL].
+
+7. When pcretest was outputing information from a callout, the caret indicator
+ for the current position in the subject line was incorrect if it was after
+ an escape sequence for a character whose code point was greater than
+ \x{ff}.
+
+8. A pattern such as (?<RA>abc)(?(R)xyz) was incorrectly compiled such that
+ the conditional was interpreted as a reference to capturing group 1 instead
+ of a test for recursion. Any group whose name began with R was
+ misinterpreted in this way. (The reference interpretation should only
+ happen if the group's name is precisely "R".)
+
+9. A number of bugs have been mended relating to match start-up optimizations
+ when the first thing in a pattern is a positive lookahead. These all
+ applied only when PCRE_NO_START_OPTIMIZE was *not* set:
+
+ (a) A pattern such as (?=.*X)X$ was incorrectly optimized as if it needed
+ both an initial 'X' and a following 'X'.
+ (b) Some patterns starting with an assertion that started with .* were
+ incorrectly optimized as having to match at the start of the subject or
+ after a newline. There are cases where this is not true, for example,
+ (?=.*[A-Z])(?=.{8,16})(?!.*[\s]) matches after the start in lines that
+ start with spaces. Starting .* in an assertion is no longer taken as an
+ indication of matching at the start (or after a newline).
+
+
Version 8.39 14-June-2016
-------------------------
diff --git a/pcre/LICENCE b/pcre/LICENCE
index dd977af971b..dd9071a8dd8 100644
--- a/pcre/LICENCE
+++ b/pcre/LICENCE
@@ -25,7 +25,7 @@ Email domain: cam.ac.uk
University of Cambridge Computing Service,
Cambridge, England.
-Copyright (c) 1997-2016 University of Cambridge
+Copyright (c) 1997-2017 University of Cambridge
All rights reserved.
@@ -36,7 +36,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2010-2016 Zoltan Herczeg
+Copyright(c) 2010-2017 Zoltan Herczeg
All rights reserved.
@@ -47,7 +47,7 @@ Written by: Zoltan Herczeg
Email local part: hzmester
Emain domain: freemail.hu
-Copyright(c) 2009-2016 Zoltan Herczeg
+Copyright(c) 2009-2017 Zoltan Herczeg
All rights reserved.
diff --git a/pcre/NEWS b/pcre/NEWS
index 0ca1bab2a4c..b92c4f9605e 100644
--- a/pcre/NEWS
+++ b/pcre/NEWS
@@ -1,6 +1,12 @@
News about PCRE releases
------------------------
+Release 8.40 11-January-2017
+----------------------------
+
+This is a bug-fix release.
+
+
Release 8.39 14-June-2016
-------------------------
diff --git a/pcre/configure.ac b/pcre/configure.ac
index 3cefaf100f9..24ef7271e05 100644
--- a/pcre/configure.ac
+++ b/pcre/configure.ac
@@ -9,17 +9,17 @@ dnl The PCRE_PRERELEASE feature is for identifying release candidates. It might
dnl be defined as -RC2, for example. For real releases, it should be empty.
m4_define(pcre_major, [8])
-m4_define(pcre_minor, [39])
+m4_define(pcre_minor, [40])
m4_define(pcre_prerelease, [])
-m4_define(pcre_date, [2016-06-14])
+m4_define(pcre_date, [2017-01-11])
# NOTE: The CMakeLists.txt file searches for the above variables in the first
# 50 lines of this file. Please update that if the variables above are moved.
# Libtool shared library interface versions (current:revision:age)
-m4_define(libpcre_version, [3:7:2])
-m4_define(libpcre16_version, [2:7:2])
-m4_define(libpcre32_version, [0:7:0])
+m4_define(libpcre_version, [3:8:2])
+m4_define(libpcre16_version, [2:8:2])
+m4_define(libpcre32_version, [0:8:0])
m4_define(libpcreposix_version, [0:4:0])
m4_define(libpcrecpp_version, [0:1:0])
diff --git a/pcre/doc/html/pcrecompat.html b/pcre/doc/html/pcrecompat.html
index 3e6226692ee..d95570ef179 100644
--- a/pcre/doc/html/pcrecompat.html
+++ b/pcre/doc/html/pcrecompat.html
@@ -128,7 +128,7 @@ the pattern /^(a(b)?)+$/ in Perl leaves $2 unset, but in PCRE it is set to "b".
14. PCRE's handling of duplicate subpattern numbers and duplicate subpattern
names is not as general as Perl's. This is a consequence of the fact the PCRE
works internally just with numbers, using an external table to translate
-between numbers and names. In particular, a pattern such as (?|(?&#60;a&#62;A)|(?&#60;b)B),
+between numbers and names. In particular, a pattern such as (?|(?&#60;a&#62;A)|(?&#60;b&#62;B),
where the two capturing parentheses have the same number but different names,
is not supported, and causes an error at compile time. If it were allowed, it
would not be possible to distinguish which parentheses matched, because both
diff --git a/pcre/doc/html/pcrepattern.html b/pcre/doc/html/pcrepattern.html
index 55034a7edf6..96fc72986f6 100644
--- a/pcre/doc/html/pcrepattern.html
+++ b/pcre/doc/html/pcrepattern.html
@@ -358,24 +358,24 @@ When PCRE is compiled in EBCDIC mode, \a, \e, \f, \n, \r, and \t
generate the appropriate EBCDIC code values. The \c escape is processed
as specified for Perl in the <b>perlebcdic</b> document. The only characters
that are allowed after \c are A-Z, a-z, or one of @, [, \, ], ^, _, or ?. Any
-other character provokes a compile-time error. The sequence \@ encodes
-character code 0; the letters (in either case) encode characters 1-26 (hex 01
-to hex 1A); [, \, ], ^, and _ encode characters 27-31 (hex 1B to hex 1F), and
-\? becomes either 255 (hex FF) or 95 (hex 5F).
+other character provokes a compile-time error. The sequence \c@ encodes
+character code 0; after \c the letters (in either case) encode characters 1-26
+(hex 01 to hex 1A); [, \, ], ^, and _ encode characters 27-31 (hex 1B to hex
+1F), and \c? becomes either 255 (hex FF) or 95 (hex 5F).
</P>
<P>
-Thus, apart from \?, these escapes generate the same character code values as
+Thus, apart from \c?, these escapes generate the same character code values as
they do in an ASCII environment, though the meanings of the values mostly
-differ. For example, \G always generates code value 7, which is BEL in ASCII
+differ. For example, \cG always generates code value 7, which is BEL in ASCII
but DEL in EBCDIC.
</P>
<P>
-The sequence \? generates DEL (127, hex 7F) in an ASCII environment, but
+The sequence \c? generates DEL (127, hex 7F) in an ASCII environment, but
because 127 is not a control character in EBCDIC, Perl makes it generate the
APC character. Unfortunately, there are several variants of EBCDIC. In most of
them the APC character has the value 255 (hex FF), but in the one Perl calls
POSIX-BC its value is 95 (hex 5F). If certain other characters have POSIX-BC
-values, PCRE makes \? generate 95; otherwise it generates 255.
+values, PCRE makes \c? generate 95; otherwise it generates 255.
</P>
<P>
After \0 up to two further octal digits are read. If there are fewer than two
@@ -1512,13 +1512,8 @@ J, U and X respectively.
<P>
When one of these option changes occurs at top level (that is, not inside
subpattern parentheses), the change applies to the remainder of the pattern
-that follows. If the change is placed right at the start of a pattern, PCRE
-extracts it into the global options (and it will therefore show up in data
-extracted by the <b>pcre_fullinfo()</b> function).
-</P>
-<P>
-An option change within a subpattern (see below for a description of
-subpatterns) affects only that part of the subpattern that follows it, so
+that follows. An option change within a subpattern (see below for a description
+of subpatterns) affects only that part of the subpattern that follows it, so
<pre>
(a(?i)b)c
</pre>
@@ -2160,6 +2155,14 @@ capturing is carried out only for positive assertions. (Perl sometimes, but not
always, does do capturing in negative assertions.)
</P>
<P>
+WARNING: If a positive assertion containing one or more capturing subpatterns
+succeeds, but failure to match later in the pattern causes backtracking over
+this assertion, the captures within the assertion are reset only if no higher
+numbered captures are already set. This is, unfortunately, a fundamental
+limitation of the current implementation, and as PCRE1 is now in
+maintenance-only status, it is unlikely ever to change.
+</P>
+<P>
For compatibility with Perl, assertion subpatterns may be repeated; though
it makes no sense to assert the same thing several times, the side effect of
capturing parentheses may occasionally be useful. In practice, there only three
@@ -3264,9 +3267,9 @@ Cambridge CB2 3QH, England.
</P>
<br><a name="SEC30" href="#TOC1">REVISION</a><br>
<P>
-Last updated: 14 June 2015
+Last updated: 23 October 2016
<br>
-Copyright &copy; 1997-2015 University of Cambridge.
+Copyright &copy; 1997-2016 University of Cambridge.
<br>
<p>
Return to the <a href="index.html">PCRE index page</a>.
diff --git a/pcre/doc/pcre.txt b/pcre/doc/pcre.txt
index f5eb347e18a..d68d5033ceb 100644
--- a/pcre/doc/pcre.txt
+++ b/pcre/doc/pcre.txt
@@ -4640,7 +4640,7 @@ DIFFERENCES BETWEEN PCRE AND PERL
pattern names is not as general as Perl's. This is a consequence of the
fact the PCRE works internally just with numbers, using an external ta-
ble to translate between numbers and names. In particular, a pattern
- such as (?|(?<a>A)|(?<b)B), where the two capturing parentheses have
+ such as (?|(?<a>A)|(?<b>B), where the two capturing parentheses have
the same number but different names, is not supported, and causes an
error at compile time. If it were allowed, it would not be possible to
distinguish which parentheses matched, because both names map to cap-
@@ -5028,55 +5028,56 @@ BACKSLASH
ate the appropriate EBCDIC code values. The \c escape is processed as
specified for Perl in the perlebcdic document. The only characters that
are allowed after \c are A-Z, a-z, or one of @, [, \, ], ^, _, or ?.
- Any other character provokes a compile-time error. The sequence \@
- encodes character code 0; the letters (in either case) encode charac-
- ters 1-26 (hex 01 to hex 1A); [, \, ], ^, and _ encode characters 27-31
- (hex 1B to hex 1F), and \? becomes either 255 (hex FF) or 95 (hex 5F).
-
- Thus, apart from \?, these escapes generate the same character code
- values as they do in an ASCII environment, though the meanings of the
- values mostly differ. For example, \G always generates code value 7,
+ Any other character provokes a compile-time error. The sequence \c@
+ encodes character code 0; after \c the letters (in either case) encode
+ characters 1-26 (hex 01 to hex 1A); [, \, ], ^, and _ encode characters
+ 27-31 (hex 1B to hex 1F), and \c? becomes either 255 (hex FF) or 95
+ (hex 5F).
+
+ Thus, apart from \c?, these escapes generate the same character code
+ values as they do in an ASCII environment, though the meanings of the
+ values mostly differ. For example, \cG always generates code value 7,
which is BEL in ASCII but DEL in EBCDIC.
- The sequence \? generates DEL (127, hex 7F) in an ASCII environment,
- but because 127 is not a control character in EBCDIC, Perl makes it
- generate the APC character. Unfortunately, there are several variants
- of EBCDIC. In most of them the APC character has the value 255 (hex
- FF), but in the one Perl calls POSIX-BC its value is 95 (hex 5F). If
- certain other characters have POSIX-BC values, PCRE makes \? generate
+ The sequence \c? generates DEL (127, hex 7F) in an ASCII environment,
+ but because 127 is not a control character in EBCDIC, Perl makes it
+ generate the APC character. Unfortunately, there are several variants
+ of EBCDIC. In most of them the APC character has the value 255 (hex
+ FF), but in the one Perl calls POSIX-BC its value is 95 (hex 5F). If
+ certain other characters have POSIX-BC values, PCRE makes \c? generate
95; otherwise it generates 255.
- After \0 up to two further octal digits are read. If there are fewer
- than two digits, just those that are present are used. Thus the
+ After \0 up to two further octal digits are read. If there are fewer
+ than two digits, just those that are present are used. Thus the
sequence \0\x\015 specifies two binary zeros followed by a CR character
(code value 13). Make sure you supply two digits after the initial zero
if the pattern character that follows is itself an octal digit.
- The escape \o must be followed by a sequence of octal digits, enclosed
- in braces. An error occurs if this is not the case. This escape is a
- recent addition to Perl; it provides way of specifying character code
- points as octal numbers greater than 0777, and it also allows octal
+ The escape \o must be followed by a sequence of octal digits, enclosed
+ in braces. An error occurs if this is not the case. This escape is a
+ recent addition to Perl; it provides way of specifying character code
+ points as octal numbers greater than 0777, and it also allows octal
numbers and back references to be unambiguously specified.
For greater clarity and unambiguity, it is best to avoid following \ by
a digit greater than zero. Instead, use \o{} or \x{} to specify charac-
- ter numbers, and \g{} to specify back references. The following para-
+ ter numbers, and \g{} to specify back references. The following para-
graphs describe the old, ambiguous syntax.
The handling of a backslash followed by a digit other than 0 is compli-
- cated, and Perl has changed in recent releases, causing PCRE also to
+ cated, and Perl has changed in recent releases, causing PCRE also to
change. Outside a character class, PCRE reads the digit and any follow-
- ing digits as a decimal number. If the number is less than 8, or if
- there have been at least that many previous capturing left parentheses
- in the expression, the entire sequence is taken as a back reference. A
- description of how this works is given later, following the discussion
+ ing digits as a decimal number. If the number is less than 8, or if
+ there have been at least that many previous capturing left parentheses
+ in the expression, the entire sequence is taken as a back reference. A
+ description of how this works is given later, following the discussion
of parenthesized subpatterns.
- Inside a character class, or if the decimal number following \ is
+ Inside a character class, or if the decimal number following \ is
greater than 7 and there have not been that many capturing subpatterns,
- PCRE handles \8 and \9 as the literal characters "8" and "9", and oth-
+ PCRE handles \8 and \9 as the literal characters "8" and "9", and oth-
erwise re-reads up to three octal digits following the backslash, using
- them to generate a data character. Any subsequent digits stand for
+ them to generate a data character. Any subsequent digits stand for
themselves. For example:
\040 is another way of writing an ASCII space
@@ -5094,31 +5095,31 @@ BACKSLASH
\81 is either a back reference, or the two
characters "8" and "1"
- Note that octal values of 100 or greater that are specified using this
- syntax must not be introduced by a leading zero, because no more than
+ Note that octal values of 100 or greater that are specified using this
+ syntax must not be introduced by a leading zero, because no more than
three octal digits are ever read.
- By default, after \x that is not followed by {, from zero to two hexa-
- decimal digits are read (letters can be in upper or lower case). Any
+ By default, after \x that is not followed by {, from zero to two hexa-
+ decimal digits are read (letters can be in upper or lower case). Any
number of hexadecimal digits may appear between \x{ and }. If a charac-
- ter other than a hexadecimal digit appears between \x{ and }, or if
+ ter other than a hexadecimal digit appears between \x{ and }, or if
there is no terminating }, an error occurs.
- If the PCRE_JAVASCRIPT_COMPAT option is set, the interpretation of \x
- is as just described only when it is followed by two hexadecimal dig-
- its. Otherwise, it matches a literal "x" character. In JavaScript
+ If the PCRE_JAVASCRIPT_COMPAT option is set, the interpretation of \x
+ is as just described only when it is followed by two hexadecimal dig-
+ its. Otherwise, it matches a literal "x" character. In JavaScript
mode, support for code points greater than 256 is provided by \u, which
- must be followed by four hexadecimal digits; otherwise it matches a
+ must be followed by four hexadecimal digits; otherwise it matches a
literal "u" character.
Characters whose value is less than 256 can be defined by either of the
- two syntaxes for \x (or by \u in JavaScript mode). There is no differ-
+ two syntaxes for \x (or by \u in JavaScript mode). There is no differ-
ence in the way they are handled. For example, \xdc is exactly the same
as \x{dc} (or \u00dc in JavaScript mode).
Constraints on character values
- Characters that are specified using octal or hexadecimal numbers are
+ Characters that are specified using octal or hexadecimal numbers are
limited to certain values, as follows:
8-bit non-UTF mode less than 0x100
@@ -5128,44 +5129,44 @@ BACKSLASH
32-bit non-UTF mode less than 0x100000000
32-bit UTF-32 mode less than 0x10ffff and a valid codepoint
- Invalid Unicode codepoints are the range 0xd800 to 0xdfff (the so-
+ Invalid Unicode codepoints are the range 0xd800 to 0xdfff (the so-
called "surrogate" codepoints), and 0xffef.
Escape sequences in character classes
All the sequences that define a single character value can be used both
- inside and outside character classes. In addition, inside a character
+ inside and outside character classes. In addition, inside a character
class, \b is interpreted as the backspace character (hex 08).
- \N is not allowed in a character class. \B, \R, and \X are not special
- inside a character class. Like other unrecognized escape sequences,
- they are treated as the literal characters "B", "R", and "X" by
- default, but cause an error if the PCRE_EXTRA option is set. Outside a
+ \N is not allowed in a character class. \B, \R, and \X are not special
+ inside a character class. Like other unrecognized escape sequences,
+ they are treated as the literal characters "B", "R", and "X" by
+ default, but cause an error if the PCRE_EXTRA option is set. Outside a
character class, these sequences have different meanings.
Unsupported escape sequences
- In Perl, the sequences \l, \L, \u, and \U are recognized by its string
- handler and used to modify the case of following characters. By
- default, PCRE does not support these escape sequences. However, if the
- PCRE_JAVASCRIPT_COMPAT option is set, \U matches a "U" character, and
+ In Perl, the sequences \l, \L, \u, and \U are recognized by its string
+ handler and used to modify the case of following characters. By
+ default, PCRE does not support these escape sequences. However, if the
+ PCRE_JAVASCRIPT_COMPAT option is set, \U matches a "U" character, and
\u can be used to define a character by code point, as described in the
previous section.
Absolute and relative back references
- The sequence \g followed by an unsigned or a negative number, option-
- ally enclosed in braces, is an absolute or relative back reference. A
+ The sequence \g followed by an unsigned or a negative number, option-
+ ally enclosed in braces, is an absolute or relative back reference. A
named back reference can be coded as \g{name}. Back references are dis-
cussed later, following the discussion of parenthesized subpatterns.
Absolute and relative subroutine calls
- For compatibility with Oniguruma, the non-Perl syntax \g followed by a
+ For compatibility with Oniguruma, the non-Perl syntax \g followed by a
name or a number enclosed either in angle brackets or single quotes, is
- an alternative syntax for referencing a subpattern as a "subroutine".
- Details are discussed later. Note that \g{...} (Perl syntax) and
- \g<...> (Oniguruma syntax) are not synonymous. The former is a back
+ an alternative syntax for referencing a subpattern as a "subroutine".
+ Details are discussed later. Note that \g{...} (Perl syntax) and
+ \g<...> (Oniguruma syntax) are not synonymous. The former is a back
reference; the latter is a subroutine call.
Generic character types
@@ -5184,59 +5185,59 @@ BACKSLASH
\W any "non-word" character
There is also the single sequence \N, which matches a non-newline char-
- acter. This is the same as the "." metacharacter when PCRE_DOTALL is
- not set. Perl also uses \N to match characters by name; PCRE does not
+ acter. This is the same as the "." metacharacter when PCRE_DOTALL is
+ not set. Perl also uses \N to match characters by name; PCRE does not
support this.
- Each pair of lower and upper case escape sequences partitions the com-
- plete set of characters into two disjoint sets. Any given character
- matches one, and only one, of each pair. The sequences can appear both
- inside and outside character classes. They each match one character of
- the appropriate type. If the current matching point is at the end of
- the subject string, all of them fail, because there is no character to
+ Each pair of lower and upper case escape sequences partitions the com-
+ plete set of characters into two disjoint sets. Any given character
+ matches one, and only one, of each pair. The sequences can appear both
+ inside and outside character classes. They each match one character of
+ the appropriate type. If the current matching point is at the end of
+ the subject string, all of them fail, because there is no character to
match.
- For compatibility with Perl, \s did not used to match the VT character
- (code 11), which made it different from the the POSIX "space" class.
- However, Perl added VT at release 5.18, and PCRE followed suit at
- release 8.34. The default \s characters are now HT (9), LF (10), VT
- (11), FF (12), CR (13), and space (32), which are defined as white
+ For compatibility with Perl, \s did not used to match the VT character
+ (code 11), which made it different from the the POSIX "space" class.
+ However, Perl added VT at release 5.18, and PCRE followed suit at
+ release 8.34. The default \s characters are now HT (9), LF (10), VT
+ (11), FF (12), CR (13), and space (32), which are defined as white
space in the "C" locale. This list may vary if locale-specific matching
- is taking place. For example, in some locales the "non-breaking space"
- character (\xA0) is recognized as white space, and in others the VT
+ is taking place. For example, in some locales the "non-breaking space"
+ character (\xA0) is recognized as white space, and in others the VT
character is not.
- A "word" character is an underscore or any character that is a letter
- or digit. By default, the definition of letters and digits is con-
- trolled by PCRE's low-valued character tables, and may vary if locale-
- specific matching is taking place (see "Locale support" in the pcreapi
- page). For example, in a French locale such as "fr_FR" in Unix-like
- systems, or "french" in Windows, some character codes greater than 127
- are used for accented letters, and these are then matched by \w. The
+ A "word" character is an underscore or any character that is a letter
+ or digit. By default, the definition of letters and digits is con-
+ trolled by PCRE's low-valued character tables, and may vary if locale-
+ specific matching is taking place (see "Locale support" in the pcreapi
+ page). For example, in a French locale such as "fr_FR" in Unix-like
+ systems, or "french" in Windows, some character codes greater than 127
+ are used for accented letters, and these are then matched by \w. The
use of locales with Unicode is discouraged.
- By default, characters whose code points are greater than 127 never
+ By default, characters whose code points are greater than 127 never
match \d, \s, or \w, and always match \D, \S, and \W, although this may
- vary for characters in the range 128-255 when locale-specific matching
- is happening. These escape sequences retain their original meanings
- from before Unicode support was available, mainly for efficiency rea-
- sons. If PCRE is compiled with Unicode property support, and the
- PCRE_UCP option is set, the behaviour is changed so that Unicode prop-
+ vary for characters in the range 128-255 when locale-specific matching
+ is happening. These escape sequences retain their original meanings
+ from before Unicode support was available, mainly for efficiency rea-
+ sons. If PCRE is compiled with Unicode property support, and the
+ PCRE_UCP option is set, the behaviour is changed so that Unicode prop-
erties are used to determine character types, as follows:
\d any character that matches \p{Nd} (decimal digit)
\s any character that matches \p{Z} or \h or \v
\w any character that matches \p{L} or \p{N}, plus underscore
- The upper case escapes match the inverse sets of characters. Note that
- \d matches only decimal digits, whereas \w matches any Unicode digit,
- as well as any Unicode letter, and underscore. Note also that PCRE_UCP
- affects \b, and \B because they are defined in terms of \w and \W.
+ The upper case escapes match the inverse sets of characters. Note that
+ \d matches only decimal digits, whereas \w matches any Unicode digit,
+ as well as any Unicode letter, and underscore. Note also that PCRE_UCP
+ affects \b, and \B because they are defined in terms of \w and \W.
Matching these sequences is noticeably slower when PCRE_UCP is set.
- The sequences \h, \H, \v, and \V are features that were added to Perl
- at release 5.10. In contrast to the other sequences, which match only
- ASCII characters by default, these always match certain high-valued
+ The sequences \h, \H, \v, and \V are features that were added to Perl
+ at release 5.10. In contrast to the other sequences, which match only
+ ASCII characters by default, these always match certain high-valued
code points, whether or not PCRE_UCP is set. The horizontal space char-
acters are:
@@ -5275,110 +5276,110 @@ BACKSLASH
Newline sequences
- Outside a character class, by default, the escape sequence \R matches
- any Unicode newline sequence. In 8-bit non-UTF-8 mode \R is equivalent
+ Outside a character class, by default, the escape sequence \R matches
+ any Unicode newline sequence. In 8-bit non-UTF-8 mode \R is equivalent
to the following:
(?>\r\n|\n|\x0b|\f|\r|\x85)
- This is an example of an "atomic group", details of which are given
+ This is an example of an "atomic group", details of which are given
below. This particular group matches either the two-character sequence
- CR followed by LF, or one of the single characters LF (linefeed,
- U+000A), VT (vertical tab, U+000B), FF (form feed, U+000C), CR (car-
- riage return, U+000D), or NEL (next line, U+0085). The two-character
+ CR followed by LF, or one of the single characters LF (linefeed,
+ U+000A), VT (vertical tab, U+000B), FF (form feed, U+000C), CR (car-
+ riage return, U+000D), or NEL (next line, U+0085). The two-character
sequence is treated as a single unit that cannot be split.
- In other modes, two additional characters whose codepoints are greater
+ In other modes, two additional characters whose codepoints are greater
than 255 are added: LS (line separator, U+2028) and PS (paragraph sepa-
- rator, U+2029). Unicode character property support is not needed for
+ rator, U+2029). Unicode character property support is not needed for
these characters to be recognized.
It is possible to restrict \R to match only CR, LF, or CRLF (instead of
- the complete set of Unicode line endings) by setting the option
+ the complete set of Unicode line endings) by setting the option
PCRE_BSR_ANYCRLF either at compile time or when the pattern is matched.
(BSR is an abbrevation for "backslash R".) This can be made the default
- when PCRE is built; if this is the case, the other behaviour can be
- requested via the PCRE_BSR_UNICODE option. It is also possible to
- specify these settings by starting a pattern string with one of the
+ when PCRE is built; if this is the case, the other behaviour can be
+ requested via the PCRE_BSR_UNICODE option. It is also possible to
+ specify these settings by starting a pattern string with one of the
following sequences:
(*BSR_ANYCRLF) CR, LF, or CRLF only
(*BSR_UNICODE) any Unicode newline sequence
These override the default and the options given to the compiling func-
- tion, but they can themselves be overridden by options given to a
- matching function. Note that these special settings, which are not
- Perl-compatible, are recognized only at the very start of a pattern,
- and that they must be in upper case. If more than one of them is
- present, the last one is used. They can be combined with a change of
+ tion, but they can themselves be overridden by options given to a
+ matching function. Note that these special settings, which are not
+ Perl-compatible, are recognized only at the very start of a pattern,
+ and that they must be in upper case. If more than one of them is
+ present, the last one is used. They can be combined with a change of
newline convention; for example, a pattern can start with:
(*ANY)(*BSR_ANYCRLF)
- They can also be combined with the (*UTF8), (*UTF16), (*UTF32), (*UTF)
+ They can also be combined with the (*UTF8), (*UTF16), (*UTF32), (*UTF)
or (*UCP) special sequences. Inside a character class, \R is treated as
- an unrecognized escape sequence, and so matches the letter "R" by
+ an unrecognized escape sequence, and so matches the letter "R" by
default, but causes an error if PCRE_EXTRA is set.
Unicode character properties
When PCRE is built with Unicode character property support, three addi-
- tional escape sequences that match characters with specific properties
- are available. When in 8-bit non-UTF-8 mode, these sequences are of
- course limited to testing characters whose codepoints are less than
+ tional escape sequences that match characters with specific properties
+ are available. When in 8-bit non-UTF-8 mode, these sequences are of
+ course limited to testing characters whose codepoints are less than
256, but they do work in this mode. The extra escape sequences are:
\p{xx} a character with the xx property
\P{xx} a character without the xx property
\X a Unicode extended grapheme cluster
- The property names represented by xx above are limited to the Unicode
+ The property names represented by xx above are limited to the Unicode
script names, the general category properties, "Any", which matches any
- character (including newline), and some special PCRE properties
- (described in the next section). Other Perl properties such as "InMu-
- sicalSymbols" are not currently supported by PCRE. Note that \P{Any}
+ character (including newline), and some special PCRE properties
+ (described in the next section). Other Perl properties such as "InMu-
+ sicalSymbols" are not currently supported by PCRE. Note that \P{Any}
does not match any characters, so always causes a match failure.
Sets of Unicode characters are defined as belonging to certain scripts.
- A character from one of these sets can be matched using a script name.
+ A character from one of these sets can be matched using a script name.
For example:
\p{Greek}
\P{Han}
- Those that are not part of an identified script are lumped together as
+ Those that are not part of an identified script are lumped together as
"Common". The current list of scripts is:
- Arabic, Armenian, Avestan, Balinese, Bamum, Bassa_Vah, Batak, Bengali,
- Bopomofo, Brahmi, Braille, Buginese, Buhid, Canadian_Aboriginal, Car-
+ Arabic, Armenian, Avestan, Balinese, Bamum, Bassa_Vah, Batak, Bengali,
+ Bopomofo, Brahmi, Braille, Buginese, Buhid, Canadian_Aboriginal, Car-
ian, Caucasian_Albanian, Chakma, Cham, Cherokee, Common, Coptic, Cunei-
form, Cypriot, Cyrillic, Deseret, Devanagari, Duployan, Egyptian_Hiero-
glyphs, Elbasan, Ethiopic, Georgian, Glagolitic, Gothic, Grantha,
- Greek, Gujarati, Gurmukhi, Han, Hangul, Hanunoo, Hebrew, Hiragana,
- Imperial_Aramaic, Inherited, Inscriptional_Pahlavi, Inscrip-
- tional_Parthian, Javanese, Kaithi, Kannada, Katakana, Kayah_Li,
- Kharoshthi, Khmer, Khojki, Khudawadi, Lao, Latin, Lepcha, Limbu, Lin-
- ear_A, Linear_B, Lisu, Lycian, Lydian, Mahajani, Malayalam, Mandaic,
- Manichaean, Meetei_Mayek, Mende_Kikakui, Meroitic_Cursive,
- Meroitic_Hieroglyphs, Miao, Modi, Mongolian, Mro, Myanmar, Nabataean,
- New_Tai_Lue, Nko, Ogham, Ol_Chiki, Old_Italic, Old_North_Arabian,
+ Greek, Gujarati, Gurmukhi, Han, Hangul, Hanunoo, Hebrew, Hiragana,
+ Imperial_Aramaic, Inherited, Inscriptional_Pahlavi, Inscrip-
+ tional_Parthian, Javanese, Kaithi, Kannada, Katakana, Kayah_Li,
+ Kharoshthi, Khmer, Khojki, Khudawadi, Lao, Latin, Lepcha, Limbu, Lin-
+ ear_A, Linear_B, Lisu, Lycian, Lydian, Mahajani, Malayalam, Mandaic,
+ Manichaean, Meetei_Mayek, Mende_Kikakui, Meroitic_Cursive,
+ Meroitic_Hieroglyphs, Miao, Modi, Mongolian, Mro, Myanmar, Nabataean,
+ New_Tai_Lue, Nko, Ogham, Ol_Chiki, Old_Italic, Old_North_Arabian,
Old_Permic, Old_Persian, Old_South_Arabian, Old_Turkic, Oriya, Osmanya,
Pahawh_Hmong, Palmyrene, Pau_Cin_Hau, Phags_Pa, Phoenician,
- Psalter_Pahlavi, Rejang, Runic, Samaritan, Saurashtra, Sharada, Sha-
- vian, Siddham, Sinhala, Sora_Sompeng, Sundanese, Syloti_Nagri, Syriac,
- Tagalog, Tagbanwa, Tai_Le, Tai_Tham, Tai_Viet, Takri, Tamil, Telugu,
- Thaana, Thai, Tibetan, Tifinagh, Tirhuta, Ugaritic, Vai, Warang_Citi,
+ Psalter_Pahlavi, Rejang, Runic, Samaritan, Saurashtra, Sharada, Sha-
+ vian, Siddham, Sinhala, Sora_Sompeng, Sundanese, Syloti_Nagri, Syriac,
+ Tagalog, Tagbanwa, Tai_Le, Tai_Tham, Tai_Viet, Takri, Tamil, Telugu,
+ Thaana, Thai, Tibetan, Tifinagh, Tirhuta, Ugaritic, Vai, Warang_Citi,
Yi.
Each character has exactly one Unicode general category property, spec-
- ified by a two-letter abbreviation. For compatibility with Perl, nega-
- tion can be specified by including a circumflex between the opening
- brace and the property name. For example, \p{^Lu} is the same as
+ ified by a two-letter abbreviation. For compatibility with Perl, nega-
+ tion can be specified by including a circumflex between the opening
+ brace and the property name. For example, \p{^Lu} is the same as
\P{Lu}.
If only one letter is specified with \p or \P, it includes all the gen-
- eral category properties that start with that letter. In this case, in
- the absence of negation, the curly brackets in the escape sequence are
+ eral category properties that start with that letter. In this case, in
+ the absence of negation, the curly brackets in the escape sequence are
optional; these two examples have the same effect:
\p{L}
@@ -5430,73 +5431,73 @@ BACKSLASH
Zp Paragraph separator
Zs Space separator
- The special property L& is also supported: it matches a character that
- has the Lu, Ll, or Lt property, in other words, a letter that is not
+ The special property L& is also supported: it matches a character that
+ has the Lu, Ll, or Lt property, in other words, a letter that is not
classified as a modifier or "other".
- The Cs (Surrogate) property applies only to characters in the range
- U+D800 to U+DFFF. Such characters are not valid in Unicode strings and
- so cannot be tested by PCRE, unless UTF validity checking has been
+ The Cs (Surrogate) property applies only to characters in the range
+ U+D800 to U+DFFF. Such characters are not valid in Unicode strings and
+ so cannot be tested by PCRE, unless UTF validity checking has been
turned off (see the discussion of PCRE_NO_UTF8_CHECK,
- PCRE_NO_UTF16_CHECK and PCRE_NO_UTF32_CHECK in the pcreapi page). Perl
+ PCRE_NO_UTF16_CHECK and PCRE_NO_UTF32_CHECK in the pcreapi page). Perl
does not support the Cs property.
- The long synonyms for property names that Perl supports (such as
- \p{Letter}) are not supported by PCRE, nor is it permitted to prefix
+ The long synonyms for property names that Perl supports (such as
+ \p{Letter}) are not supported by PCRE, nor is it permitted to prefix
any of these properties with "Is".
No character that is in the Unicode table has the Cn (unassigned) prop-
erty. Instead, this property is assumed for any code point that is not
in the Unicode table.
- Specifying caseless matching does not affect these escape sequences.
- For example, \p{Lu} always matches only upper case letters. This is
+ Specifying caseless matching does not affect these escape sequences.
+ For example, \p{Lu} always matches only upper case letters. This is
different from the behaviour of current versions of Perl.
- Matching characters by Unicode property is not fast, because PCRE has
- to do a multistage table lookup in order to find a character's prop-
+ Matching characters by Unicode property is not fast, because PCRE has
+ to do a multistage table lookup in order to find a character's prop-
erty. That is why the traditional escape sequences such as \d and \w do
not use Unicode properties in PCRE by default, though you can make them
- do so by setting the PCRE_UCP option or by starting the pattern with
+ do so by setting the PCRE_UCP option or by starting the pattern with
(*UCP).
Extended grapheme clusters
- The \X escape matches any number of Unicode characters that form an
+ The \X escape matches any number of Unicode characters that form an
"extended grapheme cluster", and treats the sequence as an atomic group
- (see below). Up to and including release 8.31, PCRE matched an ear-
+ (see below). Up to and including release 8.31, PCRE matched an ear-
lier, simpler definition that was equivalent to
(?>\PM\pM*)
- That is, it matched a character without the "mark" property, followed
- by zero or more characters with the "mark" property. Characters with
- the "mark" property are typically non-spacing accents that affect the
+ That is, it matched a character without the "mark" property, followed
+ by zero or more characters with the "mark" property. Characters with
+ the "mark" property are typically non-spacing accents that affect the
preceding character.
- This simple definition was extended in Unicode to include more compli-
- cated kinds of composite character by giving each character a grapheme
- breaking property, and creating rules that use these properties to
- define the boundaries of extended grapheme clusters. In releases of
+ This simple definition was extended in Unicode to include more compli-
+ cated kinds of composite character by giving each character a grapheme
+ breaking property, and creating rules that use these properties to
+ define the boundaries of extended grapheme clusters. In releases of
PCRE later than 8.31, \X matches one of these clusters.
- \X always matches at least one character. Then it decides whether to
+ \X always matches at least one character. Then it decides whether to
add additional characters according to the following rules for ending a
cluster:
1. End at the end of the subject string.
- 2. Do not end between CR and LF; otherwise end after any control char-
+ 2. Do not end between CR and LF; otherwise end after any control char-
acter.
- 3. Do not break Hangul (a Korean script) syllable sequences. Hangul
- characters are of five types: L, V, T, LV, and LVT. An L character may
- be followed by an L, V, LV, or LVT character; an LV or V character may
+ 3. Do not break Hangul (a Korean script) syllable sequences. Hangul
+ characters are of five types: L, V, T, LV, and LVT. An L character may
+ be followed by an L, V, LV, or LVT character; an LV or V character may
be followed by a V or T character; an LVT or T character may be follwed
only by a T character.
- 4. Do not end before extending characters or spacing marks. Characters
- with the "mark" property always have the "extend" grapheme breaking
+ 4. Do not end before extending characters or spacing marks. Characters
+ with the "mark" property always have the "extend" grapheme breaking
property.
5. Do not end after prepend characters.
@@ -5505,9 +5506,9 @@ BACKSLASH
PCRE's additional properties
- As well as the standard Unicode properties described above, PCRE sup-
- ports four more that make it possible to convert traditional escape
- sequences such as \w and \s to use Unicode properties. PCRE uses these
+ As well as the standard Unicode properties described above, PCRE sup-
+ ports four more that make it possible to convert traditional escape
+ sequences such as \w and \s to use Unicode properties. PCRE uses these
non-standard, non-Perl properties internally when PCRE_UCP is set. How-
ever, they may also be used explicitly. These properties are:
@@ -5516,54 +5517,54 @@ BACKSLASH
Xsp Any Perl space character
Xwd Any Perl "word" character
- Xan matches characters that have either the L (letter) or the N (num-
- ber) property. Xps matches the characters tab, linefeed, vertical tab,
- form feed, or carriage return, and any other character that has the Z
- (separator) property. Xsp is the same as Xps; it used to exclude ver-
- tical tab, for Perl compatibility, but Perl changed, and so PCRE fol-
- lowed at release 8.34. Xwd matches the same characters as Xan, plus
+ Xan matches characters that have either the L (letter) or the N (num-
+ ber) property. Xps matches the characters tab, linefeed, vertical tab,
+ form feed, or carriage return, and any other character that has the Z
+ (separator) property. Xsp is the same as Xps; it used to exclude ver-
+ tical tab, for Perl compatibility, but Perl changed, and so PCRE fol-
+ lowed at release 8.34. Xwd matches the same characters as Xan, plus
underscore.
- There is another non-standard property, Xuc, which matches any charac-
- ter that can be represented by a Universal Character Name in C++ and
- other programming languages. These are the characters $, @, ` (grave
- accent), and all characters with Unicode code points greater than or
- equal to U+00A0, except for the surrogates U+D800 to U+DFFF. Note that
- most base (ASCII) characters are excluded. (Universal Character Names
- are of the form \uHHHH or \UHHHHHHHH where H is a hexadecimal digit.
+ There is another non-standard property, Xuc, which matches any charac-
+ ter that can be represented by a Universal Character Name in C++ and
+ other programming languages. These are the characters $, @, ` (grave
+ accent), and all characters with Unicode code points greater than or
+ equal to U+00A0, except for the surrogates U+D800 to U+DFFF. Note that
+ most base (ASCII) characters are excluded. (Universal Character Names
+ are of the form \uHHHH or \UHHHHHHHH where H is a hexadecimal digit.
Note that the Xuc property does not match these sequences but the char-
acters that they represent.)
Resetting the match start
- The escape sequence \K causes any previously matched characters not to
+ The escape sequence \K causes any previously matched characters not to
be included in the final matched sequence. For example, the pattern:
foo\Kbar
- matches "foobar", but reports that it has matched "bar". This feature
- is similar to a lookbehind assertion (described below). However, in
- this case, the part of the subject before the real match does not have
- to be of fixed length, as lookbehind assertions do. The use of \K does
- not interfere with the setting of captured substrings. For example,
+ matches "foobar", but reports that it has matched "bar". This feature
+ is similar to a lookbehind assertion (described below). However, in
+ this case, the part of the subject before the real match does not have
+ to be of fixed length, as lookbehind assertions do. The use of \K does
+ not interfere with the setting of captured substrings. For example,
when the pattern
(foo)\Kbar
matches "foobar", the first substring is still set to "foo".
- Perl documents that the use of \K within assertions is "not well
- defined". In PCRE, \K is acted upon when it occurs inside positive
- assertions, but is ignored in negative assertions. Note that when a
- pattern such as (?=ab\K) matches, the reported start of the match can
+ Perl documents that the use of \K within assertions is "not well
+ defined". In PCRE, \K is acted upon when it occurs inside positive
+ assertions, but is ignored in negative assertions. Note that when a
+ pattern such as (?=ab\K) matches, the reported start of the match can
be greater than the end of the match.
Simple assertions
- The final use of backslash is for certain simple assertions. An asser-
- tion specifies a condition that has to be met at a particular point in
- a match, without consuming any characters from the subject string. The
- use of subpatterns for more complicated assertions is described below.
+ The final use of backslash is for certain simple assertions. An asser-
+ tion specifies a condition that has to be met at a particular point in
+ a match, without consuming any characters from the subject string. The
+ use of subpatterns for more complicated assertions is described below.
The backslashed assertions are:
\b matches at a word boundary
@@ -5574,161 +5575,161 @@ BACKSLASH
\z matches only at the end of the subject
\G matches at the first matching position in the subject
- Inside a character class, \b has a different meaning; it matches the
- backspace character. If any other of these assertions appears in a
- character class, by default it matches the corresponding literal char-
+ Inside a character class, \b has a different meaning; it matches the
+ backspace character. If any other of these assertions appears in a
+ character class, by default it matches the corresponding literal char-
acter (for example, \B matches the letter B). However, if the
- PCRE_EXTRA option is set, an "invalid escape sequence" error is gener-
+ PCRE_EXTRA option is set, an "invalid escape sequence" error is gener-
ated instead.
- A word boundary is a position in the subject string where the current
- character and the previous character do not both match \w or \W (i.e.
- one matches \w and the other matches \W), or the start or end of the
- string if the first or last character matches \w, respectively. In a
- UTF mode, the meanings of \w and \W can be changed by setting the
- PCRE_UCP option. When this is done, it also affects \b and \B. Neither
- PCRE nor Perl has a separate "start of word" or "end of word" metase-
- quence. However, whatever follows \b normally determines which it is.
+ A word boundary is a position in the subject string where the current
+ character and the previous character do not both match \w or \W (i.e.
+ one matches \w and the other matches \W), or the start or end of the
+ string if the first or last character matches \w, respectively. In a
+ UTF mode, the meanings of \w and \W can be changed by setting the
+ PCRE_UCP option. When this is done, it also affects \b and \B. Neither
+ PCRE nor Perl has a separate "start of word" or "end of word" metase-
+ quence. However, whatever follows \b normally determines which it is.
For example, the fragment \ba matches "a" at the start of a word.
- The \A, \Z, and \z assertions differ from the traditional circumflex
+ The \A, \Z, and \z assertions differ from the traditional circumflex
and dollar (described in the next section) in that they only ever match
- at the very start and end of the subject string, whatever options are
- set. Thus, they are independent of multiline mode. These three asser-
+ at the very start and end of the subject string, whatever options are
+ set. Thus, they are independent of multiline mode. These three asser-
tions are not affected by the PCRE_NOTBOL or PCRE_NOTEOL options, which
- affect only the behaviour of the circumflex and dollar metacharacters.
- However, if the startoffset argument of pcre_exec() is non-zero, indi-
+ affect only the behaviour of the circumflex and dollar metacharacters.
+ However, if the startoffset argument of pcre_exec() is non-zero, indi-
cating that matching is to start at a point other than the beginning of
- the subject, \A can never match. The difference between \Z and \z is
+ the subject, \A can never match. The difference between \Z and \z is
that \Z matches before a newline at the end of the string as well as at
the very end, whereas \z matches only at the end.
- The \G assertion is true only when the current matching position is at
- the start point of the match, as specified by the startoffset argument
- of pcre_exec(). It differs from \A when the value of startoffset is
- non-zero. By calling pcre_exec() multiple times with appropriate argu-
+ The \G assertion is true only when the current matching position is at
+ the start point of the match, as specified by the startoffset argument
+ of pcre_exec(). It differs from \A when the value of startoffset is
+ non-zero. By calling pcre_exec() multiple times with appropriate argu-
ments, you can mimic Perl's /g option, and it is in this kind of imple-
mentation where \G can be useful.
- Note, however, that PCRE's interpretation of \G, as the start of the
+ Note, however, that PCRE's interpretation of \G, as the start of the
current match, is subtly different from Perl's, which defines it as the
- end of the previous match. In Perl, these can be different when the
- previously matched string was empty. Because PCRE does just one match
+ end of the previous match. In Perl, these can be different when the
+ previously matched string was empty. Because PCRE does just one match
at a time, it cannot reproduce this behaviour.
- If all the alternatives of a pattern begin with \G, the expression is
+ If all the alternatives of a pattern begin with \G, the expression is
anchored to the starting match position, and the "anchored" flag is set
in the compiled regular expression.
CIRCUMFLEX AND DOLLAR
- The circumflex and dollar metacharacters are zero-width assertions.
- That is, they test for a particular condition being true without con-
+ The circumflex and dollar metacharacters are zero-width assertions.
+ That is, they test for a particular condition being true without con-
suming any characters from the subject string.
Outside a character class, in the default matching mode, the circumflex
- character is an assertion that is true only if the current matching
- point is at the start of the subject string. If the startoffset argu-
- ment of pcre_exec() is non-zero, circumflex can never match if the
- PCRE_MULTILINE option is unset. Inside a character class, circumflex
+ character is an assertion that is true only if the current matching
+ point is at the start of the subject string. If the startoffset argu-
+ ment of pcre_exec() is non-zero, circumflex can never match if the
+ PCRE_MULTILINE option is unset. Inside a character class, circumflex
has an entirely different meaning (see below).
- Circumflex need not be the first character of the pattern if a number
- of alternatives are involved, but it should be the first thing in each
- alternative in which it appears if the pattern is ever to match that
- branch. If all possible alternatives start with a circumflex, that is,
- if the pattern is constrained to match only at the start of the sub-
- ject, it is said to be an "anchored" pattern. (There are also other
+ Circumflex need not be the first character of the pattern if a number
+ of alternatives are involved, but it should be the first thing in each
+ alternative in which it appears if the pattern is ever to match that
+ branch. If all possible alternatives start with a circumflex, that is,
+ if the pattern is constrained to match only at the start of the sub-
+ ject, it is said to be an "anchored" pattern. (There are also other
constructs that can cause a pattern to be anchored.)
- The dollar character is an assertion that is true only if the current
- matching point is at the end of the subject string, or immediately
- before a newline at the end of the string (by default). Note, however,
- that it does not actually match the newline. Dollar need not be the
+ The dollar character is an assertion that is true only if the current
+ matching point is at the end of the subject string, or immediately
+ before a newline at the end of the string (by default). Note, however,
+ that it does not actually match the newline. Dollar need not be the
last character of the pattern if a number of alternatives are involved,
- but it should be the last item in any branch in which it appears. Dol-
+ but it should be the last item in any branch in which it appears. Dol-
lar has no special meaning in a character class.
- The meaning of dollar can be changed so that it matches only at the
- very end of the string, by setting the PCRE_DOLLAR_ENDONLY option at
+ The meaning of dollar can be changed so that it matches only at the
+ very end of the string, by setting the PCRE_DOLLAR_ENDONLY option at
compile time. This does not affect the \Z assertion.
The meanings of the circumflex and dollar characters are changed if the
- PCRE_MULTILINE option is set. When this is the case, a circumflex
- matches immediately after internal newlines as well as at the start of
- the subject string. It does not match after a newline that ends the
- string. A dollar matches before any newlines in the string, as well as
- at the very end, when PCRE_MULTILINE is set. When newline is specified
- as the two-character sequence CRLF, isolated CR and LF characters do
+ PCRE_MULTILINE option is set. When this is the case, a circumflex
+ matches immediately after internal newlines as well as at the start of
+ the subject string. It does not match after a newline that ends the
+ string. A dollar matches before any newlines in the string, as well as
+ at the very end, when PCRE_MULTILINE is set. When newline is specified
+ as the two-character sequence CRLF, isolated CR and LF characters do
not indicate newlines.
- For example, the pattern /^abc$/ matches the subject string "def\nabc"
- (where \n represents a newline) in multiline mode, but not otherwise.
- Consequently, patterns that are anchored in single line mode because
- all branches start with ^ are not anchored in multiline mode, and a
- match for circumflex is possible when the startoffset argument of
- pcre_exec() is non-zero. The PCRE_DOLLAR_ENDONLY option is ignored if
+ For example, the pattern /^abc$/ matches the subject string "def\nabc"
+ (where \n represents a newline) in multiline mode, but not otherwise.
+ Consequently, patterns that are anchored in single line mode because
+ all branches start with ^ are not anchored in multiline mode, and a
+ match for circumflex is possible when the startoffset argument of
+ pcre_exec() is non-zero. The PCRE_DOLLAR_ENDONLY option is ignored if
PCRE_MULTILINE is set.
- Note that the sequences \A, \Z, and \z can be used to match the start
- and end of the subject in both modes, and if all branches of a pattern
- start with \A it is always anchored, whether or not PCRE_MULTILINE is
+ Note that the sequences \A, \Z, and \z can be used to match the start
+ and end of the subject in both modes, and if all branches of a pattern
+ start with \A it is always anchored, whether or not PCRE_MULTILINE is
set.
FULL STOP (PERIOD, DOT) AND \N
Outside a character class, a dot in the pattern matches any one charac-
- ter in the subject string except (by default) a character that signi-
+ ter in the subject string except (by default) a character that signi-
fies the end of a line.
- When a line ending is defined as a single character, dot never matches
- that character; when the two-character sequence CRLF is used, dot does
- not match CR if it is immediately followed by LF, but otherwise it
- matches all characters (including isolated CRs and LFs). When any Uni-
- code line endings are being recognized, dot does not match CR or LF or
+ When a line ending is defined as a single character, dot never matches
+ that character; when the two-character sequence CRLF is used, dot does
+ not match CR if it is immediately followed by LF, but otherwise it
+ matches all characters (including isolated CRs and LFs). When any Uni-
+ code line endings are being recognized, dot does not match CR or LF or
any of the other line ending characters.
- The behaviour of dot with regard to newlines can be changed. If the
- PCRE_DOTALL option is set, a dot matches any one character, without
+ The behaviour of dot with regard to newlines can be changed. If the
+ PCRE_DOTALL option is set, a dot matches any one character, without
exception. If the two-character sequence CRLF is present in the subject
string, it takes two dots to match it.
- The handling of dot is entirely independent of the handling of circum-
- flex and dollar, the only relationship being that they both involve
+ The handling of dot is entirely independent of the handling of circum-
+ flex and dollar, the only relationship being that they both involve
newlines. Dot has no special meaning in a character class.
- The escape sequence \N behaves like a dot, except that it is not
- affected by the PCRE_DOTALL option. In other words, it matches any
- character except one that signifies the end of a line. Perl also uses
+ The escape sequence \N behaves like a dot, except that it is not
+ affected by the PCRE_DOTALL option. In other words, it matches any
+ character except one that signifies the end of a line. Perl also uses
\N to match characters by name; PCRE does not support this.
MATCHING A SINGLE DATA UNIT
- Outside a character class, the escape sequence \C matches any one data
- unit, whether or not a UTF mode is set. In the 8-bit library, one data
- unit is one byte; in the 16-bit library it is a 16-bit unit; in the
- 32-bit library it is a 32-bit unit. Unlike a dot, \C always matches
- line-ending characters. The feature is provided in Perl in order to
+ Outside a character class, the escape sequence \C matches any one data
+ unit, whether or not a UTF mode is set. In the 8-bit library, one data
+ unit is one byte; in the 16-bit library it is a 16-bit unit; in the
+ 32-bit library it is a 32-bit unit. Unlike a dot, \C always matches
+ line-ending characters. The feature is provided in Perl in order to
match individual bytes in UTF-8 mode, but it is unclear how it can use-
- fully be used. Because \C breaks up characters into individual data
- units, matching one unit with \C in a UTF mode means that the rest of
+ fully be used. Because \C breaks up characters into individual data
+ units, matching one unit with \C in a UTF mode means that the rest of
the string may start with a malformed UTF character. This has undefined
results, because PCRE assumes that it is dealing with valid UTF strings
- (and by default it checks this at the start of processing unless the
- PCRE_NO_UTF8_CHECK, PCRE_NO_UTF16_CHECK or PCRE_NO_UTF32_CHECK option
+ (and by default it checks this at the start of processing unless the
+ PCRE_NO_UTF8_CHECK, PCRE_NO_UTF16_CHECK or PCRE_NO_UTF32_CHECK option
is used).
- PCRE does not allow \C to appear in lookbehind assertions (described
- below) in a UTF mode, because this would make it impossible to calcu-
+ PCRE does not allow \C to appear in lookbehind assertions (described
+ below) in a UTF mode, because this would make it impossible to calcu-
late the length of the lookbehind.
In general, the \C escape sequence is best avoided. However, one way of
- using it that avoids the problem of malformed UTF characters is to use
- a lookahead to check the length of the next character, as in this pat-
- tern, which could be used with a UTF-8 string (ignore white space and
+ using it that avoids the problem of malformed UTF characters is to use
+ a lookahead to check the length of the next character, as in this pat-
+ tern, which could be used with a UTF-8 string (ignore white space and
line breaks):
(?| (?=[\x00-\x7f])(\C) |
@@ -5736,11 +5737,11 @@ MATCHING A SINGLE DATA UNIT
(?=[\x{800}-\x{ffff}])(\C)(\C)(\C) |
(?=[\x{10000}-\x{1fffff}])(\C)(\C)(\C)(\C))
- A group that starts with (?| resets the capturing parentheses numbers
- in each alternative (see "Duplicate Subpattern Numbers" below). The
- assertions at the start of each branch check the next UTF-8 character
- for values whose encoding uses 1, 2, 3, or 4 bytes, respectively. The
- character's individual bytes are then captured by the appropriate num-
+ A group that starts with (?| resets the capturing parentheses numbers
+ in each alternative (see "Duplicate Subpattern Numbers" below). The
+ assertions at the start of each branch check the next UTF-8 character
+ for values whose encoding uses 1, 2, 3, or 4 bytes, respectively. The
+ character's individual bytes are then captured by the appropriate num-
ber of groups.
@@ -5750,109 +5751,109 @@ SQUARE BRACKETS AND CHARACTER CLASSES
closing square bracket. A closing square bracket on its own is not spe-
cial by default. However, if the PCRE_JAVASCRIPT_COMPAT option is set,
a lone closing square bracket causes a compile-time error. If a closing
- square bracket is required as a member of the class, it should be the
- first data character in the class (after an initial circumflex, if
+ square bracket is required as a member of the class, it should be the
+ first data character in the class (after an initial circumflex, if
present) or escaped with a backslash.
- A character class matches a single character in the subject. In a UTF
- mode, the character may be more than one data unit long. A matched
+ A character class matches a single character in the subject. In a UTF
+ mode, the character may be more than one data unit long. A matched
character must be in the set of characters defined by the class, unless
- the first character in the class definition is a circumflex, in which
+ the first character in the class definition is a circumflex, in which
case the subject character must not be in the set defined by the class.
- If a circumflex is actually required as a member of the class, ensure
+ If a circumflex is actually required as a member of the class, ensure
it is not the first character, or escape it with a backslash.
- For example, the character class [aeiou] matches any lower case vowel,
- while [^aeiou] matches any character that is not a lower case vowel.
+ For example, the character class [aeiou] matches any lower case vowel,
+ while [^aeiou] matches any character that is not a lower case vowel.
Note that a circumflex is just a convenient notation for specifying the
- characters that are in the class by enumerating those that are not. A
- class that starts with a circumflex is not an assertion; it still con-
- sumes a character from the subject string, and therefore it fails if
+ characters that are in the class by enumerating those that are not. A
+ class that starts with a circumflex is not an assertion; it still con-
+ sumes a character from the subject string, and therefore it fails if
the current pointer is at the end of the string.
In UTF-8 (UTF-16, UTF-32) mode, characters with values greater than 255
- (0xffff) can be included in a class as a literal string of data units,
+ (0xffff) can be included in a class as a literal string of data units,
or by using the \x{ escaping mechanism.
- When caseless matching is set, any letters in a class represent both
- their upper case and lower case versions, so for example, a caseless
- [aeiou] matches "A" as well as "a", and a caseless [^aeiou] does not
- match "A", whereas a caseful version would. In a UTF mode, PCRE always
- understands the concept of case for characters whose values are less
- than 128, so caseless matching is always possible. For characters with
- higher values, the concept of case is supported if PCRE is compiled
- with Unicode property support, but not otherwise. If you want to use
- caseless matching in a UTF mode for characters 128 and above, you must
- ensure that PCRE is compiled with Unicode property support as well as
+ When caseless matching is set, any letters in a class represent both
+ their upper case and lower case versions, so for example, a caseless
+ [aeiou] matches "A" as well as "a", and a caseless [^aeiou] does not
+ match "A", whereas a caseful version would. In a UTF mode, PCRE always
+ understands the concept of case for characters whose values are less
+ than 128, so caseless matching is always possible. For characters with
+ higher values, the concept of case is supported if PCRE is compiled
+ with Unicode property support, but not otherwise. If you want to use
+ caseless matching in a UTF mode for characters 128 and above, you must
+ ensure that PCRE is compiled with Unicode property support as well as
with UTF support.
- Characters that might indicate line breaks are never treated in any
- special way when matching character classes, whatever line-ending
- sequence is in use, and whatever setting of the PCRE_DOTALL and
+ Characters that might indicate line breaks are never treated in any
+ special way when matching character classes, whatever line-ending
+ sequence is in use, and whatever setting of the PCRE_DOTALL and
PCRE_MULTILINE options is used. A class such as [^a] always matches one
of these characters.
- The minus (hyphen) character can be used to specify a range of charac-
- ters in a character class. For example, [d-m] matches any letter
- between d and m, inclusive. If a minus character is required in a
- class, it must be escaped with a backslash or appear in a position
- where it cannot be interpreted as indicating a range, typically as the
+ The minus (hyphen) character can be used to specify a range of charac-
+ ters in a character class. For example, [d-m] matches any letter
+ between d and m, inclusive. If a minus character is required in a
+ class, it must be escaped with a backslash or appear in a position
+ where it cannot be interpreted as indicating a range, typically as the
first or last character in the class, or immediately after a range. For
- example, [b-d-z] matches letters in the range b to d, a hyphen charac-
+ example, [b-d-z] matches letters in the range b to d, a hyphen charac-
ter, or z.
It is not possible to have the literal character "]" as the end charac-
- ter of a range. A pattern such as [W-]46] is interpreted as a class of
- two characters ("W" and "-") followed by a literal string "46]", so it
- would match "W46]" or "-46]". However, if the "]" is escaped with a
- backslash it is interpreted as the end of range, so [W-\]46] is inter-
- preted as a class containing a range followed by two other characters.
- The octal or hexadecimal representation of "]" can also be used to end
+ ter of a range. A pattern such as [W-]46] is interpreted as a class of
+ two characters ("W" and "-") followed by a literal string "46]", so it
+ would match "W46]" or "-46]". However, if the "]" is escaped with a
+ backslash it is interpreted as the end of range, so [W-\]46] is inter-
+ preted as a class containing a range followed by two other characters.
+ The octal or hexadecimal representation of "]" can also be used to end
a range.
- An error is generated if a POSIX character class (see below) or an
- escape sequence other than one that defines a single character appears
- at a point where a range ending character is expected. For example,
+ An error is generated if a POSIX character class (see below) or an
+ escape sequence other than one that defines a single character appears
+ at a point where a range ending character is expected. For example,
[z-\xff] is valid, but [A-\d] and [A-[:digit:]] are not.
- Ranges operate in the collating sequence of character values. They can
- also be used for characters specified numerically, for example
- [\000-\037]. Ranges can include any characters that are valid for the
+ Ranges operate in the collating sequence of character values. They can
+ also be used for characters specified numerically, for example
+ [\000-\037]. Ranges can include any characters that are valid for the
current mode.
If a range that includes letters is used when caseless matching is set,
it matches the letters in either case. For example, [W-c] is equivalent
- to [][\\^_`wxyzabc], matched caselessly, and in a non-UTF mode, if
- character tables for a French locale are in use, [\xc8-\xcb] matches
- accented E characters in both cases. In UTF modes, PCRE supports the
- concept of case for characters with values greater than 128 only when
+ to [][\\^_`wxyzabc], matched caselessly, and in a non-UTF mode, if
+ character tables for a French locale are in use, [\xc8-\xcb] matches
+ accented E characters in both cases. In UTF modes, PCRE supports the
+ concept of case for characters with values greater than 128 only when
it is compiled with Unicode property support.
- The character escape sequences \d, \D, \h, \H, \p, \P, \s, \S, \v, \V,
+ The character escape sequences \d, \D, \h, \H, \p, \P, \s, \S, \v, \V,
\w, and \W may appear in a character class, and add the characters that
- they match to the class. For example, [\dABCDEF] matches any hexadeci-
- mal digit. In UTF modes, the PCRE_UCP option affects the meanings of
- \d, \s, \w and their upper case partners, just as it does when they
- appear outside a character class, as described in the section entitled
+ they match to the class. For example, [\dABCDEF] matches any hexadeci-
+ mal digit. In UTF modes, the PCRE_UCP option affects the meanings of
+ \d, \s, \w and their upper case partners, just as it does when they
+ appear outside a character class, as described in the section entitled
"Generic character types" above. The escape sequence \b has a different
- meaning inside a character class; it matches the backspace character.
- The sequences \B, \N, \R, and \X are not special inside a character
- class. Like any other unrecognized escape sequences, they are treated
- as the literal characters "B", "N", "R", and "X" by default, but cause
+ meaning inside a character class; it matches the backspace character.
+ The sequences \B, \N, \R, and \X are not special inside a character
+ class. Like any other unrecognized escape sequences, they are treated
+ as the literal characters "B", "N", "R", and "X" by default, but cause
an error if the PCRE_EXTRA option is set.
- A circumflex can conveniently be used with the upper case character
- types to specify a more restricted set of characters than the matching
- lower case type. For example, the class [^\W_] matches any letter or
+ A circumflex can conveniently be used with the upper case character
+ types to specify a more restricted set of characters than the matching
+ lower case type. For example, the class [^\W_] matches any letter or
digit, but not underscore, whereas [\w] includes underscore. A positive
character class should be read as "something OR something OR ..." and a
negative class as "NOT something AND NOT something AND NOT ...".
- The only metacharacters that are recognized in character classes are
- backslash, hyphen (only where it can be interpreted as specifying a
- range), circumflex (only at the start), opening square bracket (only
- when it can be interpreted as introducing a POSIX class name, or for a
- special compatibility feature - see the next two sections), and the
+ The only metacharacters that are recognized in character classes are
+ backslash, hyphen (only where it can be interpreted as specifying a
+ range), circumflex (only at the start), opening square bracket (only
+ when it can be interpreted as introducing a POSIX class name, or for a
+ special compatibility feature - see the next two sections), and the
terminating closing square bracket. However, escaping other non-
alphanumeric characters does no harm.
@@ -5860,7 +5861,7 @@ SQUARE BRACKETS AND CHARACTER CLASSES
POSIX CHARACTER CLASSES
Perl supports the POSIX notation for character classes. This uses names
- enclosed by [: and :] within the enclosing square brackets. PCRE also
+ enclosed by [: and :] within the enclosing square brackets. PCRE also
supports this notation. For example,
[01[:alpha:]%]
@@ -5883,28 +5884,28 @@ POSIX CHARACTER CLASSES
word "word" characters (same as \w)
xdigit hexadecimal digits
- The default "space" characters are HT (9), LF (10), VT (11), FF (12),
- CR (13), and space (32). If locale-specific matching is taking place,
- the list of space characters may be different; there may be fewer or
+ The default "space" characters are HT (9), LF (10), VT (11), FF (12),
+ CR (13), and space (32). If locale-specific matching is taking place,
+ the list of space characters may be different; there may be fewer or
more of them. "Space" used to be different to \s, which did not include
VT, for Perl compatibility. However, Perl changed at release 5.18, and
- PCRE followed at release 8.34. "Space" and \s now match the same set
+ PCRE followed at release 8.34. "Space" and \s now match the same set
of characters.
- The name "word" is a Perl extension, and "blank" is a GNU extension
- from Perl 5.8. Another Perl extension is negation, which is indicated
+ The name "word" is a Perl extension, and "blank" is a GNU extension
+ from Perl 5.8. Another Perl extension is negation, which is indicated
by a ^ character after the colon. For example,
[12[:^digit:]]
- matches "1", "2", or any non-digit. PCRE (and Perl) also recognize the
+ matches "1", "2", or any non-digit. PCRE (and Perl) also recognize the
POSIX syntax [.ch.] and [=ch=] where "ch" is a "collating element", but
these are not supported, and an error is given if they are encountered.
By default, characters with values greater than 128 do not match any of
- the POSIX character classes. However, if the PCRE_UCP option is passed
- to pcre_compile(), some of the classes are changed so that Unicode
- character properties are used. This is achieved by replacing certain
+ the POSIX character classes. However, if the PCRE_UCP option is passed
+ to pcre_compile(), some of the classes are changed so that Unicode
+ character properties are used. This is achieved by replacing certain
POSIX classes by other sequences, as follows:
[:alnum:] becomes \p{Xan}
@@ -5916,10 +5917,10 @@ POSIX CHARACTER CLASSES
[:upper:] becomes \p{Lu}
[:word:] becomes \p{Xwd}
- Negated versions, such as [:^alpha:] use \P instead of \p. Three other
+ Negated versions, such as [:^alpha:] use \P instead of \p. Three other
POSIX classes are handled specially in UCP mode:
- [:graph:] This matches characters that have glyphs that mark the page
+ [:graph:] This matches characters that have glyphs that mark the page
when printed. In Unicode property terms, it matches all char-
acters with the L, M, N, P, S, or Cf properties, except for:
@@ -5928,58 +5929,58 @@ POSIX CHARACTER CLASSES
U+2066 - U+2069 Various "isolate"s
- [:print:] This matches the same characters as [:graph:] plus space
- characters that are not controls, that is, characters with
+ [:print:] This matches the same characters as [:graph:] plus space
+ characters that are not controls, that is, characters with
the Zs property.
[:punct:] This matches all characters that have the Unicode P (punctua-
- tion) property, plus those characters whose code points are
+ tion) property, plus those characters whose code points are
less than 128 that have the S (Symbol) property.
- The other POSIX classes are unchanged, and match only characters with
+ The other POSIX classes are unchanged, and match only characters with
code points less than 128.
COMPATIBILITY FEATURE FOR WORD BOUNDARIES
- In the POSIX.2 compliant library that was included in 4.4BSD Unix, the
- ugly syntax [[:<:]] and [[:>:]] is used for matching "start of word"
+ In the POSIX.2 compliant library that was included in 4.4BSD Unix, the
+ ugly syntax [[:<:]] and [[:>:]] is used for matching "start of word"
and "end of word". PCRE treats these items as follows:
[[:<:]] is converted to \b(?=\w)
[[:>:]] is converted to \b(?<=\w)
Only these exact character sequences are recognized. A sequence such as
- [a[:<:]b] provokes error for an unrecognized POSIX class name. This
- support is not compatible with Perl. It is provided to help migrations
+ [a[:<:]b] provokes error for an unrecognized POSIX class name. This
+ support is not compatible with Perl. It is provided to help migrations
from other environments, and is best not used in any new patterns. Note
- that \b matches at the start and the end of a word (see "Simple asser-
- tions" above), and in a Perl-style pattern the preceding or following
- character normally shows which is wanted, without the need for the
- assertions that are used above in order to give exactly the POSIX be-
+ that \b matches at the start and the end of a word (see "Simple asser-
+ tions" above), and in a Perl-style pattern the preceding or following
+ character normally shows which is wanted, without the need for the
+ assertions that are used above in order to give exactly the POSIX be-
haviour.
VERTICAL BAR
- Vertical bar characters are used to separate alternative patterns. For
+ Vertical bar characters are used to separate alternative patterns. For
example, the pattern
gilbert|sullivan
- matches either "gilbert" or "sullivan". Any number of alternatives may
- appear, and an empty alternative is permitted (matching the empty
+ matches either "gilbert" or "sullivan". Any number of alternatives may
+ appear, and an empty alternative is permitted (matching the empty
string). The matching process tries each alternative in turn, from left
- to right, and the first one that succeeds is used. If the alternatives
- are within a subpattern (defined below), "succeeds" means matching the
+ to right, and the first one that succeeds is used. If the alternatives
+ are within a subpattern (defined below), "succeeds" means matching the
rest of the main pattern as well as the alternative in the subpattern.
INTERNAL OPTION SETTING
- The settings of the PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, and
- PCRE_EXTENDED options (which are Perl-compatible) can be changed from
- within the pattern by a sequence of Perl option letters enclosed
+ The settings of the PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, and
+ PCRE_EXTENDED options (which are Perl-compatible) can be changed from
+ within the pattern by a sequence of Perl option letters enclosed
between "(?" and ")". The option letters are
i for PCRE_CASELESS
@@ -5989,51 +5990,47 @@ INTERNAL OPTION SETTING
For example, (?im) sets caseless, multiline matching. It is also possi-
ble to unset these options by preceding the letter with a hyphen, and a
- combined setting and unsetting such as (?im-sx), which sets PCRE_CASE-
- LESS and PCRE_MULTILINE while unsetting PCRE_DOTALL and PCRE_EXTENDED,
- is also permitted. If a letter appears both before and after the
+ combined setting and unsetting such as (?im-sx), which sets PCRE_CASE-
+ LESS and PCRE_MULTILINE while unsetting PCRE_DOTALL and PCRE_EXTENDED,
+ is also permitted. If a letter appears both before and after the
hyphen, the option is unset.
- The PCRE-specific options PCRE_DUPNAMES, PCRE_UNGREEDY, and PCRE_EXTRA
- can be changed in the same way as the Perl-compatible options by using
+ The PCRE-specific options PCRE_DUPNAMES, PCRE_UNGREEDY, and PCRE_EXTRA
+ can be changed in the same way as the Perl-compatible options by using
the characters J, U and X respectively.
- When one of these option changes occurs at top level (that is, not
- inside subpattern parentheses), the change applies to the remainder of
- the pattern that follows. If the change is placed right at the start of
- a pattern, PCRE extracts it into the global options (and it will there-
- fore show up in data extracted by the pcre_fullinfo() function).
-
- An option change within a subpattern (see below for a description of
- subpatterns) affects only that part of the subpattern that follows it,
- so
+ When one of these option changes occurs at top level (that is, not
+ inside subpattern parentheses), the change applies to the remainder of
+ the pattern that follows. An option change within a subpattern (see
+ below for a description of subpatterns) affects only that part of the
+ subpattern that follows it, so
(a(?i)b)c
matches abc and aBc and no other strings (assuming PCRE_CASELESS is not
- used). By this means, options can be made to have different settings
- in different parts of the pattern. Any changes made in one alternative
- do carry on into subsequent branches within the same subpattern. For
+ used). By this means, options can be made to have different settings
+ in different parts of the pattern. Any changes made in one alternative
+ do carry on into subsequent branches within the same subpattern. For
example,
(a(?i)b|c)
- matches "ab", "aB", "c", and "C", even though when matching "C" the
- first branch is abandoned before the option setting. This is because
- the effects of option settings happen at compile time. There would be
+ matches "ab", "aB", "c", and "C", even though when matching "C" the
+ first branch is abandoned before the option setting. This is because
+ the effects of option settings happen at compile time. There would be
some very weird behaviour otherwise.
- Note: There are other PCRE-specific options that can be set by the
- application when the compiling or matching functions are called. In
- some cases the pattern can contain special leading sequences such as
- (*CRLF) to override what the application has set or what has been
- defaulted. Details are given in the section entitled "Newline
- sequences" above. There are also the (*UTF8), (*UTF16),(*UTF32), and
- (*UCP) leading sequences that can be used to set UTF and Unicode prop-
- erty modes; they are equivalent to setting the PCRE_UTF8, PCRE_UTF16,
- PCRE_UTF32 and the PCRE_UCP options, respectively. The (*UTF) sequence
- is a generic version that can be used with any of the libraries. How-
- ever, the application can set the PCRE_NEVER_UTF option, which locks
+ Note: There are other PCRE-specific options that can be set by the
+ application when the compiling or matching functions are called. In
+ some cases the pattern can contain special leading sequences such as
+ (*CRLF) to override what the application has set or what has been
+ defaulted. Details are given in the section entitled "Newline
+ sequences" above. There are also the (*UTF8), (*UTF16),(*UTF32), and
+ (*UCP) leading sequences that can be used to set UTF and Unicode prop-
+ erty modes; they are equivalent to setting the PCRE_UTF8, PCRE_UTF16,
+ PCRE_UTF32 and the PCRE_UCP options, respectively. The (*UTF) sequence
+ is a generic version that can be used with any of the libraries. How-
+ ever, the application can set the PCRE_NEVER_UTF option, which locks
out the use of the (*UTF) sequences.
@@ -6046,18 +6043,18 @@ SUBPATTERNS
cat(aract|erpillar|)
- matches "cataract", "caterpillar", or "cat". Without the parentheses,
+ matches "cataract", "caterpillar", or "cat". Without the parentheses,
it would match "cataract", "erpillar" or an empty string.
- 2. It sets up the subpattern as a capturing subpattern. This means
- that, when the whole pattern matches, that portion of the subject
+ 2. It sets up the subpattern as a capturing subpattern. This means
+ that, when the whole pattern matches, that portion of the subject
string that matched the subpattern is passed back to the caller via the
- ovector argument of the matching function. (This applies only to the
- traditional matching functions; the DFA matching functions do not sup-
+ ovector argument of the matching function. (This applies only to the
+ traditional matching functions; the DFA matching functions do not sup-
port capturing.)
Opening parentheses are counted from left to right (starting from 1) to
- obtain numbers for the capturing subpatterns. For example, if the
+ obtain numbers for the capturing subpatterns. For example, if the
string "the red king" is matched against the pattern
the ((red|white) (king|queen))
@@ -6065,12 +6062,12 @@ SUBPATTERNS
the captured substrings are "red king", "red", and "king", and are num-
bered 1, 2, and 3, respectively.
- The fact that plain parentheses fulfil two functions is not always
- helpful. There are often times when a grouping subpattern is required
- without a capturing requirement. If an opening parenthesis is followed
- by a question mark and a colon, the subpattern does not do any captur-
- ing, and is not counted when computing the number of any subsequent
- capturing subpatterns. For example, if the string "the white queen" is
+ The fact that plain parentheses fulfil two functions is not always
+ helpful. There are often times when a grouping subpattern is required
+ without a capturing requirement. If an opening parenthesis is followed
+ by a question mark and a colon, the subpattern does not do any captur-
+ ing, and is not counted when computing the number of any subsequent
+ capturing subpatterns. For example, if the string "the white queen" is
matched against the pattern
the ((?:red|white) (king|queen))
@@ -6078,37 +6075,37 @@ SUBPATTERNS
the captured substrings are "white queen" and "queen", and are numbered
1 and 2. The maximum number of capturing subpatterns is 65535.
- As a convenient shorthand, if any option settings are required at the
- start of a non-capturing subpattern, the option letters may appear
+ As a convenient shorthand, if any option settings are required at the
+ start of a non-capturing subpattern, the option letters may appear
between the "?" and the ":". Thus the two patterns
(?i:saturday|sunday)
(?:(?i)saturday|sunday)
match exactly the same set of strings. Because alternative branches are
- tried from left to right, and options are not reset until the end of
- the subpattern is reached, an option setting in one branch does affect
- subsequent branches, so the above patterns match "SUNDAY" as well as
+ tried from left to right, and options are not reset until the end of
+ the subpattern is reached, an option setting in one branch does affect
+ subsequent branches, so the above patterns match "SUNDAY" as well as
"Saturday".
DUPLICATE SUBPATTERN NUMBERS
Perl 5.10 introduced a feature whereby each alternative in a subpattern
- uses the same numbers for its capturing parentheses. Such a subpattern
- starts with (?| and is itself a non-capturing subpattern. For example,
+ uses the same numbers for its capturing parentheses. Such a subpattern
+ starts with (?| and is itself a non-capturing subpattern. For example,
consider this pattern:
(?|(Sat)ur|(Sun))day
- Because the two alternatives are inside a (?| group, both sets of cap-
- turing parentheses are numbered one. Thus, when the pattern matches,
- you can look at captured substring number one, whichever alternative
- matched. This construct is useful when you want to capture part, but
+ Because the two alternatives are inside a (?| group, both sets of cap-
+ turing parentheses are numbered one. Thus, when the pattern matches,
+ you can look at captured substring number one, whichever alternative
+ matched. This construct is useful when you want to capture part, but
not all, of one of a number of alternatives. Inside a (?| group, paren-
- theses are numbered as usual, but the number is reset at the start of
- each branch. The numbers of any capturing parentheses that follow the
- subpattern start after the highest number used in any branch. The fol-
+ theses are numbered as usual, but the number is reset at the start of
+ each branch. The numbers of any capturing parentheses that follow the
+ subpattern start after the highest number used in any branch. The fol-
lowing example is taken from the Perl documentation. The numbers under-
neath show in which buffer the captured content will be stored.
@@ -6116,58 +6113,58 @@ DUPLICATE SUBPATTERN NUMBERS
/ ( a ) (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
# 1 2 2 3 2 3 4
- A back reference to a numbered subpattern uses the most recent value
- that is set for that number by any subpattern. The following pattern
+ A back reference to a numbered subpattern uses the most recent value
+ that is set for that number by any subpattern. The following pattern
matches "abcabc" or "defdef":
/(?|(abc)|(def))\1/
- In contrast, a subroutine call to a numbered subpattern always refers
- to the first one in the pattern with the given number. The following
+ In contrast, a subroutine call to a numbered subpattern always refers
+ to the first one in the pattern with the given number. The following
pattern matches "abcabc" or "defabc":
/(?|(abc)|(def))(?1)/
- If a condition test for a subpattern's having matched refers to a non-
- unique number, the test is true if any of the subpatterns of that num-
+ If a condition test for a subpattern's having matched refers to a non-
+ unique number, the test is true if any of the subpatterns of that num-
ber have matched.
- An alternative approach to using this "branch reset" feature is to use
+ An alternative approach to using this "branch reset" feature is to use
duplicate named subpatterns, as described in the next section.
NAMED SUBPATTERNS
- Identifying capturing parentheses by number is simple, but it can be
- very hard to keep track of the numbers in complicated regular expres-
- sions. Furthermore, if an expression is modified, the numbers may
- change. To help with this difficulty, PCRE supports the naming of sub-
+ Identifying capturing parentheses by number is simple, but it can be
+ very hard to keep track of the numbers in complicated regular expres-
+ sions. Furthermore, if an expression is modified, the numbers may
+ change. To help with this difficulty, PCRE supports the naming of sub-
patterns. This feature was not added to Perl until release 5.10. Python
- had the feature earlier, and PCRE introduced it at release 4.0, using
- the Python syntax. PCRE now supports both the Perl and the Python syn-
- tax. Perl allows identically numbered subpatterns to have different
+ had the feature earlier, and PCRE introduced it at release 4.0, using
+ the Python syntax. PCRE now supports both the Perl and the Python syn-
+ tax. Perl allows identically numbered subpatterns to have different
names, but PCRE does not.
- In PCRE, a subpattern can be named in one of three ways: (?<name>...)
- or (?'name'...) as in Perl, or (?P<name>...) as in Python. References
- to capturing parentheses from other parts of the pattern, such as back
- references, recursion, and conditions, can be made by name as well as
+ In PCRE, a subpattern can be named in one of three ways: (?<name>...)
+ or (?'name'...) as in Perl, or (?P<name>...) as in Python. References
+ to capturing parentheses from other parts of the pattern, such as back
+ references, recursion, and conditions, can be made by name as well as
by number.
- Names consist of up to 32 alphanumeric characters and underscores, but
- must start with a non-digit. Named capturing parentheses are still
- allocated numbers as well as names, exactly as if the names were not
- present. The PCRE API provides function calls for extracting the name-
- to-number translation table from a compiled pattern. There is also a
+ Names consist of up to 32 alphanumeric characters and underscores, but
+ must start with a non-digit. Named capturing parentheses are still
+ allocated numbers as well as names, exactly as if the names were not
+ present. The PCRE API provides function calls for extracting the name-
+ to-number translation table from a compiled pattern. There is also a
convenience function for extracting a captured substring by name.
- By default, a name must be unique within a pattern, but it is possible
+ By default, a name must be unique within a pattern, but it is possible
to relax this constraint by setting the PCRE_DUPNAMES option at compile
- time. (Duplicate names are also always permitted for subpatterns with
- the same number, set up as described in the previous section.) Dupli-
- cate names can be useful for patterns where only one instance of the
- named parentheses can match. Suppose you want to match the name of a
- weekday, either as a 3-letter abbreviation or as the full name, and in
+ time. (Duplicate names are also always permitted for subpatterns with
+ the same number, set up as described in the previous section.) Dupli-
+ cate names can be useful for patterns where only one instance of the
+ named parentheses can match. Suppose you want to match the name of a
+ weekday, either as a 3-letter abbreviation or as the full name, and in
both cases you want to extract the abbreviation. This pattern (ignoring
the line breaks) does the job:
@@ -6177,18 +6174,18 @@ NAMED SUBPATTERNS
(?<DN>Thu)(?:rsday)?|
(?<DN>Sat)(?:urday)?
- There are five capturing substrings, but only one is ever set after a
+ There are five capturing substrings, but only one is ever set after a
match. (An alternative way of solving this problem is to use a "branch
reset" subpattern, as described in the previous section.)
- The convenience function for extracting the data by name returns the
- substring for the first (and in this example, the only) subpattern of
- that name that matched. This saves searching to find which numbered
+ The convenience function for extracting the data by name returns the
+ substring for the first (and in this example, the only) subpattern of
+ that name that matched. This saves searching to find which numbered
subpattern it was.
- If you make a back reference to a non-unique named subpattern from
- elsewhere in the pattern, the subpatterns to which the name refers are
- checked in the order in which they appear in the overall pattern. The
+ If you make a back reference to a non-unique named subpattern from
+ elsewhere in the pattern, the subpatterns to which the name refers are
+ checked in the order in which they appear in the overall pattern. The
first one that is set is used for the reference. For example, this pat-
tern matches both "foofoo" and "barbar" but not "foobar" or "barfoo":
@@ -6196,29 +6193,29 @@ NAMED SUBPATTERNS
If you make a subroutine call to a non-unique named subpattern, the one
- that corresponds to the first occurrence of the name is used. In the
+ that corresponds to the first occurrence of the name is used. In the
absence of duplicate numbers (see the previous section) this is the one
with the lowest number.
If you use a named reference in a condition test (see the section about
conditions below), either to check whether a subpattern has matched, or
- to check for recursion, all subpatterns with the same name are tested.
- If the condition is true for any one of them, the overall condition is
- true. This is the same behaviour as testing by number. For further
- details of the interfaces for handling named subpatterns, see the
+ to check for recursion, all subpatterns with the same name are tested.
+ If the condition is true for any one of them, the overall condition is
+ true. This is the same behaviour as testing by number. For further
+ details of the interfaces for handling named subpatterns, see the
pcreapi documentation.
Warning: You cannot use different names to distinguish between two sub-
- patterns with the same number because PCRE uses only the numbers when
+ patterns with the same number because PCRE uses only the numbers when
matching. For this reason, an error is given at compile time if differ-
- ent names are given to subpatterns with the same number. However, you
+ ent names are given to subpatterns with the same number. However, you
can always give the same name to subpatterns with the same number, even
when PCRE_DUPNAMES is not set.
REPETITION
- Repetition is specified by quantifiers, which can follow any of the
+ Repetition is specified by quantifiers, which can follow any of the
following items:
a literal data character
@@ -6232,17 +6229,17 @@ REPETITION
a parenthesized subpattern (including assertions)
a subroutine call to a subpattern (recursive or otherwise)
- The general repetition quantifier specifies a minimum and maximum num-
- ber of permitted matches, by giving the two numbers in curly brackets
- (braces), separated by a comma. The numbers must be less than 65536,
+ The general repetition quantifier specifies a minimum and maximum num-
+ ber of permitted matches, by giving the two numbers in curly brackets
+ (braces), separated by a comma. The numbers must be less than 65536,
and the first must be less than or equal to the second. For example:
z{2,4}
- matches "zz", "zzz", or "zzzz". A closing brace on its own is not a
- special character. If the second number is omitted, but the comma is
- present, there is no upper limit; if the second number and the comma
- are both omitted, the quantifier specifies an exact number of required
+ matches "zz", "zzz", or "zzzz". A closing brace on its own is not a
+ special character. If the second number is omitted, but the comma is
+ present, there is no upper limit; if the second number and the comma
+ are both omitted, the quantifier specifies an exact number of required
matches. Thus
[aeiou]{3,}
@@ -6251,50 +6248,50 @@ REPETITION
\d{8}
- matches exactly 8 digits. An opening curly bracket that appears in a
- position where a quantifier is not allowed, or one that does not match
- the syntax of a quantifier, is taken as a literal character. For exam-
+ matches exactly 8 digits. An opening curly bracket that appears in a
+ position where a quantifier is not allowed, or one that does not match
+ the syntax of a quantifier, is taken as a literal character. For exam-
ple, {,6} is not a quantifier, but a literal string of four characters.
In UTF modes, quantifiers apply to characters rather than to individual
- data units. Thus, for example, \x{100}{2} matches two characters, each
+ data units. Thus, for example, \x{100}{2} matches two characters, each
of which is represented by a two-byte sequence in a UTF-8 string. Simi-
- larly, \X{3} matches three Unicode extended grapheme clusters, each of
- which may be several data units long (and they may be of different
+ larly, \X{3} matches three Unicode extended grapheme clusters, each of
+ which may be several data units long (and they may be of different
lengths).
The quantifier {0} is permitted, causing the expression to behave as if
the previous item and the quantifier were not present. This may be use-
- ful for subpatterns that are referenced as subroutines from elsewhere
+ ful for subpatterns that are referenced as subroutines from elsewhere
in the pattern (but see also the section entitled "Defining subpatterns
- for use by reference only" below). Items other than subpatterns that
+ for use by reference only" below). Items other than subpatterns that
have a {0} quantifier are omitted from the compiled pattern.
- For convenience, the three most common quantifiers have single-charac-
+ For convenience, the three most common quantifiers have single-charac-
ter abbreviations:
* is equivalent to {0,}
+ is equivalent to {1,}
? is equivalent to {0,1}
- It is possible to construct infinite loops by following a subpattern
+ It is possible to construct infinite loops by following a subpattern
that can match no characters with a quantifier that has no upper limit,
for example:
(a?)*
Earlier versions of Perl and PCRE used to give an error at compile time
- for such patterns. However, because there are cases where this can be
- useful, such patterns are now accepted, but if any repetition of the
- subpattern does in fact match no characters, the loop is forcibly bro-
+ for such patterns. However, because there are cases where this can be
+ useful, such patterns are now accepted, but if any repetition of the
+ subpattern does in fact match no characters, the loop is forcibly bro-
ken.
- By default, the quantifiers are "greedy", that is, they match as much
- as possible (up to the maximum number of permitted times), without
- causing the rest of the pattern to fail. The classic example of where
+ By default, the quantifiers are "greedy", that is, they match as much
+ as possible (up to the maximum number of permitted times), without
+ causing the rest of the pattern to fail. The classic example of where
this gives problems is in trying to match comments in C programs. These
- appear between /* and */ and within the comment, individual * and /
- characters may appear. An attempt to match C comments by applying the
+ appear between /* and */ and within the comment, individual * and /
+ characters may appear. An attempt to match C comments by applying the
pattern
/\*.*\*/
@@ -6303,19 +6300,19 @@ REPETITION
/* first comment */ not comment /* second comment */
- fails, because it matches the entire string owing to the greediness of
+ fails, because it matches the entire string owing to the greediness of
the .* item.
- However, if a quantifier is followed by a question mark, it ceases to
+ However, if a quantifier is followed by a question mark, it ceases to
be greedy, and instead matches the minimum number of times possible, so
the pattern
/\*.*?\*/
- does the right thing with the C comments. The meaning of the various
- quantifiers is not otherwise changed, just the preferred number of
- matches. Do not confuse this use of question mark with its use as a
- quantifier in its own right. Because it has two uses, it can sometimes
+ does the right thing with the C comments. The meaning of the various
+ quantifiers is not otherwise changed, just the preferred number of
+ matches. Do not confuse this use of question mark with its use as a
+ quantifier in its own right. Because it has two uses, it can sometimes
appear doubled, as in
\d??\d
@@ -6323,45 +6320,45 @@ REPETITION
which matches one digit by preference, but can match two if that is the
only way the rest of the pattern matches.
- If the PCRE_UNGREEDY option is set (an option that is not available in
- Perl), the quantifiers are not greedy by default, but individual ones
- can be made greedy by following them with a question mark. In other
+ If the PCRE_UNGREEDY option is set (an option that is not available in
+ Perl), the quantifiers are not greedy by default, but individual ones
+ can be made greedy by following them with a question mark. In other
words, it inverts the default behaviour.
- When a parenthesized subpattern is quantified with a minimum repeat
- count that is greater than 1 or with a limited maximum, more memory is
- required for the compiled pattern, in proportion to the size of the
+ When a parenthesized subpattern is quantified with a minimum repeat
+ count that is greater than 1 or with a limited maximum, more memory is
+ required for the compiled pattern, in proportion to the size of the
minimum or maximum.
If a pattern starts with .* or .{0,} and the PCRE_DOTALL option (equiv-
- alent to Perl's /s) is set, thus allowing the dot to match newlines,
- the pattern is implicitly anchored, because whatever follows will be
- tried against every character position in the subject string, so there
- is no point in retrying the overall match at any position after the
- first. PCRE normally treats such a pattern as though it were preceded
+ alent to Perl's /s) is set, thus allowing the dot to match newlines,
+ the pattern is implicitly anchored, because whatever follows will be
+ tried against every character position in the subject string, so there
+ is no point in retrying the overall match at any position after the
+ first. PCRE normally treats such a pattern as though it were preceded
by \A.
- In cases where it is known that the subject string contains no new-
- lines, it is worth setting PCRE_DOTALL in order to obtain this opti-
+ In cases where it is known that the subject string contains no new-
+ lines, it is worth setting PCRE_DOTALL in order to obtain this opti-
mization, or alternatively using ^ to indicate anchoring explicitly.
- However, there are some cases where the optimization cannot be used.
+ However, there are some cases where the optimization cannot be used.
When .* is inside capturing parentheses that are the subject of a back
reference elsewhere in the pattern, a match at the start may fail where
a later one succeeds. Consider, for example:
(.*)abc\1
- If the subject is "xyz123abc123" the match point is the fourth charac-
+ If the subject is "xyz123abc123" the match point is the fourth charac-
ter. For this reason, such a pattern is not implicitly anchored.
- Another case where implicit anchoring is not applied is when the lead-
- ing .* is inside an atomic group. Once again, a match at the start may
+ Another case where implicit anchoring is not applied is when the lead-
+ ing .* is inside an atomic group. Once again, a match at the start may
fail where a later one succeeds. Consider this pattern:
(?>.*?a)b
- It matches "ab" in the subject "aab". The use of the backtracking con-
+ It matches "ab" in the subject "aab". The use of the backtracking con-
trol verbs (*PRUNE) and (*SKIP) also disable this optimization.
When a capturing subpattern is repeated, the value captured is the sub-
@@ -6370,8 +6367,8 @@ REPETITION
(tweedle[dume]{3}\s*)+
has matched "tweedledum tweedledee" the value of the captured substring
- is "tweedledee". However, if there are nested capturing subpatterns,
- the corresponding captured values may have been set in previous itera-
+ is "tweedledee". However, if there are nested capturing subpatterns,
+ the corresponding captured values may have been set in previous itera-
tions. For example, after
/(a|(b))+/
@@ -6381,53 +6378,53 @@ REPETITION
ATOMIC GROUPING AND POSSESSIVE QUANTIFIERS
- With both maximizing ("greedy") and minimizing ("ungreedy" or "lazy")
- repetition, failure of what follows normally causes the repeated item
- to be re-evaluated to see if a different number of repeats allows the
- rest of the pattern to match. Sometimes it is useful to prevent this,
- either to change the nature of the match, or to cause it fail earlier
- than it otherwise might, when the author of the pattern knows there is
+ With both maximizing ("greedy") and minimizing ("ungreedy" or "lazy")
+ repetition, failure of what follows normally causes the repeated item
+ to be re-evaluated to see if a different number of repeats allows the
+ rest of the pattern to match. Sometimes it is useful to prevent this,
+ either to change the nature of the match, or to cause it fail earlier
+ than it otherwise might, when the author of the pattern knows there is
no point in carrying on.
- Consider, for example, the pattern \d+foo when applied to the subject
+ Consider, for example, the pattern \d+foo when applied to the subject
line
123456bar
After matching all 6 digits and then failing to match "foo", the normal
- action of the matcher is to try again with only 5 digits matching the
- \d+ item, and then with 4, and so on, before ultimately failing.
- "Atomic grouping" (a term taken from Jeffrey Friedl's book) provides
- the means for specifying that once a subpattern has matched, it is not
+ action of the matcher is to try again with only 5 digits matching the
+ \d+ item, and then with 4, and so on, before ultimately failing.
+ "Atomic grouping" (a term taken from Jeffrey Friedl's book) provides
+ the means for specifying that once a subpattern has matched, it is not
to be re-evaluated in this way.
- If we use atomic grouping for the previous example, the matcher gives
- up immediately on failing to match "foo" the first time. The notation
+ If we use atomic grouping for the previous example, the matcher gives
+ up immediately on failing to match "foo" the first time. The notation
is a kind of special parenthesis, starting with (?> as in this example:
(?>\d+)foo
- This kind of parenthesis "locks up" the part of the pattern it con-
- tains once it has matched, and a failure further into the pattern is
- prevented from backtracking into it. Backtracking past it to previous
+ This kind of parenthesis "locks up" the part of the pattern it con-
+ tains once it has matched, and a failure further into the pattern is
+ prevented from backtracking into it. Backtracking past it to previous
items, however, works as normal.
- An alternative description is that a subpattern of this type matches
- the string of characters that an identical standalone pattern would
+ An alternative description is that a subpattern of this type matches
+ the string of characters that an identical standalone pattern would
match, if anchored at the current point in the subject string.
Atomic grouping subpatterns are not capturing subpatterns. Simple cases
such as the above example can be thought of as a maximizing repeat that
- must swallow everything it can. So, while both \d+ and \d+? are pre-
- pared to adjust the number of digits they match in order to make the
+ must swallow everything it can. So, while both \d+ and \d+? are pre-
+ pared to adjust the number of digits they match in order to make the
rest of the pattern match, (?>\d+) can only match an entire sequence of
digits.
- Atomic groups in general can of course contain arbitrarily complicated
- subpatterns, and can be nested. However, when the subpattern for an
+ Atomic groups in general can of course contain arbitrarily complicated
+ subpatterns, and can be nested. However, when the subpattern for an
atomic group is just a single repeated item, as in the example above, a
- simpler notation, called a "possessive quantifier" can be used. This
- consists of an additional + character following a quantifier. Using
+ simpler notation, called a "possessive quantifier" can be used. This
+ consists of an additional + character following a quantifier. Using
this notation, the previous example can be rewritten as
\d++foo
@@ -6437,45 +6434,45 @@ ATOMIC GROUPING AND POSSESSIVE QUANTIFIERS
(abc|xyz){2,3}+
- Possessive quantifiers are always greedy; the setting of the
+ Possessive quantifiers are always greedy; the setting of the
PCRE_UNGREEDY option is ignored. They are a convenient notation for the
- simpler forms of atomic group. However, there is no difference in the
- meaning of a possessive quantifier and the equivalent atomic group,
- though there may be a performance difference; possessive quantifiers
+ simpler forms of atomic group. However, there is no difference in the
+ meaning of a possessive quantifier and the equivalent atomic group,
+ though there may be a performance difference; possessive quantifiers
should be slightly faster.
- The possessive quantifier syntax is an extension to the Perl 5.8 syn-
- tax. Jeffrey Friedl originated the idea (and the name) in the first
+ The possessive quantifier syntax is an extension to the Perl 5.8 syn-
+ tax. Jeffrey Friedl originated the idea (and the name) in the first
edition of his book. Mike McCloskey liked it, so implemented it when he
- built Sun's Java package, and PCRE copied it from there. It ultimately
+ built Sun's Java package, and PCRE copied it from there. It ultimately
found its way into Perl at release 5.10.
PCRE has an optimization that automatically "possessifies" certain sim-
- ple pattern constructs. For example, the sequence A+B is treated as
- A++B because there is no point in backtracking into a sequence of A's
+ ple pattern constructs. For example, the sequence A+B is treated as
+ A++B because there is no point in backtracking into a sequence of A's
when B must follow.
- When a pattern contains an unlimited repeat inside a subpattern that
- can itself be repeated an unlimited number of times, the use of an
- atomic group is the only way to avoid some failing matches taking a
+ When a pattern contains an unlimited repeat inside a subpattern that
+ can itself be repeated an unlimited number of times, the use of an
+ atomic group is the only way to avoid some failing matches taking a
very long time indeed. The pattern
(\D+|<\d+>)*[!?]
- matches an unlimited number of substrings that either consist of non-
- digits, or digits enclosed in <>, followed by either ! or ?. When it
+ matches an unlimited number of substrings that either consist of non-
+ digits, or digits enclosed in <>, followed by either ! or ?. When it
matches, it runs quickly. However, if it is applied to
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- it takes a long time before reporting failure. This is because the
- string can be divided between the internal \D+ repeat and the external
- * repeat in a large number of ways, and all have to be tried. (The
- example uses [!?] rather than a single character at the end, because
- both PCRE and Perl have an optimization that allows for fast failure
- when a single character is used. They remember the last single charac-
- ter that is required for a match, and fail early if it is not present
- in the string.) If the pattern is changed so that it uses an atomic
+ it takes a long time before reporting failure. This is because the
+ string can be divided between the internal \D+ repeat and the external
+ * repeat in a large number of ways, and all have to be tried. (The
+ example uses [!?] rather than a single character at the end, because
+ both PCRE and Perl have an optimization that allows for fast failure
+ when a single character is used. They remember the last single charac-
+ ter that is required for a match, and fail early if it is not present
+ in the string.) If the pattern is changed so that it uses an atomic
group, like this:
((?>\D+)|<\d+>)*[!?]
@@ -6487,28 +6484,28 @@ BACK REFERENCES
Outside a character class, a backslash followed by a digit greater than
0 (and possibly further digits) is a back reference to a capturing sub-
- pattern earlier (that is, to its left) in the pattern, provided there
+ pattern earlier (that is, to its left) in the pattern, provided there
have been that many previous capturing left parentheses.
However, if the decimal number following the backslash is less than 10,
- it is always taken as a back reference, and causes an error only if
- there are not that many capturing left parentheses in the entire pat-
- tern. In other words, the parentheses that are referenced need not be
- to the left of the reference for numbers less than 10. A "forward back
- reference" of this type can make sense when a repetition is involved
- and the subpattern to the right has participated in an earlier itera-
+ it is always taken as a back reference, and causes an error only if
+ there are not that many capturing left parentheses in the entire pat-
+ tern. In other words, the parentheses that are referenced need not be
+ to the left of the reference for numbers less than 10. A "forward back
+ reference" of this type can make sense when a repetition is involved
+ and the subpattern to the right has participated in an earlier itera-
tion.
- It is not possible to have a numerical "forward back reference" to a
- subpattern whose number is 10 or more using this syntax because a
- sequence such as \50 is interpreted as a character defined in octal.
+ It is not possible to have a numerical "forward back reference" to a
+ subpattern whose number is 10 or more using this syntax because a
+ sequence such as \50 is interpreted as a character defined in octal.
See the subsection entitled "Non-printing characters" above for further
- details of the handling of digits following a backslash. There is no
- such problem when named parentheses are used. A back reference to any
+ details of the handling of digits following a backslash. There is no
+ such problem when named parentheses are used. A back reference to any
subpattern is possible using named parentheses (see below).
- Another way of avoiding the ambiguity inherent in the use of digits
- following a backslash is to use the \g escape sequence. This escape
+ Another way of avoiding the ambiguity inherent in the use of digits
+ following a backslash is to use the \g escape sequence. This escape
must be followed by an unsigned number or a negative number, optionally
enclosed in braces. These examples are all identical:
@@ -6516,7 +6513,7 @@ BACK REFERENCES
(ring), \g1
(ring), \g{1}
- An unsigned number specifies an absolute reference without the ambigu-
+ An unsigned number specifies an absolute reference without the ambigu-
ity that is present in the older syntax. It is also useful when literal
digits follow the reference. A negative number is a relative reference.
Consider this example:
@@ -6525,33 +6522,33 @@ BACK REFERENCES
The sequence \g{-1} is a reference to the most recently started captur-
ing subpattern before \g, that is, is it equivalent to \2 in this exam-
- ple. Similarly, \g{-2} would be equivalent to \1. The use of relative
- references can be helpful in long patterns, and also in patterns that
- are created by joining together fragments that contain references
+ ple. Similarly, \g{-2} would be equivalent to \1. The use of relative
+ references can be helpful in long patterns, and also in patterns that
+ are created by joining together fragments that contain references
within themselves.
- A back reference matches whatever actually matched the capturing sub-
- pattern in the current subject string, rather than anything matching
+ A back reference matches whatever actually matched the capturing sub-
+ pattern in the current subject string, rather than anything matching
the subpattern itself (see "Subpatterns as subroutines" below for a way
of doing that). So the pattern
(sens|respons)e and \1ibility
- matches "sense and sensibility" and "response and responsibility", but
- not "sense and responsibility". If caseful matching is in force at the
- time of the back reference, the case of letters is relevant. For exam-
+ matches "sense and sensibility" and "response and responsibility", but
+ not "sense and responsibility". If caseful matching is in force at the
+ time of the back reference, the case of letters is relevant. For exam-
ple,
((?i)rah)\s+\1
- matches "rah rah" and "RAH RAH", but not "RAH rah", even though the
+ matches "rah rah" and "RAH RAH", but not "RAH rah", even though the
original capturing subpattern is matched caselessly.
- There are several different ways of writing back references to named
- subpatterns. The .NET syntax \k{name} and the Perl syntax \k<name> or
- \k'name' are supported, as is the Python syntax (?P=name). Perl 5.10's
+ There are several different ways of writing back references to named
+ subpatterns. The .NET syntax \k{name} and the Perl syntax \k<name> or
+ \k'name' are supported, as is the Python syntax (?P=name). Perl 5.10's
unified back reference syntax, in which \g can be used for both numeric
- and named references, is also supported. We could rewrite the above
+ and named references, is also supported. We could rewrite the above
example in any of the following ways:
(?<p1>(?i)rah)\s+\k<p1>
@@ -6559,84 +6556,92 @@ BACK REFERENCES
(?P<p1>(?i)rah)\s+(?P=p1)
(?<p1>(?i)rah)\s+\g{p1}
- A subpattern that is referenced by name may appear in the pattern
+ A subpattern that is referenced by name may appear in the pattern
before or after the reference.
- There may be more than one back reference to the same subpattern. If a
- subpattern has not actually been used in a particular match, any back
+ There may be more than one back reference to the same subpattern. If a
+ subpattern has not actually been used in a particular match, any back
references to it always fail by default. For example, the pattern
(a|(bc))\2
- always fails if it starts to match "a" rather than "bc". However, if
+ always fails if it starts to match "a" rather than "bc". However, if
the PCRE_JAVASCRIPT_COMPAT option is set at compile time, a back refer-
ence to an unset value matches an empty string.
- Because there may be many capturing parentheses in a pattern, all dig-
- its following a backslash are taken as part of a potential back refer-
- ence number. If the pattern continues with a digit character, some
- delimiter must be used to terminate the back reference. If the
- PCRE_EXTENDED option is set, this can be white space. Otherwise, the
+ Because there may be many capturing parentheses in a pattern, all dig-
+ its following a backslash are taken as part of a potential back refer-
+ ence number. If the pattern continues with a digit character, some
+ delimiter must be used to terminate the back reference. If the
+ PCRE_EXTENDED option is set, this can be white space. Otherwise, the
\g{ syntax or an empty comment (see "Comments" below) can be used.
Recursive back references
- A back reference that occurs inside the parentheses to which it refers
- fails when the subpattern is first used, so, for example, (a\1) never
- matches. However, such references can be useful inside repeated sub-
+ A back reference that occurs inside the parentheses to which it refers
+ fails when the subpattern is first used, so, for example, (a\1) never
+ matches. However, such references can be useful inside repeated sub-
patterns. For example, the pattern
(a|b\1)+
matches any number of "a"s and also "aba", "ababbaa" etc. At each iter-
- ation of the subpattern, the back reference matches the character
- string corresponding to the previous iteration. In order for this to
- work, the pattern must be such that the first iteration does not need
- to match the back reference. This can be done using alternation, as in
+ ation of the subpattern, the back reference matches the character
+ string corresponding to the previous iteration. In order for this to
+ work, the pattern must be such that the first iteration does not need
+ to match the back reference. This can be done using alternation, as in
the example above, or by a quantifier with a minimum of zero.
- Back references of this type cause the group that they reference to be
- treated as an atomic group. Once the whole group has been matched, a
- subsequent matching failure cannot cause backtracking into the middle
+ Back references of this type cause the group that they reference to be
+ treated as an atomic group. Once the whole group has been matched, a
+ subsequent matching failure cannot cause backtracking into the middle
of the group.
ASSERTIONS
- An assertion is a test on the characters following or preceding the
- current matching point that does not actually consume any characters.
- The simple assertions coded as \b, \B, \A, \G, \Z, \z, ^ and $ are
+ An assertion is a test on the characters following or preceding the
+ current matching point that does not actually consume any characters.
+ The simple assertions coded as \b, \B, \A, \G, \Z, \z, ^ and $ are
described above.
- More complicated assertions are coded as subpatterns. There are two
- kinds: those that look ahead of the current position in the subject
- string, and those that look behind it. An assertion subpattern is
- matched in the normal way, except that it does not cause the current
+ More complicated assertions are coded as subpatterns. There are two
+ kinds: those that look ahead of the current position in the subject
+ string, and those that look behind it. An assertion subpattern is
+ matched in the normal way, except that it does not cause the current
matching position to be changed.
- Assertion subpatterns are not capturing subpatterns. If such an asser-
- tion contains capturing subpatterns within it, these are counted for
- the purposes of numbering the capturing subpatterns in the whole pat-
- tern. However, substring capturing is carried out only for positive
+ Assertion subpatterns are not capturing subpatterns. If such an asser-
+ tion contains capturing subpatterns within it, these are counted for
+ the purposes of numbering the capturing subpatterns in the whole pat-
+ tern. However, substring capturing is carried out only for positive
assertions. (Perl sometimes, but not always, does do capturing in nega-
tive assertions.)
- For compatibility with Perl, assertion subpatterns may be repeated;
- though it makes no sense to assert the same thing several times, the
- side effect of capturing parentheses may occasionally be useful. In
+ WARNING: If a positive assertion containing one or more capturing sub-
+ patterns succeeds, but failure to match later in the pattern causes
+ backtracking over this assertion, the captures within the assertion are
+ reset only if no higher numbered captures are already set. This is,
+ unfortunately, a fundamental limitation of the current implementation,
+ and as PCRE1 is now in maintenance-only status, it is unlikely ever to
+ change.
+
+ For compatibility with Perl, assertion subpatterns may be repeated;
+ though it makes no sense to assert the same thing several times, the
+ side effect of capturing parentheses may occasionally be useful. In
practice, there only three cases:
- (1) If the quantifier is {0}, the assertion is never obeyed during
- matching. However, it may contain internal capturing parenthesized
+ (1) If the quantifier is {0}, the assertion is never obeyed during
+ matching. However, it may contain internal capturing parenthesized
groups that are called from elsewhere via the subroutine mechanism.
- (2) If quantifier is {0,n} where n is greater than zero, it is treated
- as if it were {0,1}. At run time, the rest of the pattern match is
+ (2) If quantifier is {0,n} where n is greater than zero, it is treated
+ as if it were {0,1}. At run time, the rest of the pattern match is
tried with and without the assertion, the order depending on the greed-
iness of the quantifier.
- (3) If the minimum repetition is greater than zero, the quantifier is
- ignored. The assertion is obeyed just once when encountered during
+ (3) If the minimum repetition is greater than zero, the quantifier is
+ ignored. The assertion is obeyed just once when encountered during
matching.
Lookahead assertions
@@ -6646,38 +6651,38 @@ ASSERTIONS
\w+(?=;)
- matches a word followed by a semicolon, but does not include the semi-
+ matches a word followed by a semicolon, but does not include the semi-
colon in the match, and
foo(?!bar)
- matches any occurrence of "foo" that is not followed by "bar". Note
+ matches any occurrence of "foo" that is not followed by "bar". Note
that the apparently similar pattern
(?!foo)bar
- does not find an occurrence of "bar" that is preceded by something
- other than "foo"; it finds any occurrence of "bar" whatsoever, because
+ does not find an occurrence of "bar" that is preceded by something
+ other than "foo"; it finds any occurrence of "bar" whatsoever, because
the assertion (?!foo) is always true when the next three characters are
"bar". A lookbehind assertion is needed to achieve the other effect.
If you want to force a matching failure at some point in a pattern, the
- most convenient way to do it is with (?!) because an empty string
- always matches, so an assertion that requires there not to be an empty
+ most convenient way to do it is with (?!) because an empty string
+ always matches, so an assertion that requires there not to be an empty
string must always fail. The backtracking control verb (*FAIL) or (*F)
is a synonym for (?!).
Lookbehind assertions
- Lookbehind assertions start with (?<= for positive assertions and (?<!
+ Lookbehind assertions start with (?<= for positive assertions and (?<!
for negative assertions. For example,
(?<!foo)bar
- does find an occurrence of "bar" that is not preceded by "foo". The
- contents of a lookbehind assertion are restricted such that all the
+ does find an occurrence of "bar" that is not preceded by "foo". The
+ contents of a lookbehind assertion are restricted such that all the
strings it matches must have a fixed length. However, if there are sev-
- eral top-level alternatives, they do not all have to have the same
+ eral top-level alternatives, they do not all have to have the same
fixed length. Thus
(?<=bullock|donkey)
@@ -6686,62 +6691,62 @@ ASSERTIONS
(?<!dogs?|cats?)
- causes an error at compile time. Branches that match different length
- strings are permitted only at the top level of a lookbehind assertion.
+ causes an error at compile time. Branches that match different length
+ strings are permitted only at the top level of a lookbehind assertion.
This is an extension compared with Perl, which requires all branches to
match the same length of string. An assertion such as
(?<=ab(c|de))
- is not permitted, because its single top-level branch can match two
+ is not permitted, because its single top-level branch can match two
different lengths, but it is acceptable to PCRE if rewritten to use two
top-level branches:
(?<=abc|abde)
- In some cases, the escape sequence \K (see above) can be used instead
+ In some cases, the escape sequence \K (see above) can be used instead
of a lookbehind assertion to get round the fixed-length restriction.
- The implementation of lookbehind assertions is, for each alternative,
- to temporarily move the current position back by the fixed length and
+ The implementation of lookbehind assertions is, for each alternative,
+ to temporarily move the current position back by the fixed length and
then try to match. If there are insufficient characters before the cur-
rent position, the assertion fails.
- In a UTF mode, PCRE does not allow the \C escape (which matches a sin-
- gle data unit even in a UTF mode) to appear in lookbehind assertions,
- because it makes it impossible to calculate the length of the lookbe-
- hind. The \X and \R escapes, which can match different numbers of data
+ In a UTF mode, PCRE does not allow the \C escape (which matches a sin-
+ gle data unit even in a UTF mode) to appear in lookbehind assertions,
+ because it makes it impossible to calculate the length of the lookbe-
+ hind. The \X and \R escapes, which can match different numbers of data
units, are also not permitted.
- "Subroutine" calls (see below) such as (?2) or (?&X) are permitted in
- lookbehinds, as long as the subpattern matches a fixed-length string.
+ "Subroutine" calls (see below) such as (?2) or (?&X) are permitted in
+ lookbehinds, as long as the subpattern matches a fixed-length string.
Recursion, however, is not supported.
- Possessive quantifiers can be used in conjunction with lookbehind
+ Possessive quantifiers can be used in conjunction with lookbehind
assertions to specify efficient matching of fixed-length strings at the
end of subject strings. Consider a simple pattern such as
abcd$
- when applied to a long string that does not match. Because matching
+ when applied to a long string that does not match. Because matching
proceeds from left to right, PCRE will look for each "a" in the subject
- and then see if what follows matches the rest of the pattern. If the
+ and then see if what follows matches the rest of the pattern. If the
pattern is specified as
^.*abcd$
- the initial .* matches the entire string at first, but when this fails
+ the initial .* matches the entire string at first, but when this fails
(because there is no following "a"), it backtracks to match all but the
- last character, then all but the last two characters, and so on. Once
- again the search for "a" covers the entire string, from right to left,
+ last character, then all but the last two characters, and so on. Once
+ again the search for "a" covers the entire string, from right to left,
so we are no better off. However, if the pattern is written as
^.*+(?<=abcd)
- there can be no backtracking for the .*+ item; it can match only the
- entire string. The subsequent lookbehind assertion does a single test
- on the last four characters. If it fails, the match fails immediately.
- For long strings, this approach makes a significant difference to the
+ there can be no backtracking for the .*+ item; it can match only the
+ entire string. The subsequent lookbehind assertion does a single test
+ on the last four characters. If it fails, the match fails immediately.
+ For long strings, this approach makes a significant difference to the
processing time.
Using multiple assertions
@@ -6750,18 +6755,18 @@ ASSERTIONS
(?<=\d{3})(?<!999)foo
- matches "foo" preceded by three digits that are not "999". Notice that
- each of the assertions is applied independently at the same point in
- the subject string. First there is a check that the previous three
- characters are all digits, and then there is a check that the same
+ matches "foo" preceded by three digits that are not "999". Notice that
+ each of the assertions is applied independently at the same point in
+ the subject string. First there is a check that the previous three
+ characters are all digits, and then there is a check that the same
three characters are not "999". This pattern does not match "foo" pre-
- ceded by six characters, the first of which are digits and the last
- three of which are not "999". For example, it doesn't match "123abc-
+ ceded by six characters, the first of which are digits and the last
+ three of which are not "999". For example, it doesn't match "123abc-
foo". A pattern to do that is
(?<=\d{3}...)(?<!999)foo
- This time the first assertion looks at the preceding six characters,
+ This time the first assertion looks at the preceding six characters,
checking that the first three are digits, and then the second assertion
checks that the preceding three characters are not "999".
@@ -6769,29 +6774,29 @@ ASSERTIONS
(?<=(?<!foo)bar)baz
- matches an occurrence of "baz" that is preceded by "bar" which in turn
+ matches an occurrence of "baz" that is preceded by "bar" which in turn
is not preceded by "foo", while
(?<=\d{3}(?!999)...)foo
- is another pattern that matches "foo" preceded by three digits and any
+ is another pattern that matches "foo" preceded by three digits and any
three characters that are not "999".
CONDITIONAL SUBPATTERNS
- It is possible to cause the matching process to obey a subpattern con-
- ditionally or to choose between two alternative subpatterns, depending
- on the result of an assertion, or whether a specific capturing subpat-
- tern has already been matched. The two possible forms of conditional
+ It is possible to cause the matching process to obey a subpattern con-
+ ditionally or to choose between two alternative subpatterns, depending
+ on the result of an assertion, or whether a specific capturing subpat-
+ tern has already been matched. The two possible forms of conditional
subpattern are:
(?(condition)yes-pattern)
(?(condition)yes-pattern|no-pattern)
- If the condition is satisfied, the yes-pattern is used; otherwise the
- no-pattern (if present) is used. If there are more than two alterna-
- tives in the subpattern, a compile-time error occurs. Each of the two
+ If the condition is satisfied, the yes-pattern is used; otherwise the
+ no-pattern (if present) is used. If there are more than two alterna-
+ tives in the subpattern, a compile-time error occurs. Each of the two
alternatives may itself contain nested subpatterns of any form, includ-
ing conditional subpatterns; the restriction to two alternatives
applies only at the level of the condition. This pattern fragment is an
@@ -6800,68 +6805,68 @@ CONDITIONAL SUBPATTERNS
(?(1) (A|B|C) | (D | (?(2)E|F) | E) )
- There are four kinds of condition: references to subpatterns, refer-
+ There are four kinds of condition: references to subpatterns, refer-
ences to recursion, a pseudo-condition called DEFINE, and assertions.
Checking for a used subpattern by number
- If the text between the parentheses consists of a sequence of digits,
+ If the text between the parentheses consists of a sequence of digits,
the condition is true if a capturing subpattern of that number has pre-
- viously matched. If there is more than one capturing subpattern with
- the same number (see the earlier section about duplicate subpattern
- numbers), the condition is true if any of them have matched. An alter-
- native notation is to precede the digits with a plus or minus sign. In
- this case, the subpattern number is relative rather than absolute. The
- most recently opened parentheses can be referenced by (?(-1), the next
- most recent by (?(-2), and so on. Inside loops it can also make sense
+ viously matched. If there is more than one capturing subpattern with
+ the same number (see the earlier section about duplicate subpattern
+ numbers), the condition is true if any of them have matched. An alter-
+ native notation is to precede the digits with a plus or minus sign. In
+ this case, the subpattern number is relative rather than absolute. The
+ most recently opened parentheses can be referenced by (?(-1), the next
+ most recent by (?(-2), and so on. Inside loops it can also make sense
to refer to subsequent groups. The next parentheses to be opened can be
- referenced as (?(+1), and so on. (The value zero in any of these forms
+ referenced as (?(+1), and so on. (The value zero in any of these forms
is not used; it provokes a compile-time error.)
- Consider the following pattern, which contains non-significant white
+ Consider the following pattern, which contains non-significant white
space to make it more readable (assume the PCRE_EXTENDED option) and to
divide it into three parts for ease of discussion:
( \( )? [^()]+ (?(1) \) )
- The first part matches an optional opening parenthesis, and if that
+ The first part matches an optional opening parenthesis, and if that
character is present, sets it as the first captured substring. The sec-
- ond part matches one or more characters that are not parentheses. The
- third part is a conditional subpattern that tests whether or not the
- first set of parentheses matched. If they did, that is, if subject
- started with an opening parenthesis, the condition is true, and so the
- yes-pattern is executed and a closing parenthesis is required. Other-
- wise, since no-pattern is not present, the subpattern matches nothing.
- In other words, this pattern matches a sequence of non-parentheses,
+ ond part matches one or more characters that are not parentheses. The
+ third part is a conditional subpattern that tests whether or not the
+ first set of parentheses matched. If they did, that is, if subject
+ started with an opening parenthesis, the condition is true, and so the
+ yes-pattern is executed and a closing parenthesis is required. Other-
+ wise, since no-pattern is not present, the subpattern matches nothing.
+ In other words, this pattern matches a sequence of non-parentheses,
optionally enclosed in parentheses.
- If you were embedding this pattern in a larger one, you could use a
+ If you were embedding this pattern in a larger one, you could use a
relative reference:
...other stuff... ( \( )? [^()]+ (?(-1) \) ) ...
- This makes the fragment independent of the parentheses in the larger
+ This makes the fragment independent of the parentheses in the larger
pattern.
Checking for a used subpattern by name
- Perl uses the syntax (?(<name>)...) or (?('name')...) to test for a
- used subpattern by name. For compatibility with earlier versions of
- PCRE, which had this facility before Perl, the syntax (?(name)...) is
+ Perl uses the syntax (?(<name>)...) or (?('name')...) to test for a
+ used subpattern by name. For compatibility with earlier versions of
+ PCRE, which had this facility before Perl, the syntax (?(name)...) is
also recognized.
Rewriting the above example to use a named subpattern gives this:
(?<OPEN> \( )? [^()]+ (?(<OPEN>) \) )
- If the name used in a condition of this kind is a duplicate, the test
- is applied to all subpatterns of the same name, and is true if any one
+ If the name used in a condition of this kind is a duplicate, the test
+ is applied to all subpatterns of the same name, and is true if any one
of them has matched.
Checking for pattern recursion
If the condition is the string (R), and there is no subpattern with the
- name R, the condition is true if a recursive call to the whole pattern
+ name R, the condition is true if a recursive call to the whole pattern
or any subpattern has been made. If digits or a name preceded by amper-
sand follow the letter R, for example:
@@ -6869,51 +6874,51 @@ CONDITIONAL SUBPATTERNS
the condition is true if the most recent recursion is into a subpattern
whose number or name is given. This condition does not check the entire
- recursion stack. If the name used in a condition of this kind is a
+ recursion stack. If the name used in a condition of this kind is a
duplicate, the test is applied to all subpatterns of the same name, and
is true if any one of them is the most recent recursion.
- At "top level", all these recursion test conditions are false. The
+ At "top level", all these recursion test conditions are false. The
syntax for recursive patterns is described below.
Defining subpatterns for use by reference only
- If the condition is the string (DEFINE), and there is no subpattern
- with the name DEFINE, the condition is always false. In this case,
- there may be only one alternative in the subpattern. It is always
- skipped if control reaches this point in the pattern; the idea of
- DEFINE is that it can be used to define subroutines that can be refer-
- enced from elsewhere. (The use of subroutines is described below.) For
- example, a pattern to match an IPv4 address such as "192.168.23.245"
+ If the condition is the string (DEFINE), and there is no subpattern
+ with the name DEFINE, the condition is always false. In this case,
+ there may be only one alternative in the subpattern. It is always
+ skipped if control reaches this point in the pattern; the idea of
+ DEFINE is that it can be used to define subroutines that can be refer-
+ enced from elsewhere. (The use of subroutines is described below.) For
+ example, a pattern to match an IPv4 address such as "192.168.23.245"
could be written like this (ignore white space and line breaks):
(?(DEFINE) (?<byte> 2[0-4]\d | 25[0-5] | 1\d\d | [1-9]?\d) )
\b (?&byte) (\.(?&byte)){3} \b
- The first part of the pattern is a DEFINE group inside which a another
- group named "byte" is defined. This matches an individual component of
- an IPv4 address (a number less than 256). When matching takes place,
- this part of the pattern is skipped because DEFINE acts like a false
- condition. The rest of the pattern uses references to the named group
- to match the four dot-separated components of an IPv4 address, insist-
+ The first part of the pattern is a DEFINE group inside which a another
+ group named "byte" is defined. This matches an individual component of
+ an IPv4 address (a number less than 256). When matching takes place,
+ this part of the pattern is skipped because DEFINE acts like a false
+ condition. The rest of the pattern uses references to the named group
+ to match the four dot-separated components of an IPv4 address, insist-
ing on a word boundary at each end.
Assertion conditions
- If the condition is not in any of the above formats, it must be an
- assertion. This may be a positive or negative lookahead or lookbehind
- assertion. Consider this pattern, again containing non-significant
+ If the condition is not in any of the above formats, it must be an
+ assertion. This may be a positive or negative lookahead or lookbehind
+ assertion. Consider this pattern, again containing non-significant
white space, and with the two alternatives on the second line:
(?(?=[^a-z]*[a-z])
\d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} )
- The condition is a positive lookahead assertion that matches an
- optional sequence of non-letters followed by a letter. In other words,
- it tests for the presence of at least one letter in the subject. If a
- letter is found, the subject is matched against the first alternative;
- otherwise it is matched against the second. This pattern matches
- strings in one of the two forms dd-aaa-dd or dd-dd-dd, where aaa are
+ The condition is a positive lookahead assertion that matches an
+ optional sequence of non-letters followed by a letter. In other words,
+ it tests for the presence of at least one letter in the subject. If a
+ letter is found, the subject is matched against the first alternative;
+ otherwise it is matched against the second. This pattern matches
+ strings in one of the two forms dd-aaa-dd or dd-dd-dd, where aaa are
letters and dd are digits.
@@ -6922,41 +6927,41 @@ COMMENTS
There are two ways of including comments in patterns that are processed
by PCRE. In both cases, the start of the comment must not be in a char-
acter class, nor in the middle of any other sequence of related charac-
- ters such as (?: or a subpattern name or number. The characters that
+ ters such as (?: or a subpattern name or number. The characters that
make up a comment play no part in the pattern matching.
- The sequence (?# marks the start of a comment that continues up to the
- next closing parenthesis. Nested parentheses are not permitted. If the
+ The sequence (?# marks the start of a comment that continues up to the
+ next closing parenthesis. Nested parentheses are not permitted. If the
PCRE_EXTENDED option is set, an unescaped # character also introduces a
- comment, which in this case continues to immediately after the next
- newline character or character sequence in the pattern. Which charac-
+ comment, which in this case continues to immediately after the next
+ newline character or character sequence in the pattern. Which charac-
ters are interpreted as newlines is controlled by the options passed to
- a compiling function or by a special sequence at the start of the pat-
+ a compiling function or by a special sequence at the start of the pat-
tern, as described in the section entitled "Newline conventions" above.
Note that the end of this type of comment is a literal newline sequence
- in the pattern; escape sequences that happen to represent a newline do
- not count. For example, consider this pattern when PCRE_EXTENDED is
+ in the pattern; escape sequences that happen to represent a newline do
+ not count. For example, consider this pattern when PCRE_EXTENDED is
set, and the default newline convention is in force:
abc #comment \n still comment
- On encountering the # character, pcre_compile() skips along, looking
- for a newline in the pattern. The sequence \n is still literal at this
- stage, so it does not terminate the comment. Only an actual character
+ On encountering the # character, pcre_compile() skips along, looking
+ for a newline in the pattern. The sequence \n is still literal at this
+ stage, so it does not terminate the comment. Only an actual character
with the code value 0x0a (the default newline) does so.
RECURSIVE PATTERNS
- Consider the problem of matching a string in parentheses, allowing for
- unlimited nested parentheses. Without the use of recursion, the best
- that can be done is to use a pattern that matches up to some fixed
- depth of nesting. It is not possible to handle an arbitrary nesting
+ Consider the problem of matching a string in parentheses, allowing for
+ unlimited nested parentheses. Without the use of recursion, the best
+ that can be done is to use a pattern that matches up to some fixed
+ depth of nesting. It is not possible to handle an arbitrary nesting
depth.
For some time, Perl has provided a facility that allows regular expres-
- sions to recurse (amongst other things). It does this by interpolating
- Perl code in the expression at run time, and the code can refer to the
+ sions to recurse (amongst other things). It does this by interpolating
+ Perl code in the expression at run time, and the code can refer to the
expression itself. A Perl pattern using code interpolation to solve the
parentheses problem can be created like this:
@@ -6966,201 +6971,201 @@ RECURSIVE PATTERNS
refers recursively to the pattern in which it appears.
Obviously, PCRE cannot support the interpolation of Perl code. Instead,
- it supports special syntax for recursion of the entire pattern, and
- also for individual subpattern recursion. After its introduction in
- PCRE and Python, this kind of recursion was subsequently introduced
+ it supports special syntax for recursion of the entire pattern, and
+ also for individual subpattern recursion. After its introduction in
+ PCRE and Python, this kind of recursion was subsequently introduced
into Perl at release 5.10.
- A special item that consists of (? followed by a number greater than
- zero and a closing parenthesis is a recursive subroutine call of the
- subpattern of the given number, provided that it occurs inside that
- subpattern. (If not, it is a non-recursive subroutine call, which is
- described in the next section.) The special item (?R) or (?0) is a
+ A special item that consists of (? followed by a number greater than
+ zero and a closing parenthesis is a recursive subroutine call of the
+ subpattern of the given number, provided that it occurs inside that
+ subpattern. (If not, it is a non-recursive subroutine call, which is
+ described in the next section.) The special item (?R) or (?0) is a
recursive call of the entire regular expression.
- This PCRE pattern solves the nested parentheses problem (assume the
+ This PCRE pattern solves the nested parentheses problem (assume the
PCRE_EXTENDED option is set so that white space is ignored):
\( ( [^()]++ | (?R) )* \)
- First it matches an opening parenthesis. Then it matches any number of
- substrings which can either be a sequence of non-parentheses, or a
- recursive match of the pattern itself (that is, a correctly parenthe-
+ First it matches an opening parenthesis. Then it matches any number of
+ substrings which can either be a sequence of non-parentheses, or a
+ recursive match of the pattern itself (that is, a correctly parenthe-
sized substring). Finally there is a closing parenthesis. Note the use
of a possessive quantifier to avoid backtracking into sequences of non-
parentheses.
- If this were part of a larger pattern, you would not want to recurse
+ If this were part of a larger pattern, you would not want to recurse
the entire pattern, so instead you could use this:
( \( ( [^()]++ | (?1) )* \) )
- We have put the pattern into parentheses, and caused the recursion to
+ We have put the pattern into parentheses, and caused the recursion to
refer to them instead of the whole pattern.
- In a larger pattern, keeping track of parenthesis numbers can be
- tricky. This is made easier by the use of relative references. Instead
+ In a larger pattern, keeping track of parenthesis numbers can be
+ tricky. This is made easier by the use of relative references. Instead
of (?1) in the pattern above you can write (?-2) to refer to the second
- most recently opened parentheses preceding the recursion. In other
- words, a negative number counts capturing parentheses leftwards from
+ most recently opened parentheses preceding the recursion. In other
+ words, a negative number counts capturing parentheses leftwards from
the point at which it is encountered.
- It is also possible to refer to subsequently opened parentheses, by
- writing references such as (?+2). However, these cannot be recursive
- because the reference is not inside the parentheses that are refer-
- enced. They are always non-recursive subroutine calls, as described in
+ It is also possible to refer to subsequently opened parentheses, by
+ writing references such as (?+2). However, these cannot be recursive
+ because the reference is not inside the parentheses that are refer-
+ enced. They are always non-recursive subroutine calls, as described in
the next section.
- An alternative approach is to use named parentheses instead. The Perl
- syntax for this is (?&name); PCRE's earlier syntax (?P>name) is also
+ An alternative approach is to use named parentheses instead. The Perl
+ syntax for this is (?&name); PCRE's earlier syntax (?P>name) is also
supported. We could rewrite the above example as follows:
(?<pn> \( ( [^()]++ | (?&pn) )* \) )
- If there is more than one subpattern with the same name, the earliest
+ If there is more than one subpattern with the same name, the earliest
one is used.
- This particular example pattern that we have been looking at contains
+ This particular example pattern that we have been looking at contains
nested unlimited repeats, and so the use of a possessive quantifier for
matching strings of non-parentheses is important when applying the pat-
- tern to strings that do not match. For example, when this pattern is
+ tern to strings that do not match. For example, when this pattern is
applied to
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()
- it yields "no match" quickly. However, if a possessive quantifier is
- not used, the match runs for a very long time indeed because there are
- so many different ways the + and * repeats can carve up the subject,
+ it yields "no match" quickly. However, if a possessive quantifier is
+ not used, the match runs for a very long time indeed because there are
+ so many different ways the + and * repeats can carve up the subject,
and all have to be tested before failure can be reported.
- At the end of a match, the values of capturing parentheses are those
- from the outermost level. If you want to obtain intermediate values, a
- callout function can be used (see below and the pcrecallout documenta-
+ At the end of a match, the values of capturing parentheses are those
+ from the outermost level. If you want to obtain intermediate values, a
+ callout function can be used (see below and the pcrecallout documenta-
tion). If the pattern above is matched against
(ab(cd)ef)
- the value for the inner capturing parentheses (numbered 2) is "ef",
- which is the last value taken on at the top level. If a capturing sub-
- pattern is not matched at the top level, its final captured value is
- unset, even if it was (temporarily) set at a deeper level during the
+ the value for the inner capturing parentheses (numbered 2) is "ef",
+ which is the last value taken on at the top level. If a capturing sub-
+ pattern is not matched at the top level, its final captured value is
+ unset, even if it was (temporarily) set at a deeper level during the
matching process.
- If there are more than 15 capturing parentheses in a pattern, PCRE has
- to obtain extra memory to store data during a recursion, which it does
+ If there are more than 15 capturing parentheses in a pattern, PCRE has
+ to obtain extra memory to store data during a recursion, which it does
by using pcre_malloc, freeing it via pcre_free afterwards. If no memory
can be obtained, the match fails with the PCRE_ERROR_NOMEMORY error.
- Do not confuse the (?R) item with the condition (R), which tests for
- recursion. Consider this pattern, which matches text in angle brack-
- ets, allowing for arbitrary nesting. Only digits are allowed in nested
- brackets (that is, when recursing), whereas any characters are permit-
+ Do not confuse the (?R) item with the condition (R), which tests for
+ recursion. Consider this pattern, which matches text in angle brack-
+ ets, allowing for arbitrary nesting. Only digits are allowed in nested
+ brackets (that is, when recursing), whereas any characters are permit-
ted at the outer level.
< (?: (?(R) \d++ | [^<>]*+) | (?R)) * >
- In this pattern, (?(R) is the start of a conditional subpattern, with
- two different alternatives for the recursive and non-recursive cases.
+ In this pattern, (?(R) is the start of a conditional subpattern, with
+ two different alternatives for the recursive and non-recursive cases.
The (?R) item is the actual recursive call.
Differences in recursion processing between PCRE and Perl
- Recursion processing in PCRE differs from Perl in two important ways.
- In PCRE (like Python, but unlike Perl), a recursive subpattern call is
+ Recursion processing in PCRE differs from Perl in two important ways.
+ In PCRE (like Python, but unlike Perl), a recursive subpattern call is
always treated as an atomic group. That is, once it has matched some of
the subject string, it is never re-entered, even if it contains untried
- alternatives and there is a subsequent matching failure. This can be
- illustrated by the following pattern, which purports to match a palin-
- dromic string that contains an odd number of characters (for example,
+ alternatives and there is a subsequent matching failure. This can be
+ illustrated by the following pattern, which purports to match a palin-
+ dromic string that contains an odd number of characters (for example,
"a", "aba", "abcba", "abcdcba"):
^(.|(.)(?1)\2)$
The idea is that it either matches a single character, or two identical
- characters surrounding a sub-palindrome. In Perl, this pattern works;
- in PCRE it does not if the pattern is longer than three characters.
+ characters surrounding a sub-palindrome. In Perl, this pattern works;
+ in PCRE it does not if the pattern is longer than three characters.
Consider the subject string "abcba":
- At the top level, the first character is matched, but as it is not at
+ At the top level, the first character is matched, but as it is not at
the end of the string, the first alternative fails; the second alterna-
tive is taken and the recursion kicks in. The recursive call to subpat-
- tern 1 successfully matches the next character ("b"). (Note that the
+ tern 1 successfully matches the next character ("b"). (Note that the
beginning and end of line tests are not part of the recursion).
- Back at the top level, the next character ("c") is compared with what
- subpattern 2 matched, which was "a". This fails. Because the recursion
- is treated as an atomic group, there are now no backtracking points,
- and so the entire match fails. (Perl is able, at this point, to re-
- enter the recursion and try the second alternative.) However, if the
+ Back at the top level, the next character ("c") is compared with what
+ subpattern 2 matched, which was "a". This fails. Because the recursion
+ is treated as an atomic group, there are now no backtracking points,
+ and so the entire match fails. (Perl is able, at this point, to re-
+ enter the recursion and try the second alternative.) However, if the
pattern is written with the alternatives in the other order, things are
different:
^((.)(?1)\2|.)$
- This time, the recursing alternative is tried first, and continues to
- recurse until it runs out of characters, at which point the recursion
- fails. But this time we do have another alternative to try at the
- higher level. That is the big difference: in the previous case the
+ This time, the recursing alternative is tried first, and continues to
+ recurse until it runs out of characters, at which point the recursion
+ fails. But this time we do have another alternative to try at the
+ higher level. That is the big difference: in the previous case the
remaining alternative is at a deeper recursion level, which PCRE cannot
use.
- To change the pattern so that it matches all palindromic strings, not
- just those with an odd number of characters, it is tempting to change
+ To change the pattern so that it matches all palindromic strings, not
+ just those with an odd number of characters, it is tempting to change
the pattern to this:
^((.)(?1)\2|.?)$
- Again, this works in Perl, but not in PCRE, and for the same reason.
- When a deeper recursion has matched a single character, it cannot be
- entered again in order to match an empty string. The solution is to
- separate the two cases, and write out the odd and even cases as alter-
+ Again, this works in Perl, but not in PCRE, and for the same reason.
+ When a deeper recursion has matched a single character, it cannot be
+ entered again in order to match an empty string. The solution is to
+ separate the two cases, and write out the odd and even cases as alter-
natives at the higher level:
^(?:((.)(?1)\2|)|((.)(?3)\4|.))
- If you want to match typical palindromic phrases, the pattern has to
+ If you want to match typical palindromic phrases, the pattern has to
ignore all non-word characters, which can be done like this:
^\W*+(?:((.)\W*+(?1)\W*+\2|)|((.)\W*+(?3)\W*+\4|\W*+.\W*+))\W*+$
If run with the PCRE_CASELESS option, this pattern matches phrases such
as "A man, a plan, a canal: Panama!" and it works well in both PCRE and
- Perl. Note the use of the possessive quantifier *+ to avoid backtrack-
- ing into sequences of non-word characters. Without this, PCRE takes a
- great deal longer (ten times or more) to match typical phrases, and
+ Perl. Note the use of the possessive quantifier *+ to avoid backtrack-
+ ing into sequences of non-word characters. Without this, PCRE takes a
+ great deal longer (ten times or more) to match typical phrases, and
Perl takes so long that you think it has gone into a loop.
- WARNING: The palindrome-matching patterns above work only if the sub-
- ject string does not start with a palindrome that is shorter than the
- entire string. For example, although "abcba" is correctly matched, if
- the subject is "ababa", PCRE finds the palindrome "aba" at the start,
- then fails at top level because the end of the string does not follow.
- Once again, it cannot jump back into the recursion to try other alter-
+ WARNING: The palindrome-matching patterns above work only if the sub-
+ ject string does not start with a palindrome that is shorter than the
+ entire string. For example, although "abcba" is correctly matched, if
+ the subject is "ababa", PCRE finds the palindrome "aba" at the start,
+ then fails at top level because the end of the string does not follow.
+ Once again, it cannot jump back into the recursion to try other alter-
natives, so the entire match fails.
- The second way in which PCRE and Perl differ in their recursion pro-
- cessing is in the handling of captured values. In Perl, when a subpat-
- tern is called recursively or as a subpattern (see the next section),
- it has no access to any values that were captured outside the recur-
- sion, whereas in PCRE these values can be referenced. Consider this
+ The second way in which PCRE and Perl differ in their recursion pro-
+ cessing is in the handling of captured values. In Perl, when a subpat-
+ tern is called recursively or as a subpattern (see the next section),
+ it has no access to any values that were captured outside the recur-
+ sion, whereas in PCRE these values can be referenced. Consider this
pattern:
^(.)(\1|a(?2))
- In PCRE, this pattern matches "bab". The first capturing parentheses
- match "b", then in the second group, when the back reference \1 fails
- to match "b", the second alternative matches "a" and then recurses. In
- the recursion, \1 does now match "b" and so the whole match succeeds.
- In Perl, the pattern fails to match because inside the recursive call
+ In PCRE, this pattern matches "bab". The first capturing parentheses
+ match "b", then in the second group, when the back reference \1 fails
+ to match "b", the second alternative matches "a" and then recurses. In
+ the recursion, \1 does now match "b" and so the whole match succeeds.
+ In Perl, the pattern fails to match because inside the recursive call
\1 cannot access the externally set value.
SUBPATTERNS AS SUBROUTINES
- If the syntax for a recursive subpattern call (either by number or by
- name) is used outside the parentheses to which it refers, it operates
- like a subroutine in a programming language. The called subpattern may
- be defined before or after the reference. A numbered reference can be
+ If the syntax for a recursive subpattern call (either by number or by
+ name) is used outside the parentheses to which it refers, it operates
+ like a subroutine in a programming language. The called subpattern may
+ be defined before or after the reference. A numbered reference can be
absolute or relative, as in these examples:
(...(absolute)...)...(?2)...
@@ -7171,79 +7176,79 @@ SUBPATTERNS AS SUBROUTINES
(sens|respons)e and \1ibility
- matches "sense and sensibility" and "response and responsibility", but
+ matches "sense and sensibility" and "response and responsibility", but
not "sense and responsibility". If instead the pattern
(sens|respons)e and (?1)ibility
- is used, it does match "sense and responsibility" as well as the other
- two strings. Another example is given in the discussion of DEFINE
+ is used, it does match "sense and responsibility" as well as the other
+ two strings. Another example is given in the discussion of DEFINE
above.
- All subroutine calls, whether recursive or not, are always treated as
- atomic groups. That is, once a subroutine has matched some of the sub-
+ All subroutine calls, whether recursive or not, are always treated as
+ atomic groups. That is, once a subroutine has matched some of the sub-
ject string, it is never re-entered, even if it contains untried alter-
- natives and there is a subsequent matching failure. Any capturing
- parentheses that are set during the subroutine call revert to their
+ natives and there is a subsequent matching failure. Any capturing
+ parentheses that are set during the subroutine call revert to their
previous values afterwards.
- Processing options such as case-independence are fixed when a subpat-
- tern is defined, so if it is used as a subroutine, such options cannot
+ Processing options such as case-independence are fixed when a subpat-
+ tern is defined, so if it is used as a subroutine, such options cannot
be changed for different calls. For example, consider this pattern:
(abc)(?i:(?-1))
- It matches "abcabc". It does not match "abcABC" because the change of
+ It matches "abcabc". It does not match "abcABC" because the change of
processing option does not affect the called subpattern.
ONIGURUMA SUBROUTINE SYNTAX
- For compatibility with Oniguruma, the non-Perl syntax \g followed by a
+ For compatibility with Oniguruma, the non-Perl syntax \g followed by a
name or a number enclosed either in angle brackets or single quotes, is
- an alternative syntax for referencing a subpattern as a subroutine,
- possibly recursively. Here are two of the examples used above, rewrit-
+ an alternative syntax for referencing a subpattern as a subroutine,
+ possibly recursively. Here are two of the examples used above, rewrit-
ten using this syntax:
(?<pn> \( ( (?>[^()]+) | \g<pn> )* \) )
(sens|respons)e and \g'1'ibility
- PCRE supports an extension to Oniguruma: if a number is preceded by a
+ PCRE supports an extension to Oniguruma: if a number is preceded by a
plus or a minus sign it is taken as a relative reference. For example:
(abc)(?i:\g<-1>)
- Note that \g{...} (Perl syntax) and \g<...> (Oniguruma syntax) are not
- synonymous. The former is a back reference; the latter is a subroutine
+ Note that \g{...} (Perl syntax) and \g<...> (Oniguruma syntax) are not
+ synonymous. The former is a back reference; the latter is a subroutine
call.
CALLOUTS
Perl has a feature whereby using the sequence (?{...}) causes arbitrary
- Perl code to be obeyed in the middle of matching a regular expression.
+ Perl code to be obeyed in the middle of matching a regular expression.
This makes it possible, amongst other things, to extract different sub-
strings that match the same pair of parentheses when there is a repeti-
tion.
PCRE provides a similar feature, but of course it cannot obey arbitrary
Perl code. The feature is called "callout". The caller of PCRE provides
- an external function by putting its entry point in the global variable
- pcre_callout (8-bit library) or pcre[16|32]_callout (16-bit or 32-bit
- library). By default, this variable contains NULL, which disables all
+ an external function by putting its entry point in the global variable
+ pcre_callout (8-bit library) or pcre[16|32]_callout (16-bit or 32-bit
+ library). By default, this variable contains NULL, which disables all
calling out.
- Within a regular expression, (?C) indicates the points at which the
- external function is to be called. If you want to identify different
- callout points, you can put a number less than 256 after the letter C.
- The default value is zero. For example, this pattern has two callout
+ Within a regular expression, (?C) indicates the points at which the
+ external function is to be called. If you want to identify different
+ callout points, you can put a number less than 256 after the letter C.
+ The default value is zero. For example, this pattern has two callout
points:
(?C1)abc(?C2)def
- If the PCRE_AUTO_CALLOUT flag is passed to a compiling function, call-
- outs are automatically installed before each item in the pattern. They
- are all numbered 255. If there is a conditional group in the pattern
+ If the PCRE_AUTO_CALLOUT flag is passed to a compiling function, call-
+ outs are automatically installed before each item in the pattern. They
+ are all numbered 255. If there is a conditional group in the pattern
whose condition is an assertion, an additional callout is inserted just
before the condition. An explicit callout may also be set at this posi-
tion, as in this example:
@@ -7253,120 +7258,120 @@ CALLOUTS
Note that this applies only to assertion conditions, not to other types
of condition.
- During matching, when PCRE reaches a callout point, the external func-
- tion is called. It is provided with the number of the callout, the
- position in the pattern, and, optionally, one item of data originally
- supplied by the caller of the matching function. The callout function
+ During matching, when PCRE reaches a callout point, the external func-
+ tion is called. It is provided with the number of the callout, the
+ position in the pattern, and, optionally, one item of data originally
+ supplied by the caller of the matching function. The callout function
may cause matching to proceed, to backtrack, or to fail altogether.
- By default, PCRE implements a number of optimizations at compile time
- and matching time, and one side-effect is that sometimes callouts are
- skipped. If you need all possible callouts to happen, you need to set
- options that disable the relevant optimizations. More details, and a
- complete description of the interface to the callout function, are
+ By default, PCRE implements a number of optimizations at compile time
+ and matching time, and one side-effect is that sometimes callouts are
+ skipped. If you need all possible callouts to happen, you need to set
+ options that disable the relevant optimizations. More details, and a
+ complete description of the interface to the callout function, are
given in the pcrecallout documentation.
BACKTRACKING CONTROL
- Perl 5.10 introduced a number of "Special Backtracking Control Verbs",
- which are still described in the Perl documentation as "experimental
- and subject to change or removal in a future version of Perl". It goes
- on to say: "Their usage in production code should be noted to avoid
- problems during upgrades." The same remarks apply to the PCRE features
+ Perl 5.10 introduced a number of "Special Backtracking Control Verbs",
+ which are still described in the Perl documentation as "experimental
+ and subject to change or removal in a future version of Perl". It goes
+ on to say: "Their usage in production code should be noted to avoid
+ problems during upgrades." The same remarks apply to the PCRE features
described in this section.
- The new verbs make use of what was previously invalid syntax: an open-
+ The new verbs make use of what was previously invalid syntax: an open-
ing parenthesis followed by an asterisk. They are generally of the form
- (*VERB) or (*VERB:NAME). Some may take either form, possibly behaving
- differently depending on whether or not a name is present. A name is
+ (*VERB) or (*VERB:NAME). Some may take either form, possibly behaving
+ differently depending on whether or not a name is present. A name is
any sequence of characters that does not include a closing parenthesis.
The maximum length of name is 255 in the 8-bit library and 65535 in the
- 16-bit and 32-bit libraries. If the name is empty, that is, if the
- closing parenthesis immediately follows the colon, the effect is as if
- the colon were not there. Any number of these verbs may occur in a
+ 16-bit and 32-bit libraries. If the name is empty, that is, if the
+ closing parenthesis immediately follows the colon, the effect is as if
+ the colon were not there. Any number of these verbs may occur in a
pattern.
- Since these verbs are specifically related to backtracking, most of
- them can be used only when the pattern is to be matched using one of
- the traditional matching functions, because these use a backtracking
- algorithm. With the exception of (*FAIL), which behaves like a failing
- negative assertion, the backtracking control verbs cause an error if
+ Since these verbs are specifically related to backtracking, most of
+ them can be used only when the pattern is to be matched using one of
+ the traditional matching functions, because these use a backtracking
+ algorithm. With the exception of (*FAIL), which behaves like a failing
+ negative assertion, the backtracking control verbs cause an error if
encountered by a DFA matching function.
- The behaviour of these verbs in repeated groups, assertions, and in
+ The behaviour of these verbs in repeated groups, assertions, and in
subpatterns called as subroutines (whether or not recursively) is docu-
mented below.
Optimizations that affect backtracking verbs
- PCRE contains some optimizations that are used to speed up matching by
+ PCRE contains some optimizations that are used to speed up matching by
running some checks at the start of each match attempt. For example, it
- may know the minimum length of matching subject, or that a particular
+ may know the minimum length of matching subject, or that a particular
character must be present. When one of these optimizations bypasses the
- running of a match, any included backtracking verbs will not, of
+ running of a match, any included backtracking verbs will not, of
course, be processed. You can suppress the start-of-match optimizations
- by setting the PCRE_NO_START_OPTIMIZE option when calling pcre_com-
+ by setting the PCRE_NO_START_OPTIMIZE option when calling pcre_com-
pile() or pcre_exec(), or by starting the pattern with (*NO_START_OPT).
There is more discussion of this option in the section entitled "Option
bits for pcre_exec()" in the pcreapi documentation.
- Experiments with Perl suggest that it too has similar optimizations,
+ Experiments with Perl suggest that it too has similar optimizations,
sometimes leading to anomalous results.
Verbs that act immediately
- The following verbs act as soon as they are encountered. They may not
+ The following verbs act as soon as they are encountered. They may not
be followed by a name.
(*ACCEPT)
- This verb causes the match to end successfully, skipping the remainder
- of the pattern. However, when it is inside a subpattern that is called
- as a subroutine, only that subpattern is ended successfully. Matching
+ This verb causes the match to end successfully, skipping the remainder
+ of the pattern. However, when it is inside a subpattern that is called
+ as a subroutine, only that subpattern is ended successfully. Matching
then continues at the outer level. If (*ACCEPT) in triggered in a posi-
- tive assertion, the assertion succeeds; in a negative assertion, the
+ tive assertion, the assertion succeeds; in a negative assertion, the
assertion fails.
- If (*ACCEPT) is inside capturing parentheses, the data so far is cap-
+ If (*ACCEPT) is inside capturing parentheses, the data so far is cap-
tured. For example:
A((?:A|B(*ACCEPT)|C)D)
- This matches "AB", "AAD", or "ACD"; when it matches "AB", "B" is cap-
+ This matches "AB", "AAD", or "ACD"; when it matches "AB", "B" is cap-
tured by the outer parentheses.
(*FAIL) or (*F)
- This verb causes a matching failure, forcing backtracking to occur. It
- is equivalent to (?!) but easier to read. The Perl documentation notes
- that it is probably useful only when combined with (?{}) or (??{}).
- Those are, of course, Perl features that are not present in PCRE. The
- nearest equivalent is the callout feature, as for example in this pat-
+ This verb causes a matching failure, forcing backtracking to occur. It
+ is equivalent to (?!) but easier to read. The Perl documentation notes
+ that it is probably useful only when combined with (?{}) or (??{}).
+ Those are, of course, Perl features that are not present in PCRE. The
+ nearest equivalent is the callout feature, as for example in this pat-
tern:
a+(?C)(*FAIL)
- A match with the string "aaaa" always fails, but the callout is taken
+ A match with the string "aaaa" always fails, but the callout is taken
before each backtrack happens (in this example, 10 times).
Recording which path was taken
- There is one verb whose main purpose is to track how a match was
- arrived at, though it also has a secondary use in conjunction with
+ There is one verb whose main purpose is to track how a match was
+ arrived at, though it also has a secondary use in conjunction with
advancing the match starting point (see (*SKIP) below).
(*MARK:NAME) or (*:NAME)
- A name is always required with this verb. There may be as many
- instances of (*MARK) as you like in a pattern, and their names do not
+ A name is always required with this verb. There may be as many
+ instances of (*MARK) as you like in a pattern, and their names do not
have to be unique.
- When a match succeeds, the name of the last-encountered (*MARK:NAME),
- (*PRUNE:NAME), or (*THEN:NAME) on the matching path is passed back to
- the caller as described in the section entitled "Extra data for
- pcre_exec()" in the pcreapi documentation. Here is an example of
- pcretest output, where the /K modifier requests the retrieval and out-
+ When a match succeeds, the name of the last-encountered (*MARK:NAME),
+ (*PRUNE:NAME), or (*THEN:NAME) on the matching path is passed back to
+ the caller as described in the section entitled "Extra data for
+ pcre_exec()" in the pcreapi documentation. Here is an example of
+ pcretest output, where the /K modifier requests the retrieval and out-
putting of (*MARK) data:
re> /X(*MARK:A)Y|X(*MARK:B)Z/K
@@ -7378,73 +7383,73 @@ BACKTRACKING CONTROL
MK: B
The (*MARK) name is tagged with "MK:" in this output, and in this exam-
- ple it indicates which of the two alternatives matched. This is a more
- efficient way of obtaining this information than putting each alterna-
+ ple it indicates which of the two alternatives matched. This is a more
+ efficient way of obtaining this information than putting each alterna-
tive in its own capturing parentheses.
- If a verb with a name is encountered in a positive assertion that is
- true, the name is recorded and passed back if it is the last-encoun-
+ If a verb with a name is encountered in a positive assertion that is
+ true, the name is recorded and passed back if it is the last-encoun-
tered. This does not happen for negative assertions or failing positive
assertions.
- After a partial match or a failed match, the last encountered name in
+ After a partial match or a failed match, the last encountered name in
the entire match process is returned. For example:
re> /X(*MARK:A)Y|X(*MARK:B)Z/K
data> XP
No match, mark = B
- Note that in this unanchored example the mark is retained from the
+ Note that in this unanchored example the mark is retained from the
match attempt that started at the letter "X" in the subject. Subsequent
match attempts starting at "P" and then with an empty string do not get
as far as the (*MARK) item, but nevertheless do not reset it.
- If you are interested in (*MARK) values after failed matches, you
- should probably set the PCRE_NO_START_OPTIMIZE option (see above) to
+ If you are interested in (*MARK) values after failed matches, you
+ should probably set the PCRE_NO_START_OPTIMIZE option (see above) to
ensure that the match is always attempted.
Verbs that act after backtracking
The following verbs do nothing when they are encountered. Matching con-
- tinues with what follows, but if there is no subsequent match, causing
- a backtrack to the verb, a failure is forced. That is, backtracking
- cannot pass to the left of the verb. However, when one of these verbs
+ tinues with what follows, but if there is no subsequent match, causing
+ a backtrack to the verb, a failure is forced. That is, backtracking
+ cannot pass to the left of the verb. However, when one of these verbs
appears inside an atomic group or an assertion that is true, its effect
- is confined to that group, because once the group has been matched,
- there is never any backtracking into it. In this situation, backtrack-
- ing can "jump back" to the left of the entire atomic group or asser-
- tion. (Remember also, as stated above, that this localization also
+ is confined to that group, because once the group has been matched,
+ there is never any backtracking into it. In this situation, backtrack-
+ ing can "jump back" to the left of the entire atomic group or asser-
+ tion. (Remember also, as stated above, that this localization also
applies in subroutine calls.)
- These verbs differ in exactly what kind of failure occurs when back-
- tracking reaches them. The behaviour described below is what happens
- when the verb is not in a subroutine or an assertion. Subsequent sec-
+ These verbs differ in exactly what kind of failure occurs when back-
+ tracking reaches them. The behaviour described below is what happens
+ when the verb is not in a subroutine or an assertion. Subsequent sec-
tions cover these special cases.
(*COMMIT)
- This verb, which may not be followed by a name, causes the whole match
+ This verb, which may not be followed by a name, causes the whole match
to fail outright if there is a later matching failure that causes back-
- tracking to reach it. Even if the pattern is unanchored, no further
+ tracking to reach it. Even if the pattern is unanchored, no further
attempts to find a match by advancing the starting point take place. If
- (*COMMIT) is the only backtracking verb that is encountered, once it
+ (*COMMIT) is the only backtracking verb that is encountered, once it
has been passed pcre_exec() is committed to finding a match at the cur-
rent starting point, or not at all. For example:
a+(*COMMIT)b
- This matches "xxaab" but not "aacaab". It can be thought of as a kind
+ This matches "xxaab" but not "aacaab". It can be thought of as a kind
of dynamic anchor, or "I've started, so I must finish." The name of the
- most recently passed (*MARK) in the path is passed back when (*COMMIT)
+ most recently passed (*MARK) in the path is passed back when (*COMMIT)
forces a match failure.
- If there is more than one backtracking verb in a pattern, a different
- one that follows (*COMMIT) may be triggered first, so merely passing
+ If there is more than one backtracking verb in a pattern, a different
+ one that follows (*COMMIT) may be triggered first, so merely passing
(*COMMIT) during a match does not always guarantee that a match must be
at this starting point.
- Note that (*COMMIT) at the start of a pattern is not the same as an
- anchor, unless PCRE's start-of-match optimizations are turned off, as
+ Note that (*COMMIT) at the start of a pattern is not the same as an
+ anchor, unless PCRE's start-of-match optimizations are turned off, as
shown in this output from pcretest:
re> /(*COMMIT)abc/
@@ -7455,207 +7460,207 @@ BACKTRACKING CONTROL
For this pattern, PCRE knows that any match must start with "a", so the
optimization skips along the subject to "a" before applying the pattern
- to the first set of data. The match attempt then succeeds. In the sec-
- ond set of data, the escape sequence \Y is interpreted by the pcretest
- program. It causes the PCRE_NO_START_OPTIMIZE option to be set when
+ to the first set of data. The match attempt then succeeds. In the sec-
+ ond set of data, the escape sequence \Y is interpreted by the pcretest
+ program. It causes the PCRE_NO_START_OPTIMIZE option to be set when
pcre_exec() is called. This disables the optimization that skips along
to the first character. The pattern is now applied starting at "x", and
- so the (*COMMIT) causes the match to fail without trying any other
+ so the (*COMMIT) causes the match to fail without trying any other
starting points.
(*PRUNE) or (*PRUNE:NAME)
- This verb causes the match to fail at the current starting position in
+ This verb causes the match to fail at the current starting position in
the subject if there is a later matching failure that causes backtrack-
- ing to reach it. If the pattern is unanchored, the normal "bumpalong"
- advance to the next starting character then happens. Backtracking can
- occur as usual to the left of (*PRUNE), before it is reached, or when
- matching to the right of (*PRUNE), but if there is no match to the
- right, backtracking cannot cross (*PRUNE). In simple cases, the use of
- (*PRUNE) is just an alternative to an atomic group or possessive quan-
+ ing to reach it. If the pattern is unanchored, the normal "bumpalong"
+ advance to the next starting character then happens. Backtracking can
+ occur as usual to the left of (*PRUNE), before it is reached, or when
+ matching to the right of (*PRUNE), but if there is no match to the
+ right, backtracking cannot cross (*PRUNE). In simple cases, the use of
+ (*PRUNE) is just an alternative to an atomic group or possessive quan-
tifier, but there are some uses of (*PRUNE) that cannot be expressed in
- any other way. In an anchored pattern (*PRUNE) has the same effect as
+ any other way. In an anchored pattern (*PRUNE) has the same effect as
(*COMMIT).
The behaviour of (*PRUNE:NAME) is the not the same as
- (*MARK:NAME)(*PRUNE). It is like (*MARK:NAME) in that the name is
- remembered for passing back to the caller. However, (*SKIP:NAME)
+ (*MARK:NAME)(*PRUNE). It is like (*MARK:NAME) in that the name is
+ remembered for passing back to the caller. However, (*SKIP:NAME)
searches only for names set with (*MARK).
(*SKIP)
- This verb, when given without a name, is like (*PRUNE), except that if
- the pattern is unanchored, the "bumpalong" advance is not to the next
+ This verb, when given without a name, is like (*PRUNE), except that if
+ the pattern is unanchored, the "bumpalong" advance is not to the next
character, but to the position in the subject where (*SKIP) was encoun-
- tered. (*SKIP) signifies that whatever text was matched leading up to
+ tered. (*SKIP) signifies that whatever text was matched leading up to
it cannot be part of a successful match. Consider:
a+(*SKIP)b
- If the subject is "aaaac...", after the first match attempt fails
- (starting at the first character in the string), the starting point
+ If the subject is "aaaac...", after the first match attempt fails
+ (starting at the first character in the string), the starting point
skips on to start the next attempt at "c". Note that a possessive quan-
- tifer does not have the same effect as this example; although it would
- suppress backtracking during the first match attempt, the second
- attempt would start at the second character instead of skipping on to
+ tifer does not have the same effect as this example; although it would
+ suppress backtracking during the first match attempt, the second
+ attempt would start at the second character instead of skipping on to
"c".
(*SKIP:NAME)
When (*SKIP) has an associated name, its behaviour is modified. When it
is triggered, the previous path through the pattern is searched for the
- most recent (*MARK) that has the same name. If one is found, the
+ most recent (*MARK) that has the same name. If one is found, the
"bumpalong" advance is to the subject position that corresponds to that
(*MARK) instead of to where (*SKIP) was encountered. If no (*MARK) with
a matching name is found, the (*SKIP) is ignored.
- Note that (*SKIP:NAME) searches only for names set by (*MARK:NAME). It
+ Note that (*SKIP:NAME) searches only for names set by (*MARK:NAME). It
ignores names that are set by (*PRUNE:NAME) or (*THEN:NAME).
(*THEN) or (*THEN:NAME)
- This verb causes a skip to the next innermost alternative when back-
- tracking reaches it. That is, it cancels any further backtracking
- within the current alternative. Its name comes from the observation
+ This verb causes a skip to the next innermost alternative when back-
+ tracking reaches it. That is, it cancels any further backtracking
+ within the current alternative. Its name comes from the observation
that it can be used for a pattern-based if-then-else block:
( COND1 (*THEN) FOO | COND2 (*THEN) BAR | COND3 (*THEN) BAZ ) ...
- If the COND1 pattern matches, FOO is tried (and possibly further items
- after the end of the group if FOO succeeds); on failure, the matcher
- skips to the second alternative and tries COND2, without backtracking
- into COND1. If that succeeds and BAR fails, COND3 is tried. If subse-
- quently BAZ fails, there are no more alternatives, so there is a back-
- track to whatever came before the entire group. If (*THEN) is not
+ If the COND1 pattern matches, FOO is tried (and possibly further items
+ after the end of the group if FOO succeeds); on failure, the matcher
+ skips to the second alternative and tries COND2, without backtracking
+ into COND1. If that succeeds and BAR fails, COND3 is tried. If subse-
+ quently BAZ fails, there are no more alternatives, so there is a back-
+ track to whatever came before the entire group. If (*THEN) is not
inside an alternation, it acts like (*PRUNE).
- The behaviour of (*THEN:NAME) is the not the same as
- (*MARK:NAME)(*THEN). It is like (*MARK:NAME) in that the name is
- remembered for passing back to the caller. However, (*SKIP:NAME)
+ The behaviour of (*THEN:NAME) is the not the same as
+ (*MARK:NAME)(*THEN). It is like (*MARK:NAME) in that the name is
+ remembered for passing back to the caller. However, (*SKIP:NAME)
searches only for names set with (*MARK).
- A subpattern that does not contain a | character is just a part of the
- enclosing alternative; it is not a nested alternation with only one
- alternative. The effect of (*THEN) extends beyond such a subpattern to
- the enclosing alternative. Consider this pattern, where A, B, etc. are
- complex pattern fragments that do not contain any | characters at this
+ A subpattern that does not contain a | character is just a part of the
+ enclosing alternative; it is not a nested alternation with only one
+ alternative. The effect of (*THEN) extends beyond such a subpattern to
+ the enclosing alternative. Consider this pattern, where A, B, etc. are
+ complex pattern fragments that do not contain any | characters at this
level:
A (B(*THEN)C) | D
- If A and B are matched, but there is a failure in C, matching does not
+ If A and B are matched, but there is a failure in C, matching does not
backtrack into A; instead it moves to the next alternative, that is, D.
- However, if the subpattern containing (*THEN) is given an alternative,
+ However, if the subpattern containing (*THEN) is given an alternative,
it behaves differently:
A (B(*THEN)C | (*FAIL)) | D
- The effect of (*THEN) is now confined to the inner subpattern. After a
+ The effect of (*THEN) is now confined to the inner subpattern. After a
failure in C, matching moves to (*FAIL), which causes the whole subpat-
- tern to fail because there are no more alternatives to try. In this
+ tern to fail because there are no more alternatives to try. In this
case, matching does now backtrack into A.
- Note that a conditional subpattern is not considered as having two
- alternatives, because only one is ever used. In other words, the |
+ Note that a conditional subpattern is not considered as having two
+ alternatives, because only one is ever used. In other words, the |
character in a conditional subpattern has a different meaning. Ignoring
white space, consider:
^.*? (?(?=a) a | b(*THEN)c )
- If the subject is "ba", this pattern does not match. Because .*? is
- ungreedy, it initially matches zero characters. The condition (?=a)
- then fails, the character "b" is matched, but "c" is not. At this
- point, matching does not backtrack to .*? as might perhaps be expected
- from the presence of the | character. The conditional subpattern is
+ If the subject is "ba", this pattern does not match. Because .*? is
+ ungreedy, it initially matches zero characters. The condition (?=a)
+ then fails, the character "b" is matched, but "c" is not. At this
+ point, matching does not backtrack to .*? as might perhaps be expected
+ from the presence of the | character. The conditional subpattern is
part of the single alternative that comprises the whole pattern, and so
- the match fails. (If there was a backtrack into .*?, allowing it to
+ the match fails. (If there was a backtrack into .*?, allowing it to
match "b", the match would succeed.)
- The verbs just described provide four different "strengths" of control
+ The verbs just described provide four different "strengths" of control
when subsequent matching fails. (*THEN) is the weakest, carrying on the
- match at the next alternative. (*PRUNE) comes next, failing the match
- at the current starting position, but allowing an advance to the next
- character (for an unanchored pattern). (*SKIP) is similar, except that
+ match at the next alternative. (*PRUNE) comes next, failing the match
+ at the current starting position, but allowing an advance to the next
+ character (for an unanchored pattern). (*SKIP) is similar, except that
the advance may be more than one character. (*COMMIT) is the strongest,
causing the entire match to fail.
More than one backtracking verb
- If more than one backtracking verb is present in a pattern, the one
- that is backtracked onto first acts. For example, consider this pat-
+ If more than one backtracking verb is present in a pattern, the one
+ that is backtracked onto first acts. For example, consider this pat-
tern, where A, B, etc. are complex pattern fragments:
(A(*COMMIT)B(*THEN)C|ABD)
- If A matches but B fails, the backtrack to (*COMMIT) causes the entire
+ If A matches but B fails, the backtrack to (*COMMIT) causes the entire
match to fail. However, if A and B match, but C fails, the backtrack to
- (*THEN) causes the next alternative (ABD) to be tried. This behaviour
- is consistent, but is not always the same as Perl's. It means that if
- two or more backtracking verbs appear in succession, all the the last
+ (*THEN) causes the next alternative (ABD) to be tried. This behaviour
+ is consistent, but is not always the same as Perl's. It means that if
+ two or more backtracking verbs appear in succession, all the the last
of them has no effect. Consider this example:
...(*COMMIT)(*PRUNE)...
If there is a matching failure to the right, backtracking onto (*PRUNE)
- causes it to be triggered, and its action is taken. There can never be
+ causes it to be triggered, and its action is taken. There can never be
a backtrack onto (*COMMIT).
Backtracking verbs in repeated groups
- PCRE differs from Perl in its handling of backtracking verbs in
+ PCRE differs from Perl in its handling of backtracking verbs in
repeated groups. For example, consider:
/(a(*COMMIT)b)+ac/
- If the subject is "abac", Perl matches, but PCRE fails because the
+ If the subject is "abac", Perl matches, but PCRE fails because the
(*COMMIT) in the second repeat of the group acts.
Backtracking verbs in assertions
- (*FAIL) in an assertion has its normal effect: it forces an immediate
+ (*FAIL) in an assertion has its normal effect: it forces an immediate
backtrack.
(*ACCEPT) in a positive assertion causes the assertion to succeed with-
- out any further processing. In a negative assertion, (*ACCEPT) causes
+ out any further processing. In a negative assertion, (*ACCEPT) causes
the assertion to fail without any further processing.
- The other backtracking verbs are not treated specially if they appear
- in a positive assertion. In particular, (*THEN) skips to the next
- alternative in the innermost enclosing group that has alternations,
+ The other backtracking verbs are not treated specially if they appear
+ in a positive assertion. In particular, (*THEN) skips to the next
+ alternative in the innermost enclosing group that has alternations,
whether or not this is within the assertion.
- Negative assertions are, however, different, in order to ensure that
- changing a positive assertion into a negative assertion changes its
+ Negative assertions are, however, different, in order to ensure that
+ changing a positive assertion into a negative assertion changes its
result. Backtracking into (*COMMIT), (*SKIP), or (*PRUNE) causes a neg-
ative assertion to be true, without considering any further alternative
branches in the assertion. Backtracking into (*THEN) causes it to skip
- to the next enclosing alternative within the assertion (the normal be-
- haviour), but if the assertion does not have such an alternative,
+ to the next enclosing alternative within the assertion (the normal be-
+ haviour), but if the assertion does not have such an alternative,
(*THEN) behaves like (*PRUNE).
Backtracking verbs in subroutines
- These behaviours occur whether or not the subpattern is called recur-
+ These behaviours occur whether or not the subpattern is called recur-
sively. Perl's treatment of subroutines is different in some cases.
- (*FAIL) in a subpattern called as a subroutine has its normal effect:
+ (*FAIL) in a subpattern called as a subroutine has its normal effect:
it forces an immediate backtrack.
- (*ACCEPT) in a subpattern called as a subroutine causes the subroutine
- match to succeed without any further processing. Matching then contin-
+ (*ACCEPT) in a subpattern called as a subroutine causes the subroutine
+ match to succeed without any further processing. Matching then contin-
ues after the subroutine call.
(*COMMIT), (*SKIP), and (*PRUNE) in a subpattern called as a subroutine
cause the subroutine match to fail.
- (*THEN) skips to the next alternative in the innermost enclosing group
- within the subpattern that has alternatives. If there is no such group
+ (*THEN) skips to the next alternative in the innermost enclosing group
+ within the subpattern that has alternatives. If there is no such group
within the subpattern, (*THEN) causes the subroutine match to fail.
SEE ALSO
- pcreapi(3), pcrecallout(3), pcrematching(3), pcresyntax(3), pcre(3),
+ pcreapi(3), pcrecallout(3), pcrematching(3), pcresyntax(3), pcre(3),
pcre16(3), pcre32(3).
@@ -7668,8 +7673,8 @@ AUTHOR
REVISION
- Last updated: 14 June 2015
- Copyright (c) 1997-2015 University of Cambridge.
+ Last updated: 23 October 2016
+ Copyright (c) 1997-2016 University of Cambridge.
------------------------------------------------------------------------------
diff --git a/pcre/doc/pcrecompat.3 b/pcre/doc/pcrecompat.3
index 0cc40198235..6156e776f53 100644
--- a/pcre/doc/pcrecompat.3
+++ b/pcre/doc/pcrecompat.3
@@ -113,7 +113,7 @@ the pattern /^(a(b)?)+$/ in Perl leaves $2 unset, but in PCRE it is set to "b".
14. PCRE's handling of duplicate subpattern numbers and duplicate subpattern
names is not as general as Perl's. This is a consequence of the fact the PCRE
works internally just with numbers, using an external table to translate
-between numbers and names. In particular, a pattern such as (?|(?<a>A)|(?<b)B),
+between numbers and names. In particular, a pattern such as (?|(?<a>A)|(?<b>B),
where the two capturing parentheses have the same number but different names,
is not supported, and causes an error at compile time. If it were allowed, it
would not be possible to distinguish which parentheses matched, because both
diff --git a/pcre/doc/pcrepattern.3 b/pcre/doc/pcrepattern.3
index 3b8c6393d21..97df217fdb2 100644
--- a/pcre/doc/pcrepattern.3
+++ b/pcre/doc/pcrepattern.3
@@ -1,4 +1,4 @@
-.TH PCREPATTERN 3 "14 June 2015" "PCRE 8.38"
+.TH PCREPATTERN 3 "23 October 2016" "PCRE 8.40"
.SH NAME
PCRE - Perl-compatible regular expressions
.SH "PCRE REGULAR EXPRESSION DETAILS"
@@ -336,22 +336,22 @@ When PCRE is compiled in EBCDIC mode, \ea, \ee, \ef, \en, \er, and \et
generate the appropriate EBCDIC code values. The \ec escape is processed
as specified for Perl in the \fBperlebcdic\fP document. The only characters
that are allowed after \ec are A-Z, a-z, or one of @, [, \e, ], ^, _, or ?. Any
-other character provokes a compile-time error. The sequence \e@ encodes
-character code 0; the letters (in either case) encode characters 1-26 (hex 01
-to hex 1A); [, \e, ], ^, and _ encode characters 27-31 (hex 1B to hex 1F), and
-\e? becomes either 255 (hex FF) or 95 (hex 5F).
+other character provokes a compile-time error. The sequence \ec@ encodes
+character code 0; after \ec the letters (in either case) encode characters 1-26
+(hex 01 to hex 1A); [, \e, ], ^, and _ encode characters 27-31 (hex 1B to hex
+1F), and \ec? becomes either 255 (hex FF) or 95 (hex 5F).
.P
-Thus, apart from \e?, these escapes generate the same character code values as
+Thus, apart from \ec?, these escapes generate the same character code values as
they do in an ASCII environment, though the meanings of the values mostly
-differ. For example, \eG always generates code value 7, which is BEL in ASCII
+differ. For example, \ecG always generates code value 7, which is BEL in ASCII
but DEL in EBCDIC.
.P
-The sequence \e? generates DEL (127, hex 7F) in an ASCII environment, but
+The sequence \ec? generates DEL (127, hex 7F) in an ASCII environment, but
because 127 is not a control character in EBCDIC, Perl makes it generate the
APC character. Unfortunately, there are several variants of EBCDIC. In most of
them the APC character has the value 255 (hex FF), but in the one Perl calls
POSIX-BC its value is 95 (hex 5F). If certain other characters have POSIX-BC
-values, PCRE makes \e? generate 95; otherwise it generates 255.
+values, PCRE makes \ec? generate 95; otherwise it generates 255.
.P
After \e0 up to two further octal digits are read. If there are fewer than two
digits, just those that are present are used. Thus the sequence \e0\ex\e015
@@ -1511,12 +1511,8 @@ J, U and X respectively.
.P
When one of these option changes occurs at top level (that is, not inside
subpattern parentheses), the change applies to the remainder of the pattern
-that follows. If the change is placed right at the start of a pattern, PCRE
-extracts it into the global options (and it will therefore show up in data
-extracted by the \fBpcre_fullinfo()\fP function).
-.P
-An option change within a subpattern (see below for a description of
-subpatterns) affects only that part of the subpattern that follows it, so
+that follows. An option change within a subpattern (see below for a description
+of subpatterns) affects only that part of the subpattern that follows it, so
.sp
(a(?i)b)c
.sp
@@ -2171,6 +2167,13 @@ numbering the capturing subpatterns in the whole pattern. However, substring
capturing is carried out only for positive assertions. (Perl sometimes, but not
always, does do capturing in negative assertions.)
.P
+WARNING: If a positive assertion containing one or more capturing subpatterns
+succeeds, but failure to match later in the pattern causes backtracking over
+this assertion, the captures within the assertion are reset only if no higher
+numbered captures are already set. This is, unfortunately, a fundamental
+limitation of the current implementation, and as PCRE1 is now in
+maintenance-only status, it is unlikely ever to change.
+.P
For compatibility with Perl, assertion subpatterns may be repeated; though
it makes no sense to assert the same thing several times, the side effect of
capturing parentheses may occasionally be useful. In practice, there only three
@@ -3296,6 +3299,6 @@ Cambridge CB2 3QH, England.
.rs
.sp
.nf
-Last updated: 14 June 2015
-Copyright (c) 1997-2015 University of Cambridge.
+Last updated: 23 October 2016
+Copyright (c) 1997-2016 University of Cambridge.
.fi
diff --git a/pcre/pcre_compile.c b/pcre/pcre_compile.c
index 7cd39501230..de92313e2f8 100644
--- a/pcre/pcre_compile.c
+++ b/pcre/pcre_compile.c
@@ -5579,6 +5579,34 @@ for (;; ptr++)
#endif
#if defined SUPPORT_UTF || !defined COMPILE_PCRE8
{
+ /* For non-UCP wide characters, in a non-negative class containing \S or
+ similar (should_flip_negation is set), all characters greater than 255
+ must be in the class. */
+
+ if (
+#if defined COMPILE_PCRE8
+ utf &&
+#endif
+ should_flip_negation && !negate_class && (options & PCRE_UCP) == 0)
+ {
+ *class_uchardata++ = XCL_RANGE;
+ if (utf) /* Will always be utf in the 8-bit library */
+ {
+ class_uchardata += PRIV(ord2utf)(0x100, class_uchardata);
+ class_uchardata += PRIV(ord2utf)(0x10ffff, class_uchardata);
+ }
+ else /* Can only happen for the 16-bit & 32-bit libraries */
+ {
+#if defined COMPILE_PCRE16
+ *class_uchardata++ = 0x100;
+ *class_uchardata++ = 0xffffu;
+#elif defined COMPILE_PCRE32
+ *class_uchardata++ = 0x100;
+ *class_uchardata++ = 0xffffffffu;
+#endif
+ }
+ }
+
*class_uchardata++ = XCL_END; /* Marks the end of extra data */
*code++ = OP_XCLASS;
code += LINK_SIZE;
@@ -6923,7 +6951,8 @@ for (;; ptr++)
slot = cd->name_table;
for (i = 0; i < cd->names_found; i++)
{
- if (STRNCMP_UC_UC(name, slot+IMM2_SIZE, namelen) == 0) break;
+ if (STRNCMP_UC_UC(name, slot+IMM2_SIZE, namelen) == 0 &&
+ slot[IMM2_SIZE+namelen] == 0) break;
slot += cd->name_entry_size;
}
@@ -7889,15 +7918,17 @@ for (;; ptr++)
}
}
- /* For a forward assertion, we take the reqchar, if set. This can be
- helpful if the pattern that follows the assertion doesn't set a different
- char. For example, it's useful for /(?=abcde).+/. We can't set firstchar
- for an assertion, however because it leads to incorrect effect for patterns
- such as /(?=a)a.+/ when the "real" "a" would then become a reqchar instead
- of a firstchar. This is overcome by a scan at the end if there's no
- firstchar, looking for an asserted first char. */
-
- else if (bravalue == OP_ASSERT && subreqcharflags >= 0)
+ /* For a forward assertion, we take the reqchar, if set, provided that the
+ group has also set a first char. This can be helpful if the pattern that
+ follows the assertion doesn't set a different char. For example, it's
+ useful for /(?=abcde).+/. We can't set firstchar for an assertion, however
+ because it leads to incorrect effect for patterns such as /(?=a)a.+/ when
+ the "real" "a" would then become a reqchar instead of a firstchar. This is
+ overcome by a scan at the end if there's no firstchar, looking for an
+ asserted first char. */
+
+ else if (bravalue == OP_ASSERT && subreqcharflags >= 0 &&
+ subfirstcharflags >= 0)
{
reqchar = subreqchar;
reqcharflags = subreqcharflags;
@@ -8686,8 +8717,8 @@ matching and for non-DOTALL patterns that start with .* (which must start at
the beginning or after \n). As in the case of is_anchored() (see above), we
have to take account of back references to capturing brackets that contain .*
because in that case we can't make the assumption. Also, the appearance of .*
-inside atomic brackets or in a pattern that contains *PRUNE or *SKIP does not
-count, because once again the assumption no longer holds.
+inside atomic brackets or in an assertion, or in a pattern that contains *PRUNE
+or *SKIP does not count, because once again the assumption no longer holds.
Arguments:
code points to start of expression (the bracket)
@@ -8696,13 +8727,14 @@ Arguments:
the less precise approach
cd points to the compile data
atomcount atomic group level
+ inassert TRUE if in an assertion
Returns: TRUE or FALSE
*/
static BOOL
is_startline(const pcre_uchar *code, unsigned int bracket_map,
- compile_data *cd, int atomcount)
+ compile_data *cd, int atomcount, BOOL inassert)
{
do {
const pcre_uchar *scode = first_significant_code(
@@ -8729,7 +8761,7 @@ do {
return FALSE;
default: /* Assertion */
- if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE;
+ if (!is_startline(scode, bracket_map, cd, atomcount, TRUE)) return FALSE;
do scode += GET(scode, 1); while (*scode == OP_ALT);
scode += 1 + LINK_SIZE;
break;
@@ -8743,7 +8775,7 @@ do {
if (op == OP_BRA || op == OP_BRAPOS ||
op == OP_SBRA || op == OP_SBRAPOS)
{
- if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE;
+ if (!is_startline(scode, bracket_map, cd, atomcount, inassert)) return FALSE;
}
/* Capturing brackets */
@@ -8753,33 +8785,33 @@ do {
{
int n = GET2(scode, 1+LINK_SIZE);
int new_map = bracket_map | ((n < 32)? (1 << n) : 1);
- if (!is_startline(scode, new_map, cd, atomcount)) return FALSE;
+ if (!is_startline(scode, new_map, cd, atomcount, inassert)) return FALSE;
}
/* Positive forward assertions */
else if (op == OP_ASSERT)
{
- if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE;
+ if (!is_startline(scode, bracket_map, cd, atomcount, TRUE)) return FALSE;
}
/* Atomic brackets */
else if (op == OP_ONCE || op == OP_ONCE_NC)
{
- if (!is_startline(scode, bracket_map, cd, atomcount + 1)) return FALSE;
+ if (!is_startline(scode, bracket_map, cd, atomcount + 1, inassert)) return FALSE;
}
/* .* means "start at start or after \n" if it isn't in atomic brackets or
- brackets that may be referenced, as long as the pattern does not contain
- *PRUNE or *SKIP, because these break the feature. Consider, for example,
- /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", i.e. not at the
- start of a line. */
+ brackets that may be referenced or an assertion, as long as the pattern does
+ not contain *PRUNE or *SKIP, because these break the feature. Consider, for
+ example, /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", i.e.
+ not at the start of a line. */
else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR)
{
if (scode[1] != OP_ANY || (bracket_map & cd->backref_map) != 0 ||
- atomcount > 0 || cd->had_pruneorskip)
+ atomcount > 0 || cd->had_pruneorskip || inassert)
return FALSE;
}
@@ -9634,7 +9666,7 @@ if ((re->options & PCRE_ANCHORED) == 0)
re->flags |= PCRE_FIRSTSET;
}
- else if (is_startline(codestart, 0, cd, 0)) re->flags |= PCRE_STARTLINE;
+ else if (is_startline(codestart, 0, cd, 0, FALSE)) re->flags |= PCRE_STARTLINE;
}
}
diff --git a/pcre/pcre_jit_compile.c b/pcre/pcre_jit_compile.c
index 4f15a27ac28..46ce6c65d54 100644
--- a/pcre/pcre_jit_compile.c
+++ b/pcre/pcre_jit_compile.c
@@ -4004,12 +4004,12 @@ sljit_emit_op_custom(compiler, instruction, 4);
if (load_twice)
{
- OP1(SLJIT_MOV, TMP3, 0, TMP2, 0);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP2, 0);
instruction[3] = 0xc0 | (tmp2_ind << 3) | 1;
sljit_emit_op_custom(compiler, instruction, 4);
OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
- OP1(SLJIT_MOV, TMP2, 0, TMP3, 0);
+ OP1(SLJIT_MOV, TMP2, 0, RETURN_ADDR, 0);
}
OP2(SLJIT_ASHR, TMP1, 0, TMP1, 0, TMP2, 0);
diff --git a/pcre/pcre_jit_test.c b/pcre/pcre_jit_test.c
index 9b61ec000fa..034cb52697f 100644
--- a/pcre/pcre_jit_test.c
+++ b/pcre/pcre_jit_test.c
@@ -687,6 +687,7 @@ static struct regression_test_case regression_test_cases[] = {
{ PCRE_FIRSTLINE | PCRE_NEWLINE_LF | PCRE_DOTALL, 0 | F_NOMATCH, "ab.", "ab" },
{ MUA | PCRE_FIRSTLINE, 1 | F_NOMATCH, "^[a-d0-9]", "\nxx\nd" },
{ PCRE_NEWLINE_ANY | PCRE_FIRSTLINE | PCRE_DOTALL, 0, "....a", "012\n0a" },
+ { MUA | PCRE_FIRSTLINE, 0, "[aC]", "a" },
/* Recurse. */
{ MUA, 0, "(a)(?1)", "aa" },
diff --git a/pcre/pcregrep.c b/pcre/pcregrep.c
index cd53c648da2..fd2a67622ba 100644
--- a/pcre/pcregrep.c
+++ b/pcre/pcregrep.c
@@ -1803,6 +1803,12 @@ while (ptr < endptr)
match = FALSE;
if (line_buffered) fflush(stdout);
rc = 0; /* Had some success */
+
+ /* If the current match ended past the end of the line (only possible
+ in multiline mode), we are done with this line. */
+
+ if ((unsigned int)offsets[1] > linelength) goto END_ONE_MATCH;
+
startoffset = offsets[1]; /* Restart after the match */
if (startoffset <= oldstartoffset)
{
diff --git a/pcre/pcretest.c b/pcre/pcretest.c
index 78ef5177df7..5b73a918075 100644
--- a/pcre/pcretest.c
+++ b/pcre/pcretest.c
@@ -1982,6 +1982,7 @@ return(result);
static int pchar(pcre_uint32 c, FILE *f)
{
int n = 0;
+char tempbuffer[16];
if (PRINTOK(c))
{
if (f != NULL) fprintf(f, "%c", c);
@@ -2003,6 +2004,8 @@ if (c < 0x100)
}
if (f != NULL) n = fprintf(f, "\\x{%02x}", c);
+ else n = sprintf(tempbuffer, "\\x{%02x}", c);
+
return n >= 0 ? n : 0;
}
@@ -5042,7 +5045,7 @@ while (!done)
if ((all_use_dfa || use_dfa) && find_match_limit)
{
- printf("**Match limit not relevant for DFA matching: ignored\n");
+ printf("** Match limit not relevant for DFA matching: ignored\n");
find_match_limit = 0;
}
@@ -5255,10 +5258,17 @@ while (!done)
if (do_allcaps)
{
- if (new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count) < 0)
- goto SKIP_DATA;
- count++; /* Allow for full match */
- if (count * 2 > use_size_offsets) count = use_size_offsets/2;
+ if (all_use_dfa || use_dfa)
+ {
+ fprintf(outfile, "** Show all captures ignored after DFA matching\n");
+ }
+ else
+ {
+ if (new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count) < 0)
+ goto SKIP_DATA;
+ count++; /* Allow for full match */
+ if (count * 2 > use_size_offsets) count = use_size_offsets/2;
+ }
}
/* Output the captured substrings. Note that, for the matched string,
diff --git a/pcre/testdata/testinput1 b/pcre/testdata/testinput1
index 8379ce04d5b..93abab3c851 100644
--- a/pcre/testdata/testinput1
+++ b/pcre/testdata/testinput1
@@ -5733,4 +5733,10 @@ AbcdCBefgBhiBqz
"(?|(\k'Pm')|(?'Pm'))"
abcd
+/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\s])/
+ \ Fred:099
+
+/(?=.*X)X$/
+ \ X
+
/-- End of testinput1 --/
diff --git a/pcre/testdata/testinput16 b/pcre/testdata/testinput16
index 15419e63fa6..7ccde0a8c80 100644
--- a/pcre/testdata/testinput16
+++ b/pcre/testdata/testinput16
@@ -38,4 +38,30 @@
/s+/i8SI
SSss\x{17f}
+/[\W\p{Any}]/BZ
+ abc
+ 123
+
+/[\W\pL]/BZ
+ abc
+ ** Failers
+ 123
+
+/[\D]/8
+ \x{1d7cf}
+
+/[\D\P{Nd}]/8
+ \x{1d7cf}
+
+/[^\D]/8
+ a9b
+ ** Failers
+ \x{1d7cf}
+
+/[^\D\P{Nd}]/8
+ a9b
+ \x{1d7cf}
+ ** Failers
+ \x{10000}
+
/-- End of testinput16 --/
diff --git a/pcre/testdata/testinput19 b/pcre/testdata/testinput19
index ce45afcb595..dfe8c7befb6 100644
--- a/pcre/testdata/testinput19
+++ b/pcre/testdata/testinput19
@@ -25,4 +25,21 @@
/s+/i8SI
SSss\x{17f}
+/[\D]/8
+ \x{1d7cf}
+
+/[\D\P{Nd}]/8
+ \x{1d7cf}
+
+/[^\D]/8
+ a9b
+ ** Failers
+ \x{1d7cf}
+
+/[^\D\P{Nd}]/8
+ a9b
+ \x{1d7cf}
+ ** Failers
+ \x{10000}
+
/-- End of testinput19 --/
diff --git a/pcre/testdata/testinput2 b/pcre/testdata/testinput2
index 75e402ee9b3..08c6f39a565 100644
--- a/pcre/testdata/testinput2
+++ b/pcre/testdata/testinput2
@@ -4243,4 +4243,10 @@ backtracking verbs. --/
/\N(?(?C)0?!.)*/
+/(?<RA>abc)(?(R)xyz)/BZ
+
+/(?<R>abc)(?(R)xyz)/BZ
+
+/(?=.*[A-Z])/I
+
/-- End of testinput2 --/
diff --git a/pcre/testdata/testinput6 b/pcre/testdata/testinput6
index a178d3d67b4..22ed1e64d57 100644
--- a/pcre/testdata/testinput6
+++ b/pcre/testdata/testinput6
@@ -1562,4 +1562,10 @@
\x{389}
\x{20ac}
+/(?=.*b)\pL/
+ 11bb
+
+/(?(?=.*b)(?=.*b)\pL|.*c)/
+ 11bb
+
/-- End of testinput6 --/
diff --git a/pcre/testdata/testinput7 b/pcre/testdata/testinput7
index 00b9738a371..f44a810f0f2 100644
--- a/pcre/testdata/testinput7
+++ b/pcre/testdata/testinput7
@@ -838,15 +838,6 @@ of case for anything other than the ASCII letters. --/
/^s?c/mi8I
scat
-/[\W\p{Any}]/BZ
- abc
- 123
-
-/[\W\pL]/BZ
- abc
- ** Failers
- 123
-
/a[[:punct:]b]/WBZ
/a[[:punct:]b]/8WBZ
diff --git a/pcre/testdata/testinput8 b/pcre/testdata/testinput8
index 931dd717e74..7f8fa8292c5 100644
--- a/pcre/testdata/testinput8
+++ b/pcre/testdata/testinput8
@@ -4841,4 +4841,8 @@
bbb
aaa
+/()()a+/O=
+ aaa\D
+ a\D
+
/-- End of testinput8 --/
diff --git a/pcre/testdata/testoutput1 b/pcre/testdata/testoutput1
index e852ab9544c..a2b3cffe9d4 100644
--- a/pcre/testdata/testoutput1
+++ b/pcre/testdata/testoutput1
@@ -9434,4 +9434,12 @@ No match
0:
1:
+/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\s])/
+ \ Fred:099
+ 0:
+
+/(?=.*X)X$/
+ \ X
+ 0: X
+
/-- End of testinput1 --/
diff --git a/pcre/testdata/testoutput16 b/pcre/testdata/testoutput16
index fd184cdbeee..e6ba26acfd4 100644
--- a/pcre/testdata/testoutput16
+++ b/pcre/testdata/testoutput16
@@ -138,4 +138,56 @@ Starting chars: S s \xc5
SSss\x{17f}
0: SSss\x{17f}
+/[\W\p{Any}]/BZ
+------------------------------------------------------------------
+ Bra
+ [\x00-/:-@[-^`{-\xff\p{Any}]
+ Ket
+ End
+------------------------------------------------------------------
+ abc
+ 0: a
+ 123
+ 0: 1
+
+/[\W\pL]/BZ
+------------------------------------------------------------------
+ Bra
+ [\x00-/:-@[-^`{-\xff\p{L}]
+ Ket
+ End
+------------------------------------------------------------------
+ abc
+ 0: a
+ ** Failers
+ 0: *
+ 123
+No match
+
+/[\D]/8
+ \x{1d7cf}
+ 0: \x{1d7cf}
+
+/[\D\P{Nd}]/8
+ \x{1d7cf}
+ 0: \x{1d7cf}
+
+/[^\D]/8
+ a9b
+ 0: 9
+ ** Failers
+No match
+ \x{1d7cf}
+No match
+
+/[^\D\P{Nd}]/8
+ a9b
+ 0: 9
+ \x{1d7cf}
+ 0: \x{1d7cf}
+ ** Failers
+No match
+ \x{10000}
+No match
+
/-- End of testinput16 --/
diff --git a/pcre/testdata/testoutput19 b/pcre/testdata/testoutput19
index eb8a8f6cd34..982bea4c136 100644
--- a/pcre/testdata/testoutput19
+++ b/pcre/testdata/testoutput19
@@ -105,4 +105,30 @@ Starting chars: S s \xff
SSss\x{17f}
0: SSss\x{17f}
+/[\D]/8
+ \x{1d7cf}
+ 0: \x{1d7cf}
+
+/[\D\P{Nd}]/8
+ \x{1d7cf}
+ 0: \x{1d7cf}
+
+/[^\D]/8
+ a9b
+ 0: 9
+ ** Failers
+No match
+ \x{1d7cf}
+No match
+
+/[^\D\P{Nd}]/8
+ a9b
+ 0: 9
+ \x{1d7cf}
+ 0: \x{1d7cf}
+ ** Failers
+No match
+ \x{10000}
+No match
+
/-- End of testinput19 --/
diff --git a/pcre/testdata/testoutput2 b/pcre/testdata/testoutput2
index 5e88d1a7091..811bbefc84c 100644
--- a/pcre/testdata/testoutput2
+++ b/pcre/testdata/testoutput2
@@ -9380,7 +9380,7 @@ No need char
/(?(?=.*b).*b|^d)/I
Capturing subpattern count = 0
No options
-First char at start or follows newline
+No first char
No need char
/xyz/C
@@ -14670,4 +14670,39 @@ No match
/\N(?(?C)0?!.)*/
Failed: assertion expected after (?( or (?(?C) at offset 4
+/(?<RA>abc)(?(R)xyz)/BZ
+------------------------------------------------------------------
+ Bra
+ CBra 1
+ abc
+ Ket
+ Cond
+ Cond recurse any
+ xyz
+ Ket
+ Ket
+ End
+------------------------------------------------------------------
+
+/(?<R>abc)(?(R)xyz)/BZ
+------------------------------------------------------------------
+ Bra
+ CBra 1
+ abc
+ Ket
+ Cond
+ 1 Cond ref
+ xyz
+ Ket
+ Ket
+ End
+------------------------------------------------------------------
+
+/(?=.*[A-Z])/I
+Capturing subpattern count = 0
+May match empty string
+No options
+No first char
+No need char
+
/-- End of testinput2 --/
diff --git a/pcre/testdata/testoutput6 b/pcre/testdata/testoutput6
index b64dc0dc366..422d3833578 100644
--- a/pcre/testdata/testoutput6
+++ b/pcre/testdata/testoutput6
@@ -2573,4 +2573,12 @@ No match
\x{20ac}
No match
+/(?=.*b)\pL/
+ 11bb
+ 0: b
+
+/(?(?=.*b)(?=.*b)\pL|.*c)/
+ 11bb
+ 0: b
+
/-- End of testinput6 --/
diff --git a/pcre/testdata/testoutput7 b/pcre/testdata/testoutput7
index fdfff646d3e..2b167b28d1c 100644
--- a/pcre/testdata/testoutput7
+++ b/pcre/testdata/testoutput7
@@ -2295,32 +2295,6 @@ Need char = 'c' (caseless)
scat
0: sc
-/[\W\p{Any}]/BZ
-------------------------------------------------------------------
- Bra
- [\x00-/:-@[-^`{-\xff\p{Any}]
- Ket
- End
-------------------------------------------------------------------
- abc
- 0: a
- 123
- 0: 1
-
-/[\W\pL]/BZ
-------------------------------------------------------------------
- Bra
- [\x00-/:-@[-^`{-\xff\p{L}]
- Ket
- End
-------------------------------------------------------------------
- abc
- 0: a
- ** Failers
- 0: *
- 123
-No match
-
/a[[:punct:]b]/WBZ
------------------------------------------------------------------
Bra
diff --git a/pcre/testdata/testoutput8 b/pcre/testdata/testoutput8
index e4fa4977561..17b667a980c 100644
--- a/pcre/testdata/testoutput8
+++ b/pcre/testdata/testoutput8
@@ -7791,4 +7791,14 @@ Matched, but offsets vector is too small to show all matches
aaa
No match
+/()()a+/O=
+ aaa\D
+** Show all captures ignored after DFA matching
+ 0: aaa
+ 1: aa
+ 2: a
+ a\D
+** Show all captures ignored after DFA matching
+ 0: a
+
/-- End of testinput8 --/
diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c
index 4c5e6992a32..de0b7bfae8e 100644
--- a/plugin/server_audit/server_audit.c
+++ b/plugin/server_audit/server_audit.c
@@ -15,7 +15,7 @@
#define PLUGIN_VERSION 0x104
-#define PLUGIN_STR_VERSION "1.4.0"
+#define PLUGIN_STR_VERSION "1.4.1"
#define _my_thread_var loc_thread_var
@@ -156,10 +156,8 @@ static File loc_open(const char *FileName, int Flags)
File fd;
#if defined(_WIN32)
fd= my_win_open(FileName, Flags);
-#elif !defined(NO_OPEN_3)
- fd = open(FileName, Flags, my_umask); /* Normal unix */
#else
- fd = open((char *) FileName, Flags);
+ fd = open(FileName, Flags, my_umask);
#endif
my_errno= errno;
return fd;
@@ -2295,10 +2293,10 @@ typedef struct loc_system_variables
} LOC_SV;
+static int init_done= 0;
+
static int server_audit_init(void *p __attribute__((unused)))
{
- const void *my_hash_init_ptr;
-
if (!serv_ver)
{
#ifdef _WIN32
@@ -2307,11 +2305,16 @@ static int server_audit_init(void *p __attribute__((unused)))
serv_ver= server_version;
#endif /*_WIN32*/
}
- my_hash_init_ptr= dlsym(RTLD_DEFAULT, "_my_hash_init");
- if (!my_hash_init_ptr)
+ if (!mysql_57_started)
{
- maria_above_5= 1;
- my_hash_init_ptr= dlsym(RTLD_DEFAULT, "my_hash_init2");
+ const void *my_hash_init_ptr= dlsym(RTLD_DEFAULT, "_my_hash_init");
+ if (!my_hash_init_ptr)
+ {
+ maria_above_5= 1;
+ my_hash_init_ptr= dlsym(RTLD_DEFAULT, "my_hash_init2");
+ }
+ if (!my_hash_init_ptr)
+ return 1;
}
if(!(int_mysql_data_home= dlsym(RTLD_DEFAULT, "mysql_data_home")))
@@ -2320,7 +2323,7 @@ static int server_audit_init(void *p __attribute__((unused)))
int_mysql_data_home= &default_home;
}
- if (!serv_ver || !my_hash_init_ptr)
+ if (!serv_ver)
return 1;
if (!started_mysql)
@@ -2400,6 +2403,7 @@ static int server_audit_init(void *p __attribute__((unused)))
if (logging)
start_logging();
+ init_done= 1;
return 0;
}
@@ -2415,6 +2419,10 @@ static int server_audit_init_mysql(void *p)
static int server_audit_deinit(void *p __attribute__((unused)))
{
+ if (!init_done)
+ return 0;
+
+ init_done= 0;
coll_free(&incl_user_coll);
coll_free(&excl_user_coll);
@@ -2837,13 +2845,15 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void)
if (sc >= 24)
use_event_data_for_disconnect= 1;
}
- else if (serv_ver[0] == '5' && serv_ver[2] == '7')
+ else if ((serv_ver[0] == '5' && serv_ver[2] == '7') ||
+ (serv_ver[0] == '8' && serv_ver[2] == '0'))
{
mysql_57_started= 1;
_mysql_plugin_declarations_[0].info= mysql_v4_descriptor;
use_event_data_for_disconnect= 1;
}
- MYSQL_SYSVAR_NAME(loc_info).flags= PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC;
+ MYSQL_SYSVAR_NAME(loc_info).flags= PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL |
+ PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC;
}
memset(locinfo_ini_value, 'O', sizeof(locinfo_ini_value)-1);
diff --git a/sql-common/client.c b/sql-common/client.c
index 756d1c1972c..0430946b2ff 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -1,5 +1,5 @@
/* Copyright (c) 2003, 2016, Oracle and/or its affiliates.
- Copyright (c) 2009, 2016, MariaDB
+ Copyright (c) 2009, 2017, MariaDB
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
@@ -3866,8 +3866,6 @@ static void mysql_close_free(MYSQL *mysql)
static void mysql_prune_stmt_list(MYSQL *mysql)
{
LIST *element= mysql->stmts;
- LIST *pruned_list= 0;
-
for (; element; element= element->next)
{
MYSQL_STMT *stmt= (MYSQL_STMT *) element->data;
@@ -3877,14 +3875,9 @@ static void mysql_prune_stmt_list(MYSQL *mysql)
stmt->last_errno= CR_SERVER_LOST;
strmov(stmt->last_error, ER(CR_SERVER_LOST));
strmov(stmt->sqlstate, unknown_sqlstate);
- }
- else
- {
- pruned_list= list_add(pruned_list, element);
+ mysql->stmts= list_delete(mysql->stmts, element);
}
}
-
- mysql->stmts= pruned_list;
}
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 90efd730875..29054c76cbe 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -7271,6 +7271,7 @@ int ha_partition::reset(void)
result= tmp;
}
bitmap_clear_all(&m_partitions_to_reset);
+ m_extra_prepare_for_update= FALSE;
DBUG_RETURN(result);
}
diff --git a/sql/handler.cc b/sql/handler.cc
index 7ac8dd63e9e..751b6d3ca3c 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -3355,6 +3355,7 @@ void handler::print_error(int error, myf errflag)
textno=ER_FILE_USED;
break;
case ENOENT:
+ case ENOTDIR:
textno=ER_FILE_NOT_FOUND;
break;
case ENOSPC:
@@ -3831,7 +3832,6 @@ int handler::delete_table(const char *name)
int saved_error= 0;
int error= 0;
int enoent_or_zero;
- char buff[FN_REFLEN];
if (ht->discover_table)
enoent_or_zero= 0; // the table may not exist in the engine, it's ok
@@ -3840,8 +3840,7 @@ int handler::delete_table(const char *name)
for (const char **ext=bas_ext(); *ext ; ext++)
{
- fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT);
- if (mysql_file_delete_with_symlink(key_file_misc, buff, MYF(0)))
+ if (my_handler_delete_with_symlink(key_file_misc, name, *ext, 0))
{
if (my_errno != ENOENT)
{
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index e78f73ee03c..d2ffa0e64f9 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -5151,6 +5151,18 @@ bool Item_func_like::with_sargable_pattern() const
}
+SEL_TREE *Item_func_like::get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
+{
+ MEM_ROOT *tmp_root= param->mem_root;
+ param->thd->mem_root= param->old_root;
+ bool sargable_pattern= with_sargable_pattern();
+ param->thd->mem_root= tmp_root;
+ return sargable_pattern ?
+ Item_bool_func2::get_mm_tree(param, cond_ptr) :
+ Item_func::get_mm_tree(param, cond_ptr);
+}
+
+
bool Item_func_like::fix_fields(THD *thd, Item **ref)
{
DBUG_ASSERT(fixed == 0);
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 697420df0e8..4015255e7ad 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -1842,12 +1842,7 @@ public:
}
void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level,
table_map usable_tables, SARGABLE_PARAM **sargables);
- SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr)
- {
- return with_sargable_pattern() ?
- Item_bool_func2::get_mm_tree(param, cond_ptr) :
- Item_func::get_mm_tree(param, cond_ptr);
- }
+ SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr);
Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond)
{
/*
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 3157d666e9b..6330165702e 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -928,7 +928,7 @@ void Item_subselect::update_used_tables()
if (!forced_const)
{
recalc_used_tables(parent_select, FALSE);
- if (!engine->uncacheable())
+ if (!(engine->uncacheable() & ~UNCACHEABLE_EXPLAIN))
{
// did all used tables become static?
if (!(used_tables_cache & ~engine->upper_select_const_tables()))
@@ -2104,6 +2104,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
We can encounter "NULL IN (SELECT ...)". Wrap the added condition
within a trig_cond.
*/
+ disable_cond_guard_for_const_null_left_expr(0);
item= new (thd->mem_root) Item_func_trig_cond(thd, item, get_cond_guard(0));
}
@@ -2128,6 +2129,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
having= new (thd->mem_root) Item_is_not_null_test(thd, this, having);
if (left_expr->maybe_null)
{
+ disable_cond_guard_for_const_null_left_expr(0);
if (!(having= new (thd->mem_root) Item_func_trig_cond(thd, having,
get_cond_guard(0))))
DBUG_RETURN(true);
@@ -2146,6 +2148,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
*/
if (!abort_on_null && left_expr->maybe_null)
{
+ disable_cond_guard_for_const_null_left_expr(0);
if (!(item= new (thd->mem_root) Item_func_trig_cond(thd, item,
get_cond_guard(0))))
DBUG_RETURN(true);
@@ -2175,6 +2178,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join,
(char *)"<result>"));
if (!abort_on_null && left_expr->maybe_null)
{
+ disable_cond_guard_for_const_null_left_expr(0);
if (!(new_having= new (thd->mem_root) Item_func_trig_cond(thd, new_having,
get_cond_guard(0))))
DBUG_RETURN(true);
@@ -2374,6 +2378,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
Item_cond_or(thd, item_eq, item_isnull);
if (!abort_on_null && left_expr->element_index(i)->maybe_null)
{
+ disable_cond_guard_for_const_null_left_expr(i);
if (!(col_item= new (thd->mem_root)
Item_func_trig_cond(thd, col_item, get_cond_guard(i))))
DBUG_RETURN(true);
@@ -2391,6 +2396,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
(char *)"<list ref>"));
if (!abort_on_null && left_expr->element_index(i)->maybe_null)
{
+ disable_cond_guard_for_const_null_left_expr(i);
if (!(item_nnull_test=
new (thd->mem_root)
Item_func_trig_cond(thd, item_nnull_test, get_cond_guard(i))))
@@ -2451,6 +2457,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
item= new (thd->mem_root) Item_cond_or(thd, item, item_isnull);
if (left_expr->element_index(i)->maybe_null)
{
+ disable_cond_guard_for_const_null_left_expr(i);
if (!(item= new (thd->mem_root)
Item_func_trig_cond(thd, item, get_cond_guard(i))))
DBUG_RETURN(true);
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index d18db4aab86..6669027338e 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -618,6 +618,15 @@ public:
bool expr_cache_is_needed(THD *thd);
inline bool left_expr_has_null();
+ void disable_cond_guard_for_const_null_left_expr(int i)
+ {
+ if (left_expr->const_item() && !left_expr->is_expensive())
+ {
+ if (left_expr->element_index(i)->is_null())
+ set_cond_guard_var(i,FALSE);
+ }
+ }
+
int optimize(double *out_rows, double *cost);
/*
Return the identifier that we could use to identify the subquery for the
diff --git a/sql/log_slow.h b/sql/log_slow.h
index 3ae2060cc27..aea5b149263 100644
--- a/sql/log_slow.h
+++ b/sql/log_slow.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2009 Monty Program Ab
+/* Copyright (C) 2009, 2017, MariaDB Corporation.
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
@@ -16,23 +16,22 @@
/* Defining what to log to slow log */
#define LOG_SLOW_VERBOSITY_INIT 0
-#define LOG_SLOW_VERBOSITY_INNODB (1 << 0)
-#define LOG_SLOW_VERBOSITY_QUERY_PLAN (1 << 1)
-#define LOG_SLOW_VERBOSITY_EXPLAIN (1 << 2)
+#define LOG_SLOW_VERBOSITY_INNODB (1U << 0)
+#define LOG_SLOW_VERBOSITY_QUERY_PLAN (1U << 1)
+#define LOG_SLOW_VERBOSITY_EXPLAIN (1U << 2)
#define QPLAN_INIT QPLAN_QC_NO
-#define QPLAN_ADMIN (1 << 0)
-#define QPLAN_FILESORT (1 << 1)
-#define QPLAN_FILESORT_DISK (1 << 2)
-#define QPLAN_FULL_JOIN (1 << 3)
-#define QPLAN_FULL_SCAN (1 << 4)
-#define QPLAN_QC (1 << 5)
-#define QPLAN_QC_NO (1 << 6)
-#define QPLAN_TMP_DISK (1 << 7)
-#define QPLAN_TMP_TABLE (1 << 8)
-#define QPLAN_FILESORT_PRIORITY_QUEUE (1 << 9)
-
+#define QPLAN_ADMIN (1U << 0)
+#define QPLAN_FILESORT (1U << 1)
+#define QPLAN_FILESORT_DISK (1U << 2)
+#define QPLAN_FULL_JOIN (1U << 3)
+#define QPLAN_FULL_SCAN (1U << 4)
+#define QPLAN_QC (1U << 5)
+#define QPLAN_QC_NO (1U << 6)
+#define QPLAN_TMP_DISK (1U << 7)
+#define QPLAN_TMP_TABLE (1U << 8)
+#define QPLAN_FILESORT_PRIORITY_QUEUE (1U << 9)
+
/* ... */
-#define QPLAN_MAX (((ulong) 1) << 31) /* reserved as placeholder */
-
+#define QPLAN_MAX (1U << 31) /* reserved as placeholder */
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 2607307ceee..4f83e19f905 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -21,7 +21,7 @@
#ifndef __WIN__
#include <netdb.h> // getservbyname, servent
#endif
-#include "sql_parse.h" // test_if_data_home_dir
+#include "sql_parse.h" // path_starts_from_data_home_dir
#include "sql_cache.h" // query_cache, query_cache_*
#include "sql_locale.h" // MY_LOCALES, my_locales, my_locale_by_name
#include "sql_show.h" // free_status_vars, add_status_vars,
@@ -8612,7 +8612,7 @@ static int mysql_init_variables(void)
mysql_home[0]= pidfile_name[0]= log_error_file[0]= 0;
#if defined(HAVE_REALPATH) && !defined(HAVE_valgrind) && !defined(HAVE_BROKEN_REALPATH)
/* We can only test for sub paths if my_symlink.c is using realpath */
- myisam_test_invalid_symlink= test_if_data_home_dir;
+ mysys_test_invalid_symlink= path_starts_from_data_home_dir;
#endif
opt_log= 0;
opt_bin_log= opt_bin_log_used= 0;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 0577ef01bb2..bb4f7370ac9 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -9300,6 +9300,13 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
if (!tmp->next_key_part)
{
+ if (key2->use_count)
+ {
+ SEL_ARG *key2_cpy= new SEL_ARG(*key2);
+ if (key2_cpy)
+ return 0;
+ key2= key2_cpy;
+ }
/*
tmp->next_key_part is empty: cut the range that is covered
by tmp from key2.
@@ -9331,13 +9338,6 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
key2: [---]
tmp: [---------]
*/
- if (key2->use_count)
- {
- SEL_ARG *key2_cpy= new SEL_ARG(*key2);
- if (key2_cpy)
- return 0;
- key2= key2_cpy;
- }
key2->copy_max_to_min(tmp);
continue;
}
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index a92c7686eaf..bc0db9e0174 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -27,7 +27,7 @@
#include "sql_partition.h" // partition_info.h: LIST_PART_ENTRY
// NOT_A_PARTITION_ID
#include "partition_info.h"
-#include "sql_parse.h" // test_if_data_home_dir
+#include "sql_parse.h"
#include "sql_acl.h" // *_ACL
#include "sql_base.h" // fill_record
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 912930d60f3..cb4c3cb1049 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -9096,13 +9096,13 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case USER_ACL:
acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str);
acl_user->user.length= user_to->user.length;
- acl_user->host.hostname= strdup_root(&acl_memroot, user_to->host.str);
- acl_user->hostname_length= user_to->host.length;
+ update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
+ acl_user->hostname_length= strlen(acl_user->host.hostname);
break;
case DB_ACL:
acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
- acl_db->host.hostname= strdup_root(&acl_memroot, user_to->host.str);
+ update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str));
break;
case COLUMN_PRIVILEGES_HASH:
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index cb50301d79d..701f4e6aa4e 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -846,7 +846,8 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent)
if there exists a table with the name 'db', so let's just do it
separately. We know this file exists and needs to be deleted anyway.
*/
- if (my_delete_with_symlink(path, MYF(0)) && my_errno != ENOENT)
+ if (my_handler_delete_with_symlink(key_file_misc, path, "", MYF(0)) &&
+ my_errno != ENOENT)
{
my_error(EE_DELETE, MYF(0), path, my_errno);
DBUG_RETURN(true);
@@ -1149,9 +1150,9 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
strxmov(filePath, path, "/", file->name, NullS);
/*
We ignore ENOENT error in order to skip files that was deleted
- by concurrently running statement like REAPIR TABLE ...
+ by concurrently running statement like REPAIR TABLE ...
*/
- if (my_delete_with_symlink(filePath, MYF(0)) &&
+ if (my_handler_delete_with_symlink(key_file_misc, filePath, "", MYF(0)) &&
my_errno != ENOENT)
{
my_error(EE_DELETE, MYF(0), filePath, my_errno);
@@ -1267,7 +1268,7 @@ long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path)
continue;
}
strxmov(filePath, org_path, "/", file->name, NullS);
- if (mysql_file_delete_with_symlink(key_file_misc, filePath, MYF(MY_WME)))
+ if (my_handler_delete_with_symlink(key_file_misc, filePath, "", MYF(MY_WME)))
{
goto err;
}
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 493f231bb39..d9457ba6624 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -447,6 +447,9 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
{
Item *expr= derived->on_expr;
expr= and_conds(thd, expr, dt_select->join ? dt_select->join->conds : 0);
+ if (expr)
+ expr->top_level_item();
+
if (expr && (derived->prep_on_expr || expr != derived->on_expr))
{
derived->on_expr= expr;
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index 818598110ca..9df993cf035 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -589,6 +589,11 @@ void JOIN_CACHE::create_remaining_fields()
{
MY_BITMAP *rem_field_set;
TABLE *table= tab->table;
+#if MYSQL_VERSION_ID < 100204
+ empty_record(table);
+#else
+#error remove
+#endif
if (all_read_fields)
rem_field_set= table->read_set;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 45815fe7a02..7af299f3f06 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2123,12 +2123,12 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
#endif
case SCH_COLUMNS:
case SCH_STATISTICS:
- {
#ifdef DONT_ALLOW_SHOW_COMMANDS
my_message(ER_NOT_ALLOWED_COMMAND,
ER_THD(thd, ER_NOT_ALLOWED_COMMAND), MYF(0));
DBUG_RETURN(1);
#else
+ {
DBUG_ASSERT(table_ident);
TABLE_LIST **query_tables_last= lex->query_tables_last;
schema_select_lex= new SELECT_LEX();
@@ -7248,7 +7248,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
}
/*
- When you modify mysql_parse(), you may need to mofify
+ When you modify mysql_parse(), you may need to modify
mysql_test_parse_for_slave() in this same file.
*/
@@ -9134,48 +9134,24 @@ bool check_ident_length(LEX_STRING *ident)
}
-C_MODE_START
-
/*
Check if path does not contain mysql data home directory
SYNOPSIS
- test_if_data_home_dir()
- dir directory
+ path_starts_from_data_home_dir()
+ dir directory, with all symlinks resolved
RETURN VALUES
0 ok
1 error ; Given path contains data directory
*/
+extern "C" {
-int test_if_data_home_dir(const char *dir)
+int path_starts_from_data_home_dir(const char *path)
{
- char path[FN_REFLEN];
- int dir_len;
- DBUG_ENTER("test_if_data_home_dir");
+ int dir_len= strlen(path);
+ DBUG_ENTER("path_starts_from_data_home_dir");
- if (!dir)
- DBUG_RETURN(0);
-
- /*
- data_file_name and index_file_name include the table name without
- extension. Mostly this does not refer to an existing file. When
- comparing data_file_name or index_file_name against the data
- directory, we try to resolve all symbolic links. On some systems,
- we use realpath(3) for the resolution. This returns ENOENT if the
- resolved path does not refer to an existing file. my_realpath()
- does then copy the requested path verbatim, without symlink
- resolution. Thereafter the comparison can fail even if the
- requested path is within the data directory. E.g. if symlinks to
- another file system are used. To make realpath(3) return the
- resolved path, we strip the table name and compare the directory
- path only. If the directory doesn't exist either, table creation
- will fail anyway.
- */
-
- (void) fn_format(path, dir, "", "",
- (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS));
- dir_len= strlen(path);
if (mysql_unpacked_real_data_home_len<= dir_len)
{
if (dir_len > mysql_unpacked_real_data_home_len &&
@@ -9203,7 +9179,31 @@ int test_if_data_home_dir(const char *dir)
DBUG_RETURN(0);
}
-C_MODE_END
+}
+
+/*
+ Check if path does not contain mysql data home directory
+
+ SYNOPSIS
+ test_if_data_home_dir()
+ dir directory
+
+ RETURN VALUES
+ 0 ok
+ 1 error ; Given path contains data directory
+*/
+
+int test_if_data_home_dir(const char *dir)
+{
+ char path[FN_REFLEN];
+ DBUG_ENTER("test_if_data_home_dir");
+
+ if (!dir)
+ DBUG_RETURN(0);
+
+ (void) fn_format(path, dir, "", "", MY_RETURN_REAL_PATH);
+ DBUG_RETURN(path_starts_from_data_home_dir(path));
+}
int error_if_data_home_dir(const char *path, const char *what)
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index c50090a95cc..da9721df0b7 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -33,7 +33,8 @@ enum enum_mysql_completiontype {
COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6
};
-extern "C" int test_if_data_home_dir(const char *dir);
+extern "C" int path_starts_from_data_home_dir(const char *dir);
+int test_if_data_home_dir(const char *dir);
int error_if_data_home_dir(const char *path, const char *what);
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index cc07067bc8d..cb39cb3a8b8 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -8679,8 +8679,6 @@ get_best_combination(JOIN *join)
form=join->table[tablenr]=j->table;
used_tables|= form->map;
form->reginfo.join_tab=j;
- if (!*j->on_expr_ref)
- form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN
DBUG_PRINT("info",("type: %d", j->type));
if (j->type == JT_CONST)
goto loop_end; // Handled in make_join_stat..
@@ -9542,7 +9540,10 @@ make_outerjoin_info(JOIN *join)
tab->cond_equal= tbl->cond_equal;
if (embedding && !embedding->is_active_sjm())
tab->first_upper= embedding->nested_join->first_nested;
- }
+ }
+ else if (!embedding)
+ tab->table->reginfo.not_exists_optimize= 0;
+
for ( ; embedding ; embedding= embedding->embedding)
{
if (embedding->is_active_sjm())
@@ -9552,7 +9553,10 @@ make_outerjoin_info(JOIN *join)
}
/* Ignore sj-nests: */
if (!(embedding->on_expr && embedding->outer_join))
+ {
+ tab->table->reginfo.not_exists_optimize= 0;
continue;
+ }
NESTED_JOIN *nested_join= embedding->nested_join;
if (!nested_join->counter)
{
@@ -9568,17 +9572,10 @@ make_outerjoin_info(JOIN *join)
}
if (!tab->first_inner)
tab->first_inner= nested_join->first_nested;
- if (tab->table->reginfo.not_exists_optimize)
- tab->first_inner->table->reginfo.not_exists_optimize= 1;
if (++nested_join->counter < nested_join->n_tables)
break;
/* Table tab is the last inner table for nested join. */
nested_join->first_nested->last_inner= tab;
- if (tab->first_inner->table->reginfo.not_exists_optimize)
- {
- for (JOIN_TAB *join_tab= tab->first_inner; join_tab <= tab; join_tab++)
- join_tab->table->reginfo.not_exists_optimize= 1;
- }
}
}
DBUG_RETURN(FALSE);
@@ -10352,7 +10349,7 @@ void JOIN::drop_unused_derived_keys()
continue;
if (!tmp_tbl->pos_in_table_list->is_materialized_derived())
continue;
- if (tmp_tbl->max_keys > 1)
+ if (tmp_tbl->max_keys > 1 && !tab->is_ref_for_hash_join())
tmp_tbl->use_index(tab->ref.key);
if (tmp_tbl->s->keys)
{
@@ -15924,7 +15921,9 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table,
DBUG_ASSERT(thd == table->in_use);
new_field= item->Item::create_tmp_field(false, table);
- if (copy_func && item->real_item()->is_result_field())
+ if (copy_func &&
+ (item->is_result_field() ||
+ (item->real_item()->is_result_field())))
*((*copy_func)++) = item; // Save for copy_funcs
if (modify_item)
item->set_result_field(new_field);
@@ -18440,32 +18439,41 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
first_unmatched->found= 1;
for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++)
{
+ /*
+ Check whether 'not exists' optimization can be used here.
+ If tab->table->reginfo.not_exists_optimize is set to true
+ then WHERE contains a conjunctive predicate IS NULL over
+ a non-nullable field of tab. When activated this predicate
+ will filter out all records with matches for the left part
+ of the outer join whose inner tables start from the
+ first_unmatched table and include table tab. To safely use
+ 'not exists' optimization we have to check that the
+ IS NULL predicate is really activated, i.e. all guards
+ that wrap it are in the 'open' state.
+ */
+ bool not_exists_opt_is_applicable=
+ tab->table->reginfo.not_exists_optimize;
+ for (JOIN_TAB *first_upper= first_unmatched->first_upper;
+ not_exists_opt_is_applicable && first_upper;
+ first_upper= first_upper->first_upper)
+ {
+ if (!first_upper->found)
+ not_exists_opt_is_applicable= false;
+ }
/* 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.
*/
- /*
- not_exists_optimize has been created from a
- select_cond containing 'is_null'. This 'is_null'
- predicate is still present on any 'tab' with
- 'not_exists_optimize'. Furthermore, the usual rules
- for condition guards also applies for
- 'not_exists_optimize' -> When 'is_null==false' we
- know all cond. guards are open and we can apply
- the 'not_exists_optimize'.
- */
- DBUG_ASSERT(!(tab->table->reginfo.not_exists_optimize &&
- !tab->select_cond));
-
if (tab->select_cond && !tab->select_cond->val_int())
{
/* The condition attached to table tab is false */
-
if (tab == join_tab)
{
found= 0;
+ if (not_exists_opt_is_applicable)
+ DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
}
else
{
@@ -18474,21 +18482,10 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
not to the last table of the current nest level.
*/
join->return_tab= tab;
- }
-
- if (tab->table->reginfo.not_exists_optimize)
- {
- /*
- When not_exists_optimize is set: No need to further
- explore more rows of 'tab' for this partial result.
- Any found 'tab' matches are known to evaluate to 'false'.
- Returning .._NO_MORE_ROWS will skip rem. 'tab' rows.
- */
- DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
- }
- else if (tab != join_tab)
- {
- DBUG_RETURN(NESTED_LOOP_OK);
+ if (not_exists_opt_is_applicable)
+ DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
+ else
+ DBUG_RETURN(NESTED_LOOP_OK);
}
}
}
diff --git a/sql/table.cc b/sql/table.cc
index 759a2d05de7..fe09ec8948d 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -571,7 +571,7 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
{
DBUG_ASSERT(flags & GTS_TABLE);
DBUG_ASSERT(flags & GTS_USE_DISCOVERY);
- mysql_file_delete_with_symlink(key_file_frm, path, MYF(0));
+ my_handler_delete_with_symlink(key_file_frm, path, "", MYF(0));
file= -1;
}
else
diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt
index ce6de424421..a602084b5bd 100644
--- a/storage/connect/CMakeLists.txt
+++ b/storage/connect/CMakeLists.txt
@@ -22,16 +22,16 @@ fmdlex.c osutil.c plugutil.c rcmsg.c rcmsg.h
array.cpp blkfil.cpp colblk.cpp csort.cpp
filamap.cpp filamdbf.cpp filamfix.cpp filamgz.cpp filamtxt.cpp
filter.cpp json.cpp jsonudf.cpp maputil.cpp myconn.cpp myutil.cpp plgdbutl.cpp
-reldef.cpp tabcol.cpp tabdos.cpp tabfix.cpp tabfmt.cpp tabjson.cpp table.cpp
-tabmul.cpp tabmysql.cpp taboccur.cpp tabpivot.cpp tabsys.cpp tabtbl.cpp tabutil.cpp
-tabvir.cpp tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.cpp
+reldef.cpp tabcol.cpp tabdos.cpp tabext.cpp tabfix.cpp tabfmt.cpp tabjson.cpp
+table.cpp tabmul.cpp tabmysql.cpp taboccur.cpp tabpivot.cpp tabsys.cpp tabtbl.cpp
+tabutil.cpp tabvir.cpp tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.cpp
array.h blkfil.h block.h catalog.h checklvl.h colblk.h connect.h csort.h
engmsg.h filamap.h filamdbf.h filamfix.h filamgz.h filamtxt.h
filter.h global.h ha_connect.h inihandl.h json.h jsonudf.h maputil.h msgid.h
mycat.h myconn.h myutil.h os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h
-resource.h tabcol.h tabdos.h tabfix.h tabfmt.h tabjson.h tabmul.h tabmysql.h
-taboccur.h tabpivot.h tabsys.h tabtbl.h tabutil.h tabvir.h tabxcl.h
+resource.h tabcol.h tabdos.h tabext.h tabfix.h tabfmt.h tabjson.h tabmul.h
+tabmysql.h taboccur.h tabpivot.h tabsys.h tabtbl.h tabutil.h tabvir.h tabxcl.h
user_connect.h valblk.h value.h xindex.h xobject.h xtable.h)
#
diff --git a/storage/connect/array.cpp b/storage/connect/array.cpp
index 193514eeb99..1998ab890e9 100644
--- a/storage/connect/array.cpp
+++ b/storage/connect/array.cpp
@@ -1,7 +1,7 @@
/************* Array C++ Functions Source Code File (.CPP) *************/
/* Name: ARRAY.CPP Version 2.3 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */
/* */
/* This file contains the XOBJECT derived class ARRAY functions. */
/* ARRAY is used for elaborate type of processing, such as sorting */
@@ -141,7 +141,7 @@ PARRAY MakeValueArray(PGLOBAL g, PPARM pp)
/* ARRAY public constructor. */
/***********************************************************************/
ARRAY::ARRAY(PGLOBAL g, int type, int size, int length, int prec)
- : CSORT(FALSE)
+ : CSORT(false)
{
Nval = 0;
Ndif = 0;
@@ -188,14 +188,14 @@ ARRAY::ARRAY(PGLOBAL g, int type, int size, int length, int prec)
else if (type != TYPE_PCHAR)
Value = AllocateValue(g, type, Len, prec);
- Constant = TRUE;
+ Constant = true;
} // end of ARRAY constructor
#if 0
/***********************************************************************/
/* ARRAY public constructor from a QUERY. */
/***********************************************************************/
-ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(FALSE)
+ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(false)
{
Type = qryp->GetColType(0);
Nval = qryp->GetNblin();
@@ -206,7 +206,7 @@ ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(FALSE)
Xsize = -1;
Len = qryp->GetColLength(0);
X = Inf = Sup = 0;
- Correlated = FALSE;
+ Correlated = false;
switch (Type) {
case TYPE_STRING:
@@ -229,13 +229,13 @@ ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(FALSE)
// The error message was built by ???
Type = TYPE_ERROR;
- Constant = TRUE;
+ Constant = true;
} // end of ARRAY constructor
/***********************************************************************/
/* ARRAY constructor from a TYPE_LIST subarray. */
/***********************************************************************/
-ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(FALSE)
+ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(false)
{
int prec;
LSTBLK *lp;
@@ -260,7 +260,7 @@ ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(FALSE)
Len = (Type == TYPE_STRING) ? Vblp->GetVlen() : 0;
prec = (Type == TYPE_FLOAT) ? 2 : 0;
Value = AllocateValue(g, Type, Len, prec, NULL);
- Constant = TRUE;
+ Constant = true;
} // end of ARRAY constructor
/***********************************************************************/
@@ -283,7 +283,7 @@ bool ARRAY::AddValue(PGLOBAL g, PSZ strp)
{
if (Type != TYPE_STRING) {
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "CHAR");
- return TRUE;
+ return true;
} // endif Type
if (trace)
@@ -292,7 +292,7 @@ bool ARRAY::AddValue(PGLOBAL g, PSZ strp)
//Value->SetValue_psz(strp);
//Vblp->SetValue(valp, Nval++);
Vblp->SetValue(strp, Nval++);
- return FALSE;
+ return false;
} // end of AddValue
/***********************************************************************/
@@ -302,14 +302,14 @@ bool ARRAY::AddValue(PGLOBAL g, void *p)
{
if (Type != TYPE_PCHAR) {
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "PCHAR");
- return TRUE;
+ return true;
} // endif Type
if (trace)
htrc(" adding pointer(%d): %p\n", Nval, p);
Vblp->SetValue((PSZ)p, Nval++);
- return FALSE;
+ return false;
} // end of AddValue
/***********************************************************************/
@@ -319,7 +319,7 @@ bool ARRAY::AddValue(PGLOBAL g, short n)
{
if (Type != TYPE_SHORT) {
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "SHORT");
- return TRUE;
+ return true;
} // endif Type
if (trace)
@@ -328,7 +328,7 @@ bool ARRAY::AddValue(PGLOBAL g, short n)
//Value->SetValue(n);
//Vblp->SetValue(valp, Nval++);
Vblp->SetValue(n, Nval++);
- return FALSE;
+ return false;
} // end of AddValue
/***********************************************************************/
@@ -338,7 +338,7 @@ bool ARRAY::AddValue(PGLOBAL g, int n)
{
if (Type != TYPE_INT) {
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "INTEGER");
- return TRUE;
+ return true;
} // endif Type
if (trace)
@@ -347,7 +347,7 @@ bool ARRAY::AddValue(PGLOBAL g, int n)
//Value->SetValue(n);
//Vblp->SetValue(valp, Nval++);
Vblp->SetValue(n, Nval++);
- return FALSE;
+ return false;
} // end of AddValue
/***********************************************************************/
@@ -357,7 +357,7 @@ bool ARRAY::AddValue(PGLOBAL g, double d)
{
if (Type != TYPE_DOUBLE) {
sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "DOUBLE");
- return TRUE;
+ return true;
} // endif Type
if (trace)
@@ -365,7 +365,7 @@ bool ARRAY::AddValue(PGLOBAL g, double d)
Value->SetValue(d);
Vblp->SetValue(Value, Nval++);
- return FALSE;
+ return false;
} // end of AddValue
/***********************************************************************/
@@ -376,7 +376,7 @@ bool ARRAY::AddValue(PGLOBAL g, PXOB xp)
if (Type != xp->GetResultType()) {
sprintf(g->Message, MSG(ADD_BAD_TYPE),
GetTypeName(xp->GetResultType()), GetTypeName(Type));
- return TRUE;
+ return true;
} // endif Type
if (trace)
@@ -384,7 +384,7 @@ bool ARRAY::AddValue(PGLOBAL g, PXOB xp)
//AddValue(xp->GetValue());
Vblp->SetValue(xp->GetValue(), Nval++);
- return FALSE;
+ return false;
} // end of AddValue
/***********************************************************************/
@@ -395,14 +395,14 @@ bool ARRAY::AddValue(PGLOBAL g, PVAL vp)
if (Type != vp->GetType()) {
sprintf(g->Message, MSG(ADD_BAD_TYPE),
GetTypeName(vp->GetType()), GetTypeName(Type));
- return TRUE;
+ return true;
} // endif Type
if (trace)
htrc(" adding (%d) from vp=%p\n", Nval, vp);
Vblp->SetValue(vp, Nval++);
- return FALSE;
+ return false;
} // end of AddValue
/***********************************************************************/
@@ -423,12 +423,12 @@ bool ARRAY::GetSubValue(PGLOBAL g, PVAL valp, int *kp)
if (Type != TYPE_LIST) {
sprintf(g->Message, MSG(NO_SUB_VAL), Type);
- return TRUE;
+ return true;
} // endif Type
vblp = ((LSTBLK*)Vblp)->Mbvk[kp[0]]->Vblk;
valp->SetValue_pvblk(vblp, kp[1]);
- return FALSE;
+ return false;
} // end of GetSubValue
#endif // 0
@@ -476,11 +476,11 @@ bool ARRAY::Find(PVAL valp)
else if (n > 0)
Inf = X;
else
- return TRUE;
+ return true;
} // endwhile
- return FALSE;
+ return false;
} // end of Find
/***********************************************************************/
@@ -504,9 +504,9 @@ bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm)
int top = Nval - 1;
if (top < 0) // Array is empty
- // Return TRUE for ALL because it means that there are no item that
+ // Return true for ALL because it means that there are no item that
// does not verify the condition, which is true indeed.
- // Return FALSE for ANY because TRUE means that there is at least
+ // Return false for ANY because true means that there is at least
// one item that verifies the condition, which is false.
return opm == 2;
@@ -528,9 +528,9 @@ bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm)
else if (opc == OP_NE && opm == 2)
return !Find(vp);
else if (opc == OP_EQ && opm == 2)
- return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : FALSE;
+ return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : false;
else if (opc == OP_NE && opm == 1)
- return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : TRUE;
+ return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : true;
if (Type != TYPE_LIST) {
if (opc == OP_GT || opc == OP_GE)
@@ -544,15 +544,15 @@ bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm)
if (opm == 2) {
for (i = 0; i < Nval; i++)
if (Vcompare(vp, i) & bt)
- return FALSE;
+ return false;
- return TRUE;
+ return true;
} else { // opm == 1
for (i = 0; i < Nval; i++)
if (!(Vcompare(vp, i) & bt))
- return TRUE;
+ return true;
- return FALSE;
+ return false;
} // endif opm
} // end of FilTest
@@ -566,7 +566,7 @@ bool ARRAY::CanBeShort(void)
int* To_Val = (int*)Valblk->GetMemp();
if (Type != TYPE_INT || !Ndif)
- return FALSE;
+ return false;
// Because the array is sorted, this is true if all the array
// int values are in the range of SHORT values
@@ -582,7 +582,7 @@ bool ARRAY::CanBeShort(void)
int ARRAY::Convert(PGLOBAL g, int k, PVAL vp)
{
int i, prec = 0;
- bool b = FALSE;
+ bool b = false;
PMBV ovblk = Valblk;
PVBLK ovblp = Vblp;
@@ -619,7 +619,7 @@ int ARRAY::Convert(PGLOBAL g, int k, PVAL vp)
if (((DTVAL*)Value)->SetFormat(g, vp))
return TYPE_ERROR;
else
- b = TRUE; // Sort the new array on date internal values
+ b = true; // Sort the new array on date internal values
/*********************************************************************/
/* Do the actual conversion. */
@@ -706,7 +706,7 @@ void ARRAY::SetPrecision(PGLOBAL g, int p)
/***********************************************************************/
/* Sort and eliminate distinct values from an array. */
/* Note: this is done by making a sorted index on distinct values. */
-/* Returns FALSE if Ok or TRUE in case of error. */
+/* Returns false if Ok or true in case of error. */
/***********************************************************************/
bool ARRAY::Sort(PGLOBAL g)
{
@@ -789,14 +789,14 @@ bool ARRAY::Sort(PGLOBAL g)
Bot = -1; // For non optimized search
Top = Ndif; // Find searches the whole array.
- return FALSE;
+ return false;
error:
Nval = Ndif = 0;
Valblk->Free();
PlgDBfree(Index);
PlgDBfree(Offset);
- return TRUE;
+ return true;
} // end of Sort
/***********************************************************************/
@@ -839,9 +839,9 @@ void *ARRAY::GetSortIndex(PGLOBAL g)
/***********************************************************************/
/* Block filter testing for IN operator on Column/Array operands. */
-/* Here we call Find that returns TRUE if the value is in the array */
+/* Here we call Find that returns true if the value is in the array */
/* with X equal to the index of the found value in the array, or */
-/* FALSE if the value is not in the array with Inf and Sup being the */
+/* false if the value is not in the array with Inf and Sup being the */
/* indexes of the array values that are immediately below and over */
/* the not found value. This enables to restrict the array to the */
/* values that are between the min and max block values and to return */
@@ -854,9 +854,9 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm,
bool bin, bax, pin, pax, veq, all = (opm == 2);
if (Ndif == 0) // Array is empty
- // Return TRUE for ALL because it means that there are no item that
+ // Return true for ALL because it means that there are no item that
// does not verify the condition, which is true indeed.
- // Return FALSE for ANY because TRUE means that there is at least
+ // Return false for ANY because true means that there is at least
// one item that verifies the condition, which is false.
return (all) ? 2 : -2;
else if (opc == OP_EQ && all && Ndif > 1)
@@ -864,7 +864,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm,
else if (opc == OP_NE && !all && Ndif > 1)
return 2;
// else if (Ndif == 1)
-// all = FALSE;
+// all = false;
// veq is true when all values in the block are equal
switch (Type) {
@@ -874,7 +874,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm,
case TYPE_SHORT: veq = *(short*)minp == *(short*)maxp; break;
case TYPE_INT: veq = *(int*)minp == *(int*)maxp; break;
case TYPE_DOUBLE: veq = *(double*)minp == *(double*)maxp; break;
- default: veq = FALSE; // Error ?
+ default: veq = false; // Error ?
} // endswitch type
if (!s)
@@ -898,7 +898,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm,
case OP_GT: return -1; break;
} // endswitch opc
- pax = (opc == OP_GE) ? (X < Ndif - 1) : TRUE;
+ pax = (opc == OP_GE) ? (X < Ndif - 1) : true;
} else if (Inf == Bot) {
// Max value is smaller than min list value
return (opc == OP_LT || opc == OP_LE || opc == OP_NE) ? 1 : -1;
@@ -924,7 +924,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm,
case OP_LT: return (s) ? -2 : -1; break;
} // endswitch opc
- pin = (opc == OP_LE) ? (X > 0) : TRUE;
+ pin = (opc == OP_LE) ? (X > 0) : true;
} else if (Sup == Ndif) {
// Min value is greater than max list value
if (opc == OP_GT || opc == OP_GE || opc == OP_NE)
@@ -956,7 +956,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm,
// the only possible overlaps between the array and the block are:
// Array: +-------+ +-------+ +-------+ +-----+
// Block: +-----+ +---+ +------+ +--------+
- // TRUE: pax pin pax pin
+ // true: pax pin pax pin
if (all) switch (opc) {
case OP_GT:
case OP_GE: return (pax) ? -1 : 0; break;
@@ -1052,7 +1052,7 @@ void ARRAY::Print(PGLOBAL, char *ps, uint z)
/***********************************************************************/
/* MULAR public constructor. */
/***********************************************************************/
-MULAR::MULAR(PGLOBAL g, int n) : CSORT(FALSE)
+MULAR::MULAR(PGLOBAL g, int n) : CSORT(false)
{
Narray = n;
Pars = (PARRAY*)PlugSubAlloc(g, NULL, n * sizeof(PARRAY));
@@ -1075,7 +1075,7 @@ int MULAR::Qcompare(int *i1, int *i2)
/***********************************************************************/
/* Sort and eliminate distinct values from multiple arrays. */
/* Note: this is done by making a sorted index on distinct values. */
-/* Returns FALSE if Ok or TRUE in case of error. */
+/* Returns false if Ok or true in case of error. */
/***********************************************************************/
bool MULAR::Sort(PGLOBAL g)
{
@@ -1087,7 +1087,7 @@ bool MULAR::Sort(PGLOBAL g)
for (n = 1; n < Narray; n++)
if (Pars[n]->Nval != nval) {
strcpy(g->Message, MSG(BAD_ARRAY_VAL));
- return TRUE;
+ return true;
} // endif nval
// Prepare non conservative sort with offet values
@@ -1161,10 +1161,10 @@ bool MULAR::Sort(PGLOBAL g)
Pars[n]->Top = ndif; // Find searches the whole array.
} // endfor n
- return FALSE;
+ return false;
error:
PlgDBfree(Index);
PlgDBfree(Offset);
- return TRUE;
+ return true;
} // end of Sort
diff --git a/storage/connect/array.h b/storage/connect/array.h
index 6fb38ae6b47..dfc3638de8a 100644
--- a/storage/connect/array.h
+++ b/storage/connect/array.h
@@ -1,7 +1,7 @@
/**************** Array H Declares Source Code File (.H) ***************/
/* Name: ARRAY.H Version 3.1 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */
/* */
/* This file contains the ARRAY and VALBASE derived classes declares. */
/***********************************************************************/
@@ -53,8 +53,8 @@ class DllExport ARRAY : public XOBJECT, public CSORT { // Array descblock
using XOBJECT::GetIntValue;
virtual void Reset(void) {Bot = -1;}
virtual int Qcompare(int *, int *);
- virtual bool Compare(PXOB) {assert(FALSE); return FALSE;}
- virtual bool SetFormat(PGLOBAL, FORMAT&) {assert(FALSE); return FALSE;}
+ virtual bool Compare(PXOB) {assert(false); return false;}
+ virtual bool SetFormat(PGLOBAL, FORMAT&) {assert(false); return false;}
//virtual int CheckSpcCol(PTDB, int) {return 0;}
virtual void Print(PGLOBAL g, FILE *f, uint n);
virtual void Print(PGLOBAL g, char *ps, uint z);
diff --git a/storage/connect/colblk.cpp b/storage/connect/colblk.cpp
index 80b405be041..58841387249 100644
--- a/storage/connect/colblk.cpp
+++ b/storage/connect/colblk.cpp
@@ -1,7 +1,7 @@
/************* Colblk C++ Functions Source Code File (.CPP) ************/
-/* Name: COLBLK.CPP Version 2.1 */
+/* Name: COLBLK.CPP Version 2.2 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2017 */
/* */
/* This file contains the COLBLK class functions. */
/***********************************************************************/
@@ -300,7 +300,7 @@ FIDBLK::FIDBLK(PCOLUMN cp, OPVAL op) : SPCBLK(cp), Op(op)
#if defined(__WIN__)
Format.Prec = 1; // Case insensitive
#endif // __WIN__
- Constant = (!((PTDBASE)To_Tdb)->GetDef()->GetMultiple() &&
+ Constant = (!To_Tdb->GetDef()->GetMultiple() &&
To_Tdb->GetAmType() != TYPE_AM_PLG &&
To_Tdb->GetAmType() != TYPE_AM_PLM);
Fn = NULL;
@@ -312,11 +312,11 @@ FIDBLK::FIDBLK(PCOLUMN cp, OPVAL op) : SPCBLK(cp), Op(op)
/***********************************************************************/
void FIDBLK::ReadColumn(PGLOBAL g)
{
- if (Fn != ((PTDBASE)To_Tdb)->GetFile(g)) {
+ if (Fn != To_Tdb->GetFile(g)) {
char filename[_MAX_PATH];
- Fn = ((PTDBASE)To_Tdb)->GetFile(g);
- PlugSetPath(filename, Fn, ((PTDBASE)To_Tdb)->GetPath());
+ Fn = To_Tdb->GetFile(g);
+ PlugSetPath(filename, Fn, To_Tdb->GetPath());
if (Op != OP_XX) {
char buff[_MAX_PATH];
@@ -378,10 +378,8 @@ void PRTBLK::ReadColumn(PGLOBAL g)
{
if (Pname == NULL) {
char *p;
- PTDBASE tdbp = (PTDBASE)To_Tdb;
-
- Pname = tdbp->GetDef()->GetStringCatInfo(g, "partname", "?");
+ Pname = To_Tdb->GetDef()->GetStringCatInfo(g, "partname", "?");
p = strrchr(Pname, '#');
Value->SetValue_psz((p) ? p + 1 : Pname);
} // endif Pname
diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc
index 460d47bcf62..a17c5dafa43 100644
--- a/storage/connect/connect.cc
+++ b/storage/connect/connect.cc
@@ -157,23 +157,22 @@ bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname)
/* Returns valid: true if this is a table info. */
/***********************************************************************/
bool CntInfo(PGLOBAL g, PTDB tp, PXF info)
- {
- bool b;
- PTDBDOS tdbp= (PTDBDOS)tp;
+{
+ if (tp) {
+ bool b = (tp->GetFtype() == RECFM_NAF);
+ PTDBDOS tdbp = b ? NULL : (PTDBDOS)tp;
- if (tdbp) {
- b= tdbp->GetFtype() != RECFM_NAF;
- info->data_file_length= (b) ? (ulonglong)tdbp->GetFileLength(g) : 0;
+ info->data_file_length = (b) ? 0 : (ulonglong)tdbp->GetFileLength(g);
- if (!b || info->data_file_length)
- info->records= (unsigned)tdbp->Cardinality(g);
-// info->records= (unsigned)tdbp->GetMaxSize(g);
+ if (b || info->data_file_length)
+ info->records= (unsigned)tp->Cardinality(g);
+// info->records= (unsigned)tp->GetMaxSize(g);
else
info->records= 0;
// info->mean_rec_length= tdbp->GetLrecl();
info->mean_rec_length= 0;
- info->data_file_name= (b) ? tdbp->GetFile(g) : NULL;
+ info->data_file_name= (b) ? NULL : tdbp->GetFile(g);
return true;
} else {
info->data_file_length= 0;
@@ -183,7 +182,7 @@ bool CntInfo(PGLOBAL g, PTDB tp, PXF info)
return false;
} // endif tdbp
- } // end of CntInfo
+} // end of CntInfo
/***********************************************************************/
/* GetTDB: Get the table description block of a CONNECT table. */
@@ -332,9 +331,9 @@ bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2,
} // endfor colp
// Attach the updated columns list to the main table
- ((PTDBASE)tdbp)->SetSetCols(utp->GetColumns());
+ tdbp->SetSetCols(utp->GetColumns());
} else if (tdbp && mode == MODE_INSERT)
- ((PTDBASE)tdbp)->SetSetCols(tdbp->GetColumns());
+ tdbp->SetSetCols(tdbp->GetColumns());
// Now do open the physical table
if (trace)
@@ -343,7 +342,7 @@ bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2,
//tdbp->SetMode(mode);
- if (del/* && ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF*/) {
+ if (del/* && (tdbp->GetFtype() != RECFM_NAF*/) {
// To avoid erasing the table when doing a partial delete
// make a fake Next
// PDOSDEF ddp= new(g) DOSDEF;
@@ -436,7 +435,7 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp)
if (!tdbp)
return RC_FX;
- else if (((PTDBASE)tdbp)->GetKindex()) {
+ else if (tdbp->GetKindex()) {
// Reading sequencially an indexed table. This happens after the
// handler function records_in_range was called and MySQL decides
// to quit using the index (!!!) Drop the index.
@@ -483,7 +482,7 @@ RCODE CntWriteRow(PGLOBAL g, PTDB tdbp)
{
RCODE rc;
PCOL colp;
- PTDBASE tp= (PTDBASE)tdbp;
+//PTDBASE tp= (PTDBASE)tdbp;
if (!tdbp)
return RC_FX;
@@ -501,13 +500,13 @@ RCODE CntWriteRow(PGLOBAL g, PTDB tdbp)
} // endif rc
// Store column values in table write buffer(s)
- for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
+ for (colp= tdbp->GetSetCols(); colp; colp= colp->GetNext())
if (!colp->GetColUse(U_VIRTUAL))
colp->WriteColumn(g);
- if (tp->IsIndexed())
+ if (tdbp->IsIndexed())
// Index values must be sorted before updating
- rc= (RCODE)((PTDBDOS)tp)->GetTxfp()->StoreValues(g, true);
+ rc= (RCODE)((PTDBDOS)tdbp)->GetTxfp()->StoreValues(g, true);
else
// Return result code from write operation
rc= (RCODE)tdbp->WriteDB(g);
@@ -535,7 +534,7 @@ RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp)
RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all)
{
RCODE rc;
- PTDBASE tp= (PTDBASE)tdbp;
+//PTDBASE tp= (PTDBASE)tdbp;
if (!tdbp || tdbp->GetMode() != MODE_DELETE)
return RC_FX;
@@ -543,16 +542,16 @@ RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all)
return RC_NF;
if (all) {
- if (((PTDBASE)tdbp)->GetDef()->Indexable())
+ if (tdbp->GetDef()->Indexable())
((PTDBDOS)tdbp)->Cardinal= 0;
// Note: if all, this call will be done when closing the table
rc= (RCODE)tdbp->DeleteDB(g, RC_FX);
-//} else if (tp->GetKindex() && !tp->GetKindex()->IsSorted() &&
-// tp->Txfp->GetAmType() != TYPE_AM_DBF) {
- } else if(tp->IsIndexed()) {
+//} else if (tdbp->GetKindex() && !((PTDBASE)tdbp)->GetKindex()->IsSorted() &&
+// ((PTDBASE)tdbp)->Txfp->GetAmType() != TYPE_AM_DBF) {
+ } else if(tdbp->IsIndexed()) {
// Index values must be sorted before updating
- rc= (RCODE)((PTDBDOS)tp)->GetTxfp()->StoreValues(g, false);
+ rc= (RCODE)((PTDBDOS)tdbp)->GetTxfp()->StoreValues(g, false);
} else // Return result code from delete operation
rc= (RCODE)tdbp->DeleteDB(g, RC_OK);
@@ -565,7 +564,7 @@ RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all)
int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort)
{
int rc= RC_OK;
- TDBASE *tbxp= (PTDBASE)tdbp;
+//TDBASE *tbxp= (PTDBASE)tdbp;
if (!tdbp)
return rc; // Nothing to do
@@ -581,13 +580,13 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort)
tdbp, tdbp->GetMode(), nox, abort);
if (tdbp->GetMode() == MODE_DELETE && tdbp->GetUse() == USE_OPEN) {
- if (tbxp->IsIndexed())
+ if (tdbp->IsIndexed())
rc= ((PTDBDOS)tdbp)->GetTxfp()->DeleteSortedRows(g);
if (!rc)
rc= tdbp->DeleteDB(g, RC_EF); // Specific A.M. delete routine
- } else if (tbxp->GetMode() == MODE_UPDATE && tbxp->IsIndexed())
+ } else if (tdbp->GetMode() == MODE_UPDATE && tdbp->IsIndexed())
rc= ((PTDBDOX)tdbp)->Txfp->UpdateSortedRows(g);
switch(rc) {
@@ -595,7 +594,7 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort)
abort= true;
break;
case RC_INFO:
- PushWarning(g, tbxp);
+ PushWarning(g, tdbp);
break;
} // endswitch rc
@@ -631,11 +630,13 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort)
if (trace > 1)
printf("About to reset opt\n");
- // Make all the eventual indexes
- tbxp= (TDBDOX*)tdbp;
- tbxp->ResetKindex(g, NULL);
- tbxp->SetKey_Col(NULL);
- rc= tbxp->ResetTableOpt(g, true, tbxp->GetDef()->Indexable() == 1);
+ if (!tdbp->IsRemote()) {
+ // Make all the eventual indexes
+ PTDBDOX tbxp = (PTDBDOX)tdbp;
+ tbxp->ResetKindex(g, NULL);
+ tbxp->SetKey_Col(NULL);
+ rc = tbxp->ResetTableOpt(g, true, tbxp->GetDef()->Indexable() == 1);
+ } // endif remote
err:
if (trace > 1)
@@ -657,10 +658,10 @@ int CntIndexInit(PGLOBAL g, PTDB ptdb, int id, bool sorted)
if (!ptdb)
return -1;
- else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) {
+ else if (!ptdb->GetDef()->Indexable()) {
sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
return 0;
- } else if (((PTDBASE)ptdb)->GetDef()->Indexable() == 3) {
+ } else if (ptdb->GetDef()->Indexable() == 3) {
return 1;
} else
tdbp= (PTDBDOX)ptdb;
@@ -745,7 +746,7 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op,
if (!ptdb)
return RC_FX;
else
- x= ((PTDBASE)ptdb)->GetDef()->Indexable();
+ x= ptdb->GetDef()->Indexable();
if (!x) {
sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
@@ -875,7 +876,7 @@ int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len,
if (!ptdb)
return -1;
- x= ((PTDBASE)ptdb)->GetDef()->Indexable();
+ x= ptdb->GetDef()->Indexable();
if (!x) {
sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName());
diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp
index eb9660b439d..1622ec16c68 100644
--- a/storage/connect/domdoc.cpp
+++ b/storage/connect/domdoc.cpp
@@ -116,7 +116,9 @@ bool DOMDOC::ParseFile(PGLOBAL g, char *fn)
// Parse an in memory document
char *xdoc = GetMemDoc(g, fn);
- b = (xdoc) ? (bool)Docp->loadXML((_bstr_t)xdoc) : false;
+ // This is not equivalent to load for UTF8 characters
+ // It is why get node content is not the same
+ b = (xdoc) ? (bool)Docp->loadXML((_bstr_t)xdoc) : false;
} else
// Load the document
b = (bool)Docp->load((_bstr_t)fn);
@@ -266,6 +268,7 @@ DOMNODE::DOMNODE(PXDOC dp, MSXML2::IXMLDOMNodePtr np) : XMLNODE(dp)
Nodep = np;
Ws = NULL;
Len = 0;
+ Zip = (bool)dp->zip;
} // end of DOMNODE constructor
/******************************************************************/
@@ -316,8 +319,10 @@ RCODE DOMNODE::GetContent(PGLOBAL g, char *buf, int len)
RCODE rc = RC_OK;
// Nodep can be null for a missing HTML table column
- if (Nodep) {
- if (!WideCharToMultiByte(CP_UTF8, 0, Nodep->text, -1,
+ if (Nodep) {
+ if (Zip) {
+ strcpy(buf, Nodep->text);
+ } else if (!WideCharToMultiByte(CP_UTF8, 0, Nodep->text, -1,
buf, len, NULL, NULL)) {
DWORD lsr = GetLastError();
diff --git a/storage/connect/domdoc.h b/storage/connect/domdoc.h
index cfec98a9422..7f269002d59 100644
--- a/storage/connect/domdoc.h
+++ b/storage/connect/domdoc.h
@@ -93,6 +93,7 @@ class DOMNODE : public XMLNODE {
char Name[64];
WCHAR *Ws;
int Len;
+ bool Zip;
}; // end of class DOMNODE
/******************************************************************/
diff --git a/storage/connect/filamap.cpp b/storage/connect/filamap.cpp
index 94c562a9981..8fffaca3d06 100644
--- a/storage/connect/filamap.cpp
+++ b/storage/connect/filamap.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -45,6 +45,7 @@
#include "maputil.h"
#include "filamap.h"
#include "tabdos.h"
+#include "tabfmt.h"
/* --------------------------- Class MAPFAM -------------------------- */
@@ -322,17 +323,20 @@ int MAPFAM::ReadBuffer(PGLOBAL g)
int rc, len;
// Are we at the end of the memory
- if (Mempos >= Top)
+ if (Mempos >= Top) {
if ((rc = GetNext(g)) != RC_OK)
return rc;
+ else if (Tdbp->GetAmType() == TYPE_AM_CSV && ((PTDBCSV)Tdbp)->Header)
+ if ((rc = SkipRecord(g, true)) != RC_OK)
+ return rc;
+
+ } // endif Mempos
if (!Placed) {
/*******************************************************************/
/* Record file position in case of UPDATE or DELETE. */
/*******************************************************************/
- int rc;
-
next:
Fpos = Mempos;
CurBlk = (int)Rows++;
diff --git a/storage/connect/filamdbf.cpp b/storage/connect/filamdbf.cpp
index a4557facbd8..9feb61d7d61 100644
--- a/storage/connect/filamdbf.cpp
+++ b/storage/connect/filamdbf.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -281,15 +281,25 @@ PQRYRES DBFColumns(PGLOBAL g, char *dp, const char *fn, bool info)
/************************************************************************/
switch (thisfield.Type) {
case 'C': // Characters
- case 'L': // Logical 'T' or 'F'
- type = TYPE_STRING;
+ case 'L': // Logical 'T' or 'F' or space
+ type = TYPE_STRING;
+ break;
+ case 'M': // Memo a .DBT block number
+ case 'B': // Binary a .DBT block number
+ case 'G': // Ole a .DBT block number
+ type = TYPE_STRING;
break;
+ //case 'I': // Long
+ //case '+': // Autoincrement
+ // type = TYPE_INT;
+ // break;
case 'N':
type = (thisfield.Decimals) ? TYPE_DOUBLE
: (len > 10) ? TYPE_BIGINT : TYPE_INT;
break;
- case 'F':
- type = TYPE_DOUBLE;
+ case 'F': // Float
+ //case 'O': // Double
+ type = TYPE_DOUBLE;
break;
case 'D':
type = TYPE_DATE; // Is this correct ???
@@ -441,6 +451,7 @@ int DBFFAM::Cardinality(PGLOBAL g)
if (Accept) {
Lrecl = rln;
+ Blksize = Nrec * rln;
PushWarning(g, Tdbp);
} else
return -1;
@@ -582,6 +593,7 @@ bool DBFFAM::AllocateBuffer(PGLOBAL g)
if (Accept) {
Lrecl = reclen;
+ Blksize = Nrec * Lrecl;
PushWarning(g, Tdbp);
} else
return true;
@@ -598,7 +610,7 @@ bool DBFFAM::AllocateBuffer(PGLOBAL g)
header->Filedate[1] = datm->tm_mon + 1;
header->Filedate[2] = datm->tm_mday;
header->SetHeadlen((ushort)hlen);
- header->SetReclen((ushort)reclen);
+ header->SetReclen(reclen);
descp = (DESCRIPTOR*)header;
// Currently only standard Xbase types are supported
@@ -664,6 +676,7 @@ bool DBFFAM::AllocateBuffer(PGLOBAL g)
if (Accept) {
Lrecl = header.Reclen();
+ Blksize = Nrec * Lrecl;
PushWarning(g, Tdbp);
} else
return true;
@@ -956,6 +969,7 @@ int DBMFAM::Cardinality(PGLOBAL g)
if (Accept) {
Lrecl = rln;
+ Blksize = Nrec * Lrecl;
PushWarning(g, Tdbp);
} else
return -1;
@@ -1008,6 +1022,7 @@ bool DBMFAM::AllocateBuffer(PGLOBAL g)
if (Accept) {
Lrecl = hp->Reclen();
+ Blksize = Nrec * Lrecl;
PushWarning(g, Tdbp);
} else
return true;
diff --git a/storage/connect/filamgz.cpp b/storage/connect/filamgz.cpp
index 07242ea633c..dc6f277ee27 100644
--- a/storage/connect/filamgz.cpp
+++ b/storage/connect/filamgz.cpp
@@ -724,20 +724,20 @@ void ZBKFAM::Rewind(void)
/***********************************************************************/
/* Constructors. */
/***********************************************************************/
-ZIXFAM::ZIXFAM(PDOSDEF tdp) : ZBKFAM(tdp)
+GZXFAM::GZXFAM(PDOSDEF tdp) : ZBKFAM(tdp)
{
//Block = tdp->GetBlock();
//Last = tdp->GetLast();
Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN;
Blksize = Nrec * Lrecl;
- } // end of ZIXFAM standard constructor
+ } // end of GZXFAM standard constructor
/***********************************************************************/
/* ZIX Cardinality: returns table cardinality in number of rows. */
/* This function can be called with a null argument to test the */
/* availability of Cardinality implementation (1 yes, 0 no). */
/***********************************************************************/
-int ZIXFAM::Cardinality(PGLOBAL g)
+int GZXFAM::Cardinality(PGLOBAL g)
{
if (Last)
return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
@@ -750,7 +750,7 @@ int ZIXFAM::Cardinality(PGLOBAL g)
/* Allocate the line buffer. For mode Delete a bigger buffer has to */
/* be allocated because is it also used to move lines into the file. */
/***********************************************************************/
-bool ZIXFAM::AllocateBuffer(PGLOBAL g)
+bool GZXFAM::AllocateBuffer(PGLOBAL g)
{
Buflen = Blksize;
To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
@@ -788,7 +788,7 @@ bool ZIXFAM::AllocateBuffer(PGLOBAL g)
/***********************************************************************/
/* ReadBuffer: Read one line from a compressed text file. */
/***********************************************************************/
-int ZIXFAM::ReadBuffer(PGLOBAL g)
+int GZXFAM::ReadBuffer(PGLOBAL g)
{
int n, rc = RC_OK;
@@ -850,7 +850,7 @@ int ZIXFAM::ReadBuffer(PGLOBAL g)
/* WriteDB: Data Base write routine for ZDOS access method. */
/* Update is not possible without using a temporary file (NIY). */
/***********************************************************************/
-int ZIXFAM::WriteBuffer(PGLOBAL g)
+int GZXFAM::WriteBuffer(PGLOBAL g)
{
/*********************************************************************/
/* In Insert mode, blocs are added sequentialy to the file end. */
diff --git a/storage/connect/filamgz.h b/storage/connect/filamgz.h
index d667fdddcc2..7a00c0d4bc7 100644
--- a/storage/connect/filamgz.h
+++ b/storage/connect/filamgz.h
@@ -12,7 +12,7 @@
typedef class GZFAM *PGZFAM;
typedef class ZBKFAM *PZBKFAM;
-typedef class ZIXFAM *PZIXFAM;
+typedef class GZXFAM *PZIXFAM;
typedef class ZLBFAM *PZLBFAM;
/***********************************************************************/
@@ -101,16 +101,16 @@ class DllExport ZBKFAM : public GZFAM {
/* length files compressed using the gzip library functions. */
/* The file is always accessed by block. */
/***********************************************************************/
-class DllExport ZIXFAM : public ZBKFAM {
+class DllExport GZXFAM : public ZBKFAM {
public:
// Constructor
- ZIXFAM(PDOSDEF tdp);
- ZIXFAM(PZIXFAM txfp) : ZBKFAM(txfp) {}
+ GZXFAM(PDOSDEF tdp);
+ GZXFAM(PZIXFAM txfp) : ZBKFAM(txfp) {}
// Implementation
virtual int GetNextPos(void) {return 0;}
virtual PTXF Duplicate(PGLOBAL g)
- {return (PTXF)new(g) ZIXFAM(this);}
+ {return (PTXF)new(g) GZXFAM(this);}
// Methods
virtual int Cardinality(PGLOBAL g);
@@ -120,7 +120,7 @@ class DllExport ZIXFAM : public ZBKFAM {
protected:
// No additional Members
- }; // end of class ZIXFAM
+ }; // end of class GZXFAM
/***********************************************************************/
/* This is the DOS/UNIX Access Method class declaration for PlugDB */
diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp
index 6aca4631f32..3d157da5e87 100644
--- a/storage/connect/filamzip.cpp
+++ b/storage/connect/filamzip.cpp
@@ -1,11 +1,11 @@
/*********** File AM Zip C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: FILAMZIP */
/* ------------- */
-/* Version 1.0 */
+/* Version 1.1 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2016 */
+/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -19,13 +19,16 @@
#include "my_global.h"
#if !defined(__WIN__)
#if defined(UNIX)
+#include <fnmatch.h>
#include <errno.h>
+#include <dirent.h>
#include <unistd.h>
#else // !UNIX
#include <io.h>
#endif // !UNIX
#include <fcntl.h>
#endif // !__WIN__
+#include <time.h>
/***********************************************************************/
/* Include application header files: */
@@ -40,12 +43,346 @@
//#include "tabzip.h"
#include "filamzip.h"
+#define WRITEBUFFERSIZE (16384)
+
+bool ZipLoadFile(PGLOBAL g, char *zfn, char *fn, char *entry, bool append, bool mul);
+
+/***********************************************************************/
+/* Compress a file in zip when creating a table. */
+/***********************************************************************/
+static bool ZipFile(PGLOBAL g, ZIPUTIL *zutp, char *fn, char *entry, char *buf)
+{
+ int rc = RC_OK, size_read, size_buf = WRITEBUFFERSIZE;
+ FILE *fin;
+
+ if (zutp->addEntry(g, entry))
+ return true;
+ else if (!(fin = fopen(fn, "rb"))) {
+ sprintf(g->Message, "error in opening %s for reading", fn);
+ return true;
+ } // endif fin
+
+ do {
+ size_read = (int)fread(buf, 1, size_buf, fin);
+
+ if (size_read < size_buf && feof(fin) == 0) {
+ sprintf(g->Message, "error in reading %s", fn);
+ rc = RC_FX;
+ } // endif size_read
+
+ if (size_read > 0) {
+ rc = zutp->writeEntry(g, buf, size_read);
+
+ if (rc == RC_FX)
+ sprintf(g->Message, "error in writing %s in the zipfile", fn);
+
+ } // endif size_read
+
+ } while (rc == RC_OK && size_read > 0);
+
+ fclose(fin);
+ zutp->closeEntry();
+ return rc != RC_OK;
+} // end of ZipFile
+
+/***********************************************************************/
+/* Find and Compress several files in zip when creating a table. */
+/***********************************************************************/
+static bool ZipFiles(PGLOBAL g, ZIPUTIL *zutp, char *pat, char *buf)
+{
+ char filename[_MAX_PATH];
+ int rc;
+
+ /*********************************************************************/
+ /* pat is a multiple file name with wildcard characters */
+ /*********************************************************************/
+ strcpy(filename, pat);
+
+#if defined(__WIN__)
+ char drive[_MAX_DRIVE], direc[_MAX_DIR];
+ WIN32_FIND_DATA FileData;
+ HANDLE hSearch;
+
+ _splitpath(filename, drive, direc, NULL, NULL);
+
+ // Start searching files in the target directory.
+ hSearch = FindFirstFile(filename, &FileData);
+
+ if (hSearch == INVALID_HANDLE_VALUE) {
+ rc = GetLastError();
+
+ if (rc != ERROR_FILE_NOT_FOUND) {
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, GetLastError(), 0, (LPTSTR)&filename, sizeof(filename), NULL);
+ sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename);
+ return true;
+ } else {
+ strcpy(g->Message, "Cannot find any file to load");
+ return true;
+ } // endif rc
+
+ } // endif hSearch
+
+ while (true) {
+ if (!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ strcat(strcat(strcpy(filename, drive), direc), FileData.cFileName);
+
+ if (ZipFile(g, zutp, filename, FileData.cFileName, buf)) {
+ FindClose(hSearch);
+ return true;
+ } // endif ZipFile
+
+ } // endif dwFileAttributes
+
+ if (!FindNextFile(hSearch, &FileData)) {
+ rc = GetLastError();
+
+ if (rc != ERROR_NO_MORE_FILES) {
+ sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
+ FindClose(hSearch);
+ return true;
+ } // endif rc
+
+ break;
+ } // endif FindNextFile
+
+ } // endwhile n
+
+ // Close the search handle.
+ if (!FindClose(hSearch)) {
+ strcpy(g->Message, MSG(SRCH_CLOSE_ERR));
+ return true;
+ } // endif FindClose
+
+#else // !__WIN__
+ struct stat fileinfo;
+ char fn[FN_REFLEN], direc[FN_REFLEN], pattern[FN_HEADLEN], ftype[FN_EXTLEN];
+ DIR *dir;
+ struct dirent *entry;
+
+ _splitpath(filename, NULL, direc, pattern, ftype);
+ strcat(pattern, ftype);
+
+ // Start searching files in the target directory.
+ if (!(dir = opendir(direc))) {
+ sprintf(g->Message, MSG(BAD_DIRECTORY), direc, strerror(errno));
+ return true;
+ } // endif dir
+
+ while ((entry = readdir(dir))) {
+ strcat(strcpy(fn, direc), entry->d_name);
+
+ if (lstat(fn, &fileinfo) < 0) {
+ sprintf(g->Message, "%s: %s", fn, strerror(errno));
+ return true;
+ } else if (!S_ISREG(fileinfo.st_mode))
+ continue; // Not a regular file (should test for links)
+
+ /*******************************************************************/
+ /* Test whether the file name matches the table name filter. */
+ /*******************************************************************/
+ if (fnmatch(pattern, entry->d_name, 0))
+ continue; // Not a match
+
+ strcat(strcpy(filename, direc), entry->d_name);
+
+ if (ZipFile(g, zutp, filename, entry->d_name, buf)) {
+ closedir(dir);
+ return true;
+ } // endif ZipFile
+
+ } // endwhile readdir
+
+ // Close the dir handle.
+ closedir(dir);
+#endif // !__WIN__
+
+ return false;
+} // end of ZipFiles
+
+/***********************************************************************/
+/* Load and Compress a file in zip when creating a table. */
+/***********************************************************************/
+bool ZipLoadFile(PGLOBAL g, char *zfn, char *fn, char *entry, bool append, bool mul)
+{
+ char *buf;
+ bool err;
+ ZIPUTIL *zutp = new(g) ZIPUTIL(NULL);
+
+ if (zutp->open(g, zfn, append))
+ return true;
+
+ buf = (char*)PlugSubAlloc(g, NULL, WRITEBUFFERSIZE);
+
+ if (mul)
+ err = ZipFiles(g, zutp, fn, buf);
+ else
+ err = ZipFile(g, zutp, fn, entry, buf);
+
+ zutp->close();
+ return err;
+} // end of ZipLoadFile
+
/* -------------------------- class ZIPUTIL -------------------------- */
/***********************************************************************/
/* Constructors. */
/***********************************************************************/
-ZIPUTIL::ZIPUTIL(PSZ tgt, bool mul)
+ZIPUTIL::ZIPUTIL(PSZ tgt)
+{
+ zipfile = NULL;
+ target = tgt;
+ fp = NULL;
+ entryopen = false;
+} // end of ZIPUTIL standard constructor
+
+#if 0
+ZIPUTIL::ZIPUTIL(ZIPUTIL *zutp)
+{
+ zipfile = zutp->zipfile;
+ target = zutp->target;
+ fp = zutp->fp;
+ entryopen = zutp->entryopen;
+} // end of UNZIPUTL copy constructor
+#endif // 0
+
+/***********************************************************************/
+/* Fill the zip time structure */
+/* param: tmZip time structure to be filled */
+/***********************************************************************/
+void ZIPUTIL::getTime(tm_zip& tmZip)
+{
+ time_t rawtime;
+ time(&rawtime);
+ struct tm *timeinfo = localtime(&rawtime);
+ tmZip.tm_sec = timeinfo->tm_sec;
+ tmZip.tm_min = timeinfo->tm_min;
+ tmZip.tm_hour = timeinfo->tm_hour;
+ tmZip.tm_mday = timeinfo->tm_mday;
+ tmZip.tm_mon = timeinfo->tm_mon;
+ tmZip.tm_year = timeinfo->tm_year;
+} // end of getTime
+
+/***********************************************************************/
+/* open a zip file for deflate. */
+/* param: filename path and the filename of the zip file to open. */
+/* append: set true to append the zip file */
+/* return: true if open, false otherwise. */
+/***********************************************************************/
+bool ZIPUTIL::open(PGLOBAL g, char *filename, bool append)
+{
+ if (!zipfile && !(zipfile = zipOpen64(filename,
+ append ? APPEND_STATUS_ADDINZIP
+ : APPEND_STATUS_CREATE)))
+ sprintf(g->Message, "Zipfile open error on %s", filename);
+
+ return (zipfile == NULL);
+} // end of open
+
+/***********************************************************************/
+/* Close the zip file. */
+/***********************************************************************/
+void ZIPUTIL::close()
+{
+ if (zipfile) {
+ closeEntry();
+ zipClose(zipfile, 0);
+ zipfile = NULL;
+ } // endif zipfile
+
+} // end of close
+
+/***********************************************************************/
+/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */
+/***********************************************************************/
+bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn, bool append)
+{
+ /*********************************************************************/
+ /* The file will be compressed. */
+ /*********************************************************************/
+ if (mode == MODE_INSERT) {
+ bool b = open(g, fn, append);
+
+ if (!b) {
+ if (addEntry(g, target))
+ return true;
+
+ /*****************************************************************/
+ /* Link a Fblock. This make possible to automatically close it */
+ /* in case of error g->jump. */
+ /*****************************************************************/
+ PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
+
+ fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
+ fp->Type = TYPE_FB_ZIP;
+ fp->Fname = PlugDup(g, fn);
+ fp->Next = dbuserp->Openlist;
+ dbuserp->Openlist = fp;
+ fp->Count = 1;
+ fp->Length = 0;
+ fp->Memory = NULL;
+ fp->Mode = mode;
+ fp->File = this;
+ fp->Handle = 0;
+ } else
+ return true;
+
+ } else {
+ strcpy(g->Message, "Only INSERT mode supported for ZIPPING files");
+ return true;
+ } // endif mode
+
+ return false;
+} // end of OpenTableFile
+
+/***********************************************************************/
+/* Add target in zip file. */
+/***********************************************************************/
+bool ZIPUTIL::addEntry(PGLOBAL g, char *entry)
+{
+ //?? we dont need the stinking time
+ zip_fileinfo zi = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ getTime(zi.tmz_date);
+ target = entry;
+
+ int err = zipOpenNewFileInZip(zipfile, target, &zi,
+ NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
+
+ return !(entryopen = (err == ZIP_OK));
+} // end of addEntry
+
+/***********************************************************************/
+/* writeEntry: Deflate the buffer to the zip file. */
+/***********************************************************************/
+int ZIPUTIL::writeEntry(PGLOBAL g, char *buf, int len)
+{
+ if (zipWriteInFileInZip(zipfile, buf, len) < 0) {
+ sprintf(g->Message, "Error writing %s in the zipfile", target);
+ return RC_FX;
+ } // endif zipWriteInFileInZip
+
+ return RC_OK;
+} // end of writeEntry
+
+/***********************************************************************/
+/* Close the zip file. */
+/***********************************************************************/
+void ZIPUTIL::closeEntry()
+{
+ if (entryopen) {
+ zipCloseFileInZip(zipfile);
+ entryopen = false;
+ } // endif entryopen
+
+} // end of closeEntry
+
+/* ------------------------- class UNZIPUTL -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+UNZIPUTL::UNZIPUTL(PSZ tgt, bool mul)
{
zipfile = NULL;
target = tgt;
@@ -62,10 +399,10 @@ ZIPUTIL::ZIPUTIL(PSZ tgt, bool mul)
#else
for (int i = 0; i < 256; ++i) mapCaseTable[i] = i;
#endif
-} // end of ZIPUTIL standard constructor
+} // end of UNZIPUTL standard constructor
#if 0
-ZIPUTIL::ZIPUTIL(PZIPUTIL zutp)
+UNZIPUTL::UNZIPUTL(PZIPUTIL zutp)
{
zipfile = zutp->zipfile;
target = zutp->target;
@@ -74,14 +411,14 @@ ZIPUTIL::ZIPUTIL(PZIPUTIL zutp)
entryopen = zutp->entryopen;
multiple = zutp->multiple;
for (int i = 0; i < 256; ++i) mapCaseTable[i] = zutp->mapCaseTable[i];
-} // end of ZIPUTIL copy constructor
+} // end of UNZIPUTL copy constructor
#endif // 0
/***********************************************************************/
/* This code is the copyright property of Alessandro Felice Cantatore. */
/* http://xoomer.virgilio.it/acantato/dev/wildcard/wildmatch.html */
/***********************************************************************/
-bool ZIPUTIL::WildMatch(PSZ pat, PSZ str) {
+bool UNZIPUTL::WildMatch(PSZ pat, PSZ str) {
PSZ s, p;
bool star = FALSE;
@@ -116,7 +453,7 @@ starCheck:
/* param: filename path and the filename of the zip file to open. */
/* return: true if open, false otherwise. */
/***********************************************************************/
-bool ZIPUTIL::open(PGLOBAL g, char *filename)
+bool UNZIPUTL::open(PGLOBAL g, char *filename)
{
if (!zipfile && !(zipfile = unzOpen64(filename)))
sprintf(g->Message, "Zipfile open error on %s", filename);
@@ -127,7 +464,7 @@ bool ZIPUTIL::open(PGLOBAL g, char *filename)
/***********************************************************************/
/* Close the zip file. */
/***********************************************************************/
-void ZIPUTIL::close()
+void UNZIPUTL::close()
{
if (zipfile) {
closeEntry();
@@ -140,7 +477,7 @@ void ZIPUTIL::close()
/***********************************************************************/
/* Find next entry matching target pattern. */
/***********************************************************************/
-int ZIPUTIL::findEntry(PGLOBAL g, bool next)
+int UNZIPUTL::findEntry(PGLOBAL g, bool next)
{
int rc;
@@ -183,7 +520,7 @@ int ZIPUTIL::findEntry(PGLOBAL g, bool next)
/***********************************************************************/
/* Get the next used entry. */
/***********************************************************************/
-int ZIPUTIL::nextEntry(PGLOBAL g)
+int UNZIPUTL::nextEntry(PGLOBAL g)
{
if (multiple) {
int rc;
@@ -206,7 +543,7 @@ int ZIPUTIL::nextEntry(PGLOBAL g)
/***********************************************************************/
/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */
/***********************************************************************/
-bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn)
+bool UNZIPUTL::OpenTable(PGLOBAL g, MODE mode, char *fn)
{
/*********************************************************************/
/* The file will be decompressed into virtual memory. */
@@ -268,7 +605,7 @@ bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn)
return true;
} else {
- strcpy(g->Message, "Only READ mode supported for ZIP files");
+ strcpy(g->Message, "Only READ mode supported for ZIPPED tables");
return true;
} // endif mode
@@ -278,7 +615,7 @@ bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn)
/***********************************************************************/
/* Open target in zip file. */
/***********************************************************************/
-bool ZIPUTIL::openEntry(PGLOBAL g)
+bool UNZIPUTL::openEntry(PGLOBAL g)
{
int rc;
@@ -316,7 +653,7 @@ bool ZIPUTIL::openEntry(PGLOBAL g)
/***********************************************************************/
/* Close the zip file. */
/***********************************************************************/
-void ZIPUTIL::closeEntry()
+void UNZIPUTL::closeEntry()
{
if (entryopen) {
unzCloseCurrentFile(zipfile);
@@ -330,36 +667,29 @@ void ZIPUTIL::closeEntry()
} // end of closeEntry
-/* -------------------------- class ZIPFAM --------------------------- */
+/* -------------------------- class UNZFAM --------------------------- */
/***********************************************************************/
/* Constructors. */
/***********************************************************************/
-ZIPFAM::ZIPFAM(PDOSDEF tdp) : MAPFAM(tdp)
+UNZFAM::UNZFAM(PDOSDEF tdp) : MAPFAM(tdp)
{
zutp = NULL;
target = tdp->GetEntry();
mul = tdp->GetMul();
-} // end of ZIPFAM standard constructor
-
-ZIPFAM::ZIPFAM(PZIPFAM txfp) : MAPFAM(txfp)
-{
- zutp = txfp->zutp;
- target = txfp->target;
- mul = txfp->mul;
-} // end of ZIPFAM copy constructor
+} // end of UNZFAM standard constructor
-ZIPFAM::ZIPFAM(PDOSDEF tdp, PZPXFAM txfp) : MAPFAM(tdp)
+UNZFAM::UNZFAM(PUNZFAM txfp) : MAPFAM(txfp)
{
zutp = txfp->zutp;
target = txfp->target;
mul = txfp->mul;
-} // end of ZIPFAM constructor used in ResetTableOpt
+} // end of UNZFAM copy constructor
/***********************************************************************/
/* ZIP GetFileLength: returns file size in number of bytes. */
/***********************************************************************/
-int ZIPFAM::GetFileLength(PGLOBAL g)
+int UNZFAM::GetFileLength(PGLOBAL g)
{
int len = (zutp && zutp->entryopen) ? Top - Memory
: TXTFAM::GetFileLength(g) * 3;
@@ -373,7 +703,7 @@ int ZIPFAM::GetFileLength(PGLOBAL g)
/***********************************************************************/
/* ZIP Cardinality: return the number of rows if possible. */
/***********************************************************************/
-int ZIPFAM::Cardinality(PGLOBAL g)
+int UNZFAM::Cardinality(PGLOBAL g)
{
if (!g)
return 1;
@@ -388,7 +718,7 @@ int ZIPFAM::Cardinality(PGLOBAL g)
/***********************************************************************/
/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */
/***********************************************************************/
-bool ZIPFAM::OpenTableFile(PGLOBAL g)
+bool UNZFAM::OpenTableFile(PGLOBAL g)
{
char filename[_MAX_PATH];
MODE mode = Tdbp->GetMode();
@@ -396,7 +726,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g)
/*********************************************************************/
/* Allocate the ZIP utility class. */
/*********************************************************************/
- zutp = new(g) ZIPUTIL(target, mul);
+ zutp = new(g) UNZIPUTL(target, mul);
// We used the file name relative to recorded datapath
PlugSetPath(filename, To_File, Tdbp->GetPath());
@@ -415,7 +745,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g)
/***********************************************************************/
/* GetNext: go to next entry. */
/***********************************************************************/
-int ZIPFAM::GetNext(PGLOBAL g)
+int UNZFAM::GetNext(PGLOBAL g)
{
int rc = zutp->nextEntry(g);
@@ -431,7 +761,7 @@ int ZIPFAM::GetNext(PGLOBAL g)
/***********************************************************************/
/* ReadBuffer: Read one line for a ZIP file. */
/***********************************************************************/
-int ZIPFAM::ReadBuffer(PGLOBAL g)
+int UNZFAM::ReadBuffer(PGLOBAL g)
{
int rc, len;
@@ -497,37 +827,37 @@ int ZIPFAM::ReadBuffer(PGLOBAL g)
/***********************************************************************/
/* Table file close routine for MAP access method. */
/***********************************************************************/
-void ZIPFAM::CloseTableFile(PGLOBAL g, bool)
+void UNZFAM::CloseTableFile(PGLOBAL g, bool)
{
close();
} // end of CloseTableFile
#endif // 0
-/* -------------------------- class ZPXFAM --------------------------- */
+/* -------------------------- class UZXFAM --------------------------- */
/***********************************************************************/
/* Constructors. */
/***********************************************************************/
-ZPXFAM::ZPXFAM(PDOSDEF tdp) : MPXFAM(tdp)
+UZXFAM::UZXFAM(PDOSDEF tdp) : MPXFAM(tdp)
{
zutp = NULL;
target = tdp->GetEntry();
mul = tdp->GetMul();
//Lrecl = tdp->GetLrecl();
-} // end of ZPXFAM standard constructor
+} // end of UZXFAM standard constructor
-ZPXFAM::ZPXFAM(PZPXFAM txfp) : MPXFAM(txfp)
+UZXFAM::UZXFAM(PUZXFAM txfp) : MPXFAM(txfp)
{
zutp = txfp->zutp;
target = txfp->target;
mul = txfp->mul;
//Lrecl = txfp->Lrecl;
-} // end of ZPXFAM copy constructor
+} // end of UZXFAM copy constructor
/***********************************************************************/
/* ZIP GetFileLength: returns file size in number of bytes. */
/***********************************************************************/
-int ZPXFAM::GetFileLength(PGLOBAL g)
+int UZXFAM::GetFileLength(PGLOBAL g)
{
int len;
@@ -545,7 +875,7 @@ int ZPXFAM::GetFileLength(PGLOBAL g)
/***********************************************************************/
/* ZIP Cardinality: return the number of rows if possible. */
/***********************************************************************/
-int ZPXFAM::Cardinality(PGLOBAL g)
+int UZXFAM::Cardinality(PGLOBAL g)
{
if (!g)
return 1;
@@ -566,7 +896,7 @@ int ZPXFAM::Cardinality(PGLOBAL g)
/***********************************************************************/
/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */
/***********************************************************************/
-bool ZPXFAM::OpenTableFile(PGLOBAL g)
+bool UZXFAM::OpenTableFile(PGLOBAL g)
{
// May have been already opened in GetFileLength
if (!zutp || !zutp->zipfile) {
@@ -577,7 +907,7 @@ bool ZPXFAM::OpenTableFile(PGLOBAL g)
/* Allocate the ZIP utility class. */
/*********************************************************************/
if (!zutp)
- zutp = new(g)ZIPUTIL(target, mul);
+ zutp = new(g)UNZIPUTL(target, mul);
// We used the file name relative to recorded datapath
PlugSetPath(filename, To_File, Tdbp->GetPath());
@@ -600,7 +930,7 @@ bool ZPXFAM::OpenTableFile(PGLOBAL g)
/***********************************************************************/
/* GetNext: go to next entry. */
/***********************************************************************/
-int ZPXFAM::GetNext(PGLOBAL g)
+int UZXFAM::GetNext(PGLOBAL g)
{
int rc = zutp->nextEntry(g);
@@ -620,3 +950,146 @@ int ZPXFAM::GetNext(PGLOBAL g)
return RC_OK;
} // end of GetNext
+/* -------------------------- class ZIPFAM --------------------------- */
+
+/***********************************************************************/
+/* Constructor. */
+/***********************************************************************/
+ZIPFAM::ZIPFAM(PDOSDEF tdp) : DOSFAM(tdp)
+{
+ zutp = NULL;
+ target = tdp->GetEntry();
+ append = tdp->GetAppend();
+} // end of ZIPFAM standard constructor
+
+/***********************************************************************/
+/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */
+/***********************************************************************/
+bool ZIPFAM::OpenTableFile(PGLOBAL g)
+{
+ char filename[_MAX_PATH];
+ MODE mode = Tdbp->GetMode();
+
+ /*********************************************************************/
+ /* Allocate the ZIP utility class. */
+ /*********************************************************************/
+ zutp = new(g) ZIPUTIL(target);
+
+ // We used the file name relative to recorded datapath
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+
+ if (!zutp->OpenTable(g, mode, filename, append)) {
+ To_Fb = zutp->fp; // Useful when closing
+ } else
+ return true;
+
+ return AllocateBuffer(g);
+} // end of OpenTableFile
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a ZIP file. */
+/***********************************************************************/
+int ZIPFAM::ReadBuffer(PGLOBAL g)
+{
+ strcpy(g->Message, "ReadBuffer should not been called when zipping");
+ return RC_FX;
+} // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteBuffer: Deflate the buffer to the zip file. */
+/***********************************************************************/
+int ZIPFAM::WriteBuffer(PGLOBAL g)
+{
+ int len;
+
+ // Prepare to write the new line
+ strcat(strcpy(To_Buf, Tdbp->GetLine()), (Bin) ? CrLf : "\n");
+ len = strchr(To_Buf, '\n') - To_Buf + 1;
+ return zutp->writeEntry(g, To_Buf, len);
+} // end of WriteBuffer
+
+/***********************************************************************/
+/* Table file close routine for ZIP access method. */
+/***********************************************************************/
+void ZIPFAM::CloseTableFile(PGLOBAL g, bool)
+{
+ To_Fb->Count = 0;
+ zutp->close();
+} // end of CloseTableFile
+
+/* -------------------------- class ZPXFAM --------------------------- */
+
+/***********************************************************************/
+/* Constructor. */
+/***********************************************************************/
+ZPXFAM::ZPXFAM(PDOSDEF tdp) : FIXFAM(tdp)
+{
+ zutp = NULL;
+ target = tdp->GetEntry();
+ append = tdp->GetAppend();
+ //Lrecl = tdp->GetLrecl();
+} // end of UZXFAM standard constructor
+
+/***********************************************************************/
+/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */
+/***********************************************************************/
+bool ZPXFAM::OpenTableFile(PGLOBAL g)
+{
+ char filename[_MAX_PATH];
+ MODE mode = Tdbp->GetMode();
+
+ /*********************************************************************/
+ /* Allocate the ZIP utility class. */
+ /*********************************************************************/
+ zutp = new(g) ZIPUTIL(target);
+
+ // We used the file name relative to recorded datapath
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+
+ if (!zutp->OpenTable(g, mode, filename, append)) {
+ To_Fb = zutp->fp; // Useful when closing
+ } else
+ return true;
+
+ return AllocateBuffer(g);
+} // end of OpenTableFile
+
+/***********************************************************************/
+/* WriteBuffer: Deflate the buffer to the zip file. */
+/***********************************************************************/
+int ZPXFAM::WriteBuffer(PGLOBAL g)
+{
+ /*********************************************************************/
+ /* In Insert mode, we write only full blocks. */
+ /*********************************************************************/
+ if (++CurNum != Rbuf) {
+ Tdbp->IncLine(Lrecl); // Used by DOSCOL functions
+ return RC_OK;
+ } // endif CurNum
+
+ // Now start the compress process.
+ if (zutp->writeEntry(g, To_Buf, Lrecl * Rbuf) != RC_OK) {
+ Closing = true;
+ return RC_FX;
+ } // endif writeEntry
+
+ CurBlk++;
+ CurNum = 0;
+ Tdbp->SetLine(To_Buf);
+ return RC_OK;
+} // end of WriteBuffer
+
+/***********************************************************************/
+/* Table file close routine for ZIP access method. */
+/***********************************************************************/
+void ZPXFAM::CloseTableFile(PGLOBAL g, bool)
+{
+ if (CurNum && !Closing) {
+ // Some more inserted lines remain to be written
+ Rbuf = CurNum--;
+ WriteBuffer(g);
+ } // endif Curnum
+
+ To_Fb->Count = 0;
+ zutp->close();
+} // end of CloseTableFile
diff --git a/storage/connect/filamzip.h b/storage/connect/filamzip.h
index 9312fb2f70e..3160703bd20 100644
--- a/storage/connect/filamzip.h
+++ b/storage/connect/filamzip.h
@@ -1,7 +1,7 @@
/************** filamzip H Declares Source Code File (.H) **************/
-/* Name: filamzip.h Version 1.0 */
+/* Name: filamzip.h Version 1.1 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2016 */
+/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */
/* */
/* This file contains the ZIP file access method classes declares. */
/***********************************************************************/
@@ -10,10 +10,14 @@
#include "block.h"
#include "filamap.h"
+#include "filamfix.h"
+#include "zip.h"
#include "unzip.h"
#define DLLEXPORT extern "C"
+typedef class UNZFAM *PUNZFAM;
+typedef class UZXFAM *PUZXFAM;
typedef class ZIPFAM *PZIPFAM;
typedef class ZPXFAM *PZPXFAM;
@@ -21,16 +25,50 @@ typedef class ZPXFAM *PZPXFAM;
/* This is the ZIP utility fonctions class. */
/***********************************************************************/
class DllExport ZIPUTIL : public BLOCK {
-public:
+ public:
// Constructor
- ZIPUTIL(PSZ tgt, bool mul);
-//ZIPUTIL(ZIPUTIL *zutp);
+ ZIPUTIL(PSZ tgt);
+ //ZIPUTIL(ZIPUTIL *zutp);
// Implementation
-//PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)ZIPFAM(this); }
+ //PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)UNZFAM(this); }
// Methods
- virtual bool OpenTable(PGLOBAL g, MODE mode, char *fn);
+ bool OpenTable(PGLOBAL g, MODE mode, char *fn, bool append);
+ bool open(PGLOBAL g, char *fn, bool append);
+ bool addEntry(PGLOBAL g, char *entry);
+ void close(void);
+ void closeEntry(void);
+ int writeEntry(PGLOBAL g, char *buf, int len);
+ void getTime(tm_zip& tmZip);
+
+ // Members
+ zipFile zipfile; // The ZIP container file
+ PSZ target; // The target file name
+//unz_file_info finfo; // The current file info
+ PFBLOCK fp;
+//char *memory;
+//uint size;
+//int multiple; // Multiple targets
+ bool entryopen; // True when open current entry
+//char fn[FILENAME_MAX]; // The current entry file name
+//char mapCaseTable[256];
+}; // end of ZIPUTIL
+
+/***********************************************************************/
+/* This is the unZIP utility fonctions class. */
+/***********************************************************************/
+class DllExport UNZIPUTL : public BLOCK {
+ public:
+ // Constructor
+ UNZIPUTL(PSZ tgt, bool mul);
+//UNZIPUTL(UNZIPUTL *zutp);
+
+ // Implementation
+//PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)UNZFAM(this); }
+
+ // Methods
+ bool OpenTable(PGLOBAL g, MODE mode, char *fn);
bool open(PGLOBAL g, char *fn);
bool openEntry(PGLOBAL g);
void close(void);
@@ -50,68 +88,120 @@ public:
bool entryopen; // True when open current entry
char fn[FILENAME_MAX]; // The current entry file name
char mapCaseTable[256];
-}; // end of ZIPFAM
+}; // end of UNZIPUTL
/***********************************************************************/
-/* This is the ZIP file access method. */
+/* This is the unzip file access method. */
/***********************************************************************/
-class DllExport ZIPFAM : public MAPFAM {
- friend class ZPXFAM;
-public:
+class DllExport UNZFAM : public MAPFAM {
+//friend class UZXFAM;
+ public:
// Constructors
- ZIPFAM(PDOSDEF tdp);
- ZIPFAM(PZIPFAM txfp);
- ZIPFAM(PDOSDEF tdp, PZPXFAM txfp);
+ UNZFAM(PDOSDEF tdp);
+ UNZFAM(PUNZFAM txfp);
// Implementation
- virtual AMT GetAmType(void) { return TYPE_AM_ZIP; }
- virtual PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)ZIPFAM(this); }
+ virtual AMT GetAmType(void) {return TYPE_AM_ZIP;}
+ virtual PTXF Duplicate(PGLOBAL g) {return (PTXF) new(g) UNZFAM(this);}
// Methods
virtual int Cardinality(PGLOBAL g);
virtual int GetFileLength(PGLOBAL g);
-//virtual int MaxBlkSize(PGLOBAL g, int s) {return s;}
+ //virtual int MaxBlkSize(PGLOBAL g, int s) {return s;}
virtual bool OpenTableFile(PGLOBAL g);
virtual bool DeferReading(void) { return false; }
virtual int GetNext(PGLOBAL g);
-//virtual int ReadBuffer(PGLOBAL g);
-//virtual int WriteBuffer(PGLOBAL g);
-//virtual int DeleteRecords(PGLOBAL g, int irc);
-//virtual void CloseTableFile(PGLOBAL g, bool abort);
+ //virtual int ReadBuffer(PGLOBAL g);
+ //virtual int WriteBuffer(PGLOBAL g);
+ //virtual int DeleteRecords(PGLOBAL g, int irc);
+ //virtual void CloseTableFile(PGLOBAL g, bool abort);
-protected:
+ protected:
// Members
- ZIPUTIL *zutp;
- PSZ target;
- bool mul;
-}; // end of ZIPFAM
+ UNZIPUTL *zutp;
+ PSZ target;
+ bool mul;
+}; // end of UNZFAM
/***********************************************************************/
-/* This is the fixed ZIP file access method. */
+/* This is the fixed unzip file access method. */
/***********************************************************************/
-class DllExport ZPXFAM : public MPXFAM {
- friend class ZIPFAM;
-public:
+class DllExport UZXFAM : public MPXFAM {
+//friend class UNZFAM;
+ public:
// Constructors
- ZPXFAM(PDOSDEF tdp);
- ZPXFAM(PZPXFAM txfp);
+ UZXFAM(PDOSDEF tdp);
+ UZXFAM(PUZXFAM txfp);
// Implementation
virtual AMT GetAmType(void) { return TYPE_AM_ZIP; }
- virtual PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)ZPXFAM(this); }
+ virtual PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)UZXFAM(this); }
// Methods
virtual int GetFileLength(PGLOBAL g);
virtual int Cardinality(PGLOBAL g);
virtual bool OpenTableFile(PGLOBAL g);
virtual int GetNext(PGLOBAL g);
-//virtual int ReadBuffer(PGLOBAL g);
+ //virtual int ReadBuffer(PGLOBAL g);
+
+ protected:
+ // Members
+ UNZIPUTL *zutp;
+ PSZ target;
+ bool mul;
+}; // end of UZXFAM
+
+/***********************************************************************/
+/* This is the zip file access method. */
+/***********************************************************************/
+class DllExport ZIPFAM : public DOSFAM {
+ public:
+ // Constructors
+ ZIPFAM(PDOSDEF tdp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_ZIP;}
+
+ // Methods
+ virtual int Cardinality(PGLOBAL g) {return 0;}
+ virtual int GetFileLength(PGLOBAL g) {return g ? 0 : 1;}
+ //virtual int MaxBlkSize(PGLOBAL g, int s) {return s;}
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int ReadBuffer(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ //virtual int DeleteRecords(PGLOBAL g, int irc);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
+
+ protected:
+ // Members
+ ZIPUTIL *zutp;
+ PSZ target;
+ bool append;
+}; // end of ZIPFAM
+
+/***********************************************************************/
+/* This is the fixed zip file access method. */
+/***********************************************************************/
+class DllExport ZPXFAM : public FIXFAM {
+ public:
+ // Constructors
+ ZPXFAM(PDOSDEF tdp);
+
+ // Implementation
+ virtual AMT GetAmType(void) {return TYPE_AM_ZIP;}
+
+ // Methods
+ virtual int Cardinality(PGLOBAL g) {return 0;}
+ virtual int GetFileLength(PGLOBAL g) {return g ? 0 : 1;}
+ virtual bool OpenTableFile(PGLOBAL g);
+ virtual int WriteBuffer(PGLOBAL g);
+ virtual void CloseTableFile(PGLOBAL g, bool abort);
-protected:
+ protected:
// Members
ZIPUTIL *zutp;
PSZ target;
- bool mul;
+ bool append;
}; // end of ZPXFAM
#endif // __FILAMZIP_H
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index e66f03e8174..d1ab18f52d5 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2016
+/* Copyright (C) Olivier Bertrand 2004 - 2017
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
@@ -125,6 +125,8 @@
#endif // UNIX
#include "global.h"
#include "plgdbsem.h"
+#include "xtable.h"
+#include "tabext.h"
#if defined(ODBC_SUPPORT)
#include "odbccat.h"
#endif // ODBC_SUPPORT
@@ -132,12 +134,11 @@
#include "tabjdbc.h"
#include "jdbconn.h"
#endif // JDBC_SUPPORT
-#include "xtable.h"
#include "tabmysql.h"
#include "filamdbf.h"
#include "tabxcl.h"
#include "tabfmt.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "tabcol.h"
#include "xindex.h"
#if defined(__WIN__)
@@ -171,9 +172,9 @@
#define JSONMAX 10 // JSON Default max grp size
extern "C" {
- char version[]= "Version 1.05.0001 December 13, 2016";
+ char version[]= "Version 1.05.0003 February 27, 2017";
#if defined(__WIN__)
- char compver[]= "Version 1.05.0001 " __DATE__ " " __TIME__;
+ char compver[]= "Version 1.05.0003 " __DATE__ " " __TIME__;
char slash= '\\';
#else // !__WIN__
char slash= '/';
@@ -214,6 +215,7 @@ int TranslateJDBCType(int stp, char *tn, int prec, int& len, char& v);
void PushWarning(PGLOBAL g, THD *thd, int level);
bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host,
const char *db, char *tab, const char *src, int port);
+bool ZipLoadFile(PGLOBAL, char*, char*, char*, bool, bool);
bool ExactInfo(void);
USETEMP UseTemp(void);
int GetConvSize(void);
@@ -556,7 +558,7 @@ ha_create_table_option connect_index_option_list[]=
/***********************************************************************/
/* Push G->Message as a MySQL warning. */
/***********************************************************************/
-bool PushWarning(PGLOBAL g, PTDBASE tdbp, int level)
+bool PushWarning(PGLOBAL g, PTDB tdbp, int level)
{
PHC phc;
THD *thd;
@@ -1024,7 +1026,7 @@ char *GetListOption(PGLOBAL g, const char *opname,
char key[16], val[256];
char *pk, *pv, *pn;
- char *opval= (char*) def;
+ char *opval= (char*)def;
int n;
for (pk= (char*)oplist; pk; pk= ++pn) {
@@ -1032,26 +1034,17 @@ char *GetListOption(PGLOBAL g, const char *opname,
pv= strchr(pk, '=');
if (pv && (!pn || pv < pn)) {
- n= pv - pk;
+ n= MY_MIN(pv - pk, sizeof(key) - 1);
memcpy(key, pk, n);
key[n]= 0;
pv++;
-
- if (pn) {
- n= pn - pv;
- memcpy(val, pv, n);
- val[n]= 0;
- } else
- strcpy(val, pv);
-
+ n= MY_MIN((pn ? pn - pv : strlen(pv)), sizeof(val) - 1);
+ memcpy(val, pv, n);
+ val[n]= 0;
} else {
- if (pn) {
- n= MY_MIN(pn - pk, 15);
- memcpy(key, pk, n);
- key[n]= 0;
- } else
- strcpy(key, pk);
-
+ n= MY_MIN((pn ? pn - pk : strlen(pk)), sizeof(key) - 1);
+ memcpy(key, pk, n);
+ key[n]= 0;
val[0]= 0;
} // endif pv
@@ -1105,7 +1098,7 @@ char *GetStringTableOption(PGLOBAL g, PTOS options, char *opname, char *sdef)
else if (!stricmp(opname, "Data_charset"))
opval= options->data_charset;
- if (!opval && options && options->oplist)
+ if (!opval && options->oplist)
opval= GetListOption(g, opname, options->oplist);
return opval ? (char*)opval : sdef;
@@ -2113,7 +2106,7 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *)
PCOL colp;
PVAL value, sdvalin;
Field *fp;
- PTDBASE tp= (PTDBASE)tdbp;
+//PTDBASE tp= (PTDBASE)tdbp;
String attribute(attr_buffer, sizeof(attr_buffer),
table->s->table_charset);
my_bitmap_map *bmap= dbug_tmp_use_all_columns(table, table->read_set);
@@ -2132,7 +2125,7 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *)
&& tdbp->GetAmType() != TYPE_AM_ODBC
&& tdbp->GetAmType() != TYPE_AM_JDBC) ||
bitmap_is_set(table->write_set, fp->field_index)) {
- for (colp= tp->GetSetCols(); colp; colp= colp->GetNext())
+ for (colp= tdbp->GetSetCols(); colp; colp= colp->GetNext())
if (!stricmp(colp->GetName(), fp->field_name))
break;
@@ -2219,7 +2212,7 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *)
} else if (xmod == MODE_UPDATE) {
PCOL cp;
- for (cp= tp->GetColumns(); cp; cp= cp->GetNext())
+ for (cp= tdbp->GetColumns(); cp; cp= cp->GetNext())
if (!stricmp(colp->GetName(), cp->GetName()))
break;
@@ -2686,7 +2679,8 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
{
AMT tty = filp->Type;
char *body= filp->Body;
- unsigned int i;
+ char *havg= filp->Having;
+ unsigned int i;
bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC);
bool nonul= ((tty == TYPE_AM_ODBC || tty == TYPE_AM_JDBC) &&
(tdbp->GetMode() == MODE_INSERT || tdbp->GetMode() == MODE_DELETE));
@@ -2699,7 +2693,8 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
htrc("Cond type=%d\n", cond->type());
if (cond->type() == COND::COND_ITEM) {
- char *p1, *p2;
+ char *pb0, *pb1, *pb2, *ph0, *ph1, *ph2;
+ bool bb = false, bh = false;
Item_cond *cond_item= (Item_cond *)cond;
if (x)
@@ -2719,38 +2714,78 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
List_iterator<Item> li(*arglist);
const Item *subitem;
- p1= body + strlen(body);
- strcpy(p1, "(");
- p2= p1 + 1;
+ pb0= pb1= body + strlen(body);
+ strcpy(pb0, "(");
+ pb2= pb1 + 1;
+
+ if (havg) {
+ ph0= ph1= havg + strlen(havg);
+ strcpy(ph0, "(");
+ ph2= ph1 + 1;
+ } // endif havg
for (i= 0; i < arglist->elements; i++)
if ((subitem= li++)) {
if (!CheckCond(g, filp, subitem)) {
if (vop == OP_OR || nonul)
return NULL;
- else
- *p2= 0;
+ else {
+ *pb2= 0;
+ if (havg) *ph2= 0;
+ } // endelse
} else {
- p1= p2 + strlen(p2);
- strcpy(p1, GetValStr(vop, false));
- p2= p1 + strlen(p1);
+ if (filp->Bd) {
+ pb1= pb2 + strlen(pb2);
+ strcpy(pb1, GetValStr(vop, false));
+ pb2= pb1 + strlen(pb1);
+ } // endif Bd
+
+ if (filp->Hv) {
+ ph1= ph2 + strlen(ph2);
+ strcpy(ph1, GetValStr(vop, false));
+ ph2= ph1 + strlen(ph1);
+ } // endif Hv
+
} // endif CheckCond
+ bb |= filp->Bd;
+ bh |= filp->Hv;
+ filp->Bd = filp->Hv = false;
} else
return NULL;
- if (*p1 != '(')
- strcpy(p1, ")");
- else
- return NULL;
+ if (bb) {
+ strcpy(pb1, ")");
+ filp->Bd = bb;
+ } else
+ *pb0= 0;
+
+ if (havg) {
+ if (bb && bh && vop == OP_OR) {
+ // Cannot or'ed a where clause with a having clause
+ bb= bh= 0;
+ *pb0 = 0;
+ *ph0 = 0;
+ } else if (bh) {
+ strcpy(ph1, ")");
+ filp->Hv= bh;
+ } else
+ *ph0 = 0;
+
+ } // endif havg
+
+ if (!bb && !bh)
+ return NULL;
} else if (cond->type() == COND::FUNC_ITEM) {
unsigned int i;
- bool iscol, neg= FALSE;
+ bool iscol, ishav= false, neg= false;
Item_func *condf= (Item_func *)cond;
Item* *args= condf->arguments();
+ filp->Bd = filp->Hv = false;
+
if (trace)
htrc("Func type=%d argnum=%d\n", condf->functype(),
condf->argument_count());
@@ -2799,8 +2834,9 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
ha_field_option_struct *fop;
Item_field *pField= (Item_field *)args[i];
- if (x && i)
- return NULL;
+ // IN and BETWEEN clauses should be col VOP list
+ if (i && (x || ismul))
+ return NULL; // IN and BETWEEN clauses should be col VOP list
else if (pField->field->table != table)
return NULL; // Field does not belong to this table
else if (tty != TYPE_AM_WMI && IsIndexed(pField->field))
@@ -2816,10 +2852,19 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
else
return NULL;
- } else if (tty == TYPE_AM_TBL)
- return NULL;
- else
- fnm= pField->field->field_name;
+ } else if (tty == TYPE_AM_TBL) {
+ return NULL;
+ } else {
+ bool h;
+
+ fnm = filp->Chk(pField->field->field_name, &h);
+
+ if (h && i && !ishav)
+ return NULL; // Having should be col VOP arg
+ else
+ ishav = h;
+
+ } // endif's
if (trace) {
htrc("Field index=%d\n", pField->field->field_index);
@@ -2828,11 +2873,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
htrc("Field_type=%d\n", args[i]->field_type());
} // endif trace
- // IN and BETWEEN clauses should be col VOP list
- if (i && ismul)
- return NULL;
-
- strcat(body, fnm);
+ strcat((ishav ? havg : body), fnm);
} else if (args[i]->type() == COND::FUNC_ITEM) {
if (tty == TYPE_AM_MYSQL) {
if (!CheckCond(g, filp, args[i]))
@@ -2871,32 +2912,34 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
return NULL;
if (!x) {
+ char *s = (ishav) ? havg : body;
+
// Append the value to the filter
switch (args[i]->field_type()) {
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATETIME:
if (tty == TYPE_AM_ODBC) {
- strcat(body, "{ts '");
- strncat(body, res->ptr(), res->length());
+ strcat(s, "{ts '");
+ strncat(s, res->ptr(), res->length());
if (res->length() < 19)
- strcat(body, "1970-01-01 00:00:00" + res->length());
+ strcat(s, "1970-01-01 00:00:00" + res->length());
- strcat(body, "'}");
+ strcat(s, "'}");
break;
} // endif ODBC
case MYSQL_TYPE_DATE:
if (tty == TYPE_AM_ODBC) {
- strcat(body, "{d '");
- strcat(strncat(body, res->ptr(), res->length()), "'}");
+ strcat(s, "{d '");
+ strcat(strncat(s, res->ptr(), res->length()), "'}");
break;
} // endif ODBC
case MYSQL_TYPE_TIME:
if (tty == TYPE_AM_ODBC) {
- strcat(body, "{t '");
- strcat(strncat(body, res->ptr(), res->length()), "'}");
+ strcat(s, "{t '");
+ strcat(strncat(s, res->ptr(), res->length()), "'}");
break;
} // endif ODBC
@@ -2905,39 +2948,39 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
switch (args[0]->field_type()) {
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATETIME:
- strcat(body, "{ts '");
- strncat(body, res->ptr(), res->length());
+ strcat(s, "{ts '");
+ strncat(s, res->ptr(), res->length());
if (res->length() < 19)
- strcat(body, "1970-01-01 00:00:00" + res->length());
+ strcat(s, "1970-01-01 00:00:00" + res->length());
- strcat(body, "'}");
+ strcat(s, "'}");
break;
case MYSQL_TYPE_DATE:
- strcat(body, "{d '");
- strncat(body, res->ptr(), res->length());
- strcat(body, "'}");
+ strcat(s, "{d '");
+ strncat(s, res->ptr(), res->length());
+ strcat(s, "'}");
break;
case MYSQL_TYPE_TIME:
- strcat(body, "{t '");
- strncat(body, res->ptr(), res->length());
- strcat(body, "'}");
+ strcat(s, "{t '");
+ strncat(s, res->ptr(), res->length());
+ strcat(s, "'}");
break;
default:
- strcat(body, "'");
- strncat(body, res->ptr(), res->length());
- strcat(body, "'");
+ strcat(s, "'");
+ strncat(s, res->ptr(), res->length());
+ strcat(s, "'");
} // endswitch field type
} else {
- strcat(body, "'");
- strncat(body, res->ptr(), res->length());
- strcat(body, "'");
+ strcat(s, "'");
+ strncat(s, res->ptr(), res->length());
+ strcat(s, "'");
} // endif tty
break;
default:
- strncat(body, res->ptr(), res->length());
+ strncat(s, res->ptr(), res->length());
} // endswitch field type
} else {
@@ -2953,22 +2996,28 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
} // endif x
- } // endif
+ } // endif's Type
if (!x) {
- if (!i)
- strcat(body, GetValStr(vop, neg));
+ char *s = (ishav) ? havg : body;
+
+ if (!i)
+ strcat(s, GetValStr(vop, neg));
else if (vop == OP_XX && i == 1)
- strcat(body, " AND ");
+ strcat(s, " AND ");
else if (vop == OP_IN)
- strcat(body, (i == condf->argument_count() - 1) ? ")" : ",");
+ strcat(s, (i == condf->argument_count() - 1) ? ")" : ",");
} // endif x
} // endfor i
- if (x)
- filp->Op= vop;
+ if (x)
+ filp->Op = vop;
+ else if (ishav)
+ filp->Hv = true;
+ else
+ filp->Bd = true;
} else {
if (trace)
@@ -3025,16 +3074,28 @@ const COND *ha_connect::cond_push(const COND *cond)
if (b) {
PCFIL filp;
+ int rc;
if ((filp= tdbp->GetCondFil()) && filp->Cond == cond &&
filp->Idx == active_index && filp->Type == tty)
goto fin; // Already done
filp= new(g) CONDFIL(cond, active_index, tty);
- filp->Body= (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0);
- *filp->Body= 0;
+ rc = filp->Init(g, this);
+
+ if (rc == RC_INFO) {
+ filp->Having = (char*)PlugSubAlloc(g, NULL, 256);
+ *filp->Having = 0;
+ } else if (rc == RC_FX)
+ goto fin;
+
+ filp->Body = (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0);
+ *filp->Body = 0;
if (CheckCond(g, filp, cond)) {
+ if (filp->Having && strlen(filp->Having) > 255)
+ goto fin; // Memory collapse
+
if (trace)
htrc("cond_push: %s\n", filp->Body);
@@ -3207,9 +3268,9 @@ int ha_connect::optimize(THD* thd, HA_CHECK_OPT*)
tdbp= GetTDB(g);
dup->Check |= CHK_OPT;
- if (tdbp) {
+ if (tdbp && !tdbp->IsRemote()) {
bool dop= IsTypeIndexable(GetRealType(NULL));
- bool dox= (((PTDBASE)tdbp)->GetDef()->Indexable() == 1);
+ bool dox= (tdbp->GetDef()->Indexable() == 1);
if ((rc= ((PTDBASE)tdbp)->ResetTableOpt(g, dop, dox))) {
if (rc == RC_INFO) {
@@ -3220,7 +3281,7 @@ int ha_connect::optimize(THD* thd, HA_CHECK_OPT*)
} // endif rc
- } else
+ } else if (!tdbp)
rc= HA_ERR_INTERNAL_ERROR;
return rc;
@@ -3463,9 +3524,9 @@ int ha_connect::index_init(uint idx, bool sorted)
htrc("index_init CONNECT: %s\n", g->Message);
active_index= MAX_KEY;
rc= HA_ERR_INTERNAL_ERROR;
- } else if (((PTDBDOX)tdbp)->To_Kindex) {
+ } else if (tdbp->GetKindex()) {
if (((PTDBDOX)tdbp)->To_Kindex->GetNum_K()) {
- if (((PTDBASE)tdbp)->GetFtype() != RECFM_NAF)
+ if (tdbp->GetFtype() != RECFM_NAF)
((PTDBDOX)tdbp)->GetTxfp()->ResetBuffer(g);
active_index= idx;
@@ -3879,11 +3940,10 @@ int ha_connect::rnd_next(uchar *buf)
void ha_connect::position(const uchar *)
{
DBUG_ENTER("ha_connect::position");
-//if (((PTDBASE)tdbp)->GetDef()->Indexable())
- my_store_ptr(ref, ref_length, (my_off_t)((PTDBASE)tdbp)->GetRecpos());
+ my_store_ptr(ref, ref_length, (my_off_t)tdbp->GetRecpos());
if (trace > 1)
- htrc("position: pos=%d\n", ((PTDBASE)tdbp)->GetRecpos());
+ htrc("position: pos=%d\n", tdbp->GetRecpos());
DBUG_VOID_RETURN;
} // end of position
@@ -3908,14 +3968,14 @@ void ha_connect::position(const uchar *)
int ha_connect::rnd_pos(uchar *buf, uchar *pos)
{
int rc;
- PTDBASE tp= (PTDBASE)tdbp;
+//PTDBASE tp= (PTDBASE)tdbp;
DBUG_ENTER("ha_connect::rnd_pos");
- if (!tp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length))) {
+ if (!tdbp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length))) {
if (trace)
- htrc("rnd_pos: %d\n", tp->GetRecpos());
+ htrc("rnd_pos: %d\n", tdbp->GetRecpos());
- tp->SetFilter(NULL);
+ tdbp->SetFilter(NULL);
rc= rnd_next(buf);
} else
rc= HA_ERR_KEY_NOT_FOUND;
@@ -4093,7 +4153,7 @@ int ha_connect::delete_all_rows()
if (tdbp && tdbp->GetUse() == USE_OPEN &&
tdbp->GetAmType() != TYPE_AM_XML &&
- ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF)
+ tdbp->GetFtype() != RECFM_NAF)
// Close and reopen the table so it will be deleted
rc= CloseTable(g);
@@ -4471,12 +4531,12 @@ int ha_connect::external_lock(THD *thd, int lock_type)
if (!tdbp) {
if (!(tdbp= GetTDB(g)))
DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
- else if (!((PTDBASE)tdbp)->GetDef()->Indexable()) {
+ else if (!tdbp->GetDef()->Indexable()) {
sprintf(g->Message, "external_lock: Table %s is not indexable", tdbp->GetName());
// DBUG_RETURN(HA_ERR_INTERNAL_ERROR); causes assert error
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message);
DBUG_RETURN(0);
- } else if (((PTDBASE)tdbp)->GetDef()->Indexable() == 1) {
+ } else if (tdbp->GetDef()->Indexable() == 1) {
bool oldsep= ((PCHK)g->Xchk)->oldsep;
bool newsep= ((PCHK)g->Xchk)->newsep;
PTDBDOS tdp= (PTDBDOS)tdbp;
@@ -4557,7 +4617,7 @@ int ha_connect::external_lock(THD *thd, int lock_type)
rc= 0;
} // endif MakeIndex
- } else if (((PTDBASE)tdbp)->GetDef()->Indexable() == 3) {
+ } else if (tdbp->GetDef()->Indexable() == 3) {
if (CheckVirtualIndex(NULL)) {
// Make it a warning to avoid crash
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
@@ -5225,6 +5285,8 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
fncn= topt->catfunc;
fnc= GetFuncID(fncn);
sep= topt->separator;
+ mul = (int)topt->multiple;
+ tbl= topt->tablist;
col= topt->colist;
if (topt->oplist) {
@@ -5422,8 +5484,8 @@ static int connect_assisted_discovery(handlerton *, THD* thd,
if (mydef->GetPassword())
pwd= mydef->GetPassword();
- if (mydef->GetDatabase())
- db= mydef->GetDatabase();
+ if (mydef->GetTabschema())
+ db = mydef->GetTabschema();
if (mydef->GetTabname())
tab= mydef->GetTabname();
@@ -6049,8 +6111,8 @@ int ha_connect::create(const char *name, TABLE *table_arg,
if (mydef->GetHostname())
host= mydef->GetHostname();
- if (mydef->GetDatabase())
- db= mydef->GetDatabase();
+ if (mydef->GetTabschema())
+ db = mydef->GetTabschema();
if (mydef->GetTabname())
tab= mydef->GetTabname();
@@ -6263,21 +6325,26 @@ int ha_connect::create(const char *name, TABLE *table_arg,
// Check for incompatible options
if (options->sepindex) {
my_message(ER_UNKNOWN_ERROR,
- "SEPINDEX is incompatible with unspecified file name",
- MYF(0));
+ "SEPINDEX is incompatible with unspecified file name", MYF(0));
DBUG_RETURN(HA_ERR_UNSUPPORTED);
- } else if (GetTypeID(options->type) == TAB_VEC)
- if (!table->s->max_rows || options->split) {
- my_printf_error(ER_UNKNOWN_ERROR,
- "%s tables whose file name is unspecified cannot be split",
- MYF(0), options->type);
- DBUG_RETURN(HA_ERR_UNSUPPORTED);
- } else if (options->header == 2) {
- my_printf_error(ER_UNKNOWN_ERROR,
- "header=2 is not allowed for %s tables whose file name is unspecified",
- MYF(0), options->type);
- DBUG_RETURN(HA_ERR_UNSUPPORTED);
- } // endif's
+ } else if (GetTypeID(options->type) == TAB_VEC) {
+ if (!table->s->max_rows || options->split) {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "%s tables whose file name is unspecified cannot be split",
+ MYF(0), options->type);
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ } else if (options->header == 2) {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "header=2 is not allowed for %s tables whose file name is unspecified",
+ MYF(0), options->type);
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ } // endif's
+
+ } else if (options->zipped) {
+ my_message(ER_UNKNOWN_ERROR,
+ "ZIPPED is incompatible with unspecified file name", MYF(0));
+ DBUG_RETURN(HA_ERR_UNSUPPORTED);
+ } // endif's options
// Fold type to lower case
for (int i= 0; i < 12; i++)
@@ -6330,6 +6397,36 @@ int ha_connect::create(const char *name, TABLE *table_arg,
if (trace)
htrc("xchk=%p createas=%d\n", g->Xchk, g->Createas);
+ if (options->zipped) {
+ // Check whether the zip entry must be made from a file
+ char *fn = GetListOption(g, "Load", options->oplist, NULL);
+
+ if (fn) {
+ char zbuf[_MAX_PATH], buf[_MAX_PATH], dbpath[_MAX_PATH];
+ char *entry = GetListOption(g, "Entry", options->oplist, NULL);
+ char *a = GetListOption(g, "Append", options->oplist, "NO");
+ bool append = *a == '1' || *a == 'Y' || *a == 'y' || !stricmp(a, "ON");
+ char *m = GetListOption(g, "Mulentries", options->oplist, "NO");
+ bool mul = *m == '1' || *m == 'Y' || *m == 'y' || !stricmp(m, "ON");
+
+ if (!entry && !mul) {
+ my_message(ER_UNKNOWN_ERROR, "Missing entry name", MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif entry
+
+ strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/");
+ PlugSetPath(zbuf, options->filename, dbpath);
+ PlugSetPath(buf, fn, dbpath);
+
+ if (ZipLoadFile(g, zbuf, buf, entry, append, mul)) {
+ my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0));
+ DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
+ } // endif LoadFile
+
+ } // endif fn
+
+ } // endif zipped
+
// To check whether indexes have to be made or remade
if (!g->Xchk) {
PIXDEF xdp;
@@ -6948,10 +7045,10 @@ maria_declare_plugin(connect)
PLUGIN_LICENSE_GPL,
connect_init_func, /* Plugin Init */
connect_done_func, /* Plugin Deinit */
- 0x0104, /* version number (1.04) */
+ 0x0105, /* version number (1.05) */
NULL, /* status variables */
connect_system_variables, /* system variables */
- "1.05.0001", /* string version */
+ "1.05.0003", /* string version */
MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */
}
maria_declare_plugin_end;
diff --git a/storage/connect/jdbconn.cpp b/storage/connect/jdbconn.cpp
index a69f84a94a1..c1d077406b7 100644
--- a/storage/connect/jdbconn.cpp
+++ b/storage/connect/jdbconn.cpp
@@ -1,7 +1,7 @@
/************ Jdbconn C++ Functions Source Code File (.CPP) ************/
-/* Name: JDBCONN.CPP Version 1.0 */
+/* Name: JDBCONN.CPP Version 1.1 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2016 */
+/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */
/* */
/* This file contains the JDBC connection classes functions. */
/***********************************************************************/
@@ -45,9 +45,9 @@
#include "plgdbsem.h"
#include "xobject.h"
#include "xtable.h"
+#include "tabext.h"
#include "tabjdbc.h"
//#include "jdbconn.h"
-//#include "plgcnx.h" // For DB types
#include "resource.h"
#include "valblk.h"
#include "osutil.h"
@@ -318,13 +318,21 @@ PQRYRES JDBCColumns(PGLOBAL g, char *db, char *table, char *colpat,
/**************************************************************************/
PQRYRES JDBCSrcCols(PGLOBAL g, char *src, PJPARM sjp)
{
+ char *sqry;
PQRYRES qrp;
JDBConn *jcp = new(g)JDBConn(g, NULL);
if (jcp->Open(sjp))
return NULL;
- qrp = jcp->GetMetaData(g, src);
+ if (strstr(src, "%s")) {
+ // Place holder for an eventual where clause
+ sqry = (char*)PlugSubAlloc(g, NULL, strlen(src) + 2);
+ sprintf(sqry, src, "1=1"); // dummy where clause
+ } else
+ sqry = src;
+
+ qrp = jcp->GetMetaData(g, sqry);
jcp->Close();
return qrp;
} // end of JDBCSrcCols
@@ -818,6 +826,11 @@ int JDBConn::Open(PJPARM sop)
jpop->Append(GetPluginDir());
jpop->Append("JdbcInterface.jar");
+ // All wrappers are pre-compiled in JavaWrappers.jar in the plugin dir
+ jpop->Append(sep);
+ jpop->Append(GetPluginDir());
+ jpop->Append("JavaWrappers.jar");
+
//================== prepare loading of Java VM ============================
JavaVMInitArgs vm_args; // Initialization arguments
JavaVMOption* options = new JavaVMOption[N]; // JVM invocation options
@@ -1157,6 +1170,9 @@ void JDBConn::Close()
jint rc;
jmethodID did = nullptr;
+ // Could have been detached in case of join
+ rc = jvm->AttachCurrentThread((void**)&env, nullptr);
+
if (gmID(m_G, did, "JdbcDisconnect", "()I"))
printf("%s\n", Msg);
else if (Check(env->CallIntMethod(job, did)))
diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp
index c45630129f1..b473871e9f7 100644
--- a/storage/connect/json.cpp
+++ b/storage/connect/json.cpp
@@ -1,7 +1,7 @@
/*************** json CPP Declares Source Code File (.H) ***************/
-/* Name: json.cpp Version 1.2 */
+/* Name: json.cpp Version 1.3 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */
+/* (C) Copyright to the author Olivier BERTRAND 2014 - 2017 */
/* */
/* This file contains the JSON classes functions. */
/***********************************************************************/
@@ -27,8 +27,33 @@
#define EL "\r\n"
#else
#define EL "\n"
+#undef SE_CATCH // Does not work for Linux
#endif
+#if defined(SE_CATCH)
+/**************************************************************************/
+/* This is the support of catching C interrupts to prevent crashes. */
+/**************************************************************************/
+#include <eh.h>
+
+class SE_Exception {
+public:
+ SE_Exception(unsigned int n, PEXCEPTION_RECORD p) : nSE(n), eRec(p) {}
+ ~SE_Exception() {}
+
+ unsigned int nSE;
+ PEXCEPTION_RECORD eRec;
+}; // end of class SE_Exception
+
+void trans_func(unsigned int u, _EXCEPTION_POINTERS* pExp)
+{
+ throw SE_Exception(u, pExp->ExceptionRecord);
+} // end of trans_func
+
+char *GetExceptionDesc(PGLOBAL g, unsigned int e);
+#endif // SE_CATCH
+
+
/***********************************************************************/
/* Parse a json string. */
/* Note: when pretty is not known, the caller set pretty to 3. */
@@ -40,6 +65,9 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int *ptyp, bool *comma)
PJSON jsp = NULL;
STRG src;
+ if (trace)
+ htrc("ParseJson: s=%.10s len=%d\n", s, len);
+
if (!s || !len) {
strcpy(g->Message, "Void JSON object");
return NULL;
@@ -53,15 +81,37 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int *ptyp, bool *comma)
if (s[0] == '[' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n')))
pty[0] = false;
+
// Save stack and allocation environment and prepare error return
if (g->jump_level == MAX_JUMP) {
strcpy(g->Message, MSG(TOO_MANY_JUMPS));
return NULL;
} // endif jump_level
- if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) {
- goto err;
- } // endif rc
+#if defined(SE_CATCH)
+ // Let's try to recover from any kind of interrupt
+ _se_translator_function f = _set_se_translator(trans_func);
+
+ try {
+#endif // SE_CATCH --------------------- try section --------------------
+ if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) {
+ goto err;
+ } // endif rc
+
+#if defined(SE_CATCH) // ------------- end of try section -----------------
+ } catch (SE_Exception e) {
+ sprintf(g->Message, "ParseJson: exception doing setjmp: %s (rc=%hd)",
+ GetExceptionDesc(g, e.nSE), e.nSE);
+ _set_se_translator(f);
+ goto err;
+ } catch (...) {
+ strcpy(g->Message, "Exception doing setjmp");
+ _set_se_translator(f);
+ goto err;
+ } // end of try-catches
+
+ _set_se_translator(f);
+#endif // SE_CATCH
for (i = 0; i < len; i++)
switch (s[i]) {
@@ -140,7 +190,7 @@ tryit:
strcpy(g->Message, "More than one item in file");
err:
- g->jump_level--;
+ g->jump_level--;
return NULL;
} // end of ParseJson
@@ -390,14 +440,14 @@ char *ParseString(PGLOBAL g, int& i, STRG& src)
// if (charset == utf8) {
char xs[5];
uint hex;
-
+
xs[0] = s[++i];
xs[1] = s[++i];
xs[2] = s[++i];
xs[3] = s[++i];
xs[4] = 0;
hex = strtoul(xs, NULL, 16);
-
+
if (hex < 0x80) {
p[n] = (uchar)hex;
} else if (hex < 0x800) {
@@ -414,7 +464,7 @@ char *ParseString(PGLOBAL g, int& i, STRG& src)
} else {
char xs[3];
UINT hex;
-
+
i += 2;
xs[0] = s[++i];
xs[1] = s[++i];
@@ -468,7 +518,7 @@ PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src)
case '.':
if (!found_digit || has_dot || has_e)
goto err;
-
+
has_dot = true;
break;
case 'e':
@@ -769,7 +819,7 @@ bool JOUTSTR::Escape(const char *s)
for (unsigned int i = 0; s[i]; i++)
switch (s[i]) {
- case '"':
+ case '"':
case '\\':
case '\t':
case '\n':
@@ -1057,7 +1107,7 @@ void JARRAY::InitArray(PGLOBAL g)
int i;
PJVAL jvp, *pjvp = &First;
- for (Size = 0, jvp = First; jvp; jvp = jvp->Next)
+ for (Size = 0, jvp = First; jvp; jvp = jvp->Next)
if (!jvp->Del)
Size++;
@@ -1191,8 +1241,8 @@ bool JARRAY::IsNull(void)
/***********************************************************************/
JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON()
{
- Jsp = NULL;
- Value = AllocateValue(g, valp);
+ Jsp = NULL;
+ Value = AllocateValue(g, valp);
Next = NULL;
Del = false;
} // end of JVALUE constructor
@@ -1297,7 +1347,7 @@ PSZ JVALUE::GetText(PGLOBAL g, PSZ text)
} // end of GetText
void JVALUE::SetValue(PJSON jsp)
-{
+{
if (jsp && jsp->GetType() == TYPE_JVAL) {
Jsp = jsp->GetJsp();
Value = jsp->GetValue();
diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp
index f9034f25739..0f693c3c0d6 100644
--- a/storage/connect/jsonudf.cpp
+++ b/storage/connect/jsonudf.cpp
@@ -1,6 +1,6 @@
/****************** jsonudf C++ Program Source Code File (.CPP) ******************/
-/* PROGRAM NAME: jsonudf Version 1.4 */
-/* (C) Copyright to the author Olivier BERTRAND 2015-2016 */
+/* PROGRAM NAME: jsonudf Version 1.5 */
+/* (C) Copyright to the author Olivier BERTRAND 2015-2017 */
/* This program are the JSON User Defined Functions . */
/*********************************************************************************/
@@ -242,13 +242,16 @@ my_bool JSNX::ParseJpath(PGLOBAL g)
// Jpath = Name;
return true;
- pbuf = PlugDup(g, Jpath);
+ if (!(pbuf = PlgDBDup(g, Jpath)))
+ return true;
// The Jpath must be analyzed
for (i = 0, p = pbuf; (p = strchr(p, ':')); i++, p++)
Nod++; // One path node found
- Nodes = (PJNODE)PlugSubAlloc(g, NULL, (++Nod) * sizeof(JNODE));
+ if (!(Nodes = (PJNODE)PlgDBSubAlloc(g, NULL, (++Nod) * sizeof(JNODE))))
+ return true;
+
memset(Nodes, 0, (Nod)* sizeof(JNODE));
// Analyze the Jpath for this column
@@ -1086,9 +1089,10 @@ inline void JsonFreeMem(PGLOBAL g)
/*********************************************************************************/
static my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args,
char *message, my_bool mbn,
- unsigned long reslen, unsigned long memlen)
+ unsigned long reslen, unsigned long memlen,
+ unsigned long more = 0)
{
- PGLOBAL g = PlugInit(NULL, memlen);
+ PGLOBAL g = PlugInit(NULL, memlen + more);
if (!g) {
strcpy(message, "Allocation error");
@@ -1100,6 +1104,7 @@ static my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args,
} // endif g
g->Mrr = (args->arg_count && args->args[0]) ? 1 : 0;
+ g->ActivityStart = (PACTIVITY)more;
initid->maybe_null = mbn;
initid->max_length = reslen;
initid->ptr = (char*)g;
@@ -1444,6 +1449,8 @@ static my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, uint n,
} // endif b
+ ml += (unsigned long)g->ActivityStart; // more
+
if (ml > g->Sarea_Size) {
free(g->Sarea);
@@ -2758,7 +2765,7 @@ void json_item_merge_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- unsigned long reslen, memlen;
+ unsigned long reslen, memlen, more;
int n = IsJson(args, 0);
if (args->arg_count < 2) {
@@ -2767,7 +2774,7 @@ my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} else if (!n && args->arg_type[0] != STRING_RESULT) {
strcpy(message, "First argument must be a json item");
return true;
- } else if (args->arg_type[1] != STRING_RESULT) {
+ } else if (args->arg_type[1] != STRING_RESULT) {
strcpy(message, "Second argument is not a string (jpath)");
return true;
} else
@@ -2780,11 +2787,13 @@ my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
memcpy(fn, args->args[0], args->lengths[0]);
fn[args->lengths[0]] = 0;
fl = GetFileLength(fn);
- memlen += fl * 3;
- } else if (n != 3)
- memlen += args->lengths[0] * 3;
+ more = fl * 3;
+ } else if (n != 3) {
+ more = args->lengths[0] * 3;
+ } else
+ more = 0;
- return JsonInit(initid, args, message, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of json_get_item_init
char *json_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -2885,7 +2894,7 @@ my_bool jsonget_string_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} // endif's
CalcLen(args, false, reslen, memlen);
- memlen += more;
+//memlen += more;
if (n == 2 && args->args[0]) {
char fn[_MAX_PATH];
@@ -2894,11 +2903,11 @@ my_bool jsonget_string_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
memcpy(fn, args->args[0], args->lengths[0]);
fn[args->lengths[0]] = 0;
fl = GetFileLength(fn);
- memlen += fl * 3;
+ more += fl * 3;
} else if (n != 3)
- memlen += args->lengths[0] * 3;
+ more += args->lengths[0] * 3;
- return JsonInit(initid, args, message, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jsonget_string_init
char *jsonget_string(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -2994,7 +3003,7 @@ void jsonget_string_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jsonget_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- unsigned long reslen, memlen;
+ unsigned long reslen, memlen, more;
if (args->arg_count != 2) {
strcpy(message, "This function must have 2 arguments");
@@ -3008,10 +3017,10 @@ my_bool jsonget_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} else
CalcLen(args, false, reslen, memlen);
- if (IsJson(args, 0) != 3)
- memlen += 1000; // TODO: calculate this
+ // TODO: calculate this
+ more = (IsJson(args, 0) != 3) ? 1000 : 0;
- return JsonInit(initid, args, message, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jsonget_int_init
long long jsonget_int(UDF_INIT *initid, UDF_ARGS *args,
@@ -3100,7 +3109,7 @@ void jsonget_int_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jsonget_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- unsigned long reslen, memlen;
+ unsigned long reslen, memlen, more;
if (args->arg_count < 2) {
strcpy(message, "At least 2 arguments required");
@@ -3123,10 +3132,10 @@ my_bool jsonget_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
CalcLen(args, false, reslen, memlen);
- if (IsJson(args, 0) != 3)
- memlen += 1000; // TODO: calculate this
+ // TODO: calculate this
+ more = (IsJson(args, 0) != 3) ? 1000 : 0;
- return JsonInit(initid, args, message, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jsonget_real_init
double jsonget_real(UDF_INIT *initid, UDF_ARGS *args,
@@ -3234,10 +3243,11 @@ my_bool jsonlocate_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
CalcLen(args, false, reslen, memlen);
- if (IsJson(args, 0) != 3)
- memlen += more; // TODO: calculate this
+ // TODO: calculate this
+ if (IsJson(args, 0) == 3)
+ more = 0;
- return JsonInit(initid, args, message, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jsonlocate_init
char *jsonlocate(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -3358,10 +3368,11 @@ my_bool json_locate_all_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
CalcLen(args, false, reslen, memlen);
- if (IsJson(args, 0) != 3)
- memlen += more; // TODO: calculate this
+ // TODO: calculate this
+ if (IsJson(args, 0) == 3)
+ more = 0;
- return JsonInit(initid, args, message, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of json_locate_all_init
char *json_locate_all(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -3485,12 +3496,12 @@ my_bool jsoncontains_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} // endif's
CalcLen(args, false, reslen, memlen);
- memlen += more;
+//memlen += more;
- if (IsJson(args, 0) != 3)
- memlen += 1000; // TODO: calculate this
+ // TODO: calculate this
+ more += (IsJson(args, 0) != 3 ? 1000 : 0);
- return JsonInit(initid, args, message, false, reslen, memlen);
+ return JsonInit(initid, args, message, false, reslen, memlen, more);
} // end of jsoncontains_init
long long jsoncontains(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -3537,12 +3548,12 @@ my_bool jsoncontains_path_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
} // endif's
CalcLen(args, false, reslen, memlen);
- memlen += more;
+//memlen += more;
- if (IsJson(args, 0) != 3)
- memlen += 1000; // TODO: calculate this
+ // TODO: calculate this
+ more += (IsJson(args, 0) != 3 ? 1000 : 0);
- return JsonInit(initid, args, message, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jsoncontains_path_init
long long jsoncontains_path(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -3736,7 +3747,7 @@ fin:
/*********************************************************************************/
my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- unsigned long reslen, memlen;
+ unsigned long reslen, memlen, more = 0;
int n = IsJson(args, 0);
if (!(args->arg_count % 2)) {
@@ -3755,11 +3766,11 @@ my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
memcpy(fn, args->args[0], args->lengths[0]);
fn[args->lengths[0]] = 0;
fl = GetFileLength(fn);
- memlen += fl * 3;
+ more += fl * 3;
} else if (n != 3)
- memlen += args->lengths[0] * 3;
+ more += args->lengths[0] * 3;
- if (!JsonInit(initid, args, message, true, reslen, memlen)) {
+ if (!JsonInit(initid, args, message, true, reslen, memlen, more)) {
PGLOBAL g = (PGLOBAL)initid->ptr;
// This is a constant function
@@ -3954,7 +3965,7 @@ void json_file_deinit(UDF_INIT* initid)
/*********************************************************************************/
my_bool jfile_make_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- unsigned long reslen, memlen, more = 1024;
+ unsigned long reslen, memlen;
if (args->arg_count < 1 || args->arg_count > 3) {
strcpy(message, "Wrong number of arguments");
@@ -4993,7 +5004,7 @@ fin:
/*********************************************************************************/
my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
- unsigned long reslen, memlen;
+ unsigned long reslen, memlen, more = 0;
int n = IsJson(args, 0);
if (!(args->arg_count % 2)) {
@@ -5012,11 +5023,11 @@ my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
memcpy(fn, args->args[0], args->lengths[0]);
fn[args->lengths[0]] = 0;
fl = GetFileLength(fn);
- memlen += fl * 3;
+ more = fl * 3;
} else if (n != 3)
- memlen += args->lengths[0] * 3;
+ more = args->lengths[0] * 3;
- return JsonInit(initid, args, message, true, reslen, memlen);
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jbin_set_item_init
char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result,
@@ -5104,8 +5115,8 @@ my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
fl = GetFileLength(args->args[0]);
reslen += fl;
more += fl * M;
- memlen += more;
- return JsonInit(initid, args, message, true, reslen, memlen);
+//memlen += more;
+ return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of jbin_file_init
char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result,
diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc
index 497fe5e1aa8..30ac7613dd6 100644
--- a/storage/connect/mycat.cc
+++ b/storage/connect/mycat.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) Olivier Bertrand 2004 - 2016
+/* Copyright (C) Olivier Bertrand 2004 - 2017
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
@@ -16,9 +16,9 @@
/*************** Mycat CC Program Source Code File (.CC) ***************/
/* PROGRAM NAME: MYCAT */
/* ------------- */
-/* Version 1.5 */
+/* Version 1.6 */
/* */
-/* Author: Olivier Bertrand 2012 - 2016 */
+/* Author: Olivier Bertrand 2012 - 2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -58,9 +58,10 @@
#endif // UNIX
#include "global.h"
#include "plgdbsem.h"
-#include "reldef.h"
-#include "tabcol.h"
+//#include "reldef.h"
#include "xtable.h"
+#include "tabext.h"
+#include "tabcol.h"
#include "filamtxt.h"
#include "tabdos.h"
#include "tabfmt.h"
@@ -559,13 +560,13 @@ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, PTABLE tablep, LPCSTR am)
case TAB_XML: tdp= new(g) XMLDEF; break;
#endif // XML_SUPPORT
#if defined(VCT_SUPPORT)
- case TAB_VEC: tdp = new(g)VCTDEF; break;
+ case TAB_VEC: tdp = new(g) VCTDEF; break;
#endif // VCT_SUPPORT
#if defined(ODBC_SUPPORT)
case TAB_ODBC: tdp= new(g) ODBCDEF; break;
#endif // ODBC_SUPPORT
#if defined(JDBC_SUPPORT)
- case TAB_JDBC: tdp= new(g)JDBCDEF; break;
+ case TAB_JDBC: tdp= new(g) JDBCDEF; break;
#endif // JDBC_SUPPORT
#if defined(__WIN__)
case TAB_MAC: tdp= new(g) MACDEF; break;
diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp
index 644ca019e4a..d05254a32a6 100644
--- a/storage/connect/myconn.cpp
+++ b/storage/connect/myconn.cpp
@@ -1,11 +1,11 @@
/************** MyConn C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: MYCONN */
/* ------------- */
-/* Version 1.8 */
+/* Version 1.9 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2007-2016 */
+/* (C) Copyright to the author Olivier BERTRAND 2007-2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -375,10 +375,18 @@ PQRYRES SrcColumns(PGLOBAL g, const char *host, const char *db,
if (!port)
port = mysqld_port;
- if (!strnicmp(srcdef, "select ", 7)) {
- query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 9);
- strcat(strcpy(query, srcdef), " LIMIT 0");
- } else
+ if (!strnicmp(srcdef, "select ", 7) || strstr(srcdef, "%s")) {
+ query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 10);
+
+ if (strstr(srcdef, "%s"))
+ sprintf(query, srcdef, "1=1"); // dummy where clause
+ else
+ strcpy(query, srcdef);
+
+ if (!strnicmp(srcdef, "select ", 7))
+ strcat(query, " LIMIT 0");
+
+ } else
query = (char *)srcdef;
// Open a MySQL connection for this table
diff --git a/storage/connect/mysql-test/connect/r/xml_zip.result b/storage/connect/mysql-test/connect/r/xml_zip.result
new file mode 100644
index 00000000000..f176149c53f
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/xml_zip.result
@@ -0,0 +1,98 @@
+Warnings:
+Warning 1105 No file name. Table will use t1.xml
+#
+# Testing zipped XML tables
+#
+CREATE TABLE t1 (
+ISBN CHAR(13) NOT NULL FIELD_FORMAT='@',
+LANG CHAR(2) NOT NULL FIELD_FORMAT='@',
+SUBJECT CHAR(12) NOT NULL FIELD_FORMAT='@',
+AUTHOR_FIRSTNAME CHAR(15) NOT NULL FIELD_FORMAT='AUTHOR/FIRSTNAME',
+AUTHOR_LASTNAME CHAR(8) NOT NULL FIELD_FORMAT='AUTHOR/LASTNAME',
+TRANSLATOR_PREFIX CHAR(24) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/@PREFIX',
+TRANSLATOR_FIRSTNAME CHAR(6) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/FIRSTNAME',
+TRANSLATOR_LASTNAME CHAR(6) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/LASTNAME',
+TITLE CHAR(30) NOT NULL,
+PUBLISHER_NAME CHAR(15) NOT NULL FIELD_FORMAT='PUBLISHER/NAME',
+PUBLISHER_PLACE CHAR(5) NOT NULL FIELD_FORMAT='PUBLISHER/PLACE',
+DATEPUB CHAR(4) NOT NULL
+) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.zip' ZIPPED=YES
+OPTION_LIST='entry=xsample2.xml,load=xsample2.xml,rownode=BOOK,xmlsup=libxml2,expand=1,mulnode=AUTHOR';
+SELECT * FROM t1;
+ISBN 9782212090819
+LANG fr
+SUBJECT applications
+AUTHOR_FIRSTNAME Jean-Christophe
+AUTHOR_LASTNAME Bernadac
+TRANSLATOR_PREFIX NULL
+TRANSLATOR_FIRSTNAME NULL
+TRANSLATOR_LASTNAME NULL
+TITLE Construire une application XML
+PUBLISHER_NAME Eyrolles
+PUBLISHER_PLACE Paris
+DATEPUB 1999
+ISBN 9782212090819
+LANG fr
+SUBJECT applications
+AUTHOR_FIRSTNAME François
+AUTHOR_LASTNAME Knab
+TRANSLATOR_PREFIX NULL
+TRANSLATOR_FIRSTNAME NULL
+TRANSLATOR_LASTNAME NULL
+TITLE Construire une application XML
+PUBLISHER_NAME Eyrolles
+PUBLISHER_PLACE Paris
+DATEPUB 1999
+ISBN 9782840825685
+LANG fr
+SUBJECT applications
+AUTHOR_FIRSTNAME William J.
+AUTHOR_LASTNAME Pardi
+TRANSLATOR_PREFIX adapté de l'anglais par
+TRANSLATOR_FIRSTNAME James
+TRANSLATOR_LASTNAME Guerin
+TITLE XML en Action
+PUBLISHER_NAME Microsoft Press
+PUBLISHER_PLACE Paris
+DATEPUB 1999
+ISBN 9782212090529
+LANG fr
+SUBJECT général
+AUTHOR_FIRSTNAME Alain
+AUTHOR_LASTNAME Michard
+TRANSLATOR_PREFIX NULL
+TRANSLATOR_FIRSTNAME NULL
+TRANSLATOR_LASTNAME NULL
+TITLE XML, Langage et Applications
+PUBLISHER_NAME Eyrolles
+PUBLISHER_PLACE Paris
+DATEPUB 2003
+CREATE TABLE t2
+ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.zip' ZIPPED=YES
+OPTION_LIST='xmlsup=libxml2';
+SELECT * FROM t2;
+ISBN 9782212090819
+LANG fr
+SUBJECT applications
+AUTHOR Jean-Christophe Bernadac
+TRANSLATOR NULL
+TITLE Construire une application XML
+PUBLISHER Eyrolles Paris
+DATEPUB 1999
+ISBN 9782840825685
+LANG fr
+SUBJECT applications
+AUTHOR William J. Pardi
+TRANSLATOR James Guerin
+TITLE XML en Action
+PUBLISHER Microsoft Press Paris
+DATEPUB 1999
+ISBN 9782212090529
+LANG fr
+SUBJECT général
+AUTHOR Alain Michard
+TRANSLATOR NULL
+TITLE XML, Langage et Applications
+PUBLISHER Eyrolles Paris
+DATEPUB 2003
+DROP TABLE t1,t2;
diff --git a/storage/connect/mysql-test/connect/r/zip.result b/storage/connect/mysql-test/connect/r/zip.result
new file mode 100644
index 00000000000..c03b27bd428
--- /dev/null
+++ b/storage/connect/mysql-test/connect/r/zip.result
@@ -0,0 +1,240 @@
+#
+# Testing zipped DOS tables
+#
+CREATE TABLE t1 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip'
+OPTION_LIST='ENTRY=new1.dos' ZIPPED=1;
+INSERT INTO t1 VALUES(1,'One'),(2,'Two'),(3,'Three'),(4,'Four'),(5,'Five'),(6,'Six'),(7,'Seven'),(8,'Eight'),(9,'Nine'),(10,'Ten');
+SELECT * FROM t1;
+digit letter
+1 One
+2 Two
+3 Three
+4 Four
+5 Five
+6 Six
+7 Seven
+8 Eight
+9 Nine
+10 Ten
+CREATE TABLE t2 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip'
+OPTION_LIST='ENTRY=new2.dos,APPEND=1' ZIPPED=1;
+INSERT INTO t2 VALUES(11,'Eleven'),(12,'Twelve'),(13,'Thirteen'),(14,'Fourteen'),(15,'Fiften'),(16,'Sixteen'),(17,'Seventeen'),(18,'Eighteen'),(19,'Nineteen'),(20,'Twenty');
+SELECT * FROM t2;
+digit letter
+11 Eleven
+12 Twelve
+13 Thirteen
+14 Fourteen
+15 Fiften
+16 Sixteen
+17 Seventeen
+18 Eighteen
+19 Nineteen
+20 Twenty
+CREATE TABLE t3 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip'
+OPTION_LIST='MULENTRIES=1' ZIPPED=1;
+SELECT * FROM t3;
+digit letter
+1 One
+2 Two
+3 Three
+4 Four
+5 Five
+6 Six
+7 Seven
+8 Eight
+9 Nine
+10 Ten
+11 Eleven
+12 Twelve
+13 Thirteen
+14 Fourteen
+15 Fiften
+16 Sixteen
+17 Seventeen
+18 Eighteen
+19 Nineteen
+20 Twenty
+CREATE TABLE t4 (
+fn VARCHAR(256)NOT NULL,
+cmpsize BIGINT NOT NULL FLAG=1,
+uncsize BIGINT NOT NULL FLAG=2,
+method INT NOT NULL FLAG=3)
+ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='newdos.zip';
+SELECT * FROM t4;
+fn cmpsize uncsize method
+new1.dos 67 79 8
+new2.dos 77 112 8
+DROP TABLE t1,t2,t3,t4;
+#
+# Testing zipped CSV tables
+#
+CREATE TABLE t1 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip'
+OPTION_LIST='ENTRY=new1.csv' HEADER=1 ZIPPED=1;
+INSERT INTO t1 VALUES(1,'One'),(2,'Two'),(3,'Three'),(4,'Four'),(5,'Five'),(6,'Six'),(7,'Seven'),(8,'Eight'),(9,'Nine'),(10,'Ten');
+SELECT * FROM t1;
+digit letter
+1 One
+2 Two
+3 Three
+4 Four
+5 Five
+6 Six
+7 Seven
+8 Eight
+9 Nine
+10 Ten
+CREATE TABLE td1
+ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip'
+OPTION_LIST='ENTRY=new1.csv' HEADER=1 ZIPPED=1;
+SELECT * FROM td1;
+digit letter
+1 One
+2 Two
+3 Three
+4 Four
+5 Five
+6 Six
+7 Seven
+8 Eight
+9 Nine
+10 Ten
+DROP TABLE td1;
+CREATE TABLE t2 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip'
+OPTION_LIST='ENTRY=new2.csv,APPEND=1' HEADER=1 ZIPPED=1;
+INSERT INTO t2 VALUES(11,'Eleven'),(12,'Twelve'),(13,'Thirteen'),(14,'Fourteen'),(15,'Fiften'),(16,'Sixteen'),(17,'Seventeen'),(18,'Eighteen'),(19,'Nineteen'),(20,'Twenty');
+SELECT * FROM t2;
+digit letter
+11 Eleven
+12 Twelve
+13 Thirteen
+14 Fourteen
+15 Fiften
+16 Sixteen
+17 Seventeen
+18 Eighteen
+19 Nineteen
+20 Twenty
+CREATE TABLE t3
+ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip'
+OPTION_LIST='MULENTRIES=1' HEADER=1 ZIPPED=1;
+SELECT * FROM t3;
+digit letter
+1 One
+2 Two
+3 Three
+4 Four
+5 Five
+6 Six
+7 Seven
+8 Eight
+9 Nine
+10 Ten
+11 Eleven
+12 Twelve
+13 Thirteen
+14 Fourteen
+15 Fiften
+16 Sixteen
+17 Seventeen
+18 Eighteen
+19 Nineteen
+20 Twenty
+CREATE TABLE t4 (
+fn VARCHAR(256)NOT NULL,
+cmpsize BIGINT NOT NULL FLAG=1,
+uncsize BIGINT NOT NULL FLAG=2,
+method INT NOT NULL FLAG=3)
+ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='newcsv.zip';
+SELECT * FROM t4;
+fn cmpsize uncsize method
+new1.csv 79 83 8
+new2.csv 94 125 8
+DROP TABLE t1,t2,t3,t4;
+#
+# Testing zipped JSON tables
+#
+CREATE TABLE t1 (
+_id INT(2) NOT NULL,
+name_first CHAR(9) NOT NULL FIELD_FORMAT='name:first',
+name_aka CHAR(4) DEFAULT NULL FIELD_FORMAT='name:aka',
+name_last CHAR(10) NOT NULL FIELD_FORMAT='name:last',
+title CHAR(12) DEFAULT NULL,
+birth CHAR(20) DEFAULT NULL,
+death CHAR(20) DEFAULT NULL,
+contribs CHAR(7) NOT NULL FIELD_FORMAT='contribs:',
+awards_award CHAR(42) DEFAULT NULL FIELD_FORMAT='awards::award',
+awards_year CHAR(4) DEFAULT NULL FIELD_FORMAT='awards::year',
+awards_by CHAR(38) DEFAULT NULL FIELD_FORMAT='awards::by'
+) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bios.zip' OPTION_LIST='ENTRY=bios.json,LOAD=bios.json' ZIPPED=YES;
+SELECT * FROM t1;
+_id name_first name_aka name_last title birth death contribs awards_award awards_year awards_by
+1 John NULL Backus NULL 1924-12-03T05:00:00Z 2007-03-17T04:00:00Z Fortran W.W. McDowell Award 1967 IEEE Computer Society
+2 John NULL McCarthy NULL 1927-09-04T04:00:00Z 2011-12-24T05:00:00Z Lisp Turing Award 1971 ACM
+3 Grace NULL Hopper Rear Admiral 1906-12-09T05:00:00Z 1992-01-01T05:00:00Z UNIVAC Computer Sciences Man of the Year 1969 Data Processing Management Association
+4 Kristen NULL Nygaard NULL 1926-08-27T04:00:00Z 2002-08-10T04:00:00Z OOP Rosing Prize 1999 Norwegian Data Association
+5 Ole-Johan NULL Dahl NULL 1931-10-12T04:00:00Z 2002-06-29T04:00:00Z OOP Rosing Prize 1999 Norwegian Data Association
+6 Guido NULL van Rossum NULL 1956-01-31T05:00:00Z NULL Python Award for the Advancement of Free Software 2001 Free Software Foundation
+7 Dennis NULL Ritchie NULL 1941-09-09T04:00:00Z 2011-10-12T04:00:00Z UNIX Turing Award 1983 ACM
+8 Yukihiro Matz Matsumoto NULL 1965-04-14T04:00:00Z NULL Ruby Award for the Advancement of Free Software 2011 Free Software Foundation
+9 James NULL Gosling NULL 1955-05-19T04:00:00Z NULL Java The Economist Innovation Award 2002 The Economist
+10 Martin NULL Odersky NULL NULL NULL Scala NULL NULL NULL
+CREATE TABLE t2
+ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bios.zip' ZIPPED=1
+OPTION_LIST='LEVEL=5';
+SELECT * FROM t2;
+_id name_first name_aka name_last title birth death contribs awards_award awards_year awards_by
+1 John NULL Backus NULL 1924-12-03T05:00:00Z 2007-03-17T04:00:00Z Fortran W.W. McDowell Award 1967 IEEE Computer Society
+2 John NULL McCarthy NULL 1927-09-04T04:00:00Z 2011-12-24T05:00:00Z Lisp Turing Award 1971 ACM
+3 Grace NULL Hopper Rear Admiral 1906-12-09T05:00:00Z 1992-01-01T05:00:00Z UNIVAC Computer Sciences Man of the Year 1969 Data Processing Management Association
+4 Kristen NULL Nygaard NULL 1926-08-27T04:00:00Z 2002-08-10T04:00:00Z OOP Rosing Prize 1999 Norwegian Data Association
+5 Ole-Johan NULL Dahl NULL 1931-10-12T04:00:00Z 2002-06-29T04:00:00Z OOP Rosing Prize 1999 Norwegian Data Association
+6 Guido NULL van Rossum NULL 1956-01-31T05:00:00Z NULL Python Award for the Advancement of Free Software 2001 Free Software Foundation
+7 Dennis NULL Ritchie NULL 1941-09-09T04:00:00Z 2011-10-12T04:00:00Z UNIX Turing Award 1983 ACM
+8 Yukihiro Matz Matsumoto NULL 1965-04-14T04:00:00Z NULL Ruby Award for the Advancement of Free Software 2011 Free Software Foundation
+9 James NULL Gosling NULL 1955-05-19T04:00:00Z NULL Java The Economist Innovation Award 2002 The Economist
+10 Martin NULL Odersky NULL NULL NULL Scala NULL NULL NULL
+CREATE TABLE t3 (
+_id INT(2) NOT NULL,
+firstname CHAR(9) NOT NULL FIELD_FORMAT='name:first',
+aka CHAR(4) DEFAULT NULL FIELD_FORMAT='name:aka',
+lastname CHAR(10) NOT NULL FIELD_FORMAT='name:last',
+title CHAR(12) DEFAULT NULL,
+birth date DEFAULT NULL date_format="YYYY-DD-MM'T'hh:mm:ss'Z'",
+death date DEFAULT NULL date_format="YYYY-DD-MM'T'hh:mm:ss'Z'",
+contribs CHAR(64) NOT NULL FIELD_FORMAT='contribs:[", "]',
+award CHAR(42) DEFAULT NULL FIELD_FORMAT='awards:[x]:award',
+year CHAR(4) DEFAULT NULL FIELD_FORMAT='awards:[x]:year',
+`by` CHAR(38) DEFAULT NULL FIELD_FORMAT='awards:[x]:by'
+) ENGINE=CONNECT TABLE_TYPE='json' FILE_NAME='bios.zip' ZIPPED=YES;
+SELECT * FROM t3 WHERE _id = 1;
+_id firstname aka lastname title birth death contribs award year by
+1 John NULL Backus NULL 1924-03-12 2008-05-03 Fortran, ALGOL, Backus-Naur Form, FP W.W. McDowell Award 1967 IEEE Computer Society
+1 John NULL Backus NULL 1924-03-12 2008-05-03 Fortran, ALGOL, Backus-Naur Form, FP National Medal of Science 1975 National Science Foundation
+1 John NULL Backus NULL 1924-03-12 2008-05-03 Fortran, ALGOL, Backus-Naur Form, FP Turing Award 1977 ACM
+1 John NULL Backus NULL 1924-03-12 2008-05-03 Fortran, ALGOL, Backus-Naur Form, FP Draper Prize 1993 National Academy of Engineering
+CREATE TABLE t4 (
+fn VARCHAR(256)NOT NULL,
+cmpsize BIGINT NOT NULL FLAG=1,
+uncsize BIGINT NOT NULL FLAG=2,
+method INT NOT NULL FLAG=3)
+ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='bios.zip';
+SELECT * FROM t4;
+fn cmpsize uncsize method
+bios.json 1096 6848 8
+DROP TABLE t1,t2,t3,t4;
diff --git a/storage/connect/mysql-test/connect/std_data/bios.json b/storage/connect/mysql-test/connect/std_data/bios.json
new file mode 100644
index 00000000000..85e4ecb933f
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/bios.json
@@ -0,0 +1,273 @@
+[
+ {
+ "_id" : 1,
+ "name" : {
+ "first" : "John",
+ "last" : "Backus"
+ },
+ "birth" : "1924-12-03T05:00:00Z",
+ "death" : "2007-03-17T04:00:00Z",
+ "contribs" : [
+ "Fortran",
+ "ALGOL",
+ "Backus-Naur Form",
+ "FP"
+ ],
+ "awards" : [
+ {
+ "award" : "W.W. McDowell Award",
+ "year" : 1967,
+ "by" : "IEEE Computer Society"
+ },
+ {
+ "award" : "National Medal of Science",
+ "year" : 1975,
+ "by" : "National Science Foundation"
+ },
+ {
+ "award" : "Turing Award",
+ "year" : 1977,
+ "by" : "ACM"
+ },
+ {
+ "award" : "Draper Prize",
+ "year" : 1993,
+ "by" : "National Academy of Engineering"
+ }
+ ]
+ },
+ {
+ "_id" : 2,
+ "name" : {
+ "first" : "John",
+ "last" : "McCarthy"
+ },
+ "birth" : "1927-09-04T04:00:00Z",
+ "death" : "2011-12-24T05:00:00Z",
+ "contribs" : [
+ "Lisp",
+ "Artificial Intelligence",
+ "ALGOL"
+ ],
+ "awards" : [
+ {
+ "award" : "Turing Award",
+ "year" : 1971,
+ "by" : "ACM"
+ },
+ {
+ "award" : "Kyoto Prize",
+ "year" : 1988,
+ "by" : "Inamori Foundation"
+ },
+ {
+ "award" : "National Medal of Science",
+ "year" : 1990,
+ "by" : "National Science Foundation"
+ }
+ ]
+ },
+ {
+ "_id" : 3,
+ "name" : {
+ "first" : "Grace",
+ "last" : "Hopper"
+ },
+ "title" : "Rear Admiral",
+ "birth" : "1906-12-09T05:00:00Z",
+ "death" : "1992-01-01T05:00:00Z",
+ "contribs" : [
+ "UNIVAC",
+ "compiler",
+ "FLOW-MATIC",
+ "COBOL"
+ ],
+ "awards" : [
+ {
+ "award" : "Computer Sciences Man of the Year",
+ "year" : 1969,
+ "by" : "Data Processing Management Association"
+ },
+ {
+ "award" : "Distinguished Fellow",
+ "year" : 1973,
+ "by" : " British Computer Society"
+ },
+ {
+ "award" : "W. W. McDowell Award",
+ "year" : 1976,
+ "by" : "IEEE Computer Society"
+ },
+ {
+ "award" : "National Medal of Technology",
+ "year" : 1991,
+ "by" : "United States"
+ }
+ ]
+ },
+ {
+ "_id" : 4,
+ "name" : {
+ "first" : "Kristen",
+ "last" : "Nygaard"
+ },
+ "birth" : "1926-08-27T04:00:00Z",
+ "death" : "2002-08-10T04:00:00Z",
+ "contribs" : [
+ "OOP",
+ "Simula"
+ ],
+ "awards" : [
+ {
+ "award" : "Rosing Prize",
+ "year" : 1999,
+ "by" : "Norwegian Data Association"
+ },
+ {
+ "award" : "Turing Award",
+ "year" : 2001,
+ "by" : "ACM"
+ },
+ {
+ "award" : "IEEE John von Neumann Medal",
+ "year" : 2001,
+ "by" : "IEEE"
+ }
+ ]
+ },
+ {
+ "_id" : 5,
+ "name" : {
+ "first" : "Ole-Johan",
+ "last" : "Dahl"
+ },
+ "birth" : "1931-10-12T04:00:00Z",
+ "death" : "2002-06-29T04:00:00Z",
+ "contribs" : [
+ "OOP",
+ "Simula"
+ ],
+ "awards" : [
+ {
+ "award" : "Rosing Prize",
+ "year" : 1999,
+ "by" : "Norwegian Data Association"
+ },
+ {
+ "award" : "Turing Award",
+ "year" : 2001,
+ "by" : "ACM"
+ },
+ {
+ "award" : "IEEE John von Neumann Medal",
+ "year" : 2001,
+ "by" : "IEEE"
+ }
+ ]
+ },
+ {
+ "_id" : 6,
+ "name" : {
+ "first" : "Guido",
+ "last" : "van Rossum"
+ },
+ "birth" : "1956-01-31T05:00:00Z",
+ "contribs" : [
+ "Python"
+ ],
+ "awards" : [
+ {
+ "award" : "Award for the Advancement of Free Software",
+ "year" : 2001,
+ "by" : "Free Software Foundation"
+ },
+ {
+ "award" : "NLUUG Award",
+ "year" : 2003,
+ "by" : "NLUUG"
+ }
+ ]
+ },
+ {
+ "_id" : 7,
+ "name" : {
+ "first" : "Dennis",
+ "last" : "Ritchie"
+ },
+ "birth" : "1941-09-09T04:00:00Z",
+ "death" : "2011-10-12T04:00:00Z",
+ "contribs" : [
+ "UNIX",
+ "C"
+ ],
+ "awards" : [
+ {
+ "award" : "Turing Award",
+ "year" : 1983,
+ "by" : "ACM"
+ },
+ {
+ "award" : "National Medal of Technology",
+ "year" : 1998,
+ "by" : "United States"
+ },
+ {
+ "award" : "Japan Prize",
+ "year" : 2011,
+ "by" : "The Japan Prize Foundation"
+ }
+ ]
+ },
+ {
+ "_id" : 8,
+ "name" : {
+ "first" : "Yukihiro",
+ "aka" : "Matz",
+ "last" : "Matsumoto"
+ },
+ "birth" : "1965-04-14T04:00:00Z",
+ "contribs" : [
+ "Ruby"
+ ],
+ "awards" : [
+ {
+ "award" : "Award for the Advancement of Free Software",
+ "year" : "2011",
+ "by" : "Free Software Foundation"
+ }
+ ]
+ },
+ {
+ "_id" : 9,
+ "name" : {
+ "first" : "James",
+ "last" : "Gosling"
+ },
+ "birth" : "1955-05-19T04:00:00Z",
+ "contribs" : [
+ "Java"
+ ],
+ "awards" : [
+ {
+ "award" : "The Economist Innovation Award",
+ "year" : 2002,
+ "by" : "The Economist"
+ },
+ {
+ "award" : "Officer of the Order of Canada",
+ "year" : 2007,
+ "by" : "Canada"
+ }
+ ]
+ },
+ {
+ "_id" : 10,
+ "name" : {
+ "first" : "Martin",
+ "last" : "Odersky"
+ },
+ "contribs" : [
+ "Scala"
+ ]
+ }
+]
diff --git a/storage/connect/mysql-test/connect/std_data/xsample2.xml b/storage/connect/mysql-test/connect/std_data/xsample2.xml
new file mode 100644
index 00000000000..35295844370
--- /dev/null
+++ b/storage/connect/mysql-test/connect/std_data/xsample2.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<BIBLIO SUBJECT="XML">
+ <BOOK ISBN="9782212090819" LANG="fr" SUBJECT="applications">
+ <AUTHOR>
+ <FIRSTNAME>Jean-Christophe</FIRSTNAME>
+ <LASTNAME>Bernadac</LASTNAME>
+ </AUTHOR>
+ <AUTHOR>
+ <FIRSTNAME>François</FIRSTNAME>
+ <LASTNAME>Knab</LASTNAME>
+ </AUTHOR>
+ <TITLE>Construire une application XML</TITLE>
+ <PUBLISHER>
+ <NAME>Eyrolles</NAME>
+ <PLACE>Paris</PLACE>
+ </PUBLISHER>
+ <DATEPUB>1999</DATEPUB>
+ </BOOK>
+ <BOOK ISBN="9782840825685" LANG="fr" SUBJECT="applications">
+ <AUTHOR>
+ <FIRSTNAME>William J.</FIRSTNAME>
+ <LASTNAME>Pardi</LASTNAME>
+ </AUTHOR>
+ <TRANSLATOR PREFIX="adapté de l'anglais par">
+ <FIRSTNAME>James</FIRSTNAME>
+ <LASTNAME>Guerin</LASTNAME>
+ </TRANSLATOR>
+ <TITLE>XML en Action</TITLE>
+ <PUBLISHER>
+ <NAME>Microsoft Press</NAME>
+ <PLACE>Paris</PLACE>
+ </PUBLISHER>
+ <DATEPUB>1999</DATEPUB>
+ </BOOK>
+ <BOOK ISBN="9782212090529" LANG="fr" SUBJECT="général">
+ <AUTHOR>
+ <FIRSTNAME>Alain</FIRSTNAME>
+ <LASTNAME>Michard</LASTNAME>
+ </AUTHOR>
+ <TITLE>XML, Langage et Applications</TITLE>
+ <PUBLISHER>
+ <NAME>Eyrolles</NAME>
+ <PLACE>Paris</PLACE>
+ </PUBLISHER>
+ <DATEPUB>2003</DATEPUB>
+ </BOOK>
+</BIBLIO>
diff --git a/storage/connect/mysql-test/connect/t/have_zip.inc b/storage/connect/mysql-test/connect/t/have_zip.inc
new file mode 100644
index 00000000000..d1283fc1d38
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/have_zip.inc
@@ -0,0 +1,19 @@
+--disable_query_log
+--error 0,ER_UNKNOWN_ERROR
+CREATE TABLE t1 (a CHAR(10)) ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='test.zip';
+if ($mysql_errno)
+{
+ Skip No ZIP support;
+}
+#if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES
+# WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'
+# AND ENGINE='CONNECT'
+# AND CREATE_OPTIONS LIKE '%`table_type`=ZIP%'
+# AND CREATE OPTIONS LIKE "%`file_name`='test.zip'%"`)
+#{
+# DROP TABLE IF EXISTS t1;
+# Skip Need ZIP support;
+#}
+DROP TABLE t1;
+--enable_query_log
+
diff --git a/storage/connect/mysql-test/connect/t/xml_zip.test b/storage/connect/mysql-test/connect/t/xml_zip.test
new file mode 100644
index 00000000000..d8c7894f861
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/xml_zip.test
@@ -0,0 +1,41 @@
+--source have_zip.inc
+--source have_libxml2.inc
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+--vertical_results
+
+--copy_file $MTR_SUITE_DIR/std_data/xsample2.xml $MYSQLD_DATADIR/test/xsample2.xml
+
+--echo #
+--echo # Testing zipped XML tables
+--echo #
+CREATE TABLE t1 (
+ISBN CHAR(13) NOT NULL FIELD_FORMAT='@',
+LANG CHAR(2) NOT NULL FIELD_FORMAT='@',
+SUBJECT CHAR(12) NOT NULL FIELD_FORMAT='@',
+AUTHOR_FIRSTNAME CHAR(15) NOT NULL FIELD_FORMAT='AUTHOR/FIRSTNAME',
+AUTHOR_LASTNAME CHAR(8) NOT NULL FIELD_FORMAT='AUTHOR/LASTNAME',
+TRANSLATOR_PREFIX CHAR(24) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/@PREFIX',
+TRANSLATOR_FIRSTNAME CHAR(6) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/FIRSTNAME',
+TRANSLATOR_LASTNAME CHAR(6) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/LASTNAME',
+TITLE CHAR(30) NOT NULL,
+PUBLISHER_NAME CHAR(15) NOT NULL FIELD_FORMAT='PUBLISHER/NAME',
+PUBLISHER_PLACE CHAR(5) NOT NULL FIELD_FORMAT='PUBLISHER/PLACE',
+DATEPUB CHAR(4) NOT NULL
+) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.zip' ZIPPED=YES
+OPTION_LIST='entry=xsample2.xml,load=xsample2.xml,rownode=BOOK,xmlsup=libxml2,expand=1,mulnode=AUTHOR';
+SELECT * FROM t1;
+
+#testing discovery
+CREATE TABLE t2
+ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.zip' ZIPPED=YES
+OPTION_LIST='xmlsup=libxml2';
+SELECT * FROM t2;
+DROP TABLE t1,t2;
+
+#
+# Clean up
+#
+--remove_file $MYSQLD_DATADIR/test/xsample2.xml
+--remove_file $MYSQLD_DATADIR/test/xsample2.zip
diff --git a/storage/connect/mysql-test/connect/t/zip.test b/storage/connect/mysql-test/connect/t/zip.test
new file mode 100644
index 00000000000..a4892e9ed4e
--- /dev/null
+++ b/storage/connect/mysql-test/connect/t/zip.test
@@ -0,0 +1,136 @@
+--source have_zip.inc
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+--copy_file $MTR_SUITE_DIR/std_data/bios.json $MYSQLD_DATADIR/test/bios.json
+
+--echo #
+--echo # Testing zipped DOS tables
+--echo #
+CREATE TABLE t1 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip'
+OPTION_LIST='ENTRY=new1.dos' ZIPPED=1;
+INSERT INTO t1 VALUES(1,'One'),(2,'Two'),(3,'Three'),(4,'Four'),(5,'Five'),(6,'Six'),(7,'Seven'),(8,'Eight'),(9,'Nine'),(10,'Ten');
+SELECT * FROM t1;
+
+CREATE TABLE t2 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip'
+OPTION_LIST='ENTRY=new2.dos,APPEND=1' ZIPPED=1;
+INSERT INTO t2 VALUES(11,'Eleven'),(12,'Twelve'),(13,'Thirteen'),(14,'Fourteen'),(15,'Fiften'),(16,'Sixteen'),(17,'Seventeen'),(18,'Eighteen'),(19,'Nineteen'),(20,'Twenty');
+SELECT * FROM t2;
+
+CREATE TABLE t3 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip'
+OPTION_LIST='MULENTRIES=1' ZIPPED=1;
+SELECT * FROM t3;
+
+CREATE TABLE t4 (
+fn VARCHAR(256)NOT NULL,
+cmpsize BIGINT NOT NULL FLAG=1,
+uncsize BIGINT NOT NULL FLAG=2,
+method INT NOT NULL FLAG=3)
+ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='newdos.zip';
+SELECT * FROM t4;
+DROP TABLE t1,t2,t3,t4;
+
+--echo #
+--echo # Testing zipped CSV tables
+--echo #
+CREATE TABLE t1 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip'
+OPTION_LIST='ENTRY=new1.csv' HEADER=1 ZIPPED=1;
+INSERT INTO t1 VALUES(1,'One'),(2,'Two'),(3,'Three'),(4,'Four'),(5,'Five'),(6,'Six'),(7,'Seven'),(8,'Eight'),(9,'Nine'),(10,'Ten');
+SELECT * FROM t1;
+
+# Test discovery
+CREATE TABLE td1
+ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip'
+OPTION_LIST='ENTRY=new1.csv' HEADER=1 ZIPPED=1;
+SELECT * FROM td1;
+DROP TABLE td1;
+
+CREATE TABLE t2 (
+digit INT(3) NOT NULL,
+letter CHAR(16) NOT NULL)
+ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip'
+OPTION_LIST='ENTRY=new2.csv,APPEND=1' HEADER=1 ZIPPED=1;
+INSERT INTO t2 VALUES(11,'Eleven'),(12,'Twelve'),(13,'Thirteen'),(14,'Fourteen'),(15,'Fiften'),(16,'Sixteen'),(17,'Seventeen'),(18,'Eighteen'),(19,'Nineteen'),(20,'Twenty');
+SELECT * FROM t2;
+
+CREATE TABLE t3
+ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip'
+OPTION_LIST='MULENTRIES=1' HEADER=1 ZIPPED=1;
+SELECT * FROM t3;
+
+CREATE TABLE t4 (
+fn VARCHAR(256)NOT NULL,
+cmpsize BIGINT NOT NULL FLAG=1,
+uncsize BIGINT NOT NULL FLAG=2,
+method INT NOT NULL FLAG=3)
+ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='newcsv.zip';
+SELECT * FROM t4;
+DROP TABLE t1,t2,t3,t4;
+
+--echo #
+--echo # Testing zipped JSON tables
+--echo #
+CREATE TABLE t1 (
+_id INT(2) NOT NULL,
+name_first CHAR(9) NOT NULL FIELD_FORMAT='name:first',
+name_aka CHAR(4) DEFAULT NULL FIELD_FORMAT='name:aka',
+name_last CHAR(10) NOT NULL FIELD_FORMAT='name:last',
+title CHAR(12) DEFAULT NULL,
+birth CHAR(20) DEFAULT NULL,
+death CHAR(20) DEFAULT NULL,
+contribs CHAR(7) NOT NULL FIELD_FORMAT='contribs:',
+awards_award CHAR(42) DEFAULT NULL FIELD_FORMAT='awards::award',
+awards_year CHAR(4) DEFAULT NULL FIELD_FORMAT='awards::year',
+awards_by CHAR(38) DEFAULT NULL FIELD_FORMAT='awards::by'
+) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bios.zip' OPTION_LIST='ENTRY=bios.json,LOAD=bios.json' ZIPPED=YES;
+SELECT * FROM t1;
+
+# Test discovery
+CREATE TABLE t2
+ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bios.zip' ZIPPED=1
+OPTION_LIST='LEVEL=5';
+SELECT * FROM t2;
+
+CREATE TABLE t3 (
+_id INT(2) NOT NULL,
+firstname CHAR(9) NOT NULL FIELD_FORMAT='name:first',
+aka CHAR(4) DEFAULT NULL FIELD_FORMAT='name:aka',
+lastname CHAR(10) NOT NULL FIELD_FORMAT='name:last',
+title CHAR(12) DEFAULT NULL,
+birth date DEFAULT NULL date_format="YYYY-DD-MM'T'hh:mm:ss'Z'",
+death date DEFAULT NULL date_format="YYYY-DD-MM'T'hh:mm:ss'Z'",
+contribs CHAR(64) NOT NULL FIELD_FORMAT='contribs:[", "]',
+award CHAR(42) DEFAULT NULL FIELD_FORMAT='awards:[x]:award',
+year CHAR(4) DEFAULT NULL FIELD_FORMAT='awards:[x]:year',
+`by` CHAR(38) DEFAULT NULL FIELD_FORMAT='awards:[x]:by'
+) ENGINE=CONNECT TABLE_TYPE='json' FILE_NAME='bios.zip' ZIPPED=YES;
+SELECT * FROM t3 WHERE _id = 1;
+
+CREATE TABLE t4 (
+fn VARCHAR(256)NOT NULL,
+cmpsize BIGINT NOT NULL FLAG=1,
+uncsize BIGINT NOT NULL FLAG=2,
+method INT NOT NULL FLAG=3)
+ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='bios.zip';
+SELECT * FROM t4;
+DROP TABLE t1,t2,t3,t4;
+
+#
+# Clean up
+#
+--remove_file $MYSQLD_DATADIR/test/newdos.zip
+--remove_file $MYSQLD_DATADIR/test/newcsv.zip
+--remove_file $MYSQLD_DATADIR/test/bios.zip
+--remove_file $MYSQLD_DATADIR/test/bios.json
+
diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp
index 7320f4cc1d9..433e392eace 100644
--- a/storage/connect/odbconn.cpp
+++ b/storage/connect/odbconn.cpp
@@ -35,8 +35,8 @@
#include "global.h"
#include "plgdbsem.h"
#include "xobject.h"
-//#include "kindex.h"
#include "xtable.h"
+#include "tabext.h"
#include "odbccat.h"
#include "tabodbc.h"
#include "plgcnx.h" // For DB types
@@ -413,12 +413,20 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *db, char *table,
/**************************************************************************/
PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src, POPARM sop)
{
+ char *sqry;
ODBConn *ocp = new(g) ODBConn(g, NULL);
if (ocp->Open(dsn, sop, 10) < 1) // openReadOnly + noOdbcDialog
return NULL;
- return ocp->GetMetaData(g, dsn, src);
+ if (strstr(src, "%s")) {
+ // Place holder for an eventual where clause
+ sqry = (char*)PlugSubAlloc(g, NULL, strlen(src) + 3);
+ sprintf(sqry, src, "1=1", "1=1"); // dummy where clause
+ } else
+ sqry = src;
+
+ return ocp->GetMetaData(g, dsn, sqry);
} // end of ODBCSrcCols
#if 0
@@ -1417,7 +1425,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols)
b = true;
if (trace)
- htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql);
+ htrc("ExecDirect hstmt=%p %.256s\n", hstmt, sql);
if (m_Tdb->Srcdef) {
// Be sure this is a query returning a result set
diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h
index cb408494319..800b1098d50 100644
--- a/storage/connect/plgdbsem.h
+++ b/storage/connect/plgdbsem.h
@@ -1,7 +1,7 @@
/************** PlgDBSem H Declares Source Code File (.H) **************/
/* Name: PLGDBSEM.H Version 3.7 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2016 */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2017 */
/* */
/* This file contains the CONNECT storage engine definitions. */
/***********************************************************************/
@@ -57,7 +57,7 @@ enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */
TAB_FIX = 2, /* Fixed column offset, fixed LRECL */
TAB_BIN = 3, /* Like FIX but can have binary fields */
TAB_CSV = 4, /* DOS files with CSV records */
- TAB_FMT = 5, /* DOS files with formatted recordss */
+ TAB_FMT = 5, /* DOS files with formatted records */
TAB_DBF = 6, /* DBF Dbase or Foxpro files */
TAB_XML = 7, /* XML or HTML files */
TAB_INI = 8, /* INI or CFG files */
@@ -212,11 +212,24 @@ enum OPVAL {OP_EQ = 1, /* Filtering operator = */
OP_SUB = 17, /* Expression Substract operator */
OP_MULT = 18, /* Expression Multiply operator */
OP_DIV = 19, /* Expression Divide operator */
- OP_NOP = 21, /* Scalar function is nopped */
OP_NUM = 22, /* Scalar function Op Num */
- OP_ABS = 23, /* Scalar function Op Abs */
OP_MAX = 24, /* Scalar function Op Max */
OP_MIN = 25, /* Scalar function Op Min */
+ OP_EXP = 36, /* Scalar function Op Exp */
+ OP_FDISK = 94, /* Operator Disk of fileid */
+ OP_FPATH = 95, /* Operator Path of fileid */
+ OP_FNAME = 96, /* Operator Name of fileid */
+ OP_FTYPE = 97, /* Operator Type of fileid */
+ OP_LAST = 82, /* Index operator Find Last */
+ OP_FIRST = 106, /* Index operator Find First */
+ OP_NEXT = 107, /* Index operator Find Next */
+ OP_SAME = 108, /* Index operator Find Next Same */
+ OP_FSTDIF = 109, /* Index operator Find First dif */
+ OP_NXTDIF = 110, /* Index operator Find Next dif */
+ OP_PREV = 116}; /* Index operator Find Previous */
+#if 0
+ OP_NOP = 21, /* Scalar function is nopped */
+ OP_ABS = 23, /* Scalar function Op Abs */
OP_CEIL = 26, /* Scalar function Op Ceil */
OP_FLOOR = 27, /* Scalar function Op Floor */
OP_MOD = 28, /* Scalar function Op Mod */
@@ -312,6 +325,7 @@ enum OPVAL {OP_EQ = 1, /* Filtering operator = */
OP_REMOVE = 201, /* Scalar function Op Remove */
OP_RENAME = 202, /* Scalar function Op Rename */
OP_FCOMP = 203}; /* Scalar function Op Compare */
+#endif // 0
enum TUSE {USE_NO = 0, /* Table is not yet linearized */
USE_LIN = 1, /* Table is linearized */
@@ -356,6 +370,7 @@ typedef class XOBJECT *PXOB;
typedef class COLBLK *PCOL;
typedef class TDB *PTDB;
typedef class TDBASE *PTDBASE;
+typedef class TDBEXT *PTDBEXT;
typedef class TDBDOS *PTDBDOS;
typedef class TDBFIX *PTDBFIX;
typedef class TDBFMT *PTDBFMT;
@@ -374,6 +389,7 @@ typedef class KXYCOL *PXCOL;
typedef class CATALOG *PCATLG;
typedef class RELDEF *PRELDEF;
typedef class TABDEF *PTABDEF;
+typedef class EXTDEF *PEXTBDEF;
typedef class DOSDEF *PDOSDEF;
typedef class CSVDEF *PCSVDEF;
typedef class VCTDEF *PVCTDEF;
@@ -619,4 +635,4 @@ int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode)
DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir);
char *MakeEscape(PGLOBAL g, char* str, char q);
-DllExport bool PushWarning(PGLOBAL, PTDBASE, int level = 1);
+DllExport bool PushWarning(PGLOBAL, PTDB, int level = 1);
diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp
index 83975c6d8fa..1910cdcdec8 100644
--- a/storage/connect/plgdbutl.cpp
+++ b/storage/connect/plgdbutl.cpp
@@ -1,11 +1,11 @@
/********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: PLGDBUTL */
/* ------------- */
-/* Version 3.9 */
+/* Version 4.0 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2016 */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -939,7 +939,11 @@ int PlugCloseFile(PGLOBAL g __attribute__((unused)), PFBLOCK fp, bool all)
#endif // LIBXML2_SUPPORT
#ifdef ZIP_SUPPORT
case TYPE_FB_ZIP:
- ((ZIPUTIL*)fp->File)->close();
+ if (fp->Mode == MODE_INSERT)
+ ((ZIPUTIL*)fp->File)->close();
+ else
+ ((UNZIPUTL*)fp->File)->close();
+
fp->Memory = NULL;
fp->Mode = MODE_ANY;
fp->Count = 0;
@@ -1119,7 +1123,7 @@ char *GetAmName(PGLOBAL g, AMT am, void *memp)
return amn;
} // end of GetAmName
-#if defined(__WIN__) && !defined(NOCATCH)
+#if defined(SE_CATCH)
/***********************************************************************/
/* GetExceptionDesc: return the description of an exception code. */
/***********************************************************************/
@@ -1207,7 +1211,7 @@ char *GetExceptionDesc(PGLOBAL g, unsigned int e)
return p;
} // end of GetExceptionDesc
-#endif // __WIN__ && !NOCATCH
+#endif // SE_CATCH
/***********************************************************************/
/* PlgDBalloc: allocates or suballocates memory conditionally. */
diff --git a/storage/connect/plgxml.cpp b/storage/connect/plgxml.cpp
index 71b72621b06..eb31e24235b 100644
--- a/storage/connect/plgxml.cpp
+++ b/storage/connect/plgxml.cpp
@@ -1,6 +1,6 @@
/******************************************************************/
/* Implementation of XML document processing using PdbXML. */
-/* Author: Olivier Bertrand 2007-2012 */
+/* Author: Olivier Bertrand 2007-2017 */
/******************************************************************/
#include "my_global.h"
#include "global.h"
@@ -49,7 +49,7 @@ bool XMLDOCUMENT::InitZip(PGLOBAL g, char *entry)
{
#if defined(ZIP_SUPPORT)
bool mul = (entry) ? strchr(entry, '*') || strchr(entry, '?') : false;
- zip = new(g) ZIPUTIL(entry, mul);
+ zip = new(g) UNZIPUTL(entry, mul);
return zip == NULL;
#else // !ZIP_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
diff --git a/storage/connect/plgxml.h b/storage/connect/plgxml.h
index db7dfa6bda5..6870764c503 100644
--- a/storage/connect/plgxml.h
+++ b/storage/connect/plgxml.h
@@ -101,7 +101,7 @@ class XMLDOCUMENT : public BLOCK {
// Members
#if defined(ZIP_SUPPORT)
- ZIPUTIL *zip; /* Used for zipped file */
+ UNZIPUTL *zip; /* Used for zipped file */
#else // !ZIP_SUPPORT
bool zip; /* Always false */
#endif // !ZIP_SUPPORT
diff --git a/storage/connect/plugutil.c b/storage/connect/plugutil.c
index 2551b603349..bfac8a5fd99 100644
--- a/storage/connect/plugutil.c
+++ b/storage/connect/plugutil.c
@@ -244,6 +244,9 @@ LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath)
char *drive = NULL, *defdrv = NULL;
#endif
+ if (trace > 1)
+ htrc("prefix=%s fn=%s path=%s\n", prefix, FileName, defpath);
+
if (!strncmp(FileName, "//", 2) || !strncmp(FileName, "\\\\", 2)) {
strcpy(pBuff, FileName); // Remote file
return pBuff;
diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp
index 30e4d49d249..5bb7848ab1c 100644
--- a/storage/connect/reldef.cpp
+++ b/storage/connect/reldef.cpp
@@ -621,8 +621,8 @@ bool OEMDEF::DefineAM(PGLOBAL g, LPCSTR, int)
/***********************************************************************/
PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode)
{
- RECFM rfm;
- PTDBASE tdbp = NULL;
+ RECFM rfm;
+ PTDB tdbp = NULL;
// If define block not here yet, get it now
if (!Pxdef && !(Pxdef = GetXdef(g)))
@@ -632,7 +632,7 @@ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode)
/* Allocate a TDB of the proper type. */
/* Column blocks will be allocated only when needed. */
/*********************************************************************/
- if (!(tdbp = (PTDBASE)Pxdef->GetTable(g, mode)))
+ if (!(tdbp = Pxdef->GetTable(g, mode)))
return NULL;
else
rfm = tdbp->GetFtype();
diff --git a/storage/connect/reldef.h b/storage/connect/reldef.h
index bc1bd2ddd74..52a131dbf3d 100644
--- a/storage/connect/reldef.h
+++ b/storage/connect/reldef.h
@@ -64,15 +64,16 @@ class DllExport RELDEF : public BLOCK { // Relation definition block
}; // end of RELDEF
/***********************************************************************/
-/* These classes correspond to the data base description contained in */
-/* a .XDB file the A.M. DOS, FIX, CSV, MAP, BIN, VCT, PLG, ODBC, DOM. */
+/* This class corresponds to the data base description for tables */
+/* of type DOS, FIX, CSV, DBF, BIN, VCT, JSON, XML... */
/***********************************************************************/
class DllExport TABDEF : public RELDEF { /* Logical table descriptor */
friend class CATALOG;
friend class PLUGCAT;
friend class MYCAT;
- friend class TDBASE;
- public:
+ friend class TDB;
+ friend class TDBEXT;
+public:
// Constructor
TABDEF(void); // Constructor
@@ -112,11 +113,11 @@ class DllExport TABDEF : public RELDEF { /* Logical table descriptor */
int Sort; /* Table already sorted ??? */
int Multiple; /* 0: No 1: DIR 2: Section 3: filelist */
int Degree; /* Number of columns in the table */
- int Pseudo; /* Bit: 1 ROWID Ok, 2 FILEID Ok */
+ int Pseudo; /* Bit: 1 ROWID }Ok, 2 FILEID Ok */
bool Read_Only; /* true for read only tables */
const CHARSET_INFO *m_data_charset;
const char *csname; /* Table charset name */
- }; // end of TABDEF
+}; // end of TABDEF
/***********************************************************************/
/* Externally defined OEM tables. */
@@ -190,11 +191,12 @@ class DllExport COLCRT : public BLOCK { /* Column description block
/***********************************************************************/
/* Column definition block. */
/***********************************************************************/
-class DllExport COLDEF : public COLCRT { /* Column description block */
+class DllExport COLDEF : public COLCRT { /* Column description block */
friend class TABDEF;
friend class COLBLK;
friend class DBFFAM;
- friend class TDBASE;
+ friend class TDB;
+ friend class TDBASE;
friend class TDBDOS;
public:
COLDEF(void); // Constructor
diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp
index 16cc6c33b44..d2bb3d7a4af 100644
--- a/storage/connect/tabdos.cpp
+++ b/storage/connect/tabdos.cpp
@@ -102,6 +102,7 @@ DOSDEF::DOSDEF(void)
Mapped = false;
Zipped = false;
Mulentries = false;
+ Append = false;
Padded = false;
Huge = false;
Accept = false;
@@ -132,10 +133,13 @@ bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
: (am && (*am == 'B' || *am == 'b')) ? "B"
: (am && !stricmp(am, "DBF")) ? "D" : "V";
- if ((Zipped = GetBoolCatInfo("Zipped", false)))
- Mulentries = ((Entry = GetStringCatInfo(g, "Entry", NULL)))
- ? strchr(Entry, '*') || strchr(Entry, '?')
- : GetBoolCatInfo("Mulentries", false);
+ if ((Zipped = GetBoolCatInfo("Zipped", false))) {
+ Entry = GetStringCatInfo(g, "Entry", NULL);
+ Mulentries = (Entry && *Entry) ? strchr(Entry, '*') || strchr(Entry, '?')
+ : false;
+ Mulentries = GetBoolCatInfo("Mulentries", Mulentries);
+ Append = GetBoolCatInfo("Append", false);
+ }
Desc = Fn = GetStringCatInfo(g, "Filename", NULL);
Ofn = GetStringCatInfo(g, "Optname", Fn);
@@ -347,10 +351,26 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
if (Zipped) {
#if defined(ZIP_SUPPORT)
if (Recfm == RECFM_VAR) {
- txfp = new(g)ZIPFAM(this);
- tdbp = new(g)TDBDOS(this, txfp);
+ if (mode == MODE_READ || mode == MODE_ANY) {
+ txfp = new(g) UNZFAM(this);
+ } else if (mode == MODE_INSERT) {
+ txfp = new(g) ZIPFAM(this);
+ } else {
+ strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ return NULL;
+ } // endif's mode
+
+ tdbp = new(g) TDBDOS(this, txfp);
} else {
- txfp = new(g)ZPXFAM(this);
+ if (mode == MODE_READ || mode == MODE_ANY) {
+ txfp = new(g) UZXFAM(this);
+ } else if (mode == MODE_INSERT) {
+ txfp = new(g) ZPXFAM(this);
+ } else {
+ strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ return NULL;
+ } // endif's mode
+
tdbp = new(g)TDBFIX(this, txfp);
} // endif Recfm
@@ -376,7 +396,7 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
txfp = new(g) MPXFAM(this);
else if (Compressed) {
#if defined(GZ_SUPPORT)
- txfp = new(g) ZIXFAM(this);
+ txfp = new(g) GZXFAM(this);
#else // !GZ_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ");
return NULL;
@@ -484,7 +504,7 @@ TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp)
} // end of TDBDOS copy constructor
// Method
-PTDB TDBDOS::CopyOne(PTABS t)
+PTDB TDBDOS::Clone(PTABS t)
{
PTDB tp;
PDOSCOL cp1, cp2;
@@ -498,7 +518,7 @@ PTDB TDBDOS::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate DOS column description block. */
diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h
index 4c8eb438a26..922d52ee399 100644
--- a/storage/connect/tabdos.h
+++ b/storage/connect/tabdos.h
@@ -28,7 +28,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */
friend class TDBFIX;
friend class TXTFAM;
friend class DBFBASE;
- friend class ZIPUTIL;
+ friend class UNZIPUTL;
public:
// Constructor
DOSDEF(void);
@@ -43,7 +43,8 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */
PSZ GetOfn(void) {return Ofn;}
PSZ GetEntry(void) {return Entry;}
bool GetMul(void) {return Mulentries;}
- void SetBlock(int block) {Block = block;}
+ bool GetAppend(void) {return Append;}
+ void SetBlock(int block) { Block = block; }
int GetBlock(void) {return Block;}
int GetLast(void) {return Last;}
void SetLast(int last) {Last = last;}
@@ -81,6 +82,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */
bool Mapped; /* 0: disk file, 1: memory mapped file */
bool Zipped; /* true for zipped table file */
bool Mulentries; /* true for multiple entries */
+ bool Append; /* Used when creating zipped table */
bool Padded; /* true for padded table file */
bool Huge; /* true for files larger than 2GB */
bool Accept; /* true if wrong lines are accepted */
@@ -140,7 +142,7 @@ class DllExport TDBDOS : public TDBASE {
{return (PTDB)new(g) TDBDOS(g, this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual void ResetDB(void) {Txfp->Reset();}
virtual bool IsUsingTemp(PGLOBAL g);
virtual bool IsIndexed(void) {return Indxd;}
diff --git a/storage/connect/tabext.cpp b/storage/connect/tabext.cpp
new file mode 100644
index 00000000000..e3518126a49
--- /dev/null
+++ b/storage/connect/tabext.cpp
@@ -0,0 +1,640 @@
+/************* Tabext C++ Functions Source Code File (.CPP) ************/
+/* Name: TABEXT.CPP Version 1.0 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2017 */
+/* */
+/* This file contains the TBX, TDB and OPJOIN classes functions. */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant MariaDB header file. */
+/***********************************************************************/
+#define MYSQL_SERVER 1
+#include "my_global.h"
+#include "sql_class.h"
+#include "sql_servers.h"
+#include "sql_string.h"
+#if !defined(__WIN__)
+#include "osutil.h"
+#endif
+
+/***********************************************************************/
+/* Include required application header files */
+/* global.h is header containing all global Plug declarations. */
+/* plgdbsem.h is header containing the DB applic. declarations. */
+/* xobject.h is header containing XOBJECT derived classes declares. */
+/***********************************************************************/
+#include "global.h"
+#include "plgdbsem.h"
+#include "xtable.h"
+#include "tabext.h"
+#include "ha_connect.h"
+
+/* -------------------------- Class CONDFIL -------------------------- */
+
+/***********************************************************************/
+/* CONDFIL Constructor. */
+/***********************************************************************/
+CONDFIL::CONDFIL(const Item *cond, uint idx, AMT type)
+{
+ Cond = cond;
+ Idx = idx;
+ Type = type;
+ Op = OP_XX;
+ Cmds = NULL;
+ Alist = NULL;
+ All = true;
+ Bd = false;
+ Hv = false;
+ Body = NULL,
+ Having = NULL;
+} // end of CONDFIL constructor
+
+/***********************************************************************/
+/* Make and allocate the alias list. */
+/***********************************************************************/
+int CONDFIL::Init(PGLOBAL g, PHC hc)
+{
+ PTOS options = hc->GetTableOptionStruct();
+ char *p, *cn, *cal, *alt = NULL;
+ int rc = RC_OK;
+ bool h;
+
+ if (options)
+ alt = GetListOption(g, "Alias", options->oplist, NULL);
+
+ while (alt) {
+ if (!(p = strchr(alt, '='))) {
+ strcpy(g->Message, "Invalid alias list");
+ rc = RC_FX;
+ break;
+ } // endif !p
+
+ cal = alt; // Alias
+ *p++ = 0;
+
+ if ((h = *p == '*')) {
+ rc = RC_INFO;
+ p++;
+ } // endif h
+
+ cn = p; // Remote column name
+
+ if ((alt = strchr(p, ';')))
+ *alt++ = 0;
+
+ if (*cn == 0)
+ cn = alt;
+
+ Alist = new(g) ALIAS(Alist, cn, cal, h);
+ } // endwhile alt
+
+ return rc;
+} // end of Init
+
+/***********************************************************************/
+/* Make and allocate the alias list. */
+/***********************************************************************/
+const char *CONDFIL::Chk(const char *fln, bool *h)
+{
+ for (PAL pal = Alist; pal; pal = pal->Next)
+ if (!stricmp(fln, pal->Alias)) {
+ *h = pal->Having;
+ return pal->Name;
+ } // endif fln
+
+ *h = false;
+ return fln;
+} // end of Chk
+
+/* --------------------------- Class EXTDEF -------------------------- */
+
+/***********************************************************************/
+/* EXTDEF Constructor. */
+/***********************************************************************/
+EXTDEF::EXTDEF(void)
+{
+ Tabname = Tabschema = Username = Password = Tabcat = Tabtyp = NULL;
+ Colpat = Srcdef = Qchar = Qrystr = Sep = Phpos = NULL;
+ Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0;
+ Scrollable = Xsrc = false;
+} // end of EXTDEF constructor
+
+/***********************************************************************/
+/* DefineAM: define specific AM block values from XDB file. */
+/***********************************************************************/
+bool EXTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
+{
+ Desc = NULL;
+ Tabname = GetStringCatInfo(g, "Name",
+ (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
+ Tabname = GetStringCatInfo(g, "Tabname", Tabname);
+ Tabschema = GetStringCatInfo(g, "Dbname", NULL);
+ Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
+ Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
+ Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
+ Username = GetStringCatInfo(g, "User", NULL);
+ Password = GetStringCatInfo(g, "Password", NULL);
+
+ if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
+ Read_Only = true;
+
+ Qrystr = GetStringCatInfo(g, "Query_String", "?");
+ Sep = GetStringCatInfo(g, "Separator", NULL);
+//Alias = GetStringCatInfo(g, "Alias", NULL);
+ Phpos = GetStringCatInfo(g, "Phpos", NULL);
+ Xsrc = GetBoolCatInfo("Execsrc", FALSE);
+ Maxerr = GetIntCatInfo("Maxerr", 0);
+ Maxres = GetIntCatInfo("Maxres", 0);
+ Quoted = GetIntCatInfo("Quoted", 0);
+ Options = 0;
+ Cto = 0;
+ Qto = 0;
+
+ if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt)
+ Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch
+
+ if (Catfunc == FNC_COL)
+ Colpat = GetStringCatInfo(g, "Colpat", NULL);
+
+ if (Catfunc == FNC_TABLE)
+ Tabtyp = GetStringCatInfo(g, "Tabtype", NULL);
+
+ // Memory was Boolean, it is now integer
+ if (!(Memory = GetIntCatInfo("Memory", 0)))
+ Memory = GetBoolCatInfo("Memory", false) ? 1 : 0;
+
+ Pseudo = 2; // FILID is Ok but not ROWID
+ return false;
+} // end of DefineAM
+
+/* ---------------------------TDBEXT class --------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBEXT class. */
+/***********************************************************************/
+TDBEXT::TDBEXT(EXTDEF *tdp) : TDB(tdp)
+{
+ Qrp = NULL;
+
+ if (tdp) {
+ TableName = tdp->Tabname;
+ Schema = tdp->Tabschema;
+ User = tdp->Username;
+ Pwd = tdp->Password;
+ Catalog = tdp->Tabcat;
+ Srcdef = tdp->Srcdef;
+ Qrystr = tdp->Qrystr;
+ Sep = tdp->GetSep();
+ Options = tdp->Options;
+ Cto = tdp->Cto;
+ Qto = tdp->Qto;
+ Quoted = MY_MAX(0, tdp->GetQuoted());
+ Rows = tdp->GetElemt();
+ Memory = tdp->Memory;
+ Scrollable = tdp->Scrollable;
+ } else {
+ TableName = NULL;
+ Schema = NULL;
+ User = NULL;
+ Pwd = NULL;
+ Catalog = NULL;
+ Srcdef = NULL;
+ Qrystr = NULL;
+ Sep = 0;
+ Options = 0;
+ Cto = 0;
+ Qto = 0;
+ Quoted = 0;
+ Rows = 0;
+ Memory = 0;
+ Scrollable = false;
+ } // endif tdp
+
+ Quote = NULL;
+ Query = NULL;
+ Count = NULL;
+ //Where = NULL;
+ MulConn = NULL;
+ DBQ = NULL;
+ Qrp = NULL;
+ Fpos = 0;
+ Curpos = 0;
+ AftRows = 0;
+ CurNum = 0;
+ Rbuf = 0;
+ BufSize = 0;
+ Nparm = 0;
+ Ncol = 0;
+ Placed = false;
+} // end of TDBEXT constructor
+
+TDBEXT::TDBEXT(PTDBEXT tdbp) : TDB(tdbp)
+{
+ Qrp = tdbp->Qrp;
+ TableName = tdbp->TableName;
+ Schema = tdbp->Schema;
+ User = tdbp->User;
+ Pwd = tdbp->Pwd;
+ Catalog = tdbp->Catalog;
+ Srcdef = tdbp->Srcdef;
+ Qrystr = tdbp->Qrystr;
+ Sep = tdbp->Sep;
+ Options = tdbp->Options;
+ Cto = tdbp->Cto;
+ Qto = tdbp->Qto;
+ Quoted = tdbp->Quoted;
+ Rows = tdbp->Rows;
+ Memory = tdbp->Memory;
+ Scrollable = tdbp->Scrollable;
+ Quote = tdbp->Quote;
+ Query = tdbp->Query;
+ Count = tdbp->Count;
+ //Where = tdbp->Where;
+ MulConn = tdbp->MulConn;
+ DBQ = tdbp->DBQ;
+ Fpos = 0;
+ Curpos = 0;
+ AftRows = 0;
+ CurNum = 0;
+ Rbuf = 0;
+ BufSize = tdbp->BufSize;
+ Nparm = tdbp->Nparm;
+ Ncol = tdbp->Ncol;
+ Placed = false;
+} // end of TDBEXT copy constructor
+
+/******************************************************************/
+/* Convert an UTF-8 string to latin characters. */
+/******************************************************************/
+int TDBEXT::Decode(char *txt, char *buf, size_t n)
+{
+ uint dummy_errors;
+ uint32 len = copy_and_convert(buf, n, &my_charset_latin1,
+ txt, strlen(txt),
+ &my_charset_utf8_general_ci,
+ &dummy_errors);
+ buf[len] = '\0';
+ return 0;
+} // end of Decode
+
+/***********************************************************************/
+/* MakeSQL: make the SQL statement use with remote connection. */
+/* TODO: when implementing remote filtering, column only used in */
+/* local filter should be removed from column list. */
+/***********************************************************************/
+bool TDBEXT::MakeSQL(PGLOBAL g, bool cnt)
+{
+ char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3];
+ int len;
+ bool oom = false, first = true;
+ PTABLE tablep = To_Table;
+ PCOL colp;
+
+ if (Srcdef) {
+ if ((catp = strstr(Srcdef, "%s"))) {
+ char *fil1, *fil2;
+ PSZ ph = ((EXTDEF*)To_Def)->Phpos;
+
+ if (!ph)
+ ph = (strstr(catp + 2, "%s")) ? const_cast<char*>("WH") :
+ const_cast<char*>("W");
+
+ if (stricmp(ph, "H")) {
+ fil1 = (To_CondFil && *To_CondFil->Body)
+ ? To_CondFil->Body : PlugDup(g, "1=1");
+ } // endif ph
+
+ if (stricmp(ph, "W")) {
+ fil2 = (To_CondFil && To_CondFil->Having && *To_CondFil->Having)
+ ? To_CondFil->Having : PlugDup(g, "1=1");
+ } // endif ph
+
+ if (!stricmp(ph, "W")) {
+ Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1));
+ Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1));
+ } else if (!stricmp(ph, "WH")) {
+ Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
+ Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1, fil2));
+ } else if (!stricmp(ph, "H")) {
+ Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil2));
+ Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2));
+ } else if (!stricmp(ph, "HW")) {
+ Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2));
+ Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2, fil1));
+ } else {
+ strcpy(g->Message, "MakeSQL: Wrong place holders specification");
+ return true;
+ } // endif's ph
+
+ } else
+ Query = new(g)STRING(g, 0, Srcdef);
+
+ return false;
+ } // endif Srcdef
+
+ // Allocate the string used to contain the Query
+ Query = new(g)STRING(g, 1023, "SELECT ");
+
+ if (!cnt) {
+ if (Columns) {
+ // Normal SQL statement to retrieve results
+ for (colp = Columns; colp; colp = colp->GetNext())
+ if (!colp->IsSpecial()) {
+ if (!first)
+ oom |= Query->Append(", ");
+ else
+ first = false;
+
+ // Column name can be encoded in UTF-8
+ Decode(colp->GetName(), buf, sizeof(buf));
+
+ if (Quote) {
+ // Put column name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ ((PEXTCOL)colp)->SetRank(++Ncol);
+ } // endif colp
+
+ } else
+ // !Columns can occur for queries such that sql count(*) from...
+ // for which we will count the rows from sql * from...
+ oom |= Query->Append('*');
+
+ } else
+ // SQL statement used to retrieve the size of the result
+ oom |= Query->Append("count(*)");
+
+ oom |= Query->Append(" FROM ");
+
+ if (Catalog && *Catalog)
+ catp = Catalog;
+
+ //if (tablep->GetSchema())
+ // schmp = (char*)tablep->GetSchema();
+ //else
+ if (Schema && *Schema)
+ schmp = Schema;
+
+ if (catp) {
+ oom |= Query->Append(catp);
+
+ if (schmp) {
+ oom |= Query->Append('.');
+ oom |= Query->Append(schmp);
+ } // endif schmp
+
+ oom |= Query->Append('.');
+ } else if (schmp) {
+ oom |= Query->Append(schmp);
+ oom |= Query->Append('.');
+ } // endif schmp
+
+ // Table name can be encoded in UTF-8
+ Decode(TableName, buf, sizeof(buf));
+
+ if (Quote) {
+ // Put table name between identifier quotes in case in contains blanks
+ oom |= Query->Append(Quote);
+ oom |= Query->Append(buf);
+ oom |= Query->Append(Quote);
+ } else
+ oom |= Query->Append(buf);
+
+ len = Query->GetLength();
+
+ if (To_CondFil) {
+ if (Mode == MODE_READ) {
+ oom |= Query->Append(" WHERE ");
+ oom |= Query->Append(To_CondFil->Body);
+ len = Query->GetLength() + 1;
+ } else
+ len += (strlen(To_CondFil->Body) + 256);
+
+ } else
+ len += ((Mode == MODE_READX) ? 256 : 1);
+
+ if (oom || Query->Resize(len)) {
+ strcpy(g->Message, "MakeSQL: Out of memory");
+ return true;
+ } // endif oom
+
+ if (trace)
+ htrc("Query=%s\n", Query->GetStr());
+
+ return false;
+} // end of MakeSQL
+
+/***********************************************************************/
+/* MakeCommand: make the Update or Delete statement to send to the */
+/* MySQL server. Limited to remote values and filtering. */
+/***********************************************************************/
+bool TDBEXT::MakeCommand(PGLOBAL g)
+{
+ char *p, *stmt, name[68], *body = NULL;
+ char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
+ bool qtd = Quoted > 0;
+ int i = 0, k = 0;
+
+ // Make a lower case copy of the originale query and change
+ // back ticks to the data source identifier quoting character
+ do {
+ qrystr[i] = (Qrystr[i] == '`') ? *Quote : tolower(Qrystr[i]);
+ } while (Qrystr[i++]);
+
+ if (To_CondFil && (p = strstr(qrystr, " where "))) {
+ p[7] = 0; // Remove where clause
+ Qrystr[(p - qrystr) + 7] = 0;
+ body = To_CondFil->Body;
+ stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr)
+ + strlen(body) + 64);
+ } else
+ stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
+
+ // Check whether the table name is equal to a keyword
+ // If so, it must be quoted in the original query
+ strlwr(strcat(strcat(strcpy(name, " "), Name), " "));
+
+ if (strstr(" update delete low_priority ignore quick from ", name)) {
+ strlwr(strcat(strcat(strcpy(name, Quote), Name), Quote));
+ k += 2;
+ } else
+ strlwr(strcpy(name, Name)); // Not a keyword
+
+ if ((p = strstr(qrystr, name))) {
+ for (i = 0; i < p - qrystr; i++)
+ stmt[i] = (Qrystr[i] == '`') ? *Quote : Qrystr[i];
+
+ stmt[i] = 0;
+ k += i + (int)strlen(Name);
+
+ if (qtd && *(p - 1) == ' ')
+ strcat(strcat(strcat(stmt, Quote), TableName), Quote);
+ else
+ strcat(stmt, TableName);
+
+ i = (int)strlen(stmt);
+
+ do {
+ stmt[i++] = (Qrystr[k] == '`') ? *Quote : Qrystr[k];
+ } while (Qrystr[k++]);
+
+ if (body)
+ strcat(stmt, body);
+
+ } else {
+ sprintf(g->Message, "Cannot use this %s command",
+ (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
+ return true;
+ } // endif p
+
+ if (trace)
+ htrc("Command=%s\n", stmt);
+
+ Query = new(g)STRING(g, 0, stmt);
+ return (!Query->GetSize());
+} // end of MakeCommand
+
+/***********************************************************************/
+/* GetRecpos: return the position of last read record. */
+/***********************************************************************/
+int TDBEXT::GetRecpos(void)
+{
+ return Fpos;
+} // end of GetRecpos
+
+/***********************************************************************/
+/* ODBC GetMaxSize: returns table size estimate in number of lines. */
+/***********************************************************************/
+int TDBEXT::GetMaxSize(PGLOBAL g)
+{
+ if (MaxSize < 0) {
+ if (Mode == MODE_DELETE)
+ // Return 0 in mode DELETE in case of delete all.
+ MaxSize = 0;
+ else if (!Cardinality(NULL))
+ MaxSize = 10; // To make MySQL happy
+ else if ((MaxSize = Cardinality(g)) < 0)
+ MaxSize = 12; // So we can see an error occurred
+
+ } // endif MaxSize
+
+ return MaxSize;
+} // end of GetMaxSize
+
+/***********************************************************************/
+/* Return max size value. */
+/***********************************************************************/
+int TDBEXT::GetProgMax(PGLOBAL g)
+{
+ return GetMaxSize(g);
+} // end of GetProgMax
+
+/* ---------------------------EXTCOL class --------------------------- */
+
+/***********************************************************************/
+/* EXTCOL public constructor. */
+/***********************************************************************/
+EXTCOL::EXTCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
+ : COLBLK(cdp, tdbp, i)
+{
+ if (cprec) {
+ Next = cprec->GetNext();
+ cprec->SetNext(this);
+ } else {
+ Next = tdbp->GetColumns();
+ tdbp->SetColumns(this);
+ } // endif cprec
+
+ if (trace)
+ htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
+
+ // Set additional remote access method information for column.
+ Crp = NULL;
+ Long = Precision;
+ To_Val = NULL;
+ Bufp = NULL;
+ Blkp = NULL;
+ Rank = 0; // Not known yet
+} // end of JDBCCOL constructor
+
+/***********************************************************************/
+/* EXTCOL private constructor. */
+/***********************************************************************/
+EXTCOL::EXTCOL(void) : COLBLK()
+{
+ Crp = NULL;
+ Buf_Type = TYPE_INT; // This is a count(*) column
+
+ // Set additional Dos access method information for column.
+ Long = sizeof(int);
+ To_Val = NULL;
+ Bufp = NULL;
+ Blkp = NULL;
+ Rank = 1;
+} // end of EXTCOL constructor
+
+/***********************************************************************/
+/* EXTCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+EXTCOL::EXTCOL(PEXTCOL col1, PTDB tdbp) : COLBLK(col1, tdbp)
+{
+ Crp = col1->Crp;
+ Long = col1->Long;
+ To_Val = col1->To_Val;
+ Bufp = col1->Bufp;
+ Blkp = col1->Blkp;
+ Rank = col1->Rank;
+} // end of JDBCCOL copy constructor
+
+/***********************************************************************/
+/* SetBuffer: prepare a column block for write operation. */
+/***********************************************************************/
+bool EXTCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
+{
+ if (!(To_Val = value)) {
+ sprintf(g->Message, MSG(VALUE_ERROR), Name);
+ return true;
+ } else if (Buf_Type == value->GetType()) {
+ // Values are of the (good) column type
+ if (Buf_Type == TYPE_DATE) {
+ // If any of the date values is formatted
+ // output format must be set for the receiving table
+ if (GetDomain() || ((DTVAL *)value)->IsFormatted())
+ goto newval; // This will make a new value;
+
+ } else if (Buf_Type == TYPE_DOUBLE)
+ // Float values must be written with the correct (column) precision
+ // Note: maybe this should be forced by ShowValue instead of this ?
+ value->SetPrec(GetScale());
+
+ Value = value; // Directly access the external value
+ } else {
+ // Values are not of the (good) column type
+ if (check) {
+ sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
+ GetTypeName(Buf_Type), GetTypeName(value->GetType()));
+ return true;
+ } // endif check
+
+ newval:
+ if (InitValue(g)) // Allocate the matching value block
+ return true;
+
+ } // endif's Value, Buf_Type
+
+ // Because Colblk's have been made from a copy of the original TDB in
+ // case of Update, we must reset them to point to the original one.
+ if (To_Tdb->GetOrig())
+ To_Tdb = (PTDB)To_Tdb->GetOrig();
+
+ // Set the Column
+ Status = (ok) ? BUF_EMPTY : BUF_NO;
+ return false;
+} // end of SetBuffer
+
diff --git a/storage/connect/tabext.h b/storage/connect/tabext.h
new file mode 100644
index 00000000000..2ef20c89f2c
--- /dev/null
+++ b/storage/connect/tabext.h
@@ -0,0 +1,200 @@
+/*************** Tabext H Declares Source Code File (.H) ***************/
+/* Name: TABEXT.H Version 1.0 */
+/* */
+/* (C) Copyright to the author Olivier BERTRAND 2017 */
+/* */
+/* This is the EXTDEF, TABEXT and EXTCOL classes definitions. */
+/***********************************************************************/
+
+#ifndef __TABEXT_H
+#define __TABEXTF_H
+
+#include "reldef.h"
+
+typedef class ALIAS *PAL;
+
+class ALIAS : public BLOCK {
+ public:
+ ALIAS(PAL x, PSZ n, PSZ a, bool h)
+ {Next = x, Name = n, Alias = a, Having = h;}
+
+ PAL Next;
+ PSZ Name;
+ PSZ Alias;
+ bool Having;
+}; // end of class ALIAS
+
+// Condition filter structure
+class CONDFIL : public BLOCK {
+ public:
+ // Constructor
+ CONDFIL(const Item *cond, uint idx, AMT type);
+
+ // Functions
+ int Init(PGLOBAL g, PHC hc);
+ const char *Chk(const char *cln, bool *h);
+
+ // Members
+ const Item *Cond;
+ AMT Type;
+ uint Idx;
+ OPVAL Op;
+ PCMD Cmds;
+ PAL Alist;
+ bool All;
+ bool Bd;
+ bool Hv;
+ char *Body;
+ char *Having;
+}; // end of class CONDFIL
+
+/***********************************************************************/
+/* This class corresponds to the data base description for external */
+/* tables of type MYSQL, ODBC, JDBC... */
+/***********************************************************************/
+class DllExport EXTDEF : public TABDEF { /* EXT table */
+ friend class TDBEXT;
+public:
+ // Constructor
+ EXTDEF(void); // Constructor
+
+ // Implementation
+ virtual const char *GetType(void) { return "EXT"; }
+ inline PSZ GetTabname(void) { return Tabname; }
+ inline PSZ GetTabschema(void) { return Tabschema; }
+ inline PSZ GetUsername(void) { return Username; };
+ inline PSZ GetPassword(void) { return Password; };
+ inline PSZ GetTabcat(void) { return Tabcat; }
+ inline PSZ GetSrcdef(void) { return Srcdef; }
+ inline char GetSep(void) { return (Sep) ? *Sep : 0; }
+ inline int GetQuoted(void) { return Quoted; }
+ inline int GetOptions(void) { return Options; }
+
+ // Methods
+ virtual int Indexable(void) { return 2; }
+ virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
+
+protected:
+ // Members
+ PSZ Tabname; /* External table name */
+ PSZ Tabschema; /* External table schema */
+ PSZ Username; /* User connect name */
+ PSZ Password; /* Password connect info */
+ PSZ Tabcat; /* External table catalog */
+ PSZ Tabtyp; /* Catalog table type */
+ PSZ Colpat; /* Catalog column pattern */
+ PSZ Srcdef; /* The source table SQL definition */
+ PSZ Qchar; /* Identifier quoting character */
+ PSZ Qrystr; /* The original query */
+ PSZ Sep; /* Decimal separator */
+//PSZ Alias; /* Column alias list */
+ PSZ Phpos; /* Place holer positions */
+ int Options; /* Open connection options */
+ int Cto; /* Open connection timeout */
+ int Qto; /* Query (command) timeout */
+ int Quoted; /* Identifier quoting level */
+ int Maxerr; /* Maxerr for an Exec table */
+ int Maxres; /* Maxres for a catalog table */
+ int Memory; /* Put result set in memory */
+ bool Scrollable; /* Use scrollable cursor */
+ bool Xsrc; /* Execution type */
+}; // end of EXTDEF
+
+/***********************************************************************/
+/* This is the base class for all external tables. */
+/***********************************************************************/
+class DllExport TDBEXT : public TDB {
+public:
+ // Constructors
+ TDBEXT(EXTDEF *tdp);
+ TDBEXT(PTDBEXT tdbp);
+
+ // Implementation
+
+ // Properties
+ virtual bool IsRemote(void) { return true; }
+
+ // Methods
+ virtual PSZ GetServer(void) { return "Remote"; }
+ virtual int GetRecpos(void);
+
+ // Database routines
+ virtual int GetMaxSize(PGLOBAL g);
+ virtual int GetProgMax(PGLOBAL g);
+
+protected:
+ // Internal functions
+ virtual bool MakeSQL(PGLOBAL g, bool cnt);
+ //virtual bool MakeInsert(PGLOBAL g);
+ virtual bool MakeCommand(PGLOBAL g);
+ int Decode(char *utf, char *buf, size_t n);
+
+ // Members
+ PQRYRES Qrp; // Points to storage result
+ PSTRG Query; // Constructed SQL query
+ char *TableName; // Points to ODBC table name
+ char *Schema; // Points to ODBC table Schema
+ char *User; // User connect info
+ char *Pwd; // Password connect info
+ char *Catalog; // Points to ODBC table Catalog
+ char *Srcdef; // The source table SQL definition
+ char *Count; // Points to count(*) SQL statement
+ //char *Where; // Points to local where clause
+ char *Quote; // The identifier quoting character
+ char *MulConn; // Used for multiple ODBC tables
+ char *DBQ; // The address part of Connect string
+ char *Qrystr; // The original query
+ char Sep; // The decimal separator
+ int Options; // Connect options
+ int Cto; // Connect timeout
+ int Qto; // Query timeout
+ int Quoted; // The identifier quoting level
+ int Fpos; // Position of last read record
+ int Curpos; // Cursor position of last fetch
+ int AftRows; // The number of affected rows
+ int Rows; // Rowset size
+ int CurNum; // Current buffer line number
+ int Rbuf; // Number of lines read in buffer
+ int BufSize; // Size of connect string buffer
+ int Nparm; // The number of statement parameters
+ int Memory; // 0: No 1: Alloc 2: Put 3: Get
+ int Ncol; // The column number (JDBC)
+ bool Scrollable; // Use scrollable cursor
+ bool Placed; // True for position reading
+}; // end of class TDBEXT
+
+/***********************************************************************/
+/* Virual class EXTCOL: external column. */
+/***********************************************************************/
+class DllExport EXTCOL : public COLBLK {
+ friend class TDBEXT;
+public:
+ // Constructor
+ EXTCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am);
+ EXTCOL(PEXTCOL colp, PTDB tdbp); // Constructor used in copy process
+
+ // Implementation
+ inline int GetRank(void) { return Rank; }
+ inline void SetRank(int k) { Rank = k; }
+ //inline PVBLK GetBlkp(void) {return Blkp;}
+ inline void SetCrp(PCOLRES crp) { Crp = crp; }
+
+ // Methods
+ virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void ReadColumn(PGLOBAL) = 0;
+ virtual void WriteColumn(PGLOBAL) = 0;
+
+protected:
+ // Constructor for count(*) column
+ EXTCOL(void);
+
+ // Members
+ PCOLRES Crp; // To storage result
+ void *Bufp; // To extended buffer
+ PVBLK Blkp; // To Value Block
+ PVAL To_Val; // To value used for Insert
+ int Rank; // Rank (position) number in the query
+ //int Flag; // ???
+}; // end of class EXTCOL
+
+#endif // __TABEXT_H
diff --git a/storage/connect/tabfix.cpp b/storage/connect/tabfix.cpp
index d99f7800f26..bf123cd36c8 100644
--- a/storage/connect/tabfix.cpp
+++ b/storage/connect/tabfix.cpp
@@ -77,7 +77,7 @@ TDBFIX::TDBFIX(PGLOBAL g, PTDBFIX tdbp) : TDBDOS(g, tdbp)
} // end of TDBFIX copy constructor
// Method
-PTDB TDBFIX::CopyOne(PTABS t)
+PTDB TDBFIX::Clone(PTABS t)
{
PTDB tp;
PGLOBAL g = t->G;
@@ -105,7 +105,7 @@ PTDB TDBFIX::CopyOne(PTABS t)
} // endif Ftype
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Reset read/write position values. */
diff --git a/storage/connect/tabfix.h b/storage/connect/tabfix.h
index 49956ba0711..4b9f9689992 100644
--- a/storage/connect/tabfix.h
+++ b/storage/connect/tabfix.h
@@ -34,7 +34,7 @@ class DllExport TDBFIX : public TDBDOS {
{return (PTDB)new(g) TDBFIX(g, this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual void ResetDB(void);
virtual bool IsUsingTemp(PGLOBAL g);
virtual int RowNumber(PGLOBAL g, bool b = false);
diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp
index b24375443f6..0da67ef5e7f 100644
--- a/storage/connect/tabfmt.cpp
+++ b/storage/connect/tabfmt.cpp
@@ -5,7 +5,7 @@
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2001 - 2016 */
+/* (C) Copyright to the author Olivier BERTRAND 2001 - 2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -98,8 +98,9 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info)
int num_read = 0, num_max = 10000000; // Statistics
int len[MAXCOL], typ[MAXCOL], prc[MAXCOL];
PCSVDEF tdp;
- PTDBCSV tdbp;
- PQRYRES qrp;
+ PTDBCSV tcvp;
+ PTDBASE tdbp;
+ PQRYRES qrp;
PCOLRES crp;
if (info) {
@@ -108,10 +109,10 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info)
goto skipit;
} // endif info
- if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
- strcpy(g->Message, "Cannot find column definition for multiple table");
- return NULL;
- } // endif Multiple
+ //if (GetIntegerTableOption(g, topt, "Multiple", 0)) {
+ // strcpy(g->Message, "Cannot find column definition for multiple table");
+ // return NULL;
+ //} // endif Multiple
// num_max = atoi(p+1); // Max num of record to test
imax = hmax = nerr = 0;
@@ -127,10 +128,20 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info)
/* Get the CSV table description block. */
/*********************************************************************/
tdp = new(g) CSVDEF;
+ tdp->Database = dp;
+
+ if ((tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false))) {
#if defined(ZIP_SUPPORT)
- tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
- tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false);
-#endif // ZIP_SUPPORT
+ tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL);
+ tdp->Mulentries = (tdp->Entry)
+ ? strchr(tdp->Entry, '*') || strchr(tdp->Entry, '?')
+ : GetBooleanTableOption(g, topt, "Mulentries", false);
+#else // !ZIP_SUPPORT
+ strcpy(g->Message, "ZIP not supported by this version");
+ return NULL;
+#endif // !ZIP_SUPPORT
+ } // endif // Zipped
+
fn = tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL);
if (!tdp->Fn) {
@@ -141,6 +152,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info)
if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0)))
tdp->Lrecl = 4096;
+ tdp->Multiple = GetIntegerTableOption(g, topt, "Multiple", 0);
p = GetStringTableOption(g, topt, "Separator", ",");
tdp->Sep = (strlen(p) == 2 && p[0] == '\\' && p[1] == 't') ? '\t' : *p;
@@ -177,17 +189,18 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info)
htrc("File %s Sep=%c Qot=%c Header=%d maxerr=%d\n",
SVP(tdp->Fn), tdp->Sep, tdp->Qot, tdp->Header, tdp->Maxerr);
- if (tdp->Zipped) {
-#if defined(ZIP_SUPPORT)
- tdbp = new(g)TDBCSV(tdp, new(g)ZIPFAM(tdp));
-#else // !ZIP_SUPPORT
- sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
- return NULL;
-#endif // !ZIP_SUPPORT
- } else
- tdbp = new(g) TDBCSV(tdp, new(g) DOSFAM(tdp));
+ if (tdp->Zipped)
+ tcvp = new(g)TDBCSV(tdp, new(g)UNZFAM(tdp));
+ else
+ tcvp = new(g) TDBCSV(tdp, new(g) DOSFAM(tdp));
+
+ tcvp->SetMode(MODE_READ);
- tdbp->SetMode(MODE_READ);
+ if (tdp->Multiple) {
+ tdbp = new(g)TDBMUL(tcvp);
+ tdbp->SetMode(MODE_READ);
+ } else
+ tdbp = tcvp;
/*********************************************************************/
/* Open the CSV file. */
@@ -202,7 +215,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info)
phase = 0;
if ((rc = tdbp->ReadDB(g)) == RC_OK) {
- p = PlgDBDup(g, tdbp->To_Line);
+ p = PlgDBDup(g, tcvp->To_Line);
//skip leading blanks
for (; *p == ' '; p++) ;
@@ -245,6 +258,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info)
for (i = 0; i < hmax; i++)
length[0] = MY_MAX(length[0], strlen(colname[i]));
+ tcvp->Header = true; // In case of multiple table
} // endif hdr
for (num_read++; num_read <= num_max; num_read++) {
@@ -265,7 +279,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info)
/*******************************************************************/
i = n = phase = blank = digit = dec = 0;
- for (p = tdbp->To_Line; *p; p++)
+ for (p = tcvp->To_Line; *p; p++)
if (*p == sep) {
if (phase != 1) {
if (i == MAXCOL - 1) {
@@ -503,7 +517,14 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode)
/*******************************************************************/
if (Zipped) {
#if defined(ZIP_SUPPORT)
- txfp = new(g) ZIPFAM(this);
+ if (mode == MODE_READ || mode == MODE_ANY) {
+ txfp = new(g) UNZFAM(this);
+ } else if (mode == MODE_INSERT) {
+ txfp = new(g) ZIPFAM(this);
+ } else {
+ strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ return NULL;
+ } // endif's mode
#else // !ZIP_SUPPORT
strcpy(g->Message, "ZIP not supported");
return NULL;
@@ -640,7 +661,7 @@ TDBCSV::TDBCSV(PGLOBAL g, PTDBCSV tdbp) : TDBDOS(g, tdbp)
} // end of TDBCSV copy constructor
// Method
-PTDB TDBCSV::CopyOne(PTABS t)
+PTDB TDBCSV::Clone(PTABS t)
{
PTDB tp;
PCSVCOL cp1, cp2;
@@ -654,7 +675,7 @@ PTDB TDBCSV::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate CSV column description block. */
@@ -1148,7 +1169,7 @@ TDBFMT::TDBFMT(PGLOBAL g, PTDBFMT tdbp) : TDBCSV(g, tdbp)
} // end of TDBFMT copy constructor
// Method
-PTDB TDBFMT::CopyOne(PTABS t)
+PTDB TDBFMT::Clone(PTABS t)
{
PTDB tp;
PCSVCOL cp1, cp2;
@@ -1165,7 +1186,7 @@ PTDB TDBFMT::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate FMT column description block. */
diff --git a/storage/connect/tabfmt.h b/storage/connect/tabfmt.h
index 5ce8d399a64..e5655435be7 100644
--- a/storage/connect/tabfmt.h
+++ b/storage/connect/tabfmt.h
@@ -52,6 +52,7 @@ public:
/***********************************************************************/
class DllExport TDBCSV : public TDBDOS {
friend class CSVCOL;
+ friend class MAPFAM;
friend PQRYRES CSVColumns(PGLOBAL, char *, PTOS, bool);
public:
// Constructor
@@ -64,7 +65,7 @@ public:
{return (PTDB)new(g) TDBCSV(g, this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
//virtual bool IsUsingTemp(PGLOBAL g);
virtual int GetBadLines(void) {return (int)Nerr;}
@@ -147,7 +148,7 @@ class DllExport TDBFMT : public TDBCSV {
{return (PTDB)new(g) TDBFMT(g, this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
diff --git a/storage/connect/tabjdbc.cpp b/storage/connect/tabjdbc.cpp
index 912e6c7d530..5431e35e0ec 100644
--- a/storage/connect/tabjdbc.cpp
+++ b/storage/connect/tabjdbc.cpp
@@ -1,11 +1,11 @@
/************* TabJDBC C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABJDBC */
/* ------------- */
-/* Version 1.1 */
+/* Version 1.2 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2016 */
+/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -69,9 +69,10 @@
#include "plgdbsem.h"
#include "mycat.h"
#include "xtable.h"
+#include "tabext.h"
#include "tabjdbc.h"
#include "tabmul.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "tabcol.h"
#include "valblk.h"
#include "ha_connect.h"
@@ -96,10 +97,7 @@ bool ExactInfo(void);
/***********************************************************************/
JDBCDEF::JDBCDEF(void)
{
- Driver = Url = Wrapname =Tabname = Tabschema = Username = Colpat = NULL;
- Password = Tabcat = Tabtype = Srcdef = Qchar = Qrystr = Sep = NULL;
- Options = Quoted = Maxerr = Maxres = Memory = 0;
- Scrollable = Xsrc = false;
+ Driver = Url = Wrapname = NULL;
} // end of JDBCDEF constructor
/***********************************************************************/
@@ -134,23 +132,26 @@ bool JDBCDEF::SetParms(PJPARM sjp)
int JDBCDEF::ParseURL(PGLOBAL g, char *url, bool b)
{
if (strncmp(url, "jdbc:", 5)) {
+ PSZ p;
+
// No "jdbc:" in connection string. Must be a straight
// "server" or "server/table"
// ok, so we do a little parsing, but not completely!
- if ((Tabname= strchr(url, '/'))) {
+ if ((p = strchr(url, '/'))) {
// If there is a single '/' in the connection string,
// this means the user is specifying a table name
- *Tabname++= '\0';
+ *p++= '\0';
// there better not be any more '/'s !
- if (strchr(Tabname, '/'))
+ if (strchr(p, '/'))
return RC_FX;
- } else if (b) {
- // Otherwise, straight server name,
- Tabname = GetStringCatInfo(g, "Name", NULL);
- Tabname = GetStringCatInfo(g, "Tabname", Tabname);
- } // endelse
+ Tabname = p;
+// } else if (b) {
+// // Otherwise, straight server name,
+// Tabname = GetStringCatInfo(g, "Name", NULL);
+// Tabname = GetStringCatInfo(g, "Tabname", Tabname);
+ } // endif
if (trace)
htrc("server: %s Tabname: %s", url, Tabname);
@@ -204,6 +205,9 @@ bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
{
int rc = RC_OK;
+ if (EXTDEF::DefineAM(g, am, poff))
+ return true;
+
Driver = GetStringCatInfo(g, "Driver", NULL);
Desc = Url = GetStringCatInfo(g, "Connect", NULL);
@@ -223,41 +227,41 @@ bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
if (rc == RC_FX) // Error
return true;
- else if (rc == RC_OK) { // Url was not a server name
- Tabname = GetStringCatInfo(g, "Name",
- (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
- Tabname = GetStringCatInfo(g, "Tabname", Tabname);
- Username = GetStringCatInfo(g, "User", NULL);
- Password = GetStringCatInfo(g, "Password", NULL);
- } // endif rc
+//else if (rc == RC_OK) { // Url was not a server name
+// Tabname = GetStringCatInfo(g, "Name",
+// (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
+// Tabname = GetStringCatInfo(g, "Tabname", Tabname);
+// Username = GetStringCatInfo(g, "User", NULL);
+// Password = GetStringCatInfo(g, "Password", NULL);
+//} // endif rc
- if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
- Read_Only = true;
+//if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
+// Read_Only = true;
Wrapname = GetStringCatInfo(g, "Wrapper", NULL);
//Prop = GetStringCatInfo(g, "Properties", NULL);
- Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
- Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
- Tabschema = GetStringCatInfo(g, "Dbname", NULL);
- Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
-
- if (Catfunc == FNC_COL)
- Colpat = GetStringCatInfo(g, "Colpat", NULL);
-
- if (Catfunc == FNC_TABLE)
- Tabtype = GetStringCatInfo(g, "Tabtype", NULL);
-
- Qrystr = GetStringCatInfo(g, "Query_String", "?");
- Sep = GetStringCatInfo(g, "Separator", NULL);
- Xsrc = GetBoolCatInfo("Execsrc", FALSE);
- Maxerr = GetIntCatInfo("Maxerr", 0);
- Maxres = GetIntCatInfo("Maxres", 0);
- Quoted = GetIntCatInfo("Quoted", 0);
-//Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT);
-//Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT);
- Scrollable = GetBoolCatInfo("Scrollable", false);
- Memory = GetIntCatInfo("Memory", 0);
- Pseudo = 2; // FILID is Ok but not ROWID
+//Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
+//Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
+//Tabschema = GetStringCatInfo(g, "Dbname", NULL);
+//Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
+
+//if (Catfunc == FNC_COL)
+// Colpat = GetStringCatInfo(g, "Colpat", NULL);
+
+//if (Catfunc == FNC_TABLE)
+// Tabtyp = GetStringCatInfo(g, "Tabtype", NULL);
+
+//Qrystr = GetStringCatInfo(g, "Query_String", "?");
+//Sep = GetStringCatInfo(g, "Separator", NULL);
+//Xsrc = GetBoolCatInfo("Execsrc", FALSE);
+//Maxerr = GetIntCatInfo("Maxerr", 0);
+//Maxres = GetIntCatInfo("Maxres", 0);
+//Quoted = GetIntCatInfo("Quoted", 0);
+// Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT);
+// Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT);
+//Scrollable = GetBoolCatInfo("Scrollable", false);
+//Memory = GetIntCatInfo("Memory", 0);
+//Pseudo = 2; // FILID is Ok but not ROWID
return false;
} // end of DefineAM
@@ -266,7 +270,7 @@ bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
/***********************************************************************/
PTDB JDBCDEF::GetTable(PGLOBAL g, MODE m)
{
- PTDBASE tdbp = NULL;
+ PTDB tdbp = NULL;
/*********************************************************************/
/* Allocate a TDB of the proper type. */
@@ -326,7 +330,7 @@ int JDBCPARM::CheckSize(int rows)
/***********************************************************************/
/* Implementation of the TDBJDBC class. */
/***********************************************************************/
-TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBASE(tdp)
+TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBEXT(tdp)
{
Jcp = NULL;
Cnp = NULL;
@@ -335,101 +339,45 @@ TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBASE(tdp)
Ops.Driver = tdp->Driver;
Ops.Url = tdp->Url;
WrapName = tdp->Wrapname;
- TableName = tdp->Tabname;
- Schema = tdp->Tabschema;
Ops.User = tdp->Username;
Ops.Pwd = tdp->Password;
// Ops.Properties = tdp->Prop;
- Catalog = tdp->Tabcat;
- Srcdef = tdp->Srcdef;
- Qrystr = tdp->Qrystr;
- Sep = tdp->GetSep();
- Options = tdp->Options;
// Ops.Cto = tdp->Cto;
// Ops.Qto = tdp->Qto;
- Quoted = MY_MAX(0, tdp->GetQuoted());
- Rows = tdp->GetElemt();
- Memory = tdp->Memory;
Ops.Scrollable = tdp->Scrollable;
} else {
WrapName = NULL;
- TableName = NULL;
- Schema = NULL;
Ops.Driver = NULL;
Ops.Url = NULL;
Ops.User = NULL;
Ops.Pwd = NULL;
// Ops.Properties = NULL;
- Catalog = NULL;
- Srcdef = NULL;
- Qrystr = NULL;
- Sep = 0;
- Options = 0;
// Ops.Cto = DEFAULT_LOGIN_TIMEOUT;
// Ops.Qto = DEFAULT_QUERY_TIMEOUT;
- Quoted = 0;
- Rows = 0;
- Memory = 0;
Ops.Scrollable = false;
} // endif tdp
- Quote = NULL;
- Query = NULL;
- Count = NULL;
-//Where = NULL;
- MulConn = NULL;
- DBQ = NULL;
- Qrp = NULL;
- Fpos = 0;
- Curpos = 0;
- AftRows = 0;
- CurNum = 0;
- Rbuf = 0;
- BufSize = 0;
- Ncol = 0;
- Nparm = 0;
- Placed = false;
+//Ncol = 0;
Prepared = false;
Werr = false;
Rerr = false;
Ops.Fsize = Ops.CheckSize(Rows);
} // end of TDBJDBC standard constructor
-TDBJDBC::TDBJDBC(PTDBJDBC tdbp) : TDBASE(tdbp)
+TDBJDBC::TDBJDBC(PTDBJDBC tdbp) : TDBEXT(tdbp)
{
Jcp = tdbp->Jcp; // is that right ?
Cnp = tdbp->Cnp;
WrapName = tdbp->WrapName;
- TableName = tdbp->TableName;
- Schema = tdbp->Schema;
Ops = tdbp->Ops;
- Catalog = tdbp->Catalog;
- Srcdef = tdbp->Srcdef;
- Qrystr = tdbp->Qrystr;
- Memory = tdbp->Memory;
-//Scrollable = tdbp->Scrollable;
- Quote = tdbp->Quote;
- Query = tdbp->Query;
- Count = tdbp->Count;
-//Where = tdbp->Where;
- MulConn = tdbp->MulConn;
- DBQ = tdbp->DBQ;
- Options = tdbp->Options;
- Quoted = tdbp->Quoted;
- Rows = tdbp->Rows;
- Fpos = 0;
- Curpos = 0;
- AftRows = 0;
- CurNum = 0;
- Rbuf = 0;
- BufSize = tdbp->BufSize;
- Nparm = tdbp->Nparm;
- Qrp = tdbp->Qrp;
- Placed = false;
+//Ncol = tdbp->Ncol;
+ Prepared = tdbp->Prepared;
+ Werr = tdbp->Werr;
+ Rerr = tdbp->Rerr;
} // end of TDBJDBC copy constructor
// Method
-PTDB TDBJDBC::CopyOne(PTABS t)
+PTDB TDBJDBC::Clone(PTABS t)
{
PTDB tp;
PJDBCCOL cp1, cp2;
@@ -443,7 +391,7 @@ PTDB TDBJDBC::CopyOne(PTABS t)
} // endfor cp1
return tp;
-} // end of CopyOne
+} // end of Clone
/***********************************************************************/
/* Allocate JDBC column description block. */
@@ -453,134 +401,6 @@ PCOL TDBJDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
return new(g)JDBCCOL(cdp, this, cprec, n);
} // end of MakeCol
-/******************************************************************/
-/* Convert an UTF-8 string to latin characters. */
-/******************************************************************/
-int TDBJDBC::Decode(char *txt, char *buf, size_t n)
-{
- uint dummy_errors;
- uint32 len= copy_and_convert(buf, n, &my_charset_latin1,
- txt, strlen(txt),
- &my_charset_utf8_general_ci,
- &dummy_errors);
- buf[len]= '\0';
- return 0;
-} // end of Decode
-
-/***********************************************************************/
-/* MakeSQL: make the SQL statement use with JDBC connection. */
-/* TODO: when implementing EOM filtering, column only used in local */
-/* filter should be removed from column list. */
-/***********************************************************************/
-bool TDBJDBC::MakeSQL(PGLOBAL g, bool cnt)
-{
- char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3];
- int len;
- bool oom = false, first = true;
- PTABLE tablep = To_Table;
- PCOL colp;
-
- if (Srcdef) {
- Query = new(g)STRING(g, 0, Srcdef);
- return false;
- } // endif Srcdef
-
- // Allocate the string used to contain the Query
- Query = new(g)STRING(g, 1023, "SELECT ");
-
- if (!cnt) {
- if (Columns) {
- // Normal SQL statement to retrieve results
- for (colp = Columns; colp; colp = colp->GetNext())
- if (!colp->IsSpecial()) {
- if (!first)
- oom |= Query->Append(", ");
- else
- first = false;
-
- // Column name can be encoded in UTF-8
- Decode(colp->GetName(), buf, sizeof(buf));
-
- if (Quote) {
- // Put column name between identifier quotes in case in contains blanks
- oom |= Query->Append(Quote);
- oom |= Query->Append(buf);
- oom |= Query->Append(Quote);
- } else
- oom |= Query->Append(buf);
-
- ((PJDBCCOL)colp)->Rank = ++Ncol;
- } // endif colp
-
- } else
- // !Columns can occur for queries such that sql count(*) from...
- // for which we will count the rows from sql * from...
- oom |= Query->Append('*');
-
- } else
- // SQL statement used to retrieve the size of the result
- oom |= Query->Append("count(*)");
-
- oom |= Query->Append(" FROM ");
-
- if (Catalog && *Catalog)
- catp = Catalog;
-
- //if (tablep->GetSchema())
- // schmp = (char*)tablep->GetSchema();
- //else
- if (Schema && *Schema)
- schmp = Schema;
-
- if (catp) {
- oom |= Query->Append(catp);
-
- if (schmp) {
- oom |= Query->Append('.');
- oom |= Query->Append(schmp);
- } // endif schmp
-
- oom |= Query->Append('.');
- } else if (schmp) {
- oom |= Query->Append(schmp);
- oom |= Query->Append('.');
- } // endif schmp
-
- // Table name can be encoded in UTF-8
- Decode(TableName, buf, sizeof(buf));
-
- if (Quote) {
- // Put table name between identifier quotes in case in contains blanks
- oom |= Query->Append(Quote);
- oom |= Query->Append(buf);
- oom |= Query->Append(Quote);
- } else
- oom |= Query->Append(buf);
-
- len = Query->GetLength();
-
- if (To_CondFil) {
- if (Mode == MODE_READ) {
- oom |= Query->Append(" WHERE ");
- oom |= Query->Append(To_CondFil->Body);
- len = Query->GetLength() + 1;
- } else
- len += (strlen(To_CondFil->Body) + 256);
-
- } else
- len += ((Mode == MODE_READX) ? 256 : 1);
-
- if (oom || Query->Resize(len)) {
- strcpy(g->Message, "MakeSQL: Out of memory");
- return true;
- } // endif oom
-
- if (trace)
- htrc("Query=%s\n", Query->GetStr());
-
- return false;
-} // end of MakeSQL
-
/***********************************************************************/
/* MakeInsert: make the Insert statement used with JDBC connection. */
/***********************************************************************/
@@ -601,7 +421,7 @@ bool TDBJDBC::MakeInsert(PGLOBAL g)
// Column name can be encoded in UTF-8
Decode(colp->GetName(), buf, sizeof(buf));
len += (strlen(buf) + 6); // comma + quotes + valist
- ((PJDBCCOL)colp)->Rank = ++Nparm;
+ ((PEXTCOL)colp)->SetRank(++Nparm);
} // endif colp
// Below 32 is enough to contain the fixed part of the query
@@ -711,76 +531,6 @@ bool TDBJDBC::SetParameters(PGLOBAL g)
} // end of SetParameters
/***********************************************************************/
-/* MakeCommand: make the Update or Delete statement to send to the */
-/* MySQL server. Limited to remote values and filtering. */
-/***********************************************************************/
-bool TDBJDBC::MakeCommand(PGLOBAL g)
-{
- char *p, *stmt, name[68], *body = NULL, *qc = Jcp->GetQuoteChar();
- char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1);
- bool qtd = Quoted > 0;
- int i = 0, k = 0;
-
- // Make a lower case copy of the originale query and change
- // back ticks to the data source identifier quoting character
- do {
- qrystr[i] = (Qrystr[i] == '`') ? *qc : tolower(Qrystr[i]);
- } while (Qrystr[i++]);
-
- if (To_CondFil && (p = strstr(qrystr, " where "))) {
- p[7] = 0; // Remove where clause
- Qrystr[(p - qrystr) + 7] = 0;
- body = To_CondFil->Body;
- stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr)
- + strlen(body) + 64);
- } else
- stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64);
-
- // Check whether the table name is equal to a keyword
- // If so, it must be quoted in the original query
- strlwr(strcat(strcat(strcpy(name, " "), Name), " "));
-
- if (strstr(" update delete low_priority ignore quick from ", name)) {
- strlwr(strcat(strcat(strcpy(name, qc), Name), qc));
- k += 2;
- } else
- strlwr(strcpy(name, Name)); // Not a keyword
-
- if ((p = strstr(qrystr, name))) {
- for (i = 0; i < p - qrystr; i++)
- stmt[i] = (Qrystr[i] == '`') ? *qc : Qrystr[i];
-
- stmt[i] = 0;
- k += i + (int)strlen(Name);
-
- if (qtd && *(p-1) == ' ')
- strcat(strcat(strcat(stmt, qc), TableName), qc);
- else
- strcat(stmt, TableName);
-
- i = (int)strlen(stmt);
-
- do {
- stmt[i++] = (Qrystr[k] == '`') ? *qc : Qrystr[k];
- } while (Qrystr[k++]);
-
- if (body)
- strcat(stmt, body);
-
- } else {
- sprintf(g->Message, "Cannot use this %s command",
- (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
- return NULL;
- } // endif p
-
- if (trace)
- htrc("Command=%s\n", stmt);
-
- Query = new(g)STRING(g, 0, stmt);
- return (!Query->GetSize());
-} // end of MakeCommand
-
-/***********************************************************************/
/* ResetSize: call by TDBMUL when calculating size estimate. */
/***********************************************************************/
void TDBJDBC::ResetSize(void)
@@ -834,33 +584,6 @@ int TDBJDBC::Cardinality(PGLOBAL g)
} // end of Cardinality
/***********************************************************************/
-/* JDBC GetMaxSize: returns table size estimate in number of lines. */
-/***********************************************************************/
-int TDBJDBC::GetMaxSize(PGLOBAL g)
-{
- if (MaxSize < 0) {
- if (Mode == MODE_DELETE)
- // Return 0 in mode DELETE in case of delete all.
- MaxSize = 0;
- else if (!Cardinality(NULL))
- MaxSize = 10; // To make MySQL happy
- else if ((MaxSize = Cardinality(g)) < 0)
- MaxSize = 12; // So we can see an error occured
-
- } // endif MaxSize
-
- return MaxSize;
-} // end of GetMaxSize
-
-/***********************************************************************/
-/* Return max size value. */
-/***********************************************************************/
-int TDBJDBC::GetProgMax(PGLOBAL g)
-{
- return GetMaxSize(g);
-} // end of GetProgMax
-
-/***********************************************************************/
/* JDBC Access Method opening routine. */
/* New method now that this routine is called recursively (last table */
/* first in reverse order): index blocks are immediately linked to */
@@ -997,6 +720,7 @@ bool TDBJDBC::OpenDB(PGLOBAL g)
return false;
} // end of OpenDB
+#if 0
/***********************************************************************/
/* GetRecpos: return the position of last read record. */
/***********************************************************************/
@@ -1004,6 +728,7 @@ int TDBJDBC::GetRecpos(void)
{
return Fpos;
} // end of GetRecpos
+#endif // 0
/***********************************************************************/
/* SetRecpos: set the position of next read record. */
@@ -1105,8 +830,7 @@ int TDBJDBC::ReadDB(PGLOBAL g)
int rc;
if (trace > 1)
- htrc("JDBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
- GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
+ htrc("JDBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
if (!Query && MakeCommand(g))
@@ -1125,12 +849,6 @@ int TDBJDBC::ReadDB(PGLOBAL g)
} // endif Mode
- if (To_Kindex) {
- // Direct access of JDBC tables is not implemented
- strcpy(g->Message, "No JDBC direct access");
- return RC_FX;
- } // endif To_Kindex
-
/*********************************************************************/
/* Now start the reading process. */
/* Here is the place to fetch the line(s). */
@@ -1302,70 +1020,26 @@ void TDBJDBC::CloseDB(PGLOBAL g)
/* JDBCCOL public constructor. */
/***********************************************************************/
JDBCCOL::JDBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
- : COLBLK(cdp, tdbp, i)
+ : EXTCOL(cdp, tdbp, cprec, i, am)
{
- if (cprec) {
- Next = cprec->GetNext();
- cprec->SetNext(this);
- } else {
- Next = tdbp->GetColumns();
- tdbp->SetColumns(this);
- } // endif cprec
-
- // Set additional JDBC access method information for column.
- Crp = NULL;
- //Long = cdp->GetLong();
- Long = Precision;
- //strcpy(F_Date, cdp->F_Date);
- To_Val = NULL;
-//Slen = 0;
-//StrLen = &Slen;
-//Sqlbuf = NULL;
- Bufp = NULL;
- Blkp = NULL;
- Rank = 0; // Not known yet
-
- if (trace)
- htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
-
} // end of JDBCCOL constructor
/***********************************************************************/
/* JDBCCOL private constructor. */
/***********************************************************************/
-JDBCCOL::JDBCCOL(void) : COLBLK()
+JDBCCOL::JDBCCOL(void) : EXTCOL()
{
- Crp = NULL;
- Buf_Type = TYPE_INT; // This is a count(*) column
- // Set additional Dos access method information for column.
- Long = sizeof(int);
- To_Val = NULL;
-//Slen = 0;
-//StrLen = &Slen;
-//Sqlbuf = NULL;
- Bufp = NULL;
- Blkp = NULL;
- Rank = 1;
} // end of JDBCCOL constructor
/***********************************************************************/
/* JDBCCOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
-JDBCCOL::JDBCCOL(JDBCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
+JDBCCOL::JDBCCOL(JDBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp)
{
- Crp = col1->Crp;
- Long = col1->Long;
- //strcpy(F_Date, col1->F_Date);
- To_Val = col1->To_Val;
-//Slen = col1->Slen;
-//StrLen = col1->StrLen;
-//Sqlbuf = col1->Sqlbuf;
- Bufp = col1->Bufp;
- Blkp = col1->Blkp;
- Rank = col1->Rank;
} // end of JDBCCOL copy constructor
+#if 0
/***********************************************************************/
/* SetBuffer: prepare a column block for write operation. */
/***********************************************************************/
@@ -1411,6 +1085,7 @@ bool JDBCCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
Status = (ok) ? BUF_EMPTY : BUF_NO;
return false;
} // end of SetBuffer
+#endif // 0
/***********************************************************************/
/* ReadColumn: when SQLFetch is used there is nothing to do as the */
@@ -1456,72 +1131,8 @@ void JDBCCOL::ReadColumn(PGLOBAL g)
} // end of ReadColumn
-#if 0
/***********************************************************************/
-/* AllocateBuffers: allocate the extended buffer for SQLExtendedFetch */
-/* or Fetch. Note: we use Long+1 here because JDBC must have space */
-/* for the ending null character. */
-/***********************************************************************/
-void JDBCCOL::AllocateBuffers(PGLOBAL g, int rows)
-{
- if (Buf_Type == TYPE_DATE)
- Sqlbuf = (TIMESTAMP_STRUCT*)PlugSubAlloc(g, NULL,
- sizeof(TIMESTAMP_STRUCT));
-
- if (!rows)
- return;
-
- if (Buf_Type == TYPE_DATE)
- Bufp = PlugSubAlloc(g, NULL, rows * sizeof(TIMESTAMP_STRUCT));
- else {
- Blkp = AllocValBlock(g, NULL, Buf_Type, rows, GetBuflen(),
- GetScale(), true, false, false);
- Bufp = Blkp->GetValPointer();
- } // endelse
-
- if (rows > 1)
- StrLen = (SQLLEN *)PlugSubAlloc(g, NULL, rows * sizeof(SQLLEN));
-
-} // end of AllocateBuffers
-
-/***********************************************************************/
-/* Returns the buffer to use for Fetch or Extended Fetch. */
-/***********************************************************************/
-void *JDBCCOL::GetBuffer(DWORD rows)
-{
- if (rows && To_Tdb) {
- assert(rows == (DWORD)((TDBJDBC*)To_Tdb)->Rows);
- return Bufp;
- } else
- return (Buf_Type == TYPE_DATE) ? Sqlbuf : Value->GetTo_Val();
-
-} // end of GetBuffer
-
-/***********************************************************************/
-/* Returns the buffer length to use for Fetch or Extended Fetch. */
-/***********************************************************************/
-SWORD JDBCCOL::GetBuflen(void)
-{
- SWORD flen;
-
- switch (Buf_Type) {
- case TYPE_DATE:
- flen = (SWORD)sizeof(TIMESTAMP_STRUCT);
- break;
- case TYPE_STRING:
- case TYPE_DECIM:
- flen = (SWORD)Value->GetClen() + 1;
- break;
- default:
- flen = (SWORD)Value->GetClen();
- } // endswitch Buf_Type
-
- return flen;
-} // end of GetBuflen
-#endif // 0
-
-/***********************************************************************/
-/* WriteColumn: make sure the bind buffer is updated. */
+/* WriteColumn: Convert if necessary. */
/***********************************************************************/
void JDBCCOL::WriteColumn(PGLOBAL g)
{
@@ -1531,30 +1142,6 @@ void JDBCCOL::WriteColumn(PGLOBAL g)
if (Value != To_Val)
Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value
-#if 0
- if (Buf_Type == TYPE_DATE) {
- struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm);
-
- Sqlbuf->second = dbtime->tm_sec;
- Sqlbuf->minute = dbtime->tm_min;
- Sqlbuf->hour = dbtime->tm_hour;
- Sqlbuf->day = dbtime->tm_mday;
- Sqlbuf->month = dbtime->tm_mon + 1;
- Sqlbuf->year = dbtime->tm_year + 1900;
- Sqlbuf->fraction = 0;
- } else if (Buf_Type == TYPE_DECIM) {
- // Some data sources require local decimal separator
- char *p, sep = ((PTDBJDBC)To_Tdb)->Sep;
-
- if (sep && (p = strchr(Value->GetCharValue(), '.')))
- *p = sep;
-
- } // endif Buf_Type
-
- if (Nullable)
- *StrLen = (Value->IsNull()) ? SQL_NULL_DATA :
- (IsTypeChar(Buf_Type)) ? SQL_NTS : 0;
-#endif // 0
} // end of WriteColumn
/* -------------------------- Class TDBXJDC -------------------------- */
@@ -1795,7 +1382,7 @@ TDBJTB::TDBJTB(PJDBCDEF tdp) : TDBJDRV(tdp)
{
Schema = tdp->Tabschema;
Tab = tdp->Tabname;
- Tabtype = tdp->Tabtype;
+ Tabtype = tdp->Tabtyp;
Ops.Driver = tdp->Driver;
Ops.Url = tdp->Url;
Ops.User = tdp->Username;
diff --git a/storage/connect/tabjdbc.h b/storage/connect/tabjdbc.h
index fee8223abaf..46d2073e923 100644
--- a/storage/connect/tabjdbc.h
+++ b/storage/connect/tabjdbc.h
@@ -21,7 +21,7 @@ typedef class JSRCCOL *PJSRCCOL;
/***********************************************************************/
/* JDBC table. */
/***********************************************************************/
-class DllExport JDBCDEF : public TABDEF { /* Logical table description */
+class DllExport JDBCDEF : public EXTDEF { /* Logical table description */
friend class TDBJDBC;
friend class TDBXJDC;
friend class TDBJDRV;
@@ -33,17 +33,8 @@ public:
// Implementation
virtual const char *GetType(void) { return "JDBC"; }
- PSZ GetTabname(void) { return Tabname; }
- PSZ GetTabschema(void) { return Tabschema; }
- PSZ GetTabcat(void) { return Tabcat; }
- PSZ GetSrcdef(void) { return Srcdef; }
- char GetSep(void) { return (Sep) ? *Sep : 0; }
- int GetQuoted(void) { return Quoted; }
-//int GetCatver(void) { return Catver; }
- int GetOptions(void) { return Options; }
// Methods
- virtual int Indexable(void) { return 2; }
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual PTDB GetTable(PGLOBAL g, MODE m);
int ParseURL(PGLOBAL g, char *url, bool b = true);
@@ -53,28 +44,7 @@ protected:
// Members
PSZ Driver; /* JDBC driver */
PSZ Url; /* JDBC driver URL */
- PSZ Tabname; /* External table name */
PSZ Wrapname; /* Java wrapper name */
- PSZ Tabschema; /* External table schema */
- PSZ Username; /* User connect name */
- PSZ Password; /* Password connect info */
-//PSZ Prop; /* Connection Properties */
- PSZ Tabcat; /* External table catalog */
- PSZ Tabtype; /* External table type */
- PSZ Colpat; /* Catalog column pattern */
- PSZ Srcdef; /* The source table SQL definition */
- PSZ Qchar; /* Identifier quoting character */
- PSZ Qrystr; /* The original query */
- PSZ Sep; /* Decimal separator */
- int Options; /* Open connection options */
-//int Cto; /* Open connection timeout */
-//int Qto; /* Query (command) timeout */
- int Quoted; /* Identifier quoting level */
- int Maxerr; /* Maxerr for an Exec table */
- int Maxres; /* Maxres for a catalog table */
- int Memory; /* Put result set in memory */
- bool Scrollable; /* Use scrollable cursor */
- bool Xsrc; /* Execution type */
}; // end of JDBCDEF
#if !defined(NJDBC)
@@ -84,34 +54,34 @@ protected:
/* This is the JDBC Access Method class declaration for files from */
/* other DB drivers to be accessed via JDBC. */
/***********************************************************************/
-class TDBJDBC : public TDBASE {
+class TDBJDBC : public TDBEXT {
friend class JDBCCOL;
friend class JDBConn;
public:
// Constructor
TDBJDBC(PJDBCDEF tdp = NULL);
- TDBJDBC(PTDBJDBC tdbp);
+ TDBJDBC(PTDBJDBC tdbp);
// Implementation
- virtual AMT GetAmType(void) { return TYPE_AM_JDBC; }
- virtual PTDB Duplicate(PGLOBAL g) { return (PTDB)new(g)TDBJDBC(this); }
+ virtual AMT GetAmType(void) {return TYPE_AM_JDBC;}
+ virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBJDBC(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
- virtual int GetRecpos(void);
+ virtual PTDB Clone(PTABS t);
+//virtual int GetRecpos(void);
virtual bool SetRecpos(PGLOBAL g, int recpos);
//virtual PSZ GetFile(PGLOBAL g);
//virtual void SetFile(PGLOBAL g, PSZ fn);
virtual void ResetSize(void);
- //virtual int GetAffectedRows(void) {return AftRows;}
+//virtual int GetAffectedRows(void) {return AftRows;}
virtual PSZ GetServer(void) { return "JDBC"; }
virtual int Indexable(void) { return 2; }
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
virtual int Cardinality(PGLOBAL g);
- virtual int GetMaxSize(PGLOBAL g);
- virtual int GetProgMax(PGLOBAL g);
+//virtual int GetMaxSize(PGLOBAL g);
+//virtual int GetProgMax(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual int WriteDB(PGLOBAL g);
@@ -121,97 +91,50 @@ public:
protected:
// Internal functions
- int Decode(char *utf, char *buf, size_t n);
- bool MakeSQL(PGLOBAL g, bool cnt);
+//int Decode(char *utf, char *buf, size_t n);
+//bool MakeSQL(PGLOBAL g, bool cnt);
bool MakeInsert(PGLOBAL g);
- bool MakeCommand(PGLOBAL g);
- //bool MakeFilter(PGLOBAL g, bool c);
+//virtual bool MakeCommand(PGLOBAL g);
+//bool MakeFilter(PGLOBAL g, bool c);
bool SetParameters(PGLOBAL g);
- //char *MakeUpdate(PGLOBAL g);
- //char *MakeDelete(PGLOBAL g);
+//char *MakeUpdate(PGLOBAL g);
+//char *MakeDelete(PGLOBAL g);
// Members
JDBConn *Jcp; // Points to a JDBC connection class
JDBCCOL *Cnp; // Points to count(*) column
JDBCPARM Ops; // Additional parameters
- PSTRG Query; // Constructed SQL query
char *WrapName; // Points to Java wrapper name
- char *TableName; // Points to JDBC table name
- char *Schema; // Points to JDBC table Schema
- char *User; // User connect info
- char *Pwd; // Password connect info
- char *Catalog; // Points to JDBC table Catalog
- char *Srcdef; // The source table SQL definition
- char *Count; // Points to count(*) SQL statement
-//char *Where; // Points to local where clause
- char *Quote; // The identifier quoting character
- char *MulConn; // Used for multiple JDBC tables
- char *DBQ; // The address part of Connect string
- char *Qrystr; // The original query
- char Sep; // The decimal separator
- int Options; // Connect options
-//int Cto; // Connect timeout
-//int Qto; // Query timeout
- int Quoted; // The identifier quoting level
- int Fpos; // Position of last read record
- int Curpos; // Cursor position of last fetch
- int AftRows; // The number of affected rows
- int Rows; // Rowset size
- int CurNum; // Current buffer line number
- int Rbuf; // Number of lines read in buffer
- int BufSize; // Size of connect string buffer
- int Ncol; // The column number
- int Nparm; // The number of statement parameters
- int Memory; // 0: No 1: Alloc 2: Put 3: Get
-//bool Scrollable; // Use scrollable cursor --> in Ops
- bool Placed; // True for position reading
+//int Ncol; // The column number
bool Prepared; // True when using prepared statement
bool Werr; // Write error
bool Rerr; // Rewind error
- PQRYRES Qrp; // Points to storage result
}; // end of class TDBJDBC
/***********************************************************************/
/* Class JDBCCOL: JDBC access method column descriptor. */
/* This A.M. is used for JDBC tables. */
/***********************************************************************/
-class JDBCCOL : public COLBLK {
+class JDBCCOL : public EXTCOL {
friend class TDBJDBC;
public:
// Constructors
JDBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "JDBC");
- JDBCCOL(JDBCCOL *colp, PTDB tdbp); // Constructor used in copy process
+ JDBCCOL(JDBCCOL *colp, PTDB tdbp); // Constructor used in copy process
// Implementation
- virtual int GetAmType(void) { return TYPE_AM_JDBC; }
-//SQLLEN *GetStrLen(void) { return StrLen; }
- int GetRank(void) { return Rank; }
-//PVBLK GetBlkp(void) {return Blkp;}
- void SetCrp(PCOLRES crp) { Crp = crp; }
+ virtual int GetAmType(void) { return TYPE_AM_JDBC; }
// Methods
- virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
- virtual void ReadColumn(PGLOBAL g);
- virtual void WriteColumn(PGLOBAL g);
-//void AllocateBuffers(PGLOBAL g, int rows);
-//void *GetBuffer(DWORD rows);
-//SWORD GetBuflen(void);
- // void Print(PGLOBAL g, FILE *, uint);
+//virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+ virtual void ReadColumn(PGLOBAL g);
+ virtual void WriteColumn(PGLOBAL g);
protected:
- // Constructor used by GetMaxSize
- JDBCCOL(void);
+ // Constructor for count(*) column
+ JDBCCOL(void);
// Members
- //TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's
- PCOLRES Crp; // To storage result
- void *Bufp; // To extended buffer
- PVBLK Blkp; // To Value Block
- //char F_Date[12]; // Internal Date format
- PVAL To_Val; // To value used for Insert
-//SQLLEN *StrLen; // As returned by JDBC
-//SQLLEN Slen; // Used with Fetch
- int Rank; // Rank (position) number in the query
}; // end of class JDBCCOL
/***********************************************************************/
@@ -268,7 +191,7 @@ public:
JSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "JDBC");
// Implementation
- //virtual int GetAmType(void) {return TYPE_AM_JDBC;}
+ virtual int GetAmType(void) {return TYPE_AM_JDBC;}
// Methods
virtual void ReadColumn(PGLOBAL g);
diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp
index 1b9ce8b64c9..1e11d454cfc 100644
--- a/storage/connect/tabjson.cpp
+++ b/storage/connect/tabjson.cpp
@@ -129,7 +129,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info)
if (tdp->Pretty == 2) {
if (tdp->Zipped) {
#if defined(ZIP_SUPPORT)
- tjsp = new(g) TDBJSON(tdp, new(g) ZIPFAM(tdp));
+ tjsp = new(g) TDBJSON(tdp, new(g) UNZFAM(tdp));
#else // !ZIP_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
return NULL;
@@ -151,7 +151,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info)
if (tdp->Zipped) {
#if defined(ZIP_SUPPORT)
- tjnp = new(g)TDBJSN(tdp, new(g)ZIPFAM(tdp));
+ tjnp = new(g)TDBJSN(tdp, new(g)UNZFAM(tdp));
#else // !ZIP_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
return NULL;
@@ -441,7 +441,14 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
if (Zipped) {
#if defined(ZIP_SUPPORT)
- txfp = new(g) ZIPFAM(this);
+ if (m == MODE_READ || m == MODE_UPDATE) {
+ txfp = new(g) UNZFAM(this);
+ } else if (m == MODE_INSERT) {
+ txfp = new(g) ZIPFAM(this);
+ } else {
+ strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ return NULL;
+ } // endif's m
#else // !ZIP_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
return NULL;
@@ -479,7 +486,15 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m)
} else {
if (Zipped) {
#if defined(ZIP_SUPPORT)
- txfp = new(g)ZIPFAM(this);
+ if (m == MODE_READ || m == MODE_UPDATE) {
+ txfp = new(g) UNZFAM(this);
+ } else if (m == MODE_INSERT) {
+ strcpy(g->Message, "INSERT supported only for zipped JSON when pretty=0");
+ return NULL;
+ } else {
+ strcpy(g->Message, "UPDATE/DELETE not supported for ZIP");
+ return NULL;
+ } // endif's m
#else // !ZIP_SUPPORT
sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP");
return NULL;
@@ -559,7 +574,7 @@ TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp)
} // end of TDBJSN copy constructor
// Used for update
-PTDB TDBJSN::CopyOne(PTABS t)
+PTDB TDBJSN::Clone(PTABS t)
{
G = NULL;
PTDB tp;
@@ -574,7 +589,7 @@ PTDB TDBJSN::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate JSN column description block. */
@@ -1563,7 +1578,7 @@ TDBJSON::TDBJSON(PJTDB tdbp) : TDBJSN(tdbp)
} // end of TDBJSON copy constructor
// Used for update
-PTDB TDBJSON::CopyOne(PTABS t)
+PTDB TDBJSON::Clone(PTABS t)
{
PTDB tp;
PJCOL cp1, cp2;
@@ -1577,7 +1592,7 @@ PTDB TDBJSON::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Make the document tree from the object path. */
diff --git a/storage/connect/tabjson.h b/storage/connect/tabjson.h
index c9d30d48f2a..924ce387900 100644
--- a/storage/connect/tabjson.h
+++ b/storage/connect/tabjson.h
@@ -82,7 +82,7 @@ public:
void SetG(PGLOBAL g) {G = g;}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
virtual PCOL InsertSpecialColumn(PCOL colp);
virtual int RowNumber(PGLOBAL g, bool b = FALSE)
@@ -188,7 +188,7 @@ class TDBJSON : public TDBJSN {
PJAR GetDoc(void) {return Doc;}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
// Database routines
virtual int Cardinality(PGLOBAL g);
diff --git a/storage/connect/table.cpp b/storage/connect/table.cpp
index c21bb1660ea..916449be6c6 100644
--- a/storage/connect/table.cpp
+++ b/storage/connect/table.cpp
@@ -1,7 +1,7 @@
/************** Table C++ Functions Source Code File (.CPP) ************/
-/* Name: TABLE.CPP Version 2.7 */
+/* Name: TABLE.CPP Version 2.8 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 1999-2016 */
+/* (C) Copyright to the author Olivier BERTRAND 1999-2017 */
/* */
/* This file contains the TBX, TDB and OPJOIN classes functions. */
/***********************************************************************/
@@ -10,6 +10,7 @@
/* Include relevant MariaDB header file. */
/***********************************************************************/
#include "my_global.h"
+#include "sql_string.h"
/***********************************************************************/
/* Include required application header files */
@@ -40,8 +41,9 @@ void AddPointer(PTABS, void *);
/* TDB public constructors. */
/***********************************************************************/
TDB::TDB(PTABDEF tdp) : Tdb_No(++Tnum)
- {
- Use = USE_NO;
+{
+ To_Def = tdp;
+ Use = USE_NO;
To_Orig = NULL;
To_Filter = NULL;
To_CondFil = NULL;
@@ -49,14 +51,20 @@ TDB::TDB(PTABDEF tdp) : Tdb_No(++Tnum)
Name = (tdp) ? tdp->GetName() : NULL;
To_Table = NULL;
Columns = NULL;
- Degree = (tdp) ? tdp->GetDegree() : 0;
+ To_SetCols = NULL;
+ Degree = (tdp) ? tdp->GetDegree() : 0;
Mode = MODE_ANY;
Cardinal = -1;
- } // end of TDB standard constructor
+ MaxSize = -1;
+ Read_Only = (tdp) ? tdp->IsReadOnly() : false;
+ m_data_charset = (tdp) ? tdp->data_charset() : NULL;
+ csname = (tdp) ? tdp->csname : NULL;
+} // end of TDB standard constructor
TDB::TDB(PTDB tdbp) : Tdb_No(++Tnum)
- {
- Use = tdbp->Use;
+{
+ To_Def = tdbp->To_Def;
+ Use = tdbp->Use;
To_Orig = tdbp;
To_Filter = NULL;
To_CondFil = NULL;
@@ -64,12 +72,192 @@ TDB::TDB(PTDB tdbp) : Tdb_No(++Tnum)
Name = tdbp->Name;
To_Table = tdbp->To_Table;
Columns = NULL;
- Degree = tdbp->Degree;
+ To_SetCols = tdbp->To_SetCols; // ???
+ Degree = tdbp->Degree;
Mode = tdbp->Mode;
Cardinal = tdbp->Cardinal;
- } // end of TDB copy constructor
+ MaxSize = tdbp->MaxSize;
+ Read_Only = tdbp->IsReadOnly();
+ m_data_charset = tdbp->data_charset();
+ csname = tdbp->csname;
+} // end of TDB copy constructor
// Methods
+/***********************************************************************/
+/* Return the pointer on the charset of this table. */
+/***********************************************************************/
+CHARSET_INFO *TDB::data_charset(void)
+{
+ // If no DATA_CHARSET is specified, we assume that character
+ // set of the remote data is the same with CHARACTER SET
+ // definition of the SQL column.
+ return m_data_charset ? m_data_charset : &my_charset_bin;
+} // end of data_charset
+
+/***********************************************************************/
+/* Return the datapath of the DB this table belongs to. */
+/***********************************************************************/
+PSZ TDB::GetPath(void)
+{
+ return To_Def->GetPath();
+} // end of GetPath
+
+/***********************************************************************/
+/* Return true if name is a special column of this table. */
+/***********************************************************************/
+bool TDB::IsSpecial(PSZ name)
+{
+ for (PCOLDEF cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext())
+ if (!stricmp(cdp->GetName(), name) && (cdp->Flags & U_SPECIAL))
+ return true; // Special column to ignore while inserting
+
+ return false; // Not found or not special or not inserting
+} // end of IsSpecial
+
+/***********************************************************************/
+/* Initialize TDB based column description block construction. */
+/* name is used to call columns by name. */
+/* num is used by TBL to construct columns by index number. */
+/* Note: name=Null and num=0 for constructing all columns (select *) */
+/***********************************************************************/
+PCOL TDB::ColDB(PGLOBAL g, PSZ name, int num)
+{
+ int i;
+ PCOLDEF cdp;
+ PCOL cp, colp = NULL, cprec = NULL;
+
+ if (trace)
+ htrc("ColDB: am=%d colname=%s tabname=%s num=%d\n",
+ GetAmType(), SVP(name), Name, num);
+
+ for (cdp = To_Def->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
+ if ((!name && !num) ||
+ (name && !stricmp(cdp->GetName(), name)) || num == i) {
+ /*****************************************************************/
+ /* Check for existence of desired column. */
+ /* Also find where to insert the new block. */
+ /*****************************************************************/
+ for (cp = Columns; cp; cp = cp->GetNext())
+ if ((num && cp->GetIndex() == i) ||
+ (name && !stricmp(cp->GetName(), name)))
+ break; // Found
+ else if (cp->GetIndex() < i)
+ cprec = cp;
+
+ if (trace)
+ htrc("cdp(%d).Name=%s cp=%p\n", i, cdp->GetName(), cp);
+
+ /*****************************************************************/
+ /* Now take care of Column Description Block. */
+ /*****************************************************************/
+ if (cp)
+ colp = cp;
+ else if (!(cdp->Flags & U_SPECIAL))
+ colp = MakeCol(g, cdp, cprec, i);
+ else if (Mode != MODE_INSERT)
+ colp = InsertSpcBlk(g, cdp);
+
+ if (trace)
+ htrc("colp=%p\n", colp);
+
+ if (name || num)
+ break;
+ else if (colp && !colp->IsSpecial())
+ cprec = colp;
+
+ } // endif Name
+
+ return (colp);
+} // end of ColDB
+
+/***********************************************************************/
+/* InsertSpecialColumn: Put a special column ahead of the column list.*/
+/***********************************************************************/
+PCOL TDB::InsertSpecialColumn(PCOL colp)
+{
+ if (!colp->IsSpecial())
+ return NULL;
+
+ colp->SetNext(Columns);
+ Columns = colp;
+ return colp;
+} // end of InsertSpecialColumn
+
+/***********************************************************************/
+/* Make a special COLBLK to insert in a table. */
+/***********************************************************************/
+PCOL TDB::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp)
+{
+ //char *name = cdp->GetName();
+ char *name = cdp->GetFmt();
+ PCOLUMN cp;
+ PCOL colp;
+
+ cp = new(g)COLUMN(cdp->GetName());
+
+ if (!To_Table) {
+ strcpy(g->Message, "Cannot make special column: To_Table is NULL");
+ return NULL;
+ } else
+ cp->SetTo_Table(To_Table);
+
+ if (!stricmp(name, "FILEID") || !stricmp(name, "FDISK") ||
+ !stricmp(name, "FPATH") || !stricmp(name, "FNAME") ||
+ !stricmp(name, "FTYPE") || !stricmp(name, "SERVID")) {
+ if (!To_Def || !(To_Def->GetPseudo() & 2)) {
+ sprintf(g->Message, MSG(BAD_SPEC_COLUMN));
+ return NULL;
+ } // endif Pseudo
+
+ if (!stricmp(name, "FILEID"))
+ colp = new(g)FIDBLK(cp, OP_XX);
+ else if (!stricmp(name, "FDISK"))
+ colp = new(g)FIDBLK(cp, OP_FDISK);
+ else if (!stricmp(name, "FPATH"))
+ colp = new(g)FIDBLK(cp, OP_FPATH);
+ else if (!stricmp(name, "FNAME"))
+ colp = new(g)FIDBLK(cp, OP_FNAME);
+ else if (!stricmp(name, "FTYPE"))
+ colp = new(g)FIDBLK(cp, OP_FTYPE);
+ else
+ colp = new(g)SIDBLK(cp);
+
+ } else if (!stricmp(name, "TABID")) {
+ colp = new(g)TIDBLK(cp);
+ } else if (!stricmp(name, "PARTID")) {
+ colp = new(g)PRTBLK(cp);
+ //} else if (!stricmp(name, "CONID")) {
+ // colp = new(g) CIDBLK(cp);
+ } else if (!stricmp(name, "ROWID")) {
+ colp = new(g)RIDBLK(cp, false);
+ } else if (!stricmp(name, "ROWNUM")) {
+ colp = new(g)RIDBLK(cp, true);
+ } else {
+ sprintf(g->Message, MSG(BAD_SPECIAL_COL), name);
+ return NULL;
+ } // endif's name
+
+ if (!(colp = InsertSpecialColumn(colp))) {
+ sprintf(g->Message, MSG(BAD_SPECIAL_COL), name);
+ return NULL;
+ } // endif Insert
+
+ return (colp);
+} // end of InsertSpcBlk
+
+/***********************************************************************/
+/* Marks DOS/MAP table columns used in internal joins. */
+/* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */
+/* points to the currently marked tdb. */
+/* Two questions here: exact meaning of U_J_INT ? */
+/* Why is the eventual reference to To_Key_Col not marked U_J_EXT ? */
+/***********************************************************************/
+void TDB::MarkDB(PGLOBAL, PTDB tdb2)
+{
+ if (trace)
+ htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2);
+
+} // end of MarkDB
/***********************************************************************/
/* RowNumber: returns the current row ordinal number. */
@@ -86,7 +274,7 @@ PTDB TDB::Copy(PTABS t)
//PGLOBAL g = t->G; // Is this really useful ???
for (tdb1 = this; tdb1; tdb1 = tdb1->Next) {
- tp = tdb1->CopyOne(t);
+ tp = tdb1->Clone(t);
if (!outp)
outp = tp;
@@ -100,6 +288,15 @@ PTDB TDB::Copy(PTABS t)
return outp;
} // end of Copy
+/***********************************************************************/
+/* SetRecpos: Replace the table at the specified position. */
+/***********************************************************************/
+bool TDB::SetRecpos(PGLOBAL g, int)
+{
+ strcpy(g->Message, MSG(SETRECPOS_NIY));
+ return true;
+} // end of SetRecpos
+
void TDB::Print(PGLOBAL g, FILE *f, uint n)
{
PCOL cp;
@@ -135,34 +332,34 @@ void TDB::Print(PGLOBAL, char *ps, uint)
/***********************************************************************/
TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp)
{
- To_Def = tdp;
+//To_Def = tdp;
To_Link = NULL;
To_Key_Col = NULL;
To_Kindex = NULL;
To_Xdp = NULL;
- To_SetCols = NULL;
+//To_SetCols = NULL;
Ftype = RECFM_NAF;
- MaxSize = -1;
+//MaxSize = -1;
Knum = 0;
- Read_Only = (tdp) ? tdp->IsReadOnly() : false;
- m_data_charset= (tdp) ? tdp->data_charset() : NULL;
- csname = (tdp) ? tdp->csname : NULL;
+//Read_Only = (tdp) ? tdp->IsReadOnly() : false;
+//m_data_charset= (tdp) ? tdp->data_charset() : NULL;
+//csname = (tdp) ? tdp->csname : NULL;
} // end of TDBASE constructor
TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp)
{
- To_Def = tdbp->To_Def;
+//To_Def = tdbp->To_Def;
To_Link = tdbp->To_Link;
To_Key_Col = tdbp->To_Key_Col;
To_Kindex = tdbp->To_Kindex;
To_Xdp = tdbp->To_Xdp;
- To_SetCols = tdbp->To_SetCols; // ???
+//To_SetCols = tdbp->To_SetCols; // ???
Ftype = tdbp->Ftype;
- MaxSize = tdbp->MaxSize;
+//MaxSize = tdbp->MaxSize;
Knum = tdbp->Knum;
- Read_Only = tdbp->Read_Only;
- m_data_charset= tdbp->m_data_charset;
- csname = tdbp->csname;
+//Read_Only = tdbp->Read_Only;
+//m_data_charset= tdbp->m_data_charset;
+//csname = tdbp->csname;
} // end of TDBASE copy constructor
/***********************************************************************/
@@ -173,6 +370,7 @@ PCATLG TDBASE::GetCat(void)
return (To_Def) ? To_Def->GetCat() : NULL;
} // end of GetCat
+#if 0
/***********************************************************************/
/* Return the pointer on the charset of this table. */
/***********************************************************************/
@@ -334,6 +532,7 @@ PCOL TDBASE::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp)
return (colp);
} // end of InsertSpcBlk
+#endif // 0
/***********************************************************************/
/* ResetTableOpt: Wrong for this table type. */
@@ -362,6 +561,7 @@ void TDBASE::ResetKindex(PGLOBAL g, PKXBASE kxp)
To_Kindex = kxp;
} // end of ResetKindex
+#if 0
/***********************************************************************/
/* SetRecpos: Replace the table at the specified position. */
/***********************************************************************/
@@ -370,6 +570,7 @@ bool TDBASE::SetRecpos(PGLOBAL g, int)
strcpy(g->Message, MSG(SETRECPOS_NIY));
return true;
} // end of SetRecpos
+#endif // 0
/***********************************************************************/
/* Methods */
@@ -379,6 +580,7 @@ void TDBASE::PrintAM(FILE *f, char *m)
fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode);
} // end of PrintAM
+#if 0
/***********************************************************************/
/* Marks DOS/MAP table columns used in internal joins. */
/* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */
@@ -392,6 +594,7 @@ void TDBASE::MarkDB(PGLOBAL, PTDB tdb2)
htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2);
} // end of MarkDB
+#endif // 0
/* ---------------------------TDBCAT class --------------------------- */
diff --git a/storage/connect/tabmac.cpp b/storage/connect/tabmac.cpp
index e6e2abb54e2..bbaba591540 100644
--- a/storage/connect/tabmac.cpp
+++ b/storage/connect/tabmac.cpp
@@ -12,7 +12,7 @@
#include "global.h"
#include "plgdbsem.h"
//#include "catalog.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "xtable.h"
#include "colblk.h"
#include "tabmac.h"
diff --git a/storage/connect/tabmac.h b/storage/connect/tabmac.h
index f9a66e82eaa..47565bb2541 100644
--- a/storage/connect/tabmac.h
+++ b/storage/connect/tabmac.h
@@ -52,7 +52,7 @@ class TDBMAC : public TDBASE {
//virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMAC(g, this);}
// Methods
-//virtual PTDB CopyOne(PTABS t);
+//virtual PTDB Clone(PTABS t);
virtual int GetRecpos(void) {return N;}
virtual int RowNumber(PGLOBAL g, bool b = false) {return N;}
diff --git a/storage/connect/tabmul.cpp b/storage/connect/tabmul.cpp
index ea287558b44..78adde81d12 100644
--- a/storage/connect/tabmul.cpp
+++ b/storage/connect/tabmul.cpp
@@ -1,11 +1,11 @@
/************* TabMul C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: TABMUL */
/* ------------- */
-/* Version 1.7 */
+/* Version 1.8 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to PlugDB Software Development 2003 - 2015 */
+/* (C) Copyright to PlugDB Software Development 2003 - 2017 */
/* Author: Olivier BERTRAND */
/* */
/* WHAT THIS PROGRAM DOES: */
@@ -73,7 +73,7 @@
/***********************************************************************/
/* TABMUL constructors. */
/***********************************************************************/
-TDBMUL::TDBMUL(PTDBASE tdbp) : TDBASE(tdbp->GetDef())
+TDBMUL::TDBMUL(PTDB tdbp) : TDBASE(tdbp->GetDef())
{
Tdbp = tdbp;
Filenames = NULL;
@@ -94,22 +94,22 @@ TDBMUL::TDBMUL(PTDBMUL tdbp) : TDBASE(tdbp)
} // end of TDBMUL copy constructor
// Method
-PTDB TDBMUL::CopyOne(PTABS t)
+PTDB TDBMUL::Clone(PTABS t)
{
PTDBMUL tp;
PGLOBAL g = t->G; // Is this really useful ???
tp = new(g) TDBMUL(this);
- tp->Tdbp = (PTDBASE)Tdbp->CopyOne(t);
+ tp->Tdbp = Tdbp->Clone(t);
tp->Columns = tp->Tdbp->GetColumns();
return tp;
- } // end of CopyOne
+ } // end of Clone
PTDB TDBMUL::Duplicate(PGLOBAL g)
{
PTDBMUL tmup = new(g) TDBMUL(this);
- tmup->Tdbp = (PTDBASE)Tdbp->Duplicate(g);
+ tmup->Tdbp = Tdbp->Duplicate(g);
return tmup;
} // end of Duplicate
@@ -658,7 +658,7 @@ TDBDIR::TDBDIR(PTDBDIR tdbp) : TDBASE(tdbp)
} // end of TDBDIR copy constructor
// Method
-PTDB TDBDIR::CopyOne(PTABS t)
+PTDB TDBDIR::Clone(PTABS t)
{
PTDB tp;
PGLOBAL g = t->G; // Is this really useful ???
@@ -666,7 +666,7 @@ PTDB TDBDIR::CopyOne(PTABS t)
tp = new(g) TDBDIR(this);
tp->SetColumns(Columns);
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Initialize/get the components of the search file pattern. */
@@ -974,7 +974,7 @@ TDBSDR::TDBSDR(PTDBSDR tdbp) : TDBDIR(tdbp)
} // end of TDBSDR copy constructor
// Method
-PTDB TDBSDR::CopyOne(PTABS t)
+PTDB TDBSDR::Clone(PTABS t)
{
PTDB tp;
PGLOBAL g = t->G; // Is this really useful ???
@@ -982,7 +982,7 @@ PTDB TDBSDR::CopyOne(PTABS t)
tp = new(g) TDBSDR(this);
tp->SetColumns(Columns);
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* SDR GetMaxSize: returns the number of retrieved files. */
@@ -1251,7 +1251,7 @@ TDBDHR::TDBDHR(PTDBDHR tdbp) : TDBASE(tdbp)
} // end of TDBDHR copy constructor
// Method
-PTDB TDBDHR::CopyOne(PTABS t)
+PTDB TDBDHR::Clone(PTABS t)
{
PTDB tp;
PGLOBAL g = t->G; // Is this really useful ???
@@ -1259,7 +1259,7 @@ PTDB TDBDHR::CopyOne(PTABS t)
tp = new(g) TDBDHR(this);
tp->Columns = Columns;
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate DHR column description block. */
diff --git a/storage/connect/tabmul.h b/storage/connect/tabmul.h
index 433cc3a2ee3..51fa7f9000a 100644
--- a/storage/connect/tabmul.h
+++ b/storage/connect/tabmul.h
@@ -1,7 +1,7 @@
/*************** Tabmul H Declares Source Code File (.H) ***************/
-/* Name: TABMUL.H Version 1.4 */
+/* Name: TABMUL.H Version 1.5 */
/* */
-/* (C) Copyright to PlugDB Software Development 2003-2012 */
+/* (C) Copyright to PlugDB Software Development 2003-2017 */
/* Author: Olivier BERTRAND */
/* */
/* This file contains the TDBMUL and TDBDIR classes declares. */
@@ -28,7 +28,7 @@ class DllExport TDBMUL : public TDBASE {
//friend class MULCOL;
public:
// Constructor
- TDBMUL(PTDBASE tdbp);
+ TDBMUL(PTDB tdbp);
TDBMUL(PTDBMUL tdbp);
// Implementation
@@ -37,7 +37,7 @@ class DllExport TDBMUL : public TDBASE {
// Methods
virtual void ResetDB(void);
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual bool IsSame(PTDB tp) {return tp == (PTDB)Tdbp;}
virtual PSZ GetFile(PGLOBAL g) {return Tdbp->GetFile(g);}
virtual int GetRecpos(void) {return 0;}
@@ -61,7 +61,7 @@ class DllExport TDBMUL : public TDBASE {
protected:
// Members
- TDBASE *Tdbp; // Points to a (file) table class
+ PTDB Tdbp; // Points to a (file) table class
char* *Filenames; // Points to file names
int Rows; // Total rows of already read files
int Mul; // Type of multiple file list
@@ -112,7 +112,7 @@ class TDBDIR : public TDBASE {
{return (PTDB)new(g) TDBDIR(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual int GetRecpos(void) {return iFile;}
// Database routines
@@ -134,7 +134,7 @@ class TDBDIR : public TDBASE {
int iFile; // Index of currently retrieved file
#if defined(__WIN__)
_finddata_t FileData; // Find data structure
- int Hsearch; // Search handle
+ intptr_t Hsearch; // Search handle
char Drive[_MAX_DRIVE]; // Drive name
#else // !__WIN__
struct stat Fileinfo; // File info structure
@@ -168,7 +168,7 @@ class TDBSDR : public TDBDIR {
{return (PTDB)new(g) TDBSDR(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
// Database routines
virtual int GetMaxSize(PGLOBAL g);
@@ -184,7 +184,7 @@ class TDBSDR : public TDBDIR {
struct _Sub_Dir *Next;
struct _Sub_Dir *Prev;
#if defined(__WIN__)
- int H; // Search handle
+ intptr_t H; // Search handle
#else // !__WIN__
DIR *D;
#endif // !__WIN__
diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp
index 98a476bf94f..1a715819fc8 100644
--- a/storage/connect/tabmysql.cpp
+++ b/storage/connect/tabmysql.cpp
@@ -1,11 +1,11 @@
/************* TabMySQL C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABMYSQL */
/* ------------- */
-/* Version 1.9 */
+/* Version 2.0 */
/* */
/* AUTHOR: */
/* ------- */
-/* Olivier BERTRAND 2007-2015 */
+/* Olivier BERTRAND 2007-2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -54,9 +54,10 @@
#include "global.h"
#include "plgdbsem.h"
#include "xtable.h"
+#include "tabext.h"
#include "tabcol.h"
#include "colblk.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "tabmysql.h"
#include "valblk.h"
#include "tabutil.h"
@@ -84,16 +85,16 @@ MYSQLDEF::MYSQLDEF(void)
{
Pseudo = 2; // SERVID is Ok but not ROWID
Hostname = NULL;
- Database = NULL;
- Tabname = NULL;
- Srcdef = NULL;
- Username = NULL;
- Password = NULL;
+//Tabschema = NULL;
+//Tabname = NULL;
+//Srcdef = NULL;
+//Username = NULL;
+//Password = NULL;
Portnumber = 0;
Isview = false;
Bind = false;
Delayed = false;
- Xsrc = false;
+//Xsrc = false;
Huge = false;
} // end of MYSQLDEF constructor
@@ -128,7 +129,7 @@ bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name)
// TODO: We need to examine which of these can really be NULL
Hostname = PlugDup(g, server->host);
- Database = PlugDup(g, server->db);
+ Tabschema = PlugDup(g, server->db);
Username = PlugDup(g, server->username);
Password = PlugDup(g, server->password);
Portnumber = (server->port) ? server->port : GetDefaultPort();
@@ -200,7 +201,7 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL;
if (trace)
- htrc("server: %s Tabname: %s", url, Tabname);
+ htrc("server: %s TableName: %s", url, Tabname);
Server = url;
return GetServerInfo(g, url);
@@ -253,10 +254,10 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
return true;
} // endif
- if ((Database = strchr(Hostname, '/'))) {
- *Database++ = 0;
+ if ((Tabschema = strchr(Hostname, '/'))) {
+ *Tabschema++ = 0;
- if ((Tabname = strchr(Database, '/'))) {
+ if ((Tabname = strchr(Tabschema, '/'))) {
*Tabname++ = 0;
// Make sure there's not an extra /
@@ -265,7 +266,7 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
return true;
} // endif /
- } // endif Tabname
+ } // endif TableName
} // endif database
@@ -283,8 +284,8 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b)
if (Hostname[0] == 0)
Hostname = (b) ? GetStringCatInfo(g, "Host", "localhost") : NULL;
- if (!Database || !*Database)
- Database = (b) ? GetStringCatInfo(g, "Database", "*") : NULL;
+ if (!Tabschema || !*Tabschema)
+ Tabschema = (b) ? GetStringCatInfo(g, "Database", "*") : NULL;
if (!Tabname || !*Tabname)
Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL;
@@ -320,7 +321,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
if (!url || !*url) {
// Not using the connection URL
Hostname = GetStringCatInfo(g, "Host", "localhost");
- Database = GetStringCatInfo(g, "Database", "*");
+ Tabschema = GetStringCatInfo(g, "Database", "*");
Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated
Tabname = GetStringCatInfo(g, "Tabname", Tabname);
Username = GetStringCatInfo(g, "User", "*");
@@ -334,7 +335,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
Delayed = !!GetIntCatInfo("Delayed", 0);
} else {
// MYSQL access from a PROXY table
- Database = GetStringCatInfo(g, "Database", Schema ? Schema : PlugDup(g, "*"));
+ Tabschema = GetStringCatInfo(g, "Database", Tabschema ? Tabschema : PlugDup(g, "*"));
Isview = GetBoolCatInfo("View", false);
// We must get other connection parms from the calling table
@@ -348,12 +349,12 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
Portnumber = GetIntCatInfo("Port", GetDefaultPort());
Server = Hostname;
} else {
- char *locdb = Database;
+ char *locdb = Tabschema;
if (ParseURL(g, url))
return true;
- Database = locdb;
+ Tabschema = locdb;
} // endif url
Tabname = Name;
@@ -362,7 +363,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) {
Read_Only = true;
Isview = true;
- } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Database,
+ } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Tabschema,
Tabname, Srcdef, Portnumber))
return true;
@@ -372,7 +373,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
// Specific for command executing tables
Xsrc = GetBoolCatInfo("Execsrc", false);
- Mxr = GetIntCatInfo("Maxerr", 0);
+ Maxerr = GetIntCatInfo("Maxerr", 0);
Huge = GetBoolCatInfo("Huge", false);
return false;
} // end of DefineAM
@@ -396,17 +397,17 @@ PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE)
/***********************************************************************/
/* Implementation of the TDBMYSQL class. */
/***********************************************************************/
-TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
+TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBEXT(tdp)
{
if (tdp) {
Host = tdp->Hostname;
- Database = tdp->Database;
- Tabname = tdp->Tabname;
- Srcdef = tdp->Srcdef;
- User = tdp->Username;
- Pwd = tdp->Password;
+// Schema = tdp->Tabschema;
+// TableName = tdp->Tabname;
+// Srcdef = tdp->Srcdef;
+// User = tdp->Username;
+// Pwd = tdp->Password;
Server = tdp->Server;
- Qrystr = tdp->Qrystr;
+// Qrystr = tdp->Qrystr;
Quoted = MY_MAX(0, tdp->Quoted);
Port = tdp->Portnumber;
Isview = tdp->Isview;
@@ -415,14 +416,14 @@ TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
Myc.m_Use = tdp->Huge;
} else {
Host = NULL;
- Database = NULL;
- Tabname = NULL;
- Srcdef = NULL;
- User = NULL;
- Pwd = NULL;
+// Schema = NULL;
+// TableName = NULL;
+// Srcdef = NULL;
+// User = NULL;
+// Pwd = NULL;
Server = NULL;
- Qrystr = NULL;
- Quoted = 0;
+// Qrystr = NULL;
+// Quoted = 0;
Port = 0;
Isview = false;
Prep = false;
@@ -430,39 +431,40 @@ TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp)
} // endif tdp
Bind = NULL;
- Query = NULL;
+//Query = NULL;
Fetched = false;
m_Rc = RC_FX;
- AftRows = 0;
+//AftRows = 0;
N = -1;
- Nparm = 0;
+//Nparm = 0;
} // end of TDBMYSQL constructor
-TDBMYSQL::TDBMYSQL(PTDBMY tdbp) : TDBASE(tdbp)
+TDBMYSQL::TDBMYSQL(PTDBMY tdbp) : TDBEXT(tdbp)
{
Host = tdbp->Host;
- Database = tdbp->Database;
- Tabname = tdbp->Tabname;
- Srcdef = tdbp->Srcdef;
- User = tdbp->User;
- Pwd = tdbp->Pwd;
- Qrystr = tdbp->Qrystr;
- Quoted = tdbp->Quoted;
+//Schema = tdbp->Schema;
+//TableName = tdbp->TableName;
+//Srcdef = tdbp->Srcdef;
+//User = tdbp->User;
+//Pwd = tdbp->Pwd;
+//Qrystr = tdbp->Qrystr;
+//Quoted = tdbp->Quoted;
+ Server = tdbp->Server;
Port = tdbp->Port;
Isview = tdbp->Isview;
Prep = tdbp->Prep;
Delayed = tdbp->Delayed;
Bind = NULL;
- Query = tdbp->Query;
+//Query = tdbp->Query;
Fetched = tdbp->Fetched;
m_Rc = tdbp->m_Rc;
- AftRows = tdbp->AftRows;
+//AftRows = tdbp->AftRows;
N = tdbp->N;
- Nparm = tdbp->Nparm;
+//Nparm = tdbp->Nparm;
} // end of TDBMYSQL copy constructor
-// Is this really useful ???
-PTDB TDBMYSQL::CopyOne(PTABS t)
+// Is this really useful ??? --> Yes for UPDATE
+PTDB TDBMYSQL::Clone(PTABS t)
{
PTDB tp;
PCOL cp1, cp2;
@@ -477,7 +479,7 @@ PTDB TDBMYSQL::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate MYSQL column description block. */
@@ -504,10 +506,18 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx)
if (Query)
return false; // already done
- if (Srcdef) {
- Query = new(g)STRING(g, 0, Srcdef);
- return false;
- } // endif Srcdef
+ if (Srcdef) {
+ if (strstr(Srcdef, "%s")) {
+ char *fil;
+
+ fil = (To_CondFil) ? To_CondFil->Body : PlugDup(g, "1=1");
+ Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil));
+ Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil));
+ } else
+ Query = new(g)STRING(g, 0, Srcdef);
+
+ return false;
+ } // endif Srcdef
// Allocate the string used to contain Query
Query = new(g) STRING(g, 1023, "SELECT ");
@@ -540,7 +550,7 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx)
oom |= Query->Append(" FROM ");
oom |= Query->Append(tk);
- oom |= Query->Append(Tabname);
+ oom |= Query->Append(TableName);
oom |= Query->Append(tk);
len = Query->GetLength();
@@ -608,7 +618,7 @@ bool TDBMYSQL::MakeInsert(PGLOBAL g)
} // endif colp
// Below 40 is enough to contain the fixed part of the query
- len += (strlen(Tabname) + 40);
+ len += (strlen(TableName) + 40);
Query = new(g) STRING(g, len);
if (Delayed)
@@ -617,7 +627,7 @@ bool TDBMYSQL::MakeInsert(PGLOBAL g)
oom = Query->Set("INSERT INTO ");
oom |= Query->Append(tk);
- oom |= Query->Append(Tabname);
+ oom |= Query->Append(TableName);
oom |= Query->Append("` (");
for (colp = Columns; colp; colp = colp->GetNext()) {
@@ -653,11 +663,11 @@ bool TDBMYSQL::MakeInsert(PGLOBAL g)
/* MakeCommand: make the Update or Delete statement to send to the */
/* MySQL server. Limited to remote values and filtering. */
/***********************************************************************/
-int TDBMYSQL::MakeCommand(PGLOBAL g)
+bool TDBMYSQL::MakeCommand(PGLOBAL g)
{
Query = new(g) STRING(g, strlen(Qrystr) + 64);
- if (Quoted > 0 || stricmp(Name, Tabname)) {
+ if (Quoted > 0 || stricmp(Name, TableName)) {
char *p, *qrystr, name[68];
bool qtd = Quoted > 0;
@@ -678,29 +688,29 @@ int TDBMYSQL::MakeCommand(PGLOBAL g)
if (qtd && *(p-1) == ' ') {
oom |= Query->Append('`');
- oom |= Query->Append(Tabname);
+ oom |= Query->Append(TableName);
oom |= Query->Append('`');
} else
- oom |= Query->Append(Tabname);
+ oom |= Query->Append(TableName);
oom |= Query->Append(Qrystr + (p - qrystr) + strlen(name));
if (oom) {
strcpy(g->Message, "MakeCommand: Out of memory");
- return RC_FX;
+ return true;
} else
strlwr(strcpy(qrystr, Query->GetStr()));
} else {
sprintf(g->Message, "Cannot use this %s command",
(Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
- return RC_FX;
+ return true;
} // endif p
} else
(void)Query->Set(Qrystr);
- return RC_OK;
+ return false;
} // end of MakeCommand
#if 0
@@ -727,7 +737,7 @@ int TDBMYSQL::MakeUpdate(PGLOBAL g)
} // endif sscanf
assert(!stricmp(cmd, "update"));
- strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), Tabname), qc);
+ strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), TableName), qc);
strcat(Query, end);
return RC_OK;
} // end of MakeUpdate
@@ -754,7 +764,7 @@ int TDBMYSQL::MakeDelete(PGLOBAL g)
} // endif sscanf
assert(!stricmp(cmd, "delete") && !stricmp(from, "from"));
- strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), Tabname), qc);
+ strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), TableName), qc);
if (*end)
strcat(Query, end);
@@ -776,15 +786,15 @@ int TDBMYSQL::Cardinality(PGLOBAL g)
char query[96];
MYSQLC myc;
- if (myc.Open(g, Host, Database, User, Pwd, Port, csname))
+ if (myc.Open(g, Host, Schema, User, Pwd, Port, csname))
return -1;
strcpy(query, "SELECT COUNT(*) FROM ");
if (Quoted > 0)
- strcat(strcat(strcat(query, "`"), Tabname), "`");
+ strcat(strcat(strcat(query, "`"), TableName), "`");
else
- strcat(query, Tabname);
+ strcat(query, TableName);
Cardinal = myc.GetTableSize(g, query);
myc.Close();
@@ -794,6 +804,7 @@ int TDBMYSQL::Cardinality(PGLOBAL g)
return Cardinal;
} // end of Cardinality
+#if 0
/***********************************************************************/
/* MYSQL GetMaxSize: returns the maximum number of rows in the table. */
/***********************************************************************/
@@ -812,6 +823,7 @@ int TDBMYSQL::GetMaxSize(PGLOBAL g)
return MaxSize;
} // end of GetMaxSize
+#endif // 0
/***********************************************************************/
/* This a fake routine as ROWID does not exist in MySQL. */
@@ -872,7 +884,7 @@ bool TDBMYSQL::OpenDB(PGLOBAL g)
/* servers allowing concurency in getting results ??? */
/*********************************************************************/
if (!Myc.Connected()) {
- if (Myc.Open(g, Host, Database, User, Pwd, Port, csname))
+ if (Myc.Open(g, Host, Schema, User, Pwd, Port, csname))
return true;
} // endif Connected
@@ -931,14 +943,14 @@ bool TDBMYSQL::OpenDB(PGLOBAL g)
char cmd[64];
int w;
- sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname);
+ sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", TableName);
m_Rc = Myc.ExecSQL(g, cmd, &w); // may fail for some engines
} // endif m_Rc
} else
// m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g);
- m_Rc = MakeCommand(g);
+ m_Rc = (MakeCommand(g)) ? RC_FX : RC_OK;
if (m_Rc == RC_FX) {
Myc.Close();
@@ -1030,7 +1042,7 @@ int TDBMYSQL::SendCommand(PGLOBAL g)
if (Myc.ExecSQLcmd(g, Query->GetStr(), &w) == RC_NF) {
AftRows = Myc.m_Afrw;
- sprintf(g->Message, "%s: %d affected rows", Tabname, AftRows);
+ sprintf(g->Message, "%s: %d affected rows", TableName, AftRows);
PushWarning(g, this, 0); // 0 means a Note
if (trace)
@@ -1039,7 +1051,7 @@ int TDBMYSQL::SendCommand(PGLOBAL g)
if (w && Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK) {
// We got warnings from the remote server
while (Myc.Fetch(g, -1) == RC_OK) {
- sprintf(g->Message, "%s: (%s) %s", Tabname,
+ sprintf(g->Message, "%s: (%s) %s", TableName,
Myc.GetCharField(1), Myc.GetCharField(2));
PushWarning(g, this);
} // endwhile Fetch
@@ -1116,8 +1128,7 @@ int TDBMYSQL::ReadDB(PGLOBAL g)
int rc;
if (trace > 1)
- htrc("MySQL ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
- GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
+ htrc("MySQL ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
if (Mode == MODE_UPDATE || Mode == MODE_DELETE)
return SendCommand(g);
@@ -1205,7 +1216,7 @@ void TDBMYSQL::CloseDB(PGLOBAL g)
PDBUSER dup = PlgGetUser(g);
dup->Step = "Enabling indexes";
- sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname);
+ sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", TableName);
Myc.m_Rows = -1; // To execute the query
m_Rc = Myc.ExecSQL(g, cmd, &w); // May fail for some engines
} // endif m_Rc
@@ -1463,7 +1474,7 @@ TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp)
Havew = false;
Isw = false;
Warnings = 0;
- Mxr = tdp->Mxr;
+ Mxr = tdp->Maxerr;
Nerr = 0;
} // end of TDBMYEXC constructor
@@ -1479,7 +1490,7 @@ TDBMYEXC::TDBMYEXC(PTDBMYX tdbp) : TDBMYSQL(tdbp)
} // end of TDBMYEXC copy constructor
// Is this really useful ???
-PTDB TDBMYEXC::CopyOne(PTABS t)
+PTDB TDBMYEXC::Clone(PTABS t)
{
PTDB tp;
PCOL cp1, cp2;
@@ -1494,7 +1505,7 @@ PTDB TDBMYEXC::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate MYSQL column description block. */
@@ -1565,7 +1576,7 @@ bool TDBMYEXC::OpenDB(PGLOBAL g)
/* servers allowing concurency in getting results ??? */
/*********************************************************************/
if (!Myc.Connected())
- if (Myc.Open(g, Host, Database, User, Pwd, Port))
+ if (Myc.Open(g, Host, Schema, User, Pwd, Port))
return true;
Use = USE_OPEN; // Do it now in case we are recursively called
@@ -1728,7 +1739,7 @@ void MYXCOL::WriteColumn(PGLOBAL)
TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp)
{
Host = tdp->Hostname;
- Db = tdp->Database;
+ Db = tdp->Tabschema;
Tab = tdp->Tabname;
User = tdp->Username;
Pwd = tdp->Password;
diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h
index edb15b5cca6..050fa59259b 100644
--- a/storage/connect/tabmysql.h
+++ b/storage/connect/tabmysql.h
@@ -1,4 +1,4 @@
-// TDBMYSQL.H Olivier Bertrand 2007-2014
+// TDBMYSQL.H Olivier Bertrand 2007-2017
#include "myconn.h" // MySQL connection declares
typedef class MYSQLDEF *PMYDEF;
@@ -18,7 +18,7 @@ typedef class MYSQLC *PMYC;
/***********************************************************************/
/* MYSQL table. */
/***********************************************************************/
-class MYSQLDEF : public TABDEF {/* Logical table description */
+class MYSQLDEF : public EXTDEF {/* Logical table description */
friend class TDBMYSQL;
friend class TDBMYEXC;
friend class TDBMCL;
@@ -27,19 +27,18 @@ class MYSQLDEF : public TABDEF {/* Logical table description */
// Constructor
MYSQLDEF(void);
-
// Implementation
virtual const char *GetType(void) {return "MYSQL";}
inline PSZ GetHostname(void) {return Hostname;};
- inline PSZ GetDatabase(void) {return Database;};
- inline PSZ GetTabname(void) {return Tabname;}
- inline PSZ GetSrcdef(void) {return Srcdef;}
- inline PSZ GetUsername(void) {return Username;};
- inline PSZ GetPassword(void) {return Password;};
+//inline PSZ GetDatabase(void) {return Tabschema;};
+//inline PSZ GetTabname(void) {return Tabname;}
+//inline PSZ GetSrcdef(void) {return Srcdef;}
+//inline PSZ GetUsername(void) {return Username;};
+//inline PSZ GetPassword(void) {return Password;};
inline int GetPortnumber(void) {return Portnumber;}
// Methods
- virtual int Indexable(void) {return 2;}
+//virtual int Indexable(void) {return 2;}
virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff);
virtual PTDB GetTable(PGLOBAL g, MODE m);
bool ParseURL(PGLOBAL g, char *url, bool b = true);
@@ -48,27 +47,27 @@ class MYSQLDEF : public TABDEF {/* Logical table description */
protected:
// Members
PSZ Hostname; /* Host machine to use */
- PSZ Database; /* Database to be used by server */
- PSZ Tabname; /* External table name */
- PSZ Srcdef; /* The source table SQL definition */
- PSZ Username; /* User logon name */
- PSZ Password; /* Password logon info */
+//PSZ Tabschema; /* Database to be used by server */
+//PSZ Tabname; /* External table name */
+//PSZ Srcdef; /* The source table SQL definition */
+//PSZ Username; /* User logon name */
+//PSZ Password; /* Password logon info */
PSZ Server; /* PServerID */
- PSZ Qrystr; /* The original query */
+//PSZ Qrystr; /* The original query */
int Portnumber; /* MySQL port number (0 = default) */
- int Mxr; /* Maxerr for an Exec table */
- int Quoted; /* Identifier quoting level */
+//int Maxerr; /* Maxerr for an Exec table */
+//int Quoted; /* Identifier quoting level */
bool Isview; /* true if this table is a MySQL view */
bool Bind; /* Use prepared statement on insert */
bool Delayed; /* Delayed insert */
- bool Xsrc; /* Execution type */
+//bool Xsrc; /* Execution type */
bool Huge; /* True for big table */
}; // end of MYSQLDEF
/***********************************************************************/
/* This is the class declaration for the MYSQL table. */
/***********************************************************************/
-class TDBMYSQL : public TDBASE {
+class TDBMYSQL : public TDBEXT {
friend class MYSQLCOL;
public:
// Constructor
@@ -80,7 +79,7 @@ class TDBMYSQL : public TDBASE {
virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMYSQL(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
//virtual int GetAffectedRows(void) {return AftRows;}
virtual int GetRecpos(void) {return N;}
virtual int GetProgMax(PGLOBAL g);
@@ -88,12 +87,12 @@ class TDBMYSQL : public TDBASE {
virtual int RowNumber(PGLOBAL g, bool b = false);
virtual bool IsView(void) {return Isview;}
virtual PSZ GetServer(void) {return Server;}
- void SetDatabase(LPCSTR db) {Database = (char*)db;}
+ void SetDatabase(LPCSTR db) {Schema = (char*)db;}
- // Database routines
+ // Schema routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
virtual int Cardinality(PGLOBAL g);
- virtual int GetMaxSize(PGLOBAL g);
+//virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual int WriteDB(PGLOBAL g);
@@ -111,7 +110,7 @@ class TDBMYSQL : public TDBASE {
bool MakeSelect(PGLOBAL g, bool mx);
bool MakeInsert(PGLOBAL g);
int BindColumns(PGLOBAL g);
- int MakeCommand(PGLOBAL g);
+ virtual bool MakeCommand(PGLOBAL g);
//int MakeUpdate(PGLOBAL g);
//int MakeDelete(PGLOBAL g);
int SendCommand(PGLOBAL g);
@@ -119,25 +118,25 @@ class TDBMYSQL : public TDBASE {
// Members
MYSQLC Myc; // MySQL connection class
MYSQL_BIND *Bind; // To the MySQL bind structure array
- PSTRG Query; // Constructed SQL query
+//PSTRG Query; // Constructed SQL query
char *Host; // Host machine to use
- char *User; // User logon info
- char *Pwd; // Password logon info
- char *Database; // Database to be used by server
- char *Tabname; // External table name
- char *Srcdef; // The source table SQL definition
+//char *User; // User logon info
+//char *Pwd; // Password logon info
+//char *Schema; // Database to be used by server
+//char *TableName; // External table name
+//char *Srcdef; // The source table SQL definition
char *Server; // The server ID
- char *Qrystr; // The original query
+//char *Qrystr; // The original query
bool Fetched; // True when fetch was done
bool Isview; // True if this table is a MySQL view
bool Prep; // Use prepared statement on insert
bool Delayed; // Use delayed insert
int m_Rc; // Return code from command
- int AftRows; // The number of affected rows
+//int AftRows; // The number of affected rows
int N; // The current table index
int Port; // MySQL port number (0 = default)
- int Nparm; // The number of statement parameters
- int Quoted; // The identifier quoting level
+//int Nparm; // The number of statement parameters
+//int Quoted; // The identifier quoting level
}; // end of class TDBMYSQL
/***********************************************************************/
@@ -162,9 +161,6 @@ class MYSQLCOL : public COLBLK {
bool FindRank(PGLOBAL g);
protected:
- // Default constructor not to be used
- MYSQLCOL(void) {}
-
// Members
MYSQL_BIND *Bind; // This column bind structure pointer
PVAL To_Val; // To value used for Update/Insert
@@ -187,7 +183,7 @@ class TDBMYEXC : public TDBMYSQL {
virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMYEXC(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual bool IsView(void) {return Isview;}
// Database routines
@@ -228,9 +224,6 @@ class MYXCOL : public MYSQLCOL {
virtual void WriteColumn(PGLOBAL g);
protected:
- // Default constructor not to be used
- MYXCOL(void) {}
-
// Members
char *Buffer; // To get returned message
int Flag; // Column content desc
diff --git a/storage/connect/taboccur.cpp b/storage/connect/taboccur.cpp
index 07e260154e0..07272d1b298 100644
--- a/storage/connect/taboccur.cpp
+++ b/storage/connect/taboccur.cpp
@@ -1,7 +1,7 @@
/************ TabOccur CPP Declares Source Code File (.CPP) ************/
-/* Name: TABOCCUR.CPP Version 1.1 */
+/* Name: TABOCCUR.CPP Version 1.2 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2013 - 2015 */
+/* (C) Copyright to the author Olivier BERTRAND 2013 - 2017 */
/* */
/* OCCUR: Table that provides a view of a source table where the */
/* contain of several columns of the source table is placed in only */
@@ -39,12 +39,13 @@
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
-#include "reldef.h"
+#include "xtable.h"
+#include "tabext.h"
+//#include "reldef.h"
#include "filamtxt.h"
#include "tabdos.h"
#include "tabcol.h"
#include "taboccur.h"
-#include "xtable.h"
#include "tabmysql.h"
#include "ha_connect.h"
diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp
index f3ffc99ac15..488acdd330d 100644
--- a/storage/connect/tabodbc.cpp
+++ b/storage/connect/tabodbc.cpp
@@ -67,10 +67,11 @@
#include "plgdbsem.h"
#include "mycat.h"
#include "xtable.h"
+#include "tabext.h"
#include "odbccat.h"
#include "tabodbc.h"
#include "tabmul.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "tabcol.h"
#include "valblk.h"
#include "ha_connect.h"
@@ -95,10 +96,9 @@ bool ExactInfo(void);
/***********************************************************************/
ODBCDEF::ODBCDEF(void)
{
- Connect = Tabname = Tabschema = Username = Password = NULL;
- Tabcat = Colpat = Srcdef = Qchar = Qrystr = Sep = NULL;
- Catver = Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0;
- Scrollable = Xsrc = UseCnc = false;
+ Connect = NULL;
+ Catver = 0;
+ UseCnc = false;
} // end of ODBCDEF constructor
/***********************************************************************/
@@ -113,47 +113,50 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
return true;
} // endif Connect
- Tabname = GetStringCatInfo(g, "Name",
- (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
- Tabname = GetStringCatInfo(g, "Tabname", Tabname);
- Tabschema = GetStringCatInfo(g, "Dbname", NULL);
- Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
- Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
- Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
- Username = GetStringCatInfo(g, "User", NULL);
- Password = GetStringCatInfo(g, "Password", NULL);
-
- if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
- Read_Only = true;
-
- Qrystr = GetStringCatInfo(g, "Query_String", "?");
- Sep = GetStringCatInfo(g, "Separator", NULL);
+ if (EXTDEF::DefineAM(g, am, poff))
+ return true;
+
+ // Tabname = GetStringCatInfo(g, "Name",
+ // (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name);
+ // Tabname = GetStringCatInfo(g, "Tabname", Tabname);
+ // Tabschema = GetStringCatInfo(g, "Dbname", NULL);
+ // Tabschema = GetStringCatInfo(g, "Schema", Tabschema);
+ // Tabcat = GetStringCatInfo(g, "Qualifier", NULL);
+ // Tabcat = GetStringCatInfo(g, "Catalog", Tabcat);
+ //Username = GetStringCatInfo(g, "User", NULL);
+ // Password = GetStringCatInfo(g, "Password", NULL);
+
+ // if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL)))
+ // Read_Only = true;
+
+ // Qrystr = GetStringCatInfo(g, "Query_String", "?");
+ // Sep = GetStringCatInfo(g, "Separator", NULL);
Catver = GetIntCatInfo("Catver", 2);
- Xsrc = GetBoolCatInfo("Execsrc", FALSE);
- Maxerr = GetIntCatInfo("Maxerr", 0);
- Maxres = GetIntCatInfo("Maxres", 0);
- Quoted = GetIntCatInfo("Quoted", 0);
+ //Xsrc = GetBoolCatInfo("Execsrc", FALSE);
+ //Maxerr = GetIntCatInfo("Maxerr", 0);
+ //Maxres = GetIntCatInfo("Maxres", 0);
+ //Quoted = GetIntCatInfo("Quoted", 0);
Options = ODBConn::noOdbcDialog;
//Options = ODBConn::noOdbcDialog | ODBConn::useCursorLib;
Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT);
Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT);
- if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt)
- Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch
+ //if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt)
+ // Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch
- if (Catfunc == FNC_COL)
- Colpat = GetStringCatInfo(g, "Colpat", NULL);
+ //if (Catfunc == FNC_COL)
+ // Colpat = GetStringCatInfo(g, "Colpat", NULL);
- if (Catfunc == FNC_TABLE)
- Tabtyp = GetStringCatInfo(g, "Tabtype", NULL);
+ //if (Catfunc == FNC_TABLE)
+ // Tabtyp = GetStringCatInfo(g, "Tabtype", NULL);
UseCnc = GetBoolCatInfo("UseDSN", false);
// Memory was Boolean, it is now integer
- if (!(Memory = GetIntCatInfo("Memory", 0)))
- Memory = GetBoolCatInfo("Memory", false) ? 1 : 0;
+ //if (!(Memory = GetIntCatInfo("Memory", 0)))
+ // Memory = GetBoolCatInfo("Memory", false) ? 1 : 0;
- Pseudo = 2; // FILID is Ok but not ROWID
+ //Pseudo = 2; // FILID is Ok but not ROWID
return false;
} // end of DefineAM
@@ -162,7 +165,7 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
/***********************************************************************/
PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m)
{
- PTDBASE tdbp = NULL;
+ PTDB tdbp = NULL;
/*********************************************************************/
/* Allocate a TDB of the proper type. */
@@ -200,103 +203,103 @@ PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m)
/***********************************************************************/
/* Implementation of the TDBODBC class. */
/***********************************************************************/
-TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp)
+TDBODBC::TDBODBC(PODEF tdp) : TDBEXT(tdp)
{
Ocp = NULL;
Cnp = NULL;
if (tdp) {
Connect = tdp->Connect;
- TableName = tdp->Tabname;
- Schema = tdp->Tabschema;
+ //TableName = tdp->Tabname;
+ //Schema = tdp->Tabschema;
Ops.User = tdp->Username;
Ops.Pwd = tdp->Password;
- Catalog = tdp->Tabcat;
- Srcdef = tdp->Srcdef;
- Qrystr = tdp->Qrystr;
- Sep = tdp->GetSep();
- Options = tdp->Options;
+ //Catalog = tdp->Tabcat;
+ //Srcdef = tdp->Srcdef;
+ //Qrystr = tdp->Qrystr;
+ //Sep = tdp->GetSep();
+ //Options = tdp->Options;
Ops.Cto = tdp->Cto;
Ops.Qto = tdp->Qto;
- Quoted = MY_MAX(0, tdp->GetQuoted());
- Rows = tdp->GetElemt();
+ //Quoted = MY_MAX(0, tdp->GetQuoted());
+ //Rows = tdp->GetElemt();
Catver = tdp->Catver;
- Memory = tdp->Memory;
- Scrollable = tdp->Scrollable;
+ //Memory = tdp->Memory;
+ //Scrollable = tdp->Scrollable;
Ops.UseCnc = tdp->UseCnc;
} else {
Connect = NULL;
- TableName = NULL;
- Schema = NULL;
+ //TableName = NULL;
+ //Schema = NULL;
Ops.User = NULL;
Ops.Pwd = NULL;
- Catalog = NULL;
- Srcdef = NULL;
- Qrystr = NULL;
- Sep = 0;
- Options = 0;
+ //Catalog = NULL;
+ //Srcdef = NULL;
+ //Qrystr = NULL;
+ //Sep = 0;
+ //Options = 0;
Ops.Cto = DEFAULT_LOGIN_TIMEOUT;
Ops.Qto = DEFAULT_QUERY_TIMEOUT;
- Quoted = 0;
- Rows = 0;
+ //Quoted = 0;
+ //Rows = 0;
Catver = 0;
- Memory = 0;
- Scrollable = false;
+ //Memory = 0;
+ //Scrollable = false;
Ops.UseCnc = false;
} // endif tdp
- Quote = NULL;
- Query = NULL;
- Count = NULL;
+ //Quote = NULL;
+ //Query = NULL;
+ //Count = NULL;
//Where = NULL;
- MulConn = NULL;
- DBQ = NULL;
- Qrp = NULL;
- Fpos = 0;
- Curpos = 0;
- AftRows = 0;
- CurNum = 0;
- Rbuf = 0;
- BufSize = 0;
- Nparm = 0;
- Placed = false;
+ //MulConn = NULL;
+ //DBQ = NULL;
+ //Qrp = NULL;
+ //Fpos = 0;
+ //Curpos = 0;
+ //AftRows = 0;
+ //CurNum = 0;
+ //Rbuf = 0;
+ //BufSize = 0;
+ //Nparm = 0;
+ //Placed = false;
} // end of TDBODBC standard constructor
-TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp)
+TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBEXT(tdbp)
{
Ocp = tdbp->Ocp; // is that right ?
Cnp = tdbp->Cnp;
Connect = tdbp->Connect;
- TableName = tdbp->TableName;
- Schema = tdbp->Schema;
+ //TableName = tdbp->TableName;
+ //Schema = tdbp->Schema;
Ops = tdbp->Ops;
- Catalog = tdbp->Catalog;
- Srcdef = tdbp->Srcdef;
- Qrystr = tdbp->Qrystr;
- Memory = tdbp->Memory;
- Scrollable = tdbp->Scrollable;
- Quote = tdbp->Quote;
- Query = tdbp->Query;
- Count = tdbp->Count;
+ //Catalog = tdbp->Catalog;
+ //Srcdef = tdbp->Srcdef;
+ //Qrystr = tdbp->Qrystr;
+ //Memory = tdbp->Memory;
+ //Scrollable = tdbp->Scrollable;
+ //Quote = tdbp->Quote;
+ //Query = tdbp->Query;
+ //Count = tdbp->Count;
//Where = tdbp->Where;
- MulConn = tdbp->MulConn;
- DBQ = tdbp->DBQ;
- Options = tdbp->Options;
- Quoted = tdbp->Quoted;
- Rows = tdbp->Rows;
- Fpos = 0;
- Curpos = 0;
- AftRows = 0;
- CurNum = 0;
- Rbuf = 0;
- BufSize = tdbp->BufSize;
- Nparm = tdbp->Nparm;
- Qrp = tdbp->Qrp;
- Placed = false;
+ //MulConn = tdbp->MulConn;
+ //DBQ = tdbp->DBQ;
+ //Options = tdbp->Options;
+ //Quoted = tdbp->Quoted;
+ //Rows = tdbp->Rows;
+ //Fpos = 0;
+ //Curpos = 0;
+ //AftRows = 0;
+ //CurNum = 0;
+ //Rbuf = 0;
+ //BufSize = tdbp->BufSize;
+ //Nparm = tdbp->Nparm;
+ //Qrp = tdbp->Qrp;
+ //Placed = false;
} // end of TDBODBC copy constructor
// Method
-PTDB TDBODBC::CopyOne(PTABS t)
+PTDB TDBODBC::Clone(PTABS t)
{
PTDB tp;
PODBCCOL cp1, cp2;
@@ -386,6 +389,7 @@ void TDBODBC::SetFile(PGLOBAL g, PSZ fn)
DBQ = fn;
} // end of SetFile
+#if 0
/******************************************************************/
/* Convert an UTF-8 string to latin characters. */
/******************************************************************/
@@ -414,7 +418,15 @@ bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
PCOL colp;
if (Srcdef) {
- Query = new(g)STRING(g, 0, Srcdef);
+ if (strstr(Srcdef, "%s")) {
+ char *fil;
+
+ fil = (To_CondFil) ? To_CondFil->Body : PlugDup(g, "1=1");
+ Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil));
+ Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil));
+ } else
+ Query = new(g)STRING(g, 0, Srcdef);
+
return false;
} // endif Srcdef
@@ -442,7 +454,8 @@ bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
} else
oom |= Query->Append(buf);
- } // endif colp
+ ((PEXTCOL)colp)->SetRank(++Ncol);
+ } // endif colp
} else
// !Columns can occur for queries such that sql count(*) from...
@@ -458,10 +471,6 @@ bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
if (Catalog && *Catalog)
catp = Catalog;
- // Following lines are commented because of MSDEV-10520
- // Indeed the schema in the tablep is the local table database and
- // is normally not related to the remote table database.
- // TODO: Try to remember why this was done and if it was useful in some case.
//if (tablep->GetSchema())
// schmp = (char*)tablep->GetSchema();
//else
@@ -516,6 +525,7 @@ bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt)
return false;
} // end of MakeSQL
+#endif // 0
/***********************************************************************/
/* MakeInsert: make the Insert statement used with ODBC connection. */
@@ -536,7 +546,7 @@ bool TDBODBC::MakeInsert(PGLOBAL g)
// Column name can be encoded in UTF-8
Decode(colp->GetName(), buf, sizeof(buf));
len += (strlen(buf) + 6); // comma + quotes + valist
- ((PODBCCOL)colp)->Rank = ++Nparm;
+ ((PEXTCOL)colp)->SetRank(++Nparm);
} // endif colp
// Below 32 is enough to contain the fixed part of the query
@@ -555,7 +565,7 @@ bool TDBODBC::MakeInsert(PGLOBAL g)
if (schmp)
len += strlen(schmp) + 1;
- // Column name can be encoded in UTF-8
+ // Table name can be encoded in UTF-8
Decode(TableName, buf, sizeof(buf));
len += (strlen(buf) + 32);
Query = new(g) STRING(g, len, "INSERT INTO ");
@@ -634,6 +644,7 @@ bool TDBODBC::BindParameters(PGLOBAL g)
return false;
} // end of BindParameters
+#if 0
/***********************************************************************/
/* MakeCommand: make the Update or Delete statement to send to the */
/* MySQL server. Limited to remote values and filtering. */
@@ -664,19 +675,20 @@ bool TDBODBC::MakeCommand(PGLOBAL g)
// If so, it must be quoted in the original query
strlwr(strcat(strcat(strcpy(name, " "), Name), " "));
- if (!strstr(" update delete low_priority ignore quick from ", name))
- strlwr(strcpy(name, Name)); // Not a keyword
- else
- strlwr(strcat(strcat(strcpy(name, qc), Name), qc));
+ if (strstr(" update delete low_priority ignore quick from ", name)) {
+ strlwr(strcat(strcat(strcpy(name, qc), Name), qc));
+ k += 2;
+ } else
+ strlwr(strcpy(name, Name)); // Not a keyword
if ((p = strstr(qrystr, name))) {
for (i = 0; i < p - qrystr; i++)
stmt[i] = (Qrystr[i] == '`') ? *qc : Qrystr[i];
stmt[i] = 0;
- k = i + (int)strlen(Name);
+ k += i + (int)strlen(Name);
- if (qtd && *(p-1) == ' ')
+ if (qtd && *(p - 1) == ' ')
strcat(strcat(strcat(stmt, qc), TableName), qc);
else
strcat(stmt, TableName);
@@ -692,15 +704,14 @@ bool TDBODBC::MakeCommand(PGLOBAL g)
} else {
sprintf(g->Message, "Cannot use this %s command",
- (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
- return false;
+ (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE");
+ return true;
} // endif p
Query = new(g) STRING(g, 0, stmt);
return (!Query->GetSize());
} // end of MakeCommand
-#if 0
/***********************************************************************/
/* MakeUpdate: make the SQL statement to send to ODBC connection. */
/***********************************************************************/
@@ -818,6 +829,7 @@ int TDBODBC::Cardinality(PGLOBAL g)
return Cardinal;
} // end of Cardinality
+#if 0
/***********************************************************************/
/* ODBC GetMaxSize: returns table size estimate in number of lines. */
/***********************************************************************/
@@ -844,6 +856,7 @@ int TDBODBC::GetProgMax(PGLOBAL g)
{
return GetMaxSize(g);
} // end of GetProgMax
+#endif // 0
/***********************************************************************/
/* ODBC Access Method opening routine. */
@@ -981,6 +994,7 @@ bool TDBODBC::OpenDB(PGLOBAL g)
return false;
} // end of OpenDB
+#if 0
/***********************************************************************/
/* GetRecpos: return the position of last read record. */
/***********************************************************************/
@@ -988,6 +1002,7 @@ int TDBODBC::GetRecpos(void)
{
return Fpos;
} // end of GetRecpos
+#endif // 0
/***********************************************************************/
/* SetRecpos: set the position of next read record. */
@@ -1081,8 +1096,9 @@ int TDBODBC::ReadDB(PGLOBAL g)
int rc;
if (trace > 1)
- htrc("ODBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
- GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
+ htrc("ODBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode);
+ //htrc("ODBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n",
+ // GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex);
if (Mode == MODE_UPDATE || Mode == MODE_DELETE) {
if (!Query && MakeCommand(g))
@@ -1102,11 +1118,11 @@ int TDBODBC::ReadDB(PGLOBAL g)
} // endif Mode
- if (To_Kindex) {
- // Direct access of ODBC tables is not implemented yet
- strcpy(g->Message, MSG(NO_ODBC_DIRECT));
- return RC_FX;
- } // endif To_Kindex
+ //if (To_Kindex) {
+ // // Direct access of ODBC tables is not implemented yet
+ // strcpy(g->Message, MSG(NO_ODBC_DIRECT));
+ // return RC_FX;
+ // } // endif To_Kindex
/*********************************************************************/
/* Now start the reading process. */
@@ -1212,70 +1228,58 @@ void TDBODBC::CloseDB(PGLOBAL g)
/* ODBCCOL public constructor. */
/***********************************************************************/
ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am)
- : COLBLK(cdp, tdbp, i)
+ : EXTCOL(cdp, tdbp, cprec, i, am)
{
- if (cprec) {
- Next = cprec->GetNext();
- cprec->SetNext(this);
- } else {
- Next = tdbp->GetColumns();
- tdbp->SetColumns(this);
- } // endif cprec
-
// Set additional ODBC access method information for column.
- Crp = NULL;
-//Long = cdp->GetLong();
- Long = Precision;
+//Crp = NULL;
+//Long = Precision;
//strcpy(F_Date, cdp->F_Date);
- To_Val = NULL;
+//To_Val = NULL;
Slen = 0;
StrLen = &Slen;
Sqlbuf = NULL;
- Bufp = NULL;
- Blkp = NULL;
- Rank = 0; // Not known yet
-
- if (trace)
- htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
-
+//Bufp = NULL;
+//Blkp = NULL;
+//Rank = 0; // Not known yet
} // end of ODBCCOL constructor
/***********************************************************************/
/* ODBCCOL private constructor. */
/***********************************************************************/
-ODBCCOL::ODBCCOL(void) : COLBLK()
+ODBCCOL::ODBCCOL(void) : EXTCOL()
{
- Crp = NULL;
- Buf_Type = TYPE_INT; // This is a count(*) column
- // Set additional Dos access method information for column.
- Long = sizeof(int);
- To_Val = NULL;
+//Crp = NULL;
+//Buf_Type = TYPE_INT; // This is a count(*) column
+//// Set additional Dos access method information for column.
+//Long = sizeof(int);
+//To_Val = NULL;
Slen = 0;
StrLen = &Slen;
Sqlbuf = NULL;
- Bufp = NULL;
- Blkp = NULL;
- Rank = 1;
+//Bufp = NULL;
+//Blkp = NULL;
+//Rank = 1;
} // end of ODBCCOL constructor
/***********************************************************************/
/* ODBCCOL constructor used for copying columns. */
/* tdbp is the pointer to the new table descriptor. */
/***********************************************************************/
-ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
+ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp)
{
- Crp = col1->Crp;
- Long = col1->Long;
+//Crp = col1->Crp;
+//Long = col1->Long;
//strcpy(F_Date, col1->F_Date);
- To_Val = col1->To_Val;
+//To_Val = col1->To_Val;
Slen = col1->Slen;
StrLen = col1->StrLen;
Sqlbuf = col1->Sqlbuf;
- Bufp = col1->Bufp;
- Blkp = col1->Blkp;
- Rank = col1->Rank;
+//Bufp = col1->Bufp;
+//Blkp = col1->Blkp;
+//Rank = col1->Rank;
} // end of ODBCCOL copy constructor
+#if 0
/***********************************************************************/
/* SetBuffer: prepare a column block for write operation. */
/***********************************************************************/
@@ -1321,6 +1325,7 @@ bool ODBCCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
Status = (ok) ? BUF_EMPTY : BUF_NO;
return false;
} // end of SetBuffer
+#endif // 0
/***********************************************************************/
/* ReadColumn: when SQLFetch is used there is nothing to do as the */
@@ -1526,7 +1531,7 @@ TDBXDBC::TDBXDBC(PTDBXDBC tdbp) : TDBODBC(tdbp)
Nerr = tdbp->Nerr;
} // end of TDBXDBC copy constructor
-PTDB TDBXDBC::CopyOne(PTABS t)
+PTDB TDBXDBC::Clone(PTABS t)
{
PTDB tp;
PXSRCCOL cp1, cp2;
diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h
index aa6592d8abf..fcefad5647b 100644
--- a/storage/connect/tabodbc.h
+++ b/storage/connect/tabodbc.h
@@ -20,7 +20,7 @@ typedef class TDBSRC *PTDBSRC;
/***********************************************************************/
/* ODBC table. */
/***********************************************************************/
-class DllExport ODBCDEF : public TABDEF { /* Logical table description */
+class DllExport ODBCDEF : public EXTDEF { /* Logical table description */
friend class TDBODBC;
friend class TDBXDBC;
friend class TDBDRV;
@@ -33,14 +33,14 @@ public:
// Implementation
virtual const char *GetType(void) {return "ODBC";}
PSZ GetConnect(void) {return Connect;}
- PSZ GetTabname(void) {return Tabname;}
- PSZ GetTabschema(void) {return Tabschema;}
- PSZ GetTabcat(void) {return Tabcat;}
- PSZ GetSrcdef(void) {return Srcdef;}
- char GetSep(void) {return (Sep) ? *Sep : 0;}
- int GetQuoted(void) {return Quoted;}
+ //PSZ GetTabname(void) {return Tabname;}
+ //PSZ GetTabschema(void) {return Tabschema;}
+ //PSZ GetTabcat(void) {return Tabcat;}
+ //PSZ GetSrcdef(void) {return Srcdef;}
+ //char GetSep(void) {return (Sep) ? *Sep : 0;}
+ //int GetQuoted(void) {return Quoted;}
int GetCatver(void) {return Catver;}
- int GetOptions(void) {return Options;}
+ //int GetOptions(void) {return Options;}
// Methods
virtual int Indexable(void) {return 2;}
@@ -50,27 +50,27 @@ public:
protected:
// Members
PSZ Connect; /* ODBC connection string */
- PSZ Tabname; /* External table name */
- PSZ Tabschema; /* External table schema */
- PSZ Username; /* User connect name */
- PSZ Password; /* Password connect info */
- PSZ Tabcat; /* External table catalog */
- PSZ Tabtyp; /* Catalog table type */
- PSZ Colpat; /* Catalog column pattern */
- PSZ Srcdef; /* The source table SQL definition */
- PSZ Qchar; /* Identifier quoting character */
- PSZ Qrystr; /* The original query */
- PSZ Sep; /* Decimal separator */
+ //PSZ Tabname; /* External table name */
+ //PSZ Tabschema; /* External table schema */
+ //PSZ Username; /* User connect name */
+ //PSZ Password; /* Password connect info */
+ //PSZ Tabcat; /* External table catalog */
+ //PSZ Tabtyp; /* Catalog table type */
+ //PSZ Colpat; /* Catalog column pattern */
+ //PSZ Srcdef; /* The source table SQL definition */
+ //PSZ Qchar; /* Identifier quoting character */
+ //PSZ Qrystr; /* The original query */
+ //PSZ Sep; /* Decimal separator */
int Catver; /* ODBC version for catalog functions */
- int Options; /* Open connection options */
- int Cto; /* Open connection timeout */
- int Qto; /* Query (command) timeout */
- int Quoted; /* Identifier quoting level */
- int Maxerr; /* Maxerr for an Exec table */
- int Maxres; /* Maxres for a catalog table */
- int Memory; /* Put result set in memory */
- bool Scrollable; /* Use scrollable cursor */
- bool Xsrc; /* Execution type */
+ //int Options; /* Open connection options */
+ //int Cto; /* Open connection timeout */
+ //int Qto; /* Query (command) timeout */
+ //int Quoted; /* Identifier quoting level */
+ //int Maxerr; /* Maxerr for an Exec table */
+ //int Maxres; /* Maxres for a catalog table */
+ //int Memory; /* Put result set in memory */
+ //bool Scrollable; /* Use scrollable cursor */
+ //bool Xsrc; /* Execution type */
bool UseCnc; /* Use SQLConnect (!SQLDriverConnect) */
}; // end of ODBCDEF
@@ -81,7 +81,7 @@ public:
/* This is the ODBC Access Method class declaration for files from */
/* other DB drivers to be accessed via ODBC. */
/***********************************************************************/
-class TDBODBC : public TDBASE {
+class TDBODBC : public TDBEXT {
friend class ODBCCOL;
friend class ODBConn;
public:
@@ -95,8 +95,8 @@ class TDBODBC : public TDBASE {
{return (PTDB)new(g) TDBODBC(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
- virtual int GetRecpos(void);
+ virtual PTDB Clone(PTABS t);
+//virtual int GetRecpos(void);
virtual bool SetRecpos(PGLOBAL g, int recpos);
virtual PSZ GetFile(PGLOBAL g);
virtual void SetFile(PGLOBAL g, PSZ fn);
@@ -108,8 +108,8 @@ class TDBODBC : public TDBASE {
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
virtual int Cardinality(PGLOBAL g);
- virtual int GetMaxSize(PGLOBAL g);
- virtual int GetProgMax(PGLOBAL g);
+//virtual int GetMaxSize(PGLOBAL g);
+//virtual int GetProgMax(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual int WriteDB(PGLOBAL g);
@@ -119,10 +119,10 @@ class TDBODBC : public TDBASE {
protected:
// Internal functions
- int Decode(char *utf, char *buf, size_t n);
- bool MakeSQL(PGLOBAL g, bool cnt);
+//int Decode(char *utf, char *buf, size_t n);
+//bool MakeSQL(PGLOBAL g, bool cnt);
bool MakeInsert(PGLOBAL g);
- bool MakeCommand(PGLOBAL g);
+//virtual bool MakeCommand(PGLOBAL g);
//bool MakeFilter(PGLOBAL g, bool c);
bool BindParameters(PGLOBAL g);
//char *MakeUpdate(PGLOBAL g);
@@ -132,46 +132,16 @@ class TDBODBC : public TDBASE {
ODBConn *Ocp; // Points to an ODBC connection class
ODBCCOL *Cnp; // Points to count(*) column
ODBCPARM Ops; // Additional parameters
- PSTRG Query; // Constructed SQL query
char *Connect; // Points to connection string
- char *TableName; // Points to ODBC table name
- char *Schema; // Points to ODBC table Schema
- char *User; // User connect info
- char *Pwd; // Password connect info
- char *Catalog; // Points to ODBC table Catalog
- char *Srcdef; // The source table SQL definition
- char *Count; // Points to count(*) SQL statement
-//char *Where; // Points to local where clause
- char *Quote; // The identifier quoting character
- char *MulConn; // Used for multiple ODBC tables
- char *DBQ; // The address part of Connect string
- char *Qrystr; // The original query
- char Sep; // The decimal separator
- int Options; // Connect options
- int Cto; // Connect timeout
- int Qto; // Query timeout
- int Quoted; // The identifier quoting level
- int Fpos; // Position of last read record
- int Curpos; // Cursor position of last fetch
- int AftRows; // The number of affected rows
- int Rows; // Rowset size
int Catver; // Catalog ODBC version
- int CurNum; // Current buffer line number
- int Rbuf; // Number of lines read in buffer
- int BufSize; // Size of connect string buffer
- int Nparm; // The number of statement parameters
- int Memory; // 0: No 1: Alloc 2: Put 3: Get
- bool Scrollable; // Use scrollable cursor
- bool Placed; // True for position reading
bool UseCnc; // Use SQLConnect (!SQLDriverConnect)
- PQRYRES Qrp; // Points to storage result
}; // end of class TDBODBC
/***********************************************************************/
/* Class ODBCCOL: ODBC access method column descriptor. */
/* This A.M. is used for ODBC tables. */
/***********************************************************************/
-class ODBCCOL : public COLBLK {
+class ODBCCOL : public EXTCOL {
friend class TDBODBC;
public:
// Constructors
@@ -181,12 +151,12 @@ class ODBCCOL : public COLBLK {
// Implementation
virtual int GetAmType(void) {return TYPE_AM_ODBC;}
SQLLEN *GetStrLen(void) {return StrLen;}
- int GetRank(void) {return Rank;}
+// int GetRank(void) {return Rank;}
// PVBLK GetBlkp(void) {return Blkp;}
- void SetCrp(PCOLRES crp) {Crp = crp;}
+// void SetCrp(PCOLRES crp) {Crp = crp;}
// Methods
- virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
+//virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check);
virtual void ReadColumn(PGLOBAL g);
virtual void WriteColumn(PGLOBAL g);
void AllocateBuffers(PGLOBAL g, int rows);
@@ -195,19 +165,19 @@ class ODBCCOL : public COLBLK {
// void Print(PGLOBAL g, FILE *, uint);
protected:
- // Constructor used by GetMaxSize
+ // Constructor for count(*) column
ODBCCOL(void);
// Members
TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's
- PCOLRES Crp; // To storage result
- void *Bufp; // To extended buffer
- PVBLK Blkp; // To Value Block
+//PCOLRES Crp; // To storage result
+//void *Bufp; // To extended buffer
+//PVBLK Blkp; // To Value Block
//char F_Date[12]; // Internal Date format
- PVAL To_Val; // To value used for Insert
+//PVAL To_Val; // To value used for Insert
SQLLEN *StrLen; // As returned by ODBC
SQLLEN Slen; // Used with Fetch
- int Rank; // Rank (position) number in the query
+//int Rank; // Rank (position) number in the query
}; // end of class ODBCCOL
/***********************************************************************/
@@ -228,28 +198,19 @@ class TDBXDBC : public TDBODBC {
{return (PTDB)new(g) TDBXDBC(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
-//virtual int GetRecpos(void);
-//virtual PSZ GetFile(PGLOBAL g);
-//virtual void SetFile(PGLOBAL g, PSZ fn);
-//virtual void ResetSize(void);
-//virtual int GetAffectedRows(void) {return AftRows;}
-//virtual PSZ GetServer(void) {return "ODBC";}
+ virtual PTDB Clone(PTABS t);
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
-//virtual int GetProgMax(PGLOBAL g);
virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual int WriteDB(PGLOBAL g);
virtual int DeleteDB(PGLOBAL g, int irc);
-//virtual void CloseDB(PGLOBAL g);
protected:
// Internal functions
PCMD MakeCMD(PGLOBAL g);
-//bool BindParameters(PGLOBAL g);
// Members
PCMD Cmdlist; // The commands to execute
diff --git a/storage/connect/tabpivot.cpp b/storage/connect/tabpivot.cpp
index 256b454741c..c6d32884417 100644
--- a/storage/connect/tabpivot.cpp
+++ b/storage/connect/tabpivot.cpp
@@ -1,11 +1,11 @@
/************ TabPivot C++ Program Source Code File (.CPP) *************/
/* PROGRAM NAME: TABPIVOT */
/* ------------- */
-/* Version 1.6 */
+/* Version 1.7 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
@@ -41,6 +41,7 @@
#include "global.h"
#include "plgdbsem.h"
#include "xtable.h"
+#include "tabext.h"
#include "tabcol.h"
#include "colblk.h"
#include "tabmysql.h"
@@ -883,7 +884,7 @@ SRCCOL::SRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int n)
/***********************************************************************/
/* Initialize the column as pointing to the source column. */
/***********************************************************************/
-bool SRCCOL::Init(PGLOBAL g, PTDBASE tp)
+bool SRCCOL::Init(PGLOBAL g, PTDB tp)
{
if (PRXCOL::Init(g, tp))
return true;
diff --git a/storage/connect/tabpivot.h b/storage/connect/tabpivot.h
index c397af05234..07d5c3e456b 100644
--- a/storage/connect/tabpivot.h
+++ b/storage/connect/tabpivot.h
@@ -183,7 +183,7 @@ class SRCCOL : public PRXCOL {
using PRXCOL::Init;
virtual void Reset(void) {}
void SetColumn(void);
- virtual bool Init(PGLOBAL g, PTDBASE tp);
+ virtual bool Init(PGLOBAL g, PTDB tp);
bool CompareLast(void);
protected:
diff --git a/storage/connect/tabsys.cpp b/storage/connect/tabsys.cpp
index 76890e84429..2ddd1c3c753 100644
--- a/storage/connect/tabsys.cpp
+++ b/storage/connect/tabsys.cpp
@@ -159,7 +159,7 @@ TDBINI::TDBINI(PTDBINI tdbp) : TDBASE(tdbp)
} // end of TDBINI copy constructor
// Is this really useful ???
-PTDB TDBINI::CopyOne(PTABS t)
+PTDB TDBINI::Clone(PTABS t)
{
PTDB tp;
PINICOL cp1, cp2;
@@ -173,7 +173,7 @@ PTDB TDBINI::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Get the section list from the INI file. */
@@ -565,7 +565,7 @@ TDBXIN::TDBXIN(PTDBXIN tdbp) : TDBINI(tdbp)
} // end of TDBXIN copy constructor
// Is this really useful ???
-PTDB TDBXIN::CopyOne(PTABS t)
+PTDB TDBXIN::Clone(PTABS t)
{
PTDB tp;
PXINCOL cp1, cp2;
@@ -579,7 +579,7 @@ PTDB TDBXIN::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Get the key list from the INI file. */
diff --git a/storage/connect/tabsys.h b/storage/connect/tabsys.h
index 6b454322906..ff1b8335690 100644
--- a/storage/connect/tabsys.h
+++ b/storage/connect/tabsys.h
@@ -57,7 +57,7 @@ class TDBINI : public TDBASE {
virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBINI(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual int GetRecpos(void) {return N;}
virtual int GetProgCur(void) {return N;}
//virtual int GetAffectedRows(void) {return 0;}
@@ -136,7 +136,7 @@ class TDBXIN : public TDBINI {
virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXIN(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual int GetRecpos(void);
virtual bool SetRecpos(PGLOBAL g, int recpos);
virtual void ResetDB(void)
diff --git a/storage/connect/tabtbl.cpp b/storage/connect/tabtbl.cpp
index e3baf7c3da5..0bf3f6beb43 100644
--- a/storage/connect/tabtbl.cpp
+++ b/storage/connect/tabtbl.cpp
@@ -1,11 +1,11 @@
/************* TabTbl C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: TABTBL */
/* ------------- */
-/* Version 1.7 */
+/* Version 1.8 */
/* */
/* COPYRIGHT: */
/* ---------- */
-/* (C) Copyright to PlugDB Software Development 2008-2016 */
+/* (C) Copyright to PlugDB Software Development 2008-2017 */
/* Author: Olivier BERTRAND */
/* */
/* WHAT THIS PROGRAM DOES: */
@@ -70,6 +70,7 @@
#include "tabcol.h"
#include "tabdos.h" // TDBDOS and DOSCOL class dcls
#include "tabtbl.h"
+#include "tabext.h"
#include "tabmysql.h"
#include "ha_connect.h"
@@ -411,9 +412,9 @@ void TDBTBL::ResetDB(void)
colp->COLBLK::Reset();
for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext())
- ((PTDBASE)tabp->GetTo_Tdb())->ResetDB();
+ tabp->GetTo_Tdb()->ResetDB();
- Tdbp = (PTDBASE)Tablist->GetTo_Tdb();
+ Tdbp = Tablist->GetTo_Tdb();
Crp = 0;
} // end of ResetDB
@@ -458,7 +459,7 @@ bool TDBTBL::OpenDB(PGLOBAL g)
return TRUE;
if ((CurTable = Tablist)) {
- Tdbp = (PTDBASE)CurTable->GetTo_Tdb();
+ Tdbp = CurTable->GetTo_Tdb();
// Tdbp->SetMode(Mode);
// Tdbp->ResetDB();
// Tdbp->ResetSize();
@@ -515,7 +516,7 @@ int TDBTBL::ReadDB(PGLOBAL g)
/* Continue reading from next table file. */
/***************************************************************/
Tdbp->CloseDB(g);
- Tdbp = (PTDBASE)CurTable->GetTo_Tdb();
+ Tdbp = CurTable->GetTo_Tdb();
// Check and initialize the subtable columns
for (PCOL cp = Columns; cp; cp = cp->GetNext())
@@ -609,13 +610,13 @@ void TDBTBM::ResetDB(void)
// Local tables
for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext())
- ((PTDBASE)tabp->GetTo_Tdb())->ResetDB();
+ tabp->GetTo_Tdb()->ResetDB();
// Remote tables
for (PTBMT tp = Tmp; tp; tp = tp->Next)
- ((PTDBASE)tp->Tap->GetTo_Tdb())->ResetDB();
+ tp->Tap->GetTo_Tdb()->ResetDB();
- Tdbp = (Tablist) ? (PTDBASE)Tablist->GetTo_Tdb() : NULL;
+ Tdbp = (Tablist) ? Tablist->GetTo_Tdb() : NULL;
Crp = 0;
} // end of ResetDB
@@ -716,7 +717,7 @@ bool TDBTBM::OpenDB(PGLOBAL g)
/* Proceed with local tables. */
/*********************************************************************/
if ((CurTable = Tablist)) {
- Tdbp = (PTDBASE)CurTable->GetTo_Tdb();
+ Tdbp = CurTable->GetTo_Tdb();
// Tdbp->SetMode(Mode);
// Check and initialize the subtable columns
@@ -808,7 +809,7 @@ int TDBTBM::ReadNextRemote(PGLOBAL g)
} // endif Curtable
- Tdbp = (PTDBASE)Cmp->Tap->GetTo_Tdb();
+ Tdbp = Cmp->Tap->GetTo_Tdb();
// Check and initialize the subtable columns
for (PCOL cp = Columns; cp; cp = cp->GetNext())
diff --git a/storage/connect/tabutil.cpp b/storage/connect/tabutil.cpp
index 4069cdbed2a..762c61bd1a1 100644
--- a/storage/connect/tabutil.cpp
+++ b/storage/connect/tabutil.cpp
@@ -1,7 +1,7 @@
/************* Tabutil cpp Declares Source Code File (.CPP) ************/
-/* Name: TABUTIL.CPP Version 1.1 */
+/* Name: TABUTIL.CPP Version 1.2 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2013 - 2016 */
+/* (C) Copyright to the author Olivier BERTRAND 2013 - 2017 */
/* */
/* Utility function used by the PROXY, XCOL, OCCUR, and TBL tables. */
/***********************************************************************/
@@ -45,8 +45,9 @@
#include "myutil.h"
#include "valblk.h"
#include "resource.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "xtable.h"
+#include "tabext.h"
#include "tabmysql.h"
#include "tabcol.h"
#include "tabutil.h"
@@ -356,7 +357,7 @@ TDBPRX::TDBPRX(PTDBPRX tdbp) : TDBASE(tdbp)
} // end of TDBPRX copy constructor
// Method
-PTDB TDBPRX::CopyOne(PTABS t)
+PTDB TDBPRX::Clone(PTABS t)
{
PTDB tp;
PPRXCOL cp1, cp2;
@@ -370,12 +371,12 @@ PTDB TDBPRX::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Get the PTDB of the sub-table. */
/***********************************************************************/
-PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b)
+PTDB TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b)
{
const char *sp = NULL;
char *db, *name;
@@ -456,13 +457,13 @@ PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b)
if (trace && tdbp)
htrc("Subtable %s in %s\n",
- name, SVP(((PTDBASE)tdbp)->GetDef()->GetDB()));
+ name, SVP(tdbp->GetDef()->GetDB()));
err:
if (s)
free_table_share(s);
- return (PTDBASE)tdbp;
+ return tdbp;
} // end of GetSubTable
/***********************************************************************/
@@ -560,9 +561,9 @@ bool TDBPRX::OpenDB(PGLOBAL g)
/* its column blocks in mode write (required by XML tables). */
/*********************************************************************/
if (Mode == MODE_UPDATE) {
- PTDBASE utp;
+ PTDB utp;
- if (!(utp= (PTDBASE)Tdbp->Duplicate(g))) {
+ if (!(utp= Tdbp->Duplicate(g))) {
sprintf(g->Message, MSG(INV_UPDT_TABLE), Tdbp->GetName());
return true;
} // endif tp
@@ -681,7 +682,7 @@ char *PRXCOL::Decode(PGLOBAL g, const char *cnm)
/* PRXCOL initialization routine. */
/* Look for the matching column in the object table. */
/***********************************************************************/
-bool PRXCOL::Init(PGLOBAL g, PTDBASE tp)
+bool PRXCOL::Init(PGLOBAL g, PTDB tp)
{
if (!tp)
tp = ((PTDBPRX)To_Tdb)->Tdbp;
diff --git a/storage/connect/tabutil.h b/storage/connect/tabutil.h
index b320d169b36..8e56aecff86 100644
--- a/storage/connect/tabutil.h
+++ b/storage/connect/tabutil.h
@@ -67,7 +67,7 @@ class DllExport TDBPRX : public TDBASE {
{return (PTDB)new(g) TDBPRX(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual int GetRecpos(void) {return Tdbp->GetRecpos();}
virtual void ResetDB(void) {Tdbp->ResetDB();}
virtual int RowNumber(PGLOBAL g, bool b = FALSE);
@@ -83,12 +83,12 @@ class DllExport TDBPRX : public TDBASE {
virtual int WriteDB(PGLOBAL g);
virtual int DeleteDB(PGLOBAL g, int irc);
virtual void CloseDB(PGLOBAL g) {if (Tdbp) Tdbp->CloseDB(g);}
- PTDBASE GetSubTable(PGLOBAL g, PTABLE tabp, bool b = false);
+ PTDB GetSubTable(PGLOBAL g, PTABLE tabp, bool b = false);
void RemoveNext(PTABLE tp);
protected:
// Members
- PTDBASE Tdbp; // The object table
+ PTDB Tdbp; // The object table
}; // end of class TDBPRX
/***********************************************************************/
@@ -115,7 +115,7 @@ class DllExport PRXCOL : public COLBLK {
{return false;}
virtual void ReadColumn(PGLOBAL g);
virtual void WriteColumn(PGLOBAL g);
- virtual bool Init(PGLOBAL g, PTDBASE tp);
+ virtual bool Init(PGLOBAL g, PTDB tp);
protected:
char *Decode(PGLOBAL g, const char *cnm);
diff --git a/storage/connect/tabvct.cpp b/storage/connect/tabvct.cpp
index e788529075f..282fb55a43c 100644
--- a/storage/connect/tabvct.cpp
+++ b/storage/connect/tabvct.cpp
@@ -241,7 +241,7 @@ PTDB VCTDEF::GetTable(PGLOBAL g, MODE mode)
/*********************************************************************/
if (mode != MODE_INSERT)
if (tdbp->GetBlockValues(g))
- PushWarning(g, (PTDBASE)tdbp);
+ PushWarning(g, tdbp);
// return NULL; // causes a crash when deleting index
return tdbp;
@@ -263,7 +263,7 @@ TDBVCT::TDBVCT(PGLOBAL g, PTDBVCT tdbp) : TDBFIX(g, tdbp)
} // end of TDBVCT copy constructor
// Method
-PTDB TDBVCT::CopyOne(PTABS t)
+PTDB TDBVCT::Clone(PTABS t)
{
PTDB tp;
PVCTCOL cp1, cp2;
@@ -277,7 +277,7 @@ PTDB TDBVCT::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate VCT column description block. */
diff --git a/storage/connect/tabvct.h b/storage/connect/tabvct.h
index 8ad3c8e21be..189a9ae2221 100644
--- a/storage/connect/tabvct.h
+++ b/storage/connect/tabvct.h
@@ -68,7 +68,7 @@ class DllExport TDBVCT : public TDBFIX {
bool IsSplit(void) {return ((VCTDEF*)To_Def)->Split;}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual bool IsUsingTemp(PGLOBAL g);
// Database routines
diff --git a/storage/connect/tabvir.cpp b/storage/connect/tabvir.cpp
index 356fc981357..155c71fe268 100644
--- a/storage/connect/tabvir.cpp
+++ b/storage/connect/tabvir.cpp
@@ -20,7 +20,7 @@
#include "plgdbsem.h"
#include "filter.h"
#include "xtable.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "colblk.h"
#include "mycat.h" // for FNC_COL
#include "tabvir.h"
diff --git a/storage/connect/tabwmi.cpp b/storage/connect/tabwmi.cpp
index 98a44b9d635..4871a1d66dc 100644
--- a/storage/connect/tabwmi.cpp
+++ b/storage/connect/tabwmi.cpp
@@ -1,5 +1,5 @@
/***********************************************************************/
-/* TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 - 2013 */
+/* TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 - 2017 */
/* TABWMI: Virtual table to get WMI information. */
/***********************************************************************/
#if !defined(__WIN__)
@@ -11,8 +11,9 @@
#include "global.h"
#include "plgdbsem.h"
#include "mycat.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "xtable.h"
+#include "tabext.h"
#include "colblk.h"
//#include "filter.h"
//#include "xindex.h"
@@ -62,7 +63,7 @@ PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname)
if (FAILED(res)) {
sprintf(g->Message, "Failed to initialize COM library. "
- "Error code = %p", res);
+ "Error code = %x", res);
return NULL;
} // endif res
@@ -85,7 +86,7 @@ PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname)
(void**) &loc);
if (FAILED(res)) {
sprintf(g->Message, "Failed to create Locator. "
- "Error code = %p", res);
+ "Error code = %x", res);
CoUninitialize();
return NULL;
} // endif res
@@ -94,7 +95,7 @@ PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname)
NULL, NULL, NULL, 0, NULL, NULL, &wp->Svc);
if (FAILED(res)) {
- sprintf(g->Message, "Could not connect. Error code = %p", res);
+ sprintf(g->Message, "Could not connect. Error code = %x", res);
loc->Release();
CoUninitialize();
return NULL;
@@ -423,7 +424,7 @@ bool TDBWMI::Initialize(PGLOBAL g)
if (FAILED(Res)) {
sprintf(g->Message, "Failed to initialize COM library. "
- "Error code = %p", Res);
+ "Error code = %x", Res);
return true; // Program has failed.
} // endif Res
@@ -436,7 +437,7 @@ bool TDBWMI::Initialize(PGLOBAL g)
if (FAILED(Res)) {
sprintf(g->Message, "Failed to create Locator. "
- "Error code = %p", Res);
+ "Error code = %x", Res);
CoUninitialize();
return true; // Program has failed.
} // endif Res
@@ -448,7 +449,7 @@ bool TDBWMI::Initialize(PGLOBAL g)
NULL, NULL,0, NULL, 0, 0, &Svc);
if (FAILED(Res)) {
- sprintf(g->Message, "Could not connect. Error code = %p", Res);
+ sprintf(g->Message, "Could not connect. Error code = %x", Res);
loc->Release();
CoUninitialize();
return true; // Program has failed.
@@ -463,7 +464,7 @@ bool TDBWMI::Initialize(PGLOBAL g)
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
if (FAILED(Res)) {
- sprintf(g->Message, "Could not set proxy. Error code = 0x", Res);
+ sprintf(g->Message, "Could not set proxy. Error code = %x", Res);
Svc->Release();
CoUninitialize();
return true; // Program has failed.
@@ -573,7 +574,7 @@ bool TDBWMI::GetWMIInfo(PGLOBAL g)
NULL, &Enumerator);
if (FAILED(Rc)) {
- sprintf(g->Message, "Query %s failed. Error code = %p", cmd, Rc);
+ sprintf(g->Message, "Query %s failed. Error code = %x", cmd, Rc);
Svc->Release();
CoUninitialize();
return true; // Program has failed.
diff --git a/storage/connect/tabxcl.cpp b/storage/connect/tabxcl.cpp
index add61431493..93a24accc3c 100644
--- a/storage/connect/tabxcl.cpp
+++ b/storage/connect/tabxcl.cpp
@@ -1,7 +1,7 @@
/************* TabXcl CPP Declares Source Code File (.CPP) *************/
/* Name: TABXCL.CPP Version 1.0 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 2013 */
+/* (C) Copyright to the author Olivier BERTRAND 2013-2017 */
/* */
/* XCOL: Table having one column containing several values */
/* comma separated. When creating the table, the name of the X */
@@ -45,12 +45,12 @@
#include "plgdbsem.h"
#include "plgcnx.h" // For DB types
#include "resource.h"
-#include "reldef.h"
+#include "xtable.h"
+#include "tabext.h"
#include "filamtxt.h"
#include "tabdos.h"
#include "tabcol.h"
#include "tabxcl.h"
-#include "xtable.h"
#include "tabmysql.h"
#include "ha_connect.h"
@@ -246,7 +246,7 @@ XCLCOL::XCLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i)
/* XCLCOL initialization routine. */
/* Allocate Cbuf that will contain the Colp value. */
/***********************************************************************/
-bool XCLCOL::Init(PGLOBAL g, PTDBASE tp)
+bool XCLCOL::Init(PGLOBAL g, PTDB tp)
{
if (PRXCOL::Init(g, tp))
return true;
diff --git a/storage/connect/tabxcl.h b/storage/connect/tabxcl.h
index 291f0b4263a..fde000ee709 100644
--- a/storage/connect/tabxcl.h
+++ b/storage/connect/tabxcl.h
@@ -91,7 +91,7 @@ class XCLCOL : public PRXCOL {
using PRXCOL::Init;
virtual void Reset(void) {} // Evaluated only by TDBXCL
virtual void ReadColumn(PGLOBAL g);
- virtual bool Init(PGLOBAL g, PTDBASE tp = NULL);
+ virtual bool Init(PGLOBAL g, PTDB tp = NULL);
protected:
// Default constructor not to be used
diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp
index 3b8229fcf51..52cf3d3812f 100644
--- a/storage/connect/tabxml.cpp
+++ b/storage/connect/tabxml.cpp
@@ -42,7 +42,7 @@
/***********************************************************************/
#include "global.h"
#include "plgdbsem.h"
-#include "reldef.h"
+//#include "reldef.h"
#include "xtable.h"
#include "colblk.h"
#include "mycat.h"
@@ -537,6 +537,11 @@ PTDB XMLDEF::GetTable(PGLOBAL g, MODE m)
if (Catfunc == FNC_COL)
return new(g) TDBXCT(this);
+ if (Zipped && !(m == MODE_READ || m == MODE_ANY)) {
+ strcpy(g->Message, "ZIpped XML tables are read only");
+ return NULL;
+ } // endif Zipped
+
PTDBASE tdbp = new(g) TDBXML(this);
if (Multiple)
@@ -655,7 +660,7 @@ TDBXML::TDBXML(PTDBXML tdbp) : TDBASE(tdbp)
} // end of TDBXML copy constructor
// Used for update
-PTDB TDBXML::CopyOne(PTABS t)
+PTDB TDBXML::Clone(PTABS t)
{
PTDB tp;
PXMLCOL cp1, cp2;
@@ -669,7 +674,7 @@ PTDB TDBXML::CopyOne(PTABS t)
} // endfor cp1
return tp;
- } // end of CopyOne
+ } // end of Clone
/***********************************************************************/
/* Allocate XML column description block. */
@@ -926,7 +931,7 @@ bool TDBXML::Initialize(PGLOBAL g)
if (rc)
sprintf(g->Message, "%s: %s", MSG(COM_ERROR), buf);
else
- sprintf(g->Message, "%s hr=%p", MSG(COM_ERROR), e.Error());
+ sprintf(g->Message, "%s hr=%x", MSG(COM_ERROR), e.Error());
goto error;
#endif // __WIN__
diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h
index 6c586d79dec..65b353072cb 100644
--- a/storage/connect/tabxml.h
+++ b/storage/connect/tabxml.h
@@ -71,7 +71,7 @@ class DllExport TDBXML : public TDBASE {
virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXML(this);}
// Methods
- virtual PTDB CopyOne(PTABS t);
+ virtual PTDB Clone(PTABS t);
virtual int GetRecpos(void);
virtual int GetProgCur(void) {return N;}
virtual PSZ GetFile(PGLOBAL g) {return Xfile;}
diff --git a/storage/connect/tabzip.cpp b/storage/connect/tabzip.cpp
index 11f414ee154..b91059a3843 100644
--- a/storage/connect/tabzip.cpp
+++ b/storage/connect/tabzip.cpp
@@ -70,8 +70,12 @@ PCOL TDBZIP::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
/* param: filename path and the filename of the zip file to open. */
/* return: true if open, false otherwise. */
/***********************************************************************/
-bool TDBZIP::open(PGLOBAL g, const char *filename)
+bool TDBZIP::open(PGLOBAL g, const char *fn)
{
+ char filename[_MAX_PATH];
+
+ PlugSetPath(filename, fn, GetPath());
+
if (!zipfile && !(zipfile = unzOpen64(filename)))
sprintf(g->Message, "Zipfile open error");
@@ -102,7 +106,7 @@ int TDBZIP::Cardinality(PGLOBAL g)
unz_global_info64 ginfo;
int err = unzGetGlobalInfo64(zipfile, &ginfo);
- Cardinal = (err == UNZ_OK) ? ginfo.number_entry : 0;
+ Cardinal = (err == UNZ_OK) ? (int)ginfo.number_entry : 0;
} else
Cardinal = 0;
@@ -221,6 +225,14 @@ void ZIPCOL::ReadColumn(PGLOBAL g)
case 3:
Value->SetValue((int)Tdbz->finfo.compression_method);
break;
+ case 4:
+ Tdbz->finfo.tmu_date.tm_year -= 1900;
+
+ if (((DTVAL*)Value)->MakeTime((tm*)&Tdbz->finfo.tmu_date))
+ Value->SetNull(true);
+
+ Tdbz->finfo.tmu_date.tm_year += 1900;
+ break;
default:
Value->SetValue_psz((PSZ)Tdbz->fn);
} // endswitch flag
diff --git a/storage/connect/tabzip.h b/storage/connect/tabzip.h
index 6f1735258e7..dcec3475371 100644
--- a/storage/connect/tabzip.h
+++ b/storage/connect/tabzip.h
@@ -20,7 +20,7 @@ typedef class ZIPCOL *PZIPCOL;
/***********************************************************************/
class DllExport ZIPDEF : public DOSDEF { /* Table description */
friend class TDBZIP;
- friend class ZIPFAM;
+ friend class UNZFAM;
public:
// Constructor
ZIPDEF(void) {}
diff --git a/storage/connect/value.h b/storage/connect/value.h
index a670ade4c28..14a568c3549 100644
--- a/storage/connect/value.h
+++ b/storage/connect/value.h
@@ -271,7 +271,7 @@ class DllExport TYPVAL<PSZ>: public VALUE {
virtual void Reset(void) {*Strp = 0;}
virtual int GetValLen(void) {return Len;};
virtual int GetValPrec() {return (Ci) ? 1 : 0;}
- virtual int GetSize(void) {return (Strp) ? strlen(Strp) : 0;}
+ virtual int GetSize(void) {return (Strp) ? (int)strlen(Strp) : 0;}
virtual PSZ GetCharValue(void) {return Strp;}
virtual char GetTinyValue(void);
virtual uchar GetUTinyValue(void);
diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp
index a2cf4e77b80..15fb71ab88a 100755
--- a/storage/connect/xindex.cpp
+++ b/storage/connect/xindex.cpp
@@ -81,7 +81,7 @@ int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
{
int rc;
PTABLE tablep;
- PTDBASE tdbp;
+ PTDB tdbp;
PCATLG cat = PlgGetCatalog(g, true);
/*********************************************************************/
@@ -89,12 +89,12 @@ int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add)
/*********************************************************************/
tablep = new(g) XTAB(name);
- if (!(tdbp = (PTDBASE)cat->GetTable(g, tablep)))
+ if (!(tdbp = cat->GetTable(g, tablep)))
rc = RC_NF;
else if (!tdbp->GetDef()->Indexable()) {
sprintf(g->Message, MSG(TABLE_NO_INDEX), name);
rc = RC_NF;
- } else if ((rc = tdbp->MakeIndex(g, pxdf, add)) == RC_INFO)
+ } else if ((rc = ((PTDBASE)tdbp)->MakeIndex(g, pxdf, add)) == RC_INFO)
rc = RC_OK; // No or remote index
return rc;
@@ -2738,7 +2738,7 @@ bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size)
} // endif nbr
} else {
- char *buf[256];
+ char buf[256];
DWORD drc = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
diff --git a/storage/connect/xindex.h b/storage/connect/xindex.h
index ef2e934e5ee..2d10d72722e 100644
--- a/storage/connect/xindex.h
+++ b/storage/connect/xindex.h
@@ -184,7 +184,7 @@ class DllExport XXBASE : public CSORT, public BLOCK {
virtual bool IsRandom(void) {return true;}
virtual bool IsDynamic(void) {return Dynamic;}
virtual void SetDynamic(bool dyn) {Dynamic = dyn;}
- virtual bool HaveSame(void) {return false;}
+//virtual bool HaveSame(void) {return false;}
virtual int GetCurPos(void) {return Cur_K;}
virtual void SetNval(int n) {assert(n == 1);}
virtual void SetOp(OPVAL op) {Op = op;}
@@ -256,7 +256,7 @@ class DllExport XINDEX : public XXBASE {
// Implementation
virtual IDT GetType(void) {return TYPE_IDX_INDX;}
virtual bool IsMul(void) {return (Nval < Nk) ? true : Mul;}
- virtual bool HaveSame(void) {return Op == OP_SAME;}
+//virtual bool HaveSame(void) {return Op == OP_SAME;}
virtual int GetCurPos(void) {return (Pex) ? Pex[Cur_K] : Cur_K;}
virtual void SetNval(int n) {Nval = n;}
int GetMaxSame(void) {return MaxSame;}
diff --git a/storage/connect/xobject.h b/storage/connect/xobject.h
index d78cd09f9a4..8f6c23c4aeb 100644
--- a/storage/connect/xobject.h
+++ b/storage/connect/xobject.h
@@ -127,7 +127,8 @@ class DllExport STRING : public BLOCK {
// Implementation
inline int GetLength(void) {return (int)Length;}
- inline PSZ GetStr(void) {return Strp;}
+ inline void SetLength(uint n) {Length = n;}
+ inline PSZ GetStr(void) {return Strp;}
inline uint32 GetSize(void) {return Size;}
// Methods
diff --git a/storage/connect/xtable.h b/storage/connect/xtable.h
index e18a08a54b8..4aeea05946a 100644
--- a/storage/connect/xtable.h
+++ b/storage/connect/xtable.h
@@ -1,7 +1,7 @@
/**************** Table H Declares Source Code File (.H) ***************/
-/* Name: TABLE.H Version 2.3 */
+/* Name: TABLE.H Version 2.4 */
/* */
-/* (C) Copyright to the author Olivier BERTRAND 1999-2015 */
+/* (C) Copyright to the author Olivier BERTRAND 1999-2017 */
/* */
/* This file contains the TBX, OPJOIN and TDB class definitions. */
/***********************************************************************/
@@ -17,6 +17,7 @@
#include "block.h"
#include "colblk.h"
#include "m_ctype.h"
+#include "reldef.h"
typedef class CMD *PCMD;
typedef struct st_key_range key_range;
@@ -32,24 +33,30 @@ class CMD : public BLOCK {
char *Cmd;
}; // end of class CMD
+#if 0
// Condition filter structure
class CONDFIL : public BLOCK {
public:
// Constructor
CONDFIL(const Item *cond, uint idx, AMT type)
{
- Cond = cond; Idx = idx; Type = type; Body = NULL; Op = OP_XX; Cmds = NULL;
+ Cond = cond; Idx = idx; Type = type; Op = OP_XX;
+ Cmds = NULL; All = true; Body = NULL, Having = NULL;
}
// Members
const Item *Cond;
AMT Type;
uint Idx;
- char *Body;
OPVAL Op;
PCMD Cmds;
+ bool All;
+ char *Body;
+ char *Having;
}; // end of class CONDFIL
+#endif // 0
+typedef class EXTCOL *PEXTCOL;
typedef class CONDFIL *PCFIL;
typedef class TDBCAT *PTDBCAT;
typedef class CATCOL *PCATCOL;
@@ -64,47 +71,61 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
TDB(PTDB tdbp);
// Implementation
- static void SetTnum(int n) {Tnum = n;}
- inline PTDB GetOrig(void) {return To_Orig;}
- inline TUSE GetUse(void) {return Use;}
- inline PCFIL GetCondFil(void) {return To_CondFil;}
- inline LPCSTR GetName(void) {return Name;}
- inline PTABLE GetTable(void) {return To_Table;}
- inline PCOL GetColumns(void) {return Columns;}
- inline int GetDegree(void) {return Degree;}
- inline MODE GetMode(void) {return Mode;}
- inline PFIL GetFilter(void) {return To_Filter;}
- inline void SetFilter(PFIL fp) {To_Filter = fp;}
- inline void SetOrig(PTDB txp) {To_Orig = txp;}
- inline void SetUse(TUSE n) {Use = n;}
- inline void SetCondFil(PCFIL cfp) {To_CondFil = cfp;}
- inline void SetNext(PTDB tdbp) {Next = tdbp;}
- inline void SetName(LPCSTR name) {Name = name;}
- inline void SetTable(PTABLE tablep) {To_Table = tablep;}
- inline void SetColumns(PCOL colp) {Columns = colp;}
- inline void SetDegree(int degree) {Degree = degree;}
- inline void SetMode(MODE mode) {Mode = mode;}
+ static void SetTnum(int n) {Tnum = n;}
+ inline PTABDEF GetDef(void) {return To_Def;}
+ inline PTDB GetOrig(void) {return To_Orig;}
+ inline TUSE GetUse(void) {return Use;}
+ inline PCFIL GetCondFil(void) {return To_CondFil;}
+ inline LPCSTR GetName(void) {return Name;}
+ inline PTABLE GetTable(void) {return To_Table;}
+ inline PCOL GetColumns(void) {return Columns;}
+ inline int GetDegree(void) {return Degree;}
+ inline MODE GetMode(void) {return Mode;}
+ inline PFIL GetFilter(void) {return To_Filter;}
+ inline PCOL GetSetCols(void) {return To_SetCols;}
+ inline void SetSetCols(PCOL colp) {To_SetCols = colp;}
+ inline void SetFilter(PFIL fp) {To_Filter = fp;}
+ inline void SetOrig(PTDB txp) {To_Orig = txp;}
+ inline void SetUse(TUSE n) {Use = n;}
+ inline void SetCondFil(PCFIL cfp) {To_CondFil = cfp;}
+ inline void SetNext(PTDB tdbp) {Next = tdbp;}
+ inline void SetName(LPCSTR name) {Name = name;}
+ inline void SetTable(PTABLE tablep) {To_Table = tablep;}
+ inline void SetColumns(PCOL colp) {Columns = colp;}
+ inline void SetDegree(int degree) {Degree = degree;}
+ inline void SetMode(MODE mode) {Mode = mode;}
// Properties
- virtual AMT GetAmType(void) {return TYPE_AM_ERROR;}
- virtual int GetTdb_No(void) {return Tdb_No;}
- virtual PTDB GetNext(void) {return Next;}
- virtual PCATLG GetCat(void) {return NULL;}
- virtual void SetAbort(bool) {;}
+ virtual AMT GetAmType(void) {return TYPE_AM_ERROR;}
+ virtual bool IsRemote(void) {return false;}
+ virtual bool IsIndexed(void) {return false;}
+ virtual int GetTdb_No(void) {return Tdb_No;}
+ virtual PTDB GetNext(void) {return Next;}
+ virtual PCATLG GetCat(void) {return NULL;}
+ virtual void SetAbort(bool) {;}
+ virtual PKXBASE GetKindex(void) {return NULL;}
// Methods
virtual bool IsSame(PTDB tp) {return tp == this;}
- virtual bool IsSpecial(PSZ name) = 0;
- virtual bool GetBlockValues(PGLOBAL) {return false;}
+ virtual bool IsSpecial(PSZ name);
+ virtual bool IsReadOnly(void) {return Read_Only;}
+ virtual bool IsView(void) {return FALSE;}
+ virtual PSZ GetPath(void);
+ virtual RECFM GetFtype(void) {return RECFM_NAF;}
+ virtual bool GetBlockValues(PGLOBAL) { return false; }
virtual int Cardinality(PGLOBAL) {return 0;}
- virtual int GetMaxSize(PGLOBAL) = 0;
+ virtual int GetRecpos(void) = 0;
+ virtual bool SetRecpos(PGLOBAL g, int recpos);
+ virtual int GetMaxSize(PGLOBAL) = 0;
virtual int GetProgMax(PGLOBAL) = 0;
- virtual int GetProgCur(void) = 0;
- virtual int RowNumber(PGLOBAL g, bool b = false);
- virtual bool IsReadOnly(void) {return true;}
- virtual const CHARSET_INFO *data_charset() {return NULL;}
+ virtual int GetProgCur(void) {return GetRecpos();}
+ virtual PSZ GetFile(PGLOBAL) {return "Not a file";}
+ virtual void SetFile(PGLOBAL, PSZ) {}
+ virtual void ResetDB(void) {}
+ virtual void ResetSize(void) {MaxSize = -1;}
+ virtual int RowNumber(PGLOBAL g, bool b = false);
virtual PTDB Duplicate(PGLOBAL) {return NULL;}
- virtual PTDB CopyOne(PTABS) {return this;}
+ virtual PTDB Clone(PTABS) {return this;}
virtual PTDB Copy(PTABS t);
virtual void PrintAM(FILE *f, char *m)
{fprintf(f, "%s AM(%d)\n", m, GetAmType());}
@@ -112,10 +133,15 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
virtual void Print(PGLOBAL g, char *ps, uint z);
virtual PSZ GetServer(void) = 0;
virtual int GetBadLines(void) {return 0;}
+ virtual CHARSET_INFO *data_charset(void);
- // Database pure virtual routines
- virtual PCOL ColDB(PGLOBAL g, PSZ name, int num) = 0;
- virtual void MarkDB(PGLOBAL, PTDB) = 0;
+ // Database routines
+ virtual PCOL ColDB(PGLOBAL g, PSZ name, int num);
+ virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int)
+ {assert(false); return NULL;}
+ virtual PCOL InsertSpecialColumn(PCOL colp);
+ virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp);
+ virtual void MarkDB(PGLOBAL g, PTDB tdb2);
virtual bool OpenDB(PGLOBAL) = 0;
virtual int ReadDB(PGLOBAL) = 0;
virtual int WriteDB(PGLOBAL) = 0;
@@ -126,20 +152,26 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block.
protected:
// Members
- PTDB To_Orig; // Pointer to original if it is a copy
- TUSE Use;
- PFIL To_Filter;
- PCFIL To_CondFil; // To condition filter structure
- static int Tnum; // Used to generate Tdb_no's
- const int Tdb_No; // GetTdb_No() is always 0 for OPJOIN
- PTDB Next; // Next in linearized queries
- PTABLE To_Table; // Points to the XTAB object
- LPCSTR Name; // Table name
- PCOL Columns; // Points to the first column of the table
- MODE Mode; // 10 Read, 30 Update, 40 Insert, 50 Delete
- int Degree; // Number of columns
- int Cardinal; // Table number of rows
- }; // end of class TDB
+ PTDB To_Orig; // Pointer to original if it is a copy
+ PTABDEF To_Def; // Points to catalog description block
+ TUSE Use;
+ PFIL To_Filter;
+ PCFIL To_CondFil; // To condition filter structure
+ static int Tnum; // Used to generate Tdb_no's
+ const int Tdb_No; // GetTdb_No() is always 0 for OPJOIN
+ PTDB Next; // Next in linearized queries
+ PTABLE To_Table; // Points to the XTAB object
+ LPCSTR Name; // Table name
+ PCOL Columns; // Points to the first column of the table
+ PCOL To_SetCols; // Points to updated columns
+ MODE Mode; // 10 Read, 30 Update, 40 Insert, 50 Delete
+ int Degree; // Number of columns
+ int Cardinal; // Table number of rows
+ int MaxSize; // Max size in number of lines
+ bool Read_Only; // True for read only tables
+ const CHARSET_INFO *m_data_charset;
+ const char *csname; // Table charset name
+}; // end of class TDB
/***********************************************************************/
/* This is the base class for all query tables (except decode). */
@@ -155,50 +187,50 @@ class DllExport TDBASE : public TDB {
// Implementation
inline int GetKnum(void) {return Knum;}
- inline PTABDEF GetDef(void) {return To_Def;}
- inline PKXBASE GetKindex(void) {return To_Kindex;}
- inline PCOL GetSetCols(void) {return To_SetCols;}
- inline void SetSetCols(PCOL colp) {To_SetCols = colp;}
+//inline PTABDEF GetDef(void) {return To_Def;}
+//inline PCOL GetSetCols(void) {return To_SetCols;}
+//inline void SetSetCols(PCOL colp) {To_SetCols = colp;}
inline void SetKey_Col(PCOL *cpp) {To_Key_Col = cpp;}
inline void SetXdp(PIXDEF xdp) {To_Xdp = xdp;}
inline void SetKindex(PKXBASE kxp) {To_Kindex = kxp;}
// Properties
- void ResetKindex(PGLOBAL g, PKXBASE kxp);
+ virtual PKXBASE GetKindex(void) {return To_Kindex;}
+ void ResetKindex(PGLOBAL g, PKXBASE kxp);
PCOL Key(int i) {return (To_Key_Col) ? To_Key_Col[i] : NULL;}
// Methods
virtual bool IsUsingTemp(PGLOBAL) {return false;}
- virtual bool IsIndexed(void) {return false;}
- virtual bool IsSpecial(PSZ name);
+//virtual bool IsIndexed(void) {return false;}
+//virtual bool IsSpecial(PSZ name);
virtual PCATLG GetCat(void);
- virtual PSZ GetPath(void);
+//virtual PSZ GetPath(void);
virtual void PrintAM(FILE *f, char *m);
- virtual RECFM GetFtype(void) {return RECFM_NAF;}
+//virtual RECFM GetFtype(void) {return RECFM_NAF;}
//virtual int GetAffectedRows(void) {return -1;}
- virtual int GetRecpos(void) = 0;
- virtual bool SetRecpos(PGLOBAL g, int recpos);
- virtual bool IsReadOnly(void) {return Read_Only;}
- virtual bool IsView(void) {return FALSE;}
- virtual CHARSET_INFO *data_charset(void);
+//virtual int GetRecpos(void) = 0;
+//virtual bool SetRecpos(PGLOBAL g, int recpos);
+//virtual bool IsReadOnly(void) {return Read_Only;}
+//virtual bool IsView(void) {return FALSE;}
+//virtual CHARSET_INFO *data_charset(void);
virtual int GetProgMax(PGLOBAL g) {return GetMaxSize(g);}
- virtual int GetProgCur(void) {return GetRecpos();}
- virtual PSZ GetFile(PGLOBAL) {return "Not a file";}
- virtual int GetRemote(void) {return 0;}
- virtual void SetFile(PGLOBAL, PSZ) {}
- virtual void ResetDB(void) {}
- virtual void ResetSize(void) {MaxSize = -1;}
+//virtual int GetProgCur(void) {return GetRecpos();}
+//virtual PSZ GetFile(PGLOBAL) {return "Not a file";}
+//virtual int GetRemote(void) {return 0;}
+//virtual void SetFile(PGLOBAL, PSZ) {}
+//virtual void ResetDB(void) {}
+//virtual void ResetSize(void) {MaxSize = -1;}
virtual void RestoreNrec(void) {}
virtual int ResetTableOpt(PGLOBAL g, bool dop, bool dox);
virtual PSZ GetServer(void) {return "Current";}
// Database routines
- virtual PCOL ColDB(PGLOBAL g, PSZ name, int num);
- virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int)
- {assert(false); return NULL;}
- virtual PCOL InsertSpecialColumn(PCOL colp);
- virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp);
- virtual void MarkDB(PGLOBAL g, PTDB tdb2);
+//virtual PCOL ColDB(PGLOBAL g, PSZ name, int num);
+//virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int)
+// {assert(false); return NULL;}
+//virtual PCOL InsertSpecialColumn(PCOL colp);
+//virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp);
+//virtual void MarkDB(PGLOBAL g, PTDB tdb2);
virtual int MakeIndex(PGLOBAL g, PIXDEF, bool)
{strcpy(g->Message, "Remote index"); return RC_INFO;}
virtual bool ReadKey(PGLOBAL, OPVAL, const key_range *)
@@ -209,19 +241,19 @@ class DllExport TDBASE : public TDB {
"This function should not be called for this table"); return true;}
// Members
- PTABDEF To_Def; // Points to catalog description block
+//PTABDEF To_Def; // Points to catalog description block
PXOB *To_Link; // Points to column of previous relations
PCOL *To_Key_Col; // Points to key columns in current file
PKXBASE To_Kindex; // Points to table key index
PIXDEF To_Xdp; // To the index definition block
- PCOL To_SetCols; // Points to updated columns
+//PCOL To_SetCols; // Points to updated columns
RECFM Ftype; // File type: 0-var 1-fixed 2-binary (VCT)
- int MaxSize; // Max size in number of lines
+//int MaxSize; // Max size in number of lines
int Knum; // Size of key arrays
- bool Read_Only; // True for read only tables
- const CHARSET_INFO *m_data_charset;
- const char *csname; // Table charset name
- }; // end of class TDBASE
+//bool Read_Only; // True for read only tables
+//const CHARSET_INFO *m_data_charset;
+//const char *csname; // Table charset name
+}; // end of class TDBASE
/***********************************************************************/
/* The abstract base class declaration for the catalog tables. */
@@ -243,7 +275,8 @@ class DllExport TDBCAT : public TDBASE {
// Database routines
virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n);
- virtual int GetMaxSize(PGLOBAL g);
+ virtual int Cardinality(PGLOBAL) {return 10;} // To avoid assert
+ virtual int GetMaxSize(PGLOBAL g);
virtual bool OpenDB(PGLOBAL g);
virtual int ReadDB(PGLOBAL g);
virtual int WriteDB(PGLOBAL g);
@@ -275,7 +308,7 @@ class DllExport CATCOL : public COLBLK {
virtual int GetAmType(void) {return TYPE_AM_ODBC;}
// Methods
- virtual void ReadColumn(PGLOBAL g);
+ virtual void ReadColumn(PGLOBAL g);
protected:
CATCOL(void) {} // Default constructor not to be used
diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc
index 77b915bce92..07451f71f66 100644
--- a/storage/innobase/btr/btr0cur.cc
+++ b/storage/innobase/btr/btr0cur.cc
@@ -1946,8 +1946,6 @@ btr_cur_update_alloc_zip_func(
const page_t* page = page_cur_get_page(cursor);
ut_ad(page_zip == page_cur_get_page_zip(cursor));
-
- ut_ad(page_zip);
ut_ad(!dict_index_is_ibuf(index));
ut_ad(rec_offs_validate(page_cur_get_rec(cursor), index, offsets));
@@ -4371,7 +4369,6 @@ btr_cur_disown_inherited_fields(
ut_ad(rec_offs_validate(rec, index, offsets));
ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec));
ut_ad(rec_offs_any_extern(offsets));
- ut_ad(mtr);
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
if (rec_offs_nth_extern(offsets, i)
@@ -4434,9 +4431,6 @@ btr_push_update_extern_fields(
ulint n;
const upd_field_t* uf;
- ut_ad(tuple);
- ut_ad(update);
-
uf = update->fields;
n = upd_get_n_fields(update);
@@ -4608,7 +4602,6 @@ btr_store_big_rec_extern_fields(
ut_ad(rec_offs_validate(rec, index, offsets));
ut_ad(rec_offs_any_extern(offsets));
- ut_ad(btr_mtr);
ut_ad(mtr_memo_contains(btr_mtr, dict_index_get_lock(index),
MTR_MEMO_X_LOCK));
ut_ad(mtr_memo_contains(btr_mtr, rec_block, MTR_MEMO_PAGE_X_FIX));
diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc
index 94d285b64e7..2a11c15441a 100644
--- a/storage/innobase/dict/dict0dict.cc
+++ b/storage/innobase/dict/dict0dict.cc
@@ -6214,7 +6214,6 @@ dict_set_corrupted(
row_mysql_lock_data_dictionary(trx);
}
- ut_ad(index);
ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(!dict_table_is_comp(dict_sys->sys_tables));
ut_ad(!dict_table_is_comp(dict_sys->sys_indexes));
diff --git a/storage/innobase/dyn/dyn0dyn.cc b/storage/innobase/dyn/dyn0dyn.cc
index 3ef5297a7c9..dd1f6863c14 100644
--- a/storage/innobase/dyn/dyn0dyn.cc
+++ b/storage/innobase/dyn/dyn0dyn.cc
@@ -40,7 +40,6 @@ dyn_array_add_block(
mem_heap_t* heap;
dyn_block_t* block;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
if (arr->heap == NULL) {
diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc
index 4aa30d8ebd2..c5289b48ccc 100644
--- a/storage/innobase/fsp/fsp0fsp.cc
+++ b/storage/innobase/fsp/fsp0fsp.cc
@@ -1071,8 +1071,6 @@ fsp_fill_free_list(
ulint i;
mtr_t ibuf_mtr;
- ut_ad(header != NULL);
- ut_ad(mtr != NULL);
ut_ad(page_offset(header) == FSP_HEADER_OFFSET);
/* Check if we can fill free list from above the free list limit */
@@ -1355,9 +1353,6 @@ fsp_alloc_free_page(
ulint page_no;
ulint space_size;
- ut_ad(mtr);
- ut_ad(init_mtr);
-
header = fsp_get_space_header(space, zip_size, mtr);
/* Get the hinted descriptor */
@@ -2370,7 +2365,6 @@ fseg_alloc_free_page_low(
ibool success;
ulint n;
- ut_ad(mtr);
ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR));
ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
== FSEG_MAGIC_N_VALUE);
diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic
index 800065ddaa1..dafc36f1cab 100644
--- a/storage/innobase/include/dict0dict.ic
+++ b/storage/innobase/include/dict0dict.ic
@@ -267,7 +267,6 @@ dict_index_is_clust(
/*================*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
return(index->type & DICT_CLUSTERED);
@@ -281,7 +280,6 @@ dict_index_is_unique(
/*=================*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
return(index->type & DICT_UNIQUE);
@@ -296,7 +294,6 @@ dict_index_is_ibuf(
/*===============*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
return(index->type & DICT_IBUF);
@@ -328,7 +325,6 @@ dict_index_is_sec_or_ibuf(
{
ulint type;
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
type = index->type;
@@ -346,7 +342,6 @@ dict_table_get_n_user_cols(
/*=======================*/
const dict_table_t* table) /*!< in: table */
{
- ut_ad(table);
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
return(table->n_cols - DATA_N_SYS_COLS);
@@ -378,7 +373,6 @@ dict_table_get_n_cols(
/*==================*/
const dict_table_t* table) /*!< in: table */
{
- ut_ad(table);
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
return(table->n_cols);
@@ -1546,7 +1540,6 @@ dict_index_is_corrupted(
/*====================*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
return((index->type & DICT_CORRUPT)
diff --git a/storage/innobase/include/dyn0dyn.ic b/storage/innobase/include/dyn0dyn.ic
index f18f2e6dff9..13003862638 100644
--- a/storage/innobase/include/dyn0dyn.ic
+++ b/storage/innobase/include/dyn0dyn.ic
@@ -47,8 +47,6 @@ dyn_block_get_used(
/*===============*/
const dyn_block_t* block) /*!< in: dyn array block */
{
- ut_ad(block);
-
return((block->used) & ~DYN_BLOCK_FULL_FLAG);
}
@@ -76,7 +74,6 @@ dyn_array_create(
dyn_array_t* arr) /*!< in/out: memory buffer of
size sizeof(dyn_array_t) */
{
- ut_ad(arr);
#if DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG
# error "DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG"
#endif
@@ -119,7 +116,6 @@ dyn_array_push(
dyn_block_t* block;
ulint used;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
ut_ad(size <= DYN_ARRAY_DATA_SIZE);
ut_ad(size);
@@ -159,7 +155,6 @@ dyn_array_open(
{
dyn_block_t* block;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
ut_ad(size <= DYN_ARRAY_DATA_SIZE);
ut_ad(size);
@@ -195,7 +190,6 @@ dyn_array_close(
{
dyn_block_t* block;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
block = dyn_array_get_last_block(arr);
@@ -222,7 +216,6 @@ dyn_array_get_element(
{
const dyn_block_t* block;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
/* Get the first array block */
@@ -260,7 +253,6 @@ dyn_array_get_data_size(
const dyn_block_t* block;
ulint sum = 0;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
if (arr->heap == NULL) {
diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h
index b6c977bdc74..9ca1c46d72b 100644
--- a/storage/innobase/include/log0recv.h
+++ b/storage/innobase/include/log0recv.h
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, MariaDB Corporation.
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
@@ -271,20 +272,12 @@ void
recv_sys_var_init(void);
/*===================*/
#endif /* !UNIV_HOTBACKUP */
-/*******************************************************************//**
-Empties the hash table of stored log records, applying them to appropriate
-pages. */
+/** Apply the hash table of stored log records to persistent data pages.
+@param[in] last_batch whether the change buffer merge will be
+ performed as part of the operation */
UNIV_INTERN
-dberr_t
-recv_apply_hashed_log_recs(
-/*=======================*/
- ibool allow_ibuf); /*!< in: if TRUE, also ibuf operations are
- allowed during the application; if FALSE,
- no ibuf operations are allowed, and after
- the application all file pages are flushed to
- disk and invalidated in buffer pool: this
- alternative means that no new log records
- can be generated during the application */
+void
+recv_apply_hashed_log_recs(bool last_batch);
#ifdef UNIV_HOTBACKUP
/*******************************************************************//**
Applies log records in the hash table to a backup. */
@@ -434,6 +427,8 @@ struct recv_sys_t{
scan find a corrupt log block, or a corrupt
log record, or there is a log parsing
buffer overflow */
+ /** the time when progress was last reported */
+ ib_time_t progress_time;
#ifdef UNIV_LOG_ARCHIVE
log_group_t* archive_group;
/*!< in archive recovery: the log group whose
@@ -446,6 +441,20 @@ struct recv_sys_t{
addresses in the hash table */
recv_dblwr_t dblwr;
+
+ /** Determine whether redo log recovery progress should be reported.
+ @param[in] time the current time
+ @return whether progress should be reported
+ (the last report was at least 15 seconds ago) */
+ bool report(ib_time_t time)
+ {
+ if (time - progress_time < 15) {
+ return false;
+ }
+
+ progress_time = time;
+ return true;
+ }
};
/** The recovery system */
diff --git a/storage/innobase/include/mach0data.ic b/storage/innobase/include/mach0data.ic
index 881b2b6055f..215bb12cbe7 100644
--- a/storage/innobase/include/mach0data.ic
+++ b/storage/innobase/include/mach0data.ic
@@ -53,7 +53,6 @@ mach_read_from_1(
/*=============*/
const byte* b) /*!< in: pointer to byte */
{
- ut_ad(b);
return((ulint)(b[0]));
}
@@ -148,7 +147,6 @@ mach_read_from_3(
/*=============*/
const byte* b) /*!< in: pointer to 3 bytes */
{
- ut_ad(b);
return( ((ulint)(b[0]) << 16)
| ((ulint)(b[1]) << 8)
| (ulint)(b[2])
@@ -185,7 +183,6 @@ mach_read_from_4(
/*=============*/
const byte* b) /*!< in: pointer to four bytes */
{
- ut_ad(b);
return( ((ulint)(b[0]) << 24)
| ((ulint)(b[1]) << 16)
| ((ulint)(b[2]) << 8)
@@ -264,8 +261,6 @@ mach_read_compressed(
{
ulint flag;
- ut_ad(b);
-
flag = mach_read_from_1(b);
if (flag < 0x80UL) {
@@ -346,8 +341,6 @@ mach_read_from_7(
/*=============*/
const byte* b) /*!< in: pointer to 7 bytes */
{
- ut_ad(b);
-
return(ut_ull_create(mach_read_from_3(b), mach_read_from_4(b + 3)));
}
@@ -377,8 +370,6 @@ mach_read_from_6(
/*=============*/
const byte* b) /*!< in: pointer to 6 bytes */
{
- ut_ad(b);
-
return(ut_ull_create(mach_read_from_2(b), mach_read_from_4(b + 2)));
}
@@ -426,8 +417,6 @@ mach_ull_read_compressed(
ib_uint64_t n;
ulint size;
- ut_ad(b);
-
n = (ib_uint64_t) mach_read_compressed(b);
size = mach_get_compressed_size((ulint) n);
@@ -493,8 +482,6 @@ mach_ull_read_much_compressed(
ib_uint64_t n;
ulint size;
- ut_ad(b);
-
if (*b != (byte)0xFF) {
n = 0;
size = 0;
diff --git a/storage/innobase/include/page0page.ic b/storage/innobase/include/page0page.ic
index d7f1db82858..c5775188bcf 100644
--- a/storage/innobase/include/page0page.ic
+++ b/storage/innobase/include/page0page.ic
@@ -161,7 +161,6 @@ page_header_get_offs(
{
ulint offs;
- ut_ad(page);
ut_ad((field == PAGE_FREE)
|| (field == PAGE_LAST_INSERT)
|| (field == PAGE_HEAP_TOP));
diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc
index 624ee0bb35d..704e88ff646 100644
--- a/storage/innobase/log/log0log.cc
+++ b/storage/innobase/log/log0log.cc
@@ -2,7 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2009, Google Inc.
-Copyright (c) 2014, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2014, 2017, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -38,6 +38,10 @@ Created 12/9/1995 Heikki Tuuri
#endif
#ifndef UNIV_HOTBACKUP
+#if MYSQL_VERSION_ID < 100200
+# include <my_systemd.h> /* sd_notifyf() */
+#endif
+
#include "mem0mem.h"
#include "buf0buf.h"
#include "buf0flu.h"
@@ -1756,7 +1760,7 @@ log_preflush_pool_modified_pages(
and we could not make a new checkpoint on the basis of the
info on the buffer pool only. */
- recv_apply_hashed_log_recs(TRUE);
+ recv_apply_hashed_log_recs(true);
}
success = buf_flush_list(ULINT_MAX, new_oldest, &n_pages);
@@ -2099,7 +2103,7 @@ log_checkpoint(
ut_ad(!srv_read_only_mode);
if (recv_recovery_is_on()) {
- recv_apply_hashed_log_recs(TRUE);
+ recv_apply_hashed_log_recs(true);
}
if (srv_unix_file_flush_method != SRV_UNIX_NOSYNC) {
@@ -2374,6 +2378,13 @@ loop:
start_lsn += len;
buf += len;
+ if (recv_sys->report(ut_time())) {
+ ib_logf(IB_LOG_LEVEL_INFO, "Read redo log up to LSN=" LSN_PF,
+ start_lsn);
+ sd_notifyf(0, "STATUS=Read redo log up to LSN=" LSN_PF,
+ start_lsn);
+ }
+
if (start_lsn != end_lsn) {
goto loop;
diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc
index 104b5b6b421..d088ae9f3df 100644
--- a/storage/innobase/log/log0recv.cc
+++ b/storage/innobase/log/log0recv.cc
@@ -2,7 +2,7 @@
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
-Copyright (c) 2013, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2013, 2017, MariaDB Corporation.
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
@@ -78,7 +78,7 @@ this must be less than UNIV_PAGE_SIZE as it is stored in the buffer pool */
#define RECV_READ_AHEAD_AREA 32
/** The recovery system */
-UNIV_INTERN recv_sys_t* recv_sys = NULL;
+UNIV_INTERN recv_sys_t* recv_sys;
/** TRUE when applying redo log records during crash recovery; FALSE
otherwise. Note that this is FALSE while a background thread is
rolling back incomplete transactions. */
@@ -134,9 +134,6 @@ UNIV_INTERN ibool recv_is_making_a_backup = FALSE;
UNIV_INTERN ibool recv_is_from_backup = FALSE;
# define buf_pool_get_curr_size() (5 * 1024 * 1024)
#endif /* !UNIV_HOTBACKUP */
-/** The following counter is used to decide when to print info on
-log scan */
-static ulint recv_scan_print_counter;
/** The type of the previous parsed redo log record */
static ulint recv_previous_parsed_rec_type;
@@ -311,8 +308,6 @@ recv_sys_var_init(void)
recv_no_ibuf_operations = FALSE;
- recv_scan_print_counter = 0;
-
recv_previous_parsed_rec_type = 999999;
recv_previous_parsed_rec_offset = 0;
@@ -426,6 +421,7 @@ recv_sys_init(
recv_sys->last_block_buf_start, OS_FILE_LOG_BLOCK_SIZE));
recv_sys->found_corrupt_log = FALSE;
+ recv_sys->progress_time = ut_time();
recv_max_page_lsn = 0;
@@ -435,33 +431,18 @@ recv_sys_init(
mutex_exit(&(recv_sys->mutex));
}
-/********************************************************//**
-Empties the hash table when it has been fully processed.
-@return DB_SUCCESS when successfull or DB_ERROR when fails. */
+/** Empty a fully processed hash table. */
static
-dberr_t
-recv_sys_empty_hash(void)
-/*=====================*/
+void
+recv_sys_empty_hash()
{
ut_ad(mutex_own(&(recv_sys->mutex)));
-
- if (recv_sys->n_addrs != 0) {
- fprintf(stderr,
- "InnoDB: Error: %lu pages with log records"
- " were left unprocessed!\n"
- "InnoDB: Maximum page number with"
- " log records on it %lu\n",
- (ulong) recv_sys->n_addrs,
- (ulong) recv_max_parsed_page_no);
- return DB_ERROR;
- }
+ ut_a(recv_sys->n_addrs == 0);
hash_table_free(recv_sys->addr_hash);
mem_heap_empty(recv_sys->heap);
recv_sys->addr_hash = hash_create(buf_pool_get_curr_size() / 512);
-
- return DB_SUCCESS;
}
#ifndef UNIV_HOTBACKUP
@@ -1716,7 +1697,9 @@ recv_recover_page_func(
mtr_commit(&mtr);
- mutex_enter(&(recv_sys->mutex));
+ ib_time_t time = ut_time();
+
+ mutex_enter(&recv_sys->mutex);
if (recv_max_page_lsn < page_lsn) {
recv_max_page_lsn = page_lsn;
@@ -1724,11 +1707,17 @@ recv_recover_page_func(
recv_addr->state = RECV_PROCESSED;
- ut_a(recv_sys->n_addrs);
- recv_sys->n_addrs--;
-
- mutex_exit(&(recv_sys->mutex));
+ ut_a(recv_sys->n_addrs > 0);
+ if (ulint n = --recv_sys->n_addrs) {
+ if (recv_sys->report(time)) {
+ ib_logf(IB_LOG_LEVEL_INFO,
+ "To recover: " ULINTPF " pages from log", n);
+ sd_notifyf(0, "STATUS=To recover: " ULINTPF
+ " pages from log", n);
+ }
+ }
+ mutex_exit(&recv_sys->mutex);
}
#ifndef UNIV_HOTBACKUP
@@ -1774,62 +1763,50 @@ recv_read_in_area(
}
buf_read_recv_pages(FALSE, space, zip_size, page_nos, n);
- /*
- fprintf(stderr, "Recv pages at %lu n %lu\n", page_nos[0], n);
- */
return(n);
}
-/*******************************************************************//**
-Empties the hash table of stored log records, applying them to appropriate
-pages.
-@return DB_SUCCESS when successfull or DB_ERROR when fails. */
+/** Apply the hash table of stored log records to persistent data pages.
+@param[in] last_batch whether the change buffer merge will be
+ performed as part of the operation */
UNIV_INTERN
-dberr_t
-recv_apply_hashed_log_recs(
-/*=======================*/
- ibool allow_ibuf) /*!< in: if TRUE, also ibuf operations are
- allowed during the application; if FALSE,
- no ibuf operations are allowed, and after
- the application all file pages are flushed to
- disk and invalidated in buffer pool: this
- alternative means that no new log records
- can be generated during the application;
- the caller must in this case own the log
- mutex */
+void
+recv_apply_hashed_log_recs(bool last_batch)
{
- recv_addr_t* recv_addr;
- ulint i;
- ibool has_printed = FALSE;
- ulong progress;
- mtr_t mtr;
- dberr_t err = DB_SUCCESS;
-loop:
- mutex_enter(&(recv_sys->mutex));
-
- if (recv_sys->apply_batch_on) {
+ for (;;) {
+ mutex_enter(&recv_sys->mutex);
- mutex_exit(&(recv_sys->mutex));
+ if (!recv_sys->apply_batch_on) {
+ break;
+ }
+ mutex_exit(&recv_sys->mutex);
os_thread_sleep(500000);
-
- goto loop;
}
- ut_ad((!allow_ibuf) == mutex_own(&log_sys->mutex));
+ ut_ad(!last_batch == mutex_own(&log_sys->mutex));
- if (!allow_ibuf) {
+ if (!last_batch) {
recv_no_ibuf_operations = TRUE;
}
+ if (ulint n = recv_sys->n_addrs) {
+ const char* msg = last_batch
+ ? "Starting final batch to recover "
+ : "Starting a batch to recover ";
+ ib_logf(IB_LOG_LEVEL_INFO,
+ "%s" ULINTPF " pages from redo log", msg, n);
+ sd_notifyf(0, "STATUS=%s" ULINTPF " pages from redo log",
+ msg, n);
+ }
+
recv_sys->apply_log_recs = TRUE;
recv_sys->apply_batch_on = TRUE;
- for (i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) {
-
- for (recv_addr = static_cast<recv_addr_t*>(
- HASH_GET_FIRST(recv_sys->addr_hash, i));
- recv_addr != 0;
+ for (ulint i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) {
+ for (recv_addr_t* recv_addr = static_cast<recv_addr_t*>(
+ HASH_GET_FIRST(recv_sys->addr_hash, i));
+ recv_addr;
recv_addr = static_cast<recv_addr_t*>(
HASH_GET_NEXT(addr_hash, recv_addr))) {
@@ -1838,24 +1815,12 @@ loop:
ulint page_no = recv_addr->page_no;
if (recv_addr->state == RECV_NOT_PROCESSED) {
- if (!has_printed) {
- ib_logf(IB_LOG_LEVEL_INFO,
- "Starting an apply batch"
- " of log records"
- " to the database...");
- fputs("InnoDB: Progress in percent: ",
- stderr);
- has_printed = TRUE;
- }
-
- mutex_exit(&(recv_sys->mutex));
+ mutex_exit(&recv_sys->mutex);
if (buf_page_peek(space, page_no)) {
- buf_block_t* block;
-
+ mtr_t mtr;
mtr_start(&mtr);
-
- block = buf_page_get(
+ buf_block_t* block = buf_page_get(
space, zip_size, page_no,
RW_X_LATCH, &mtr);
buf_block_dbg_add_level(
@@ -1868,21 +1833,9 @@ loop:
page_no);
}
- mutex_enter(&(recv_sys->mutex));
+ mutex_enter(&recv_sys->mutex);
}
}
-
- progress = (ulong) (i * 100)
- / hash_get_n_cells(recv_sys->addr_hash);
- if (has_printed
- && progress
- != ((i + 1) * 100)
- / hash_get_n_cells(recv_sys->addr_hash)) {
-
- fprintf(stderr, "%lu ", progress);
- sd_notifyf(0, "STATUS=Applying batch of log records for"
- " InnoDB: Progress %lu", progress);
- }
}
/* Wait until all the pages have been processed */
@@ -1896,12 +1849,7 @@ loop:
mutex_enter(&(recv_sys->mutex));
}
- if (has_printed) {
-
- fprintf(stderr, "\n");
- }
-
- if (!allow_ibuf) {
+ if (!last_batch) {
bool success;
/* Flush all the file pages to disk and invalidate them in
@@ -1939,16 +1887,9 @@ loop:
recv_sys->apply_log_recs = FALSE;
recv_sys->apply_batch_on = FALSE;
- err = recv_sys_empty_hash();
-
- if (has_printed) {
- fprintf(stderr, "InnoDB: Apply batch completed\n");
- sd_notify(0, "STATUS=InnoDB: Apply batch completed");
- }
-
- mutex_exit(&(recv_sys->mutex));
+ recv_sys_empty_hash();
- return err;
+ mutex_exit(&recv_sys->mutex);
}
#else /* !UNIV_HOTBACKUP */
/*******************************************************************//**
@@ -1971,11 +1912,6 @@ recv_apply_log_recs_for_backup(void)
block = back_block1;
- ib_logf(IB_LOG_LEVEL_INFO,
- "Starting an apply batch of log records to the database...");
-
- fputs("InnoDB: Progress in percent: ", stderr);
-
n_hash_cells = hash_get_n_cells(recv_sys->addr_hash);
for (i = 0; i < n_hash_cells; i++) {
@@ -2087,16 +2023,6 @@ recv_apply_log_recs_for_backup(void)
skip_this_recv_addr:
recv_addr = HASH_GET_NEXT(addr_hash, recv_addr);
}
-
- if ((100 * i) / n_hash_cells
- != (100 * (i + 1)) / n_hash_cells) {
- fprintf(stderr, "%lu ",
- (ulong) ((100 * i) / n_hash_cells));
- fflush(stderr);
- sd_notifyf(0, "STATUS=Applying batch of log records for"
- " backup InnoDB: Progress %lu",
- (ulong) (100 * i) / n_hash_cells);
- }
}
sd_notify(0, "STATUS=InnoDB: Apply batch for backup completed");
@@ -2797,11 +2723,10 @@ recv_scan_log_recs(
#ifndef UNIV_HOTBACKUP
if (recv_log_scan_is_startup_type
&& !recv_needed_recovery) {
-
if (!srv_read_only_mode) {
ib_logf(IB_LOG_LEVEL_INFO,
- "Log scan progressed past the "
- "checkpoint lsn " LSN_PF "",
+ "Starting crash recovery from "
+ "checkpoint LSN=" LSN_PF,
recv_sys->scanned_lsn);
recv_init_crash_recovery();
@@ -2861,19 +2786,6 @@ recv_scan_log_recs(
*group_scanned_lsn = scanned_lsn;
- if (recv_needed_recovery
- || (recv_is_from_backup && !recv_is_making_a_backup)) {
- recv_scan_print_counter++;
-
- if (finished || (recv_scan_print_counter % 80 == 0)) {
-
- fprintf(stderr,
- "InnoDB: Doing recovery: scanned up to"
- " log sequence number " LSN_PF "\n",
- *group_scanned_lsn);
- }
- }
-
if (more_data && !recv_sys->found_corrupt_log) {
/* Try to parse more log records */
@@ -2893,12 +2805,7 @@ recv_scan_log_recs(
log yet: they would be produced by ibuf
operations */
- *err = recv_apply_hashed_log_recs(FALSE);
-
- if (*err != DB_SUCCESS) {
- /* Finish processing because of error */
- return (TRUE);
- }
+ recv_apply_hashed_log_recs(false);
}
#endif /* !UNIV_HOTBACKUP */
@@ -2982,11 +2889,6 @@ recv_init_crash_recovery(void)
recv_needed_recovery = TRUE;
- ib_logf(IB_LOG_LEVEL_INFO, "Database was not shutdown normally!");
- ib_logf(IB_LOG_LEVEL_INFO, "Starting crash recovery.");
- ib_logf(IB_LOG_LEVEL_INFO,
- "Reading tablespace information from the .ibd files...");
-
fil_load_single_table_tablespaces();
/* If we are using the doublewrite method, we will
@@ -2997,9 +2899,7 @@ recv_init_crash_recovery(void)
if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {
ib_logf(IB_LOG_LEVEL_INFO,
- "Restoring possible half-written data pages ");
-
- ib_logf(IB_LOG_LEVEL_INFO,
+ "Restoring possible half-written data pages "
"from the doublewrite buffer...");
buf_dblwr_process();
@@ -3984,7 +3884,7 @@ recv_recovery_from_archive_start(
if (limit_lsn != LSN_MAX) {
- recv_apply_hashed_log_recs(FALSE);
+ recv_apply_hashed_log_recs(false);
recv_reset_logs(0, FALSE, recv_sys->recovered_lsn);
}
diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc
index 5843dd80524..e40aa43193a 100644
--- a/storage/innobase/mtr/mtr0mtr.cc
+++ b/storage/innobase/mtr/mtr0mtr.cc
@@ -309,7 +309,6 @@ mtr_commit(
/*=======*/
mtr_t* mtr) /*!< in: mini-transaction */
{
- ut_ad(mtr);
ut_ad(mtr->magic_n == MTR_MAGIC_N);
ut_ad(mtr->state == MTR_ACTIVE);
ut_ad(!mtr->inside_ibuf);
diff --git a/storage/innobase/page/page0page.cc b/storage/innobase/page/page0page.cc
index a09f270a54f..41f56fd4559 100644
--- a/storage/innobase/page/page0page.cc
+++ b/storage/innobase/page/page0page.cc
@@ -1450,7 +1450,6 @@ page_dir_split_slot(
ulint i;
ulint n_owned;
- ut_ad(page);
ut_ad(!page_zip || page_is_comp(page));
ut_ad(slot_no > 0);
@@ -1512,7 +1511,6 @@ page_dir_balance_slot(
rec_t* old_rec;
rec_t* new_rec;
- ut_ad(page);
ut_ad(!page_zip || page_is_comp(page));
ut_ad(slot_no > 0);
diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc
index 2bf1f324784..ee430435da2 100644
--- a/storage/innobase/page/page0zip.cc
+++ b/storage/innobase/page/page0zip.cc
@@ -4807,8 +4807,6 @@ page_zip_parse_compress(
ulint size;
ulint trailer_size;
- ut_ad(ptr != NULL);
- ut_ad(end_ptr != NULL);
ut_ad(!page == !page_zip);
if (UNIV_UNLIKELY(ptr + (2 + 2) > end_ptr)) {
diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc
index 926e8a44152..047f2685d7a 100644
--- a/storage/innobase/row/row0merge.cc
+++ b/storage/innobase/row/row0merge.cc
@@ -1065,14 +1065,8 @@ row_merge_read_rec(
ulint data_size;
ulint avail_size;
- ut_ad(block);
- ut_ad(buf);
ut_ad(b >= &block[0]);
ut_ad(b < &block[srv_sort_buf_size]);
- ut_ad(index);
- ut_ad(foffs);
- ut_ad(mrec);
- ut_ad(offsets);
ut_ad(*offsets == 1 + REC_OFFS_HEADER_SIZE
+ dict_index_get_n_fields(index));
diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc
index 54bf50cba3d..13b14a0d174 100644
--- a/storage/innobase/row/row0upd.cc
+++ b/storage/innobase/row/row0upd.cc
@@ -1282,8 +1282,6 @@ row_upd_index_replace_new_col_vals_index_pos(
ulint n_fields;
const ulint zip_size = dict_table_zip_size(index->table);
- ut_ad(index);
-
dtuple_set_info_bits(entry, update->info_bits);
if (order_only) {
@@ -1468,8 +1466,6 @@ row_upd_changes_ord_field_binary_func(
ulint i;
const dict_index_t* clust_index;
- ut_ad(index);
- ut_ad(update);
ut_ad(thr);
ut_ad(thr->graph);
ut_ad(thr->graph->trx);
diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc
index 79cd3aebdd0..58117859de8 100644
--- a/storage/innobase/srv/srv0start.cc
+++ b/storage/innobase/srv/srv0start.cc
@@ -3,7 +3,7 @@
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2009, Percona Inc.
-Copyright (c) 2013, 2017, MariaDB Corporation
+Copyright (c) 2013, 2017, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -2483,7 +2483,7 @@ files_checked:
return(err);
}
- /* This must precede recv_apply_hashed_log_recs(TRUE). */
+ /* This must precede recv_apply_hashed_log_recs(true). */
ib_bh = trx_sys_init_at_db_start();
if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {
@@ -2491,12 +2491,8 @@ files_checked:
respective file pages, for the last batch of
recv_group_scan_log_recs(). */
- err = recv_apply_hashed_log_recs(TRUE);
+ recv_apply_hashed_log_recs(true);
DBUG_PRINT("ib_log", ("apply completed"));
-
- if (err != DB_SUCCESS) {
- return(err);
- }
}
if (!srv_read_only_mode) {
diff --git a/storage/innobase/sync/sync0sync.cc b/storage/innobase/sync/sync0sync.cc
index 628925fcc9b..1109d86c146 100644
--- a/storage/innobase/sync/sync0sync.cc
+++ b/storage/innobase/sync/sync0sync.cc
@@ -2,7 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
-Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2017, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc
index c1a99370d80..29edcea74d7 100644
--- a/storage/maria/ha_maria.cc
+++ b/storage/maria/ha_maria.cc
@@ -1,6 +1,6 @@
/* Copyright (C) 2004-2008 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
Copyright (C) 2008-2009 Sun Microsystems, Inc.
- Copyright (c) 2009, 2014, SkySQL Ab.
+ Copyright (c) 2009, 2017, MariaDB Corporation.
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
@@ -1282,75 +1282,75 @@ int ha_maria::write_row(uchar * buf)
int ha_maria::check(THD * thd, HA_CHECK_OPT * check_opt)
{
int error;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
MARIA_SHARE *share= file->s;
const char *old_proc_info;
TRN *old_trn= file->trn;
- if (!file || !&param) return HA_ADMIN_INTERNAL_ERROR;
+ if (!file || !param) return HA_ADMIN_INTERNAL_ERROR;
- maria_chk_init(&param);
- param.thd= thd;
- param.op_name= "check";
- param.db_name= table->s->db.str;
- param.table_name= table->alias.c_ptr();
- param.testflag= check_opt->flags | T_CHECK | T_SILENT;
- param.stats_method= (enum_handler_stats_method)THDVAR(thd,stats_method);
+ maria_chk_init(param);
+ param->thd= thd;
+ param->op_name= "check";
+ param->db_name= table->s->db.str;
+ param->table_name= table->alias.c_ptr();
+ param->testflag= check_opt->flags | T_CHECK | T_SILENT;
+ param->stats_method= (enum_handler_stats_method)THDVAR(thd,stats_method);
if (!(table->db_stat & HA_READ_ONLY))
- param.testflag |= T_STATISTICS;
- param.using_global_keycache= 1;
+ param->testflag |= T_STATISTICS;
+ param->using_global_keycache= 1;
if (!maria_is_crashed(file) &&
- (((param.testflag & T_CHECK_ONLY_CHANGED) &&
+ (((param->testflag & T_CHECK_ONLY_CHANGED) &&
!(share->state.changed & (STATE_CHANGED | STATE_CRASHED_FLAGS |
STATE_IN_REPAIR)) &&
share->state.open_count == 0) ||
- ((param.testflag & T_FAST) && (share->state.open_count ==
+ ((param->testflag & T_FAST) && (share->state.open_count ==
(uint) (share->global_changed ? 1 :
0)))))
return HA_ADMIN_ALREADY_DONE;
- maria_chk_init_for_check(&param, file);
+ maria_chk_init_for_check(param, file);
if ((file->s->state.changed & (STATE_CRASHED_FLAGS | STATE_MOVED)) ==
STATE_MOVED)
{
- _ma_check_print_error(&param, "%s", zerofill_error_msg);
+ _ma_check_print_error(param, "%s", zerofill_error_msg);
return HA_ADMIN_CORRUPT;
}
old_proc_info= thd_proc_info(thd, "Checking status");
thd_progress_init(thd, 3);
- error= maria_chk_status(&param, file); // Not fatal
- if (maria_chk_size(&param, file))
+ error= maria_chk_status(param, file); // Not fatal
+ if (maria_chk_size(param, file))
error= 1;
if (!error)
- error|= maria_chk_del(&param, file, param.testflag);
+ error|= maria_chk_del(param, file, param->testflag);
thd_proc_info(thd, "Checking keys");
thd_progress_next_stage(thd);
if (!error)
- error= maria_chk_key(&param, file);
+ error= maria_chk_key(param, file);
thd_proc_info(thd, "Checking data");
thd_progress_next_stage(thd);
if (!error)
{
- if ((!(param.testflag & T_QUICK) &&
+ if ((!(param->testflag & T_QUICK) &&
((share->options &
(HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
- (param.testflag & (T_EXTEND | T_MEDIUM)))) || maria_is_crashed(file))
+ (param->testflag & (T_EXTEND | T_MEDIUM)))) || maria_is_crashed(file))
{
- ulonglong old_testflag= param.testflag;
- param.testflag |= T_MEDIUM;
- if (!(error= init_io_cache(&param.read_cache, file->dfile.file,
+ ulonglong old_testflag= param->testflag;
+ param->testflag |= T_MEDIUM;
+ if (!(error= init_io_cache(&param->read_cache, file->dfile.file,
my_default_record_cache_size, READ_CACHE,
share->pack.header_length, 1, MYF(MY_WME))))
{
- error= maria_chk_data_link(&param, file,
- MY_TEST(param.testflag & T_EXTEND));
- end_io_cache(&(param.read_cache));
+ error= maria_chk_data_link(param, file,
+ MY_TEST(param->testflag & T_EXTEND));
+ end_io_cache(&param->read_cache);
}
- param.testflag= old_testflag;
+ param->testflag= old_testflag;
}
}
if (!error)
@@ -1358,7 +1358,7 @@ int ha_maria::check(THD * thd, HA_CHECK_OPT * check_opt)
if ((share->state.changed & (STATE_CHANGED |
STATE_CRASHED_FLAGS |
STATE_IN_REPAIR | STATE_NOT_ANALYZED)) ||
- (param.testflag & T_STATISTICS) || maria_is_crashed(file))
+ (param->testflag & T_STATISTICS) || maria_is_crashed(file))
{
file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
mysql_mutex_lock(&share->intern_lock);
@@ -1366,7 +1366,7 @@ int ha_maria::check(THD * thd, HA_CHECK_OPT * check_opt)
share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED_FLAGS |
STATE_IN_REPAIR);
if (!(table->db_stat & HA_READ_ONLY))
- error= maria_update_state_info(&param, file,
+ error= maria_update_state_info(param, file,
UPDATE_TIME | UPDATE_OPEN_COUNT |
UPDATE_STAT);
mysql_mutex_unlock(&share->intern_lock);
@@ -1397,33 +1397,33 @@ int ha_maria::check(THD * thd, HA_CHECK_OPT * check_opt)
int ha_maria::analyze(THD *thd, HA_CHECK_OPT * check_opt)
{
int error= 0;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
MARIA_SHARE *share= file->s;
const char *old_proc_info;
- if (!&param)
+ if (!param)
return HA_ADMIN_INTERNAL_ERROR;
- maria_chk_init(&param);
- param.thd= thd;
- param.op_name= "analyze";
- param.db_name= table->s->db.str;
- param.table_name= table->alias.c_ptr();
- param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
+ maria_chk_init(param);
+ param->thd= thd;
+ param->op_name= "analyze";
+ param->db_name= table->s->db.str;
+ param->table_name= table->alias.c_ptr();
+ param->testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
T_DONT_CHECK_CHECKSUM);
- param.using_global_keycache= 1;
- param.stats_method= (enum_handler_stats_method)THDVAR(thd,stats_method);
+ param->using_global_keycache= 1;
+ param->stats_method= (enum_handler_stats_method)THDVAR(thd,stats_method);
if (!(share->state.changed & STATE_NOT_ANALYZED))
return HA_ADMIN_ALREADY_DONE;
old_proc_info= thd_proc_info(thd, "Scanning");
thd_progress_init(thd, 1);
- error= maria_chk_key(&param, file);
+ error= maria_chk_key(param, file);
if (!error)
{
mysql_mutex_lock(&share->intern_lock);
- error= maria_update_state_info(&param, file, UPDATE_STAT);
+ error= maria_update_state_info(param, file, UPDATE_STAT);
mysql_mutex_unlock(&share->intern_lock);
}
else if (!maria_is_crashed(file) && !thd->killed)
@@ -1436,46 +1436,46 @@ int ha_maria::analyze(THD *thd, HA_CHECK_OPT * check_opt)
int ha_maria::repair(THD * thd, HA_CHECK_OPT *check_opt)
{
int error;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
ha_rows start_records;
const char *old_proc_info;
- if (!file || !&param)
+ if (!file || !param)
return HA_ADMIN_INTERNAL_ERROR;
- maria_chk_init(&param);
- param.thd= thd;
- param.op_name= "repair";
- param.testflag= ((check_opt->flags & ~(T_EXTEND)) |
+ maria_chk_init(param);
+ param->thd= thd;
+ param->op_name= "repair";
+ param->testflag= ((check_opt->flags & ~(T_EXTEND)) |
T_SILENT | T_FORCE_CREATE | T_CALC_CHECKSUM |
(check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT));
- param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
- param.backup_time= check_opt->start_time;
+ param->sort_buffer_length= THDVAR(thd, sort_buffer_size);
+ param->backup_time= check_opt->start_time;
start_records= file->state->records;
old_proc_info= thd_proc_info(thd, "Checking table");
thd_progress_init(thd, 1);
- while ((error= repair(thd, &param, 0)) && param.retry_repair)
+ while ((error= repair(thd, param, 0)) && param->retry_repair)
{
- param.retry_repair= 0;
- if (test_all_bits(param.testflag,
+ param->retry_repair= 0;
+ if (test_all_bits(param->testflag,
(uint) (T_RETRY_WITHOUT_QUICK | T_QUICK)))
{
- param.testflag&= ~(T_RETRY_WITHOUT_QUICK | T_QUICK);
+ param->testflag&= ~(T_RETRY_WITHOUT_QUICK | T_QUICK);
/* Ensure we don't loose any rows when retrying without quick */
- param.testflag|= T_SAFE_REPAIR;
+ param->testflag|= T_SAFE_REPAIR;
if (thd->vio_ok())
- _ma_check_print_info(&param, "Retrying repair without quick");
+ _ma_check_print_info(param, "Retrying repair without quick");
else
sql_print_information("Retrying repair of: '%s' without quick",
table->s->path.str);
continue;
}
- param.testflag &= ~T_QUICK;
- if ((param.testflag & T_REP_BY_SORT))
+ param->testflag &= ~T_QUICK;
+ if (param->testflag & T_REP_BY_SORT)
{
- param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP;
+ param->testflag= (param->testflag & ~T_REP_BY_SORT) | T_REP;
if (thd->vio_ok())
- _ma_check_print_info(&param, "Retrying repair with keycache");
+ _ma_check_print_info(param, "Retrying repair with keycache");
sql_print_information("Retrying repair of: '%s' with keycache",
table->s->path.str);
continue;
@@ -1499,20 +1499,20 @@ int ha_maria::repair(THD * thd, HA_CHECK_OPT *check_opt)
int ha_maria::zerofill(THD * thd, HA_CHECK_OPT *check_opt)
{
int error;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
TRN *old_trn;
MARIA_SHARE *share= file->s;
- if (!file || !&param)
+ if (!file || !param)
return HA_ADMIN_INTERNAL_ERROR;
old_trn= file->trn;
- maria_chk_init(&param);
- param.thd= thd;
- param.op_name= "zerofill";
- param.testflag= check_opt->flags | T_SILENT | T_ZEROFILL;
- param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
- error=maria_zerofill(&param, file, share->open_file_name.str);
+ maria_chk_init(param);
+ param->thd= thd;
+ param->op_name= "zerofill";
+ param->testflag= check_opt->flags | T_SILENT | T_ZEROFILL;
+ param->sort_buffer_length= THDVAR(thd, sort_buffer_size);
+ error=maria_zerofill(param, file, share->open_file_name.str);
/* Reset trn, that may have been set by repair */
_ma_set_trn_for_table(file, old_trn);
@@ -1522,7 +1522,7 @@ int ha_maria::zerofill(THD * thd, HA_CHECK_OPT *check_opt)
TrID create_trid= trnman_get_min_safe_trid();
mysql_mutex_lock(&share->intern_lock);
share->state.changed|= STATE_NOT_MOVABLE;
- maria_update_state_info(&param, file, UPDATE_TIME | UPDATE_OPEN_COUNT);
+ maria_update_state_info(param, file, UPDATE_TIME | UPDATE_OPEN_COUNT);
_ma_update_state_lsns_sub(share, LSN_IMPOSSIBLE, create_trid,
TRUE, TRUE);
mysql_mutex_unlock(&share->intern_lock);
@@ -1533,24 +1533,24 @@ int ha_maria::zerofill(THD * thd, HA_CHECK_OPT *check_opt)
int ha_maria::optimize(THD * thd, HA_CHECK_OPT *check_opt)
{
int error;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
- if (!file || !&param)
+ if (!file || !param)
return HA_ADMIN_INTERNAL_ERROR;
- maria_chk_init(&param);
- param.thd= thd;
- param.op_name= "optimize";
- param.testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE |
+ maria_chk_init(param);
+ param->thd= thd;
+ param->op_name= "optimize";
+ param->testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE |
T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX);
- param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
+ param->sort_buffer_length= THDVAR(thd, sort_buffer_size);
thd_progress_init(thd, 1);
- if ((error= repair(thd, &param, 1)) && param.retry_repair)
+ if ((error= repair(thd, param, 1)) && param->retry_repair)
{
sql_print_warning("Warning: Optimize table got errno %d on %s.%s, retrying",
- my_errno, param.db_name, param.table_name);
- param.testflag &= ~T_REP_BY_SORT;
- error= repair(thd, &param, 0);
+ my_errno, param->db_name, param->table_name);
+ param->testflag &= ~T_REP_BY_SORT;
+ error= repair(thd, param, 0);
}
thd_progress_end(thd);
return error;
@@ -1800,17 +1800,17 @@ int ha_maria::assign_to_keycache(THD * thd, HA_CHECK_OPT *check_opt)
if (error != HA_ADMIN_OK)
{
/* Send error to user */
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
- if (!&param)
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
+ if (!param)
return HA_ADMIN_INTERNAL_ERROR;
- maria_chk_init(&param);
- param.thd= thd;
- param.op_name= "assign_to_keycache";
- param.db_name= table->s->db.str;
- param.table_name= table->s->table_name.str;
- param.testflag= 0;
- _ma_check_print_error(&param, errmsg);
+ maria_chk_init(param);
+ param->thd= thd;
+ param->op_name= "assign_to_keycache";
+ param->db_name= table->s->db.str;
+ param->table_name= table->s->table_name.str;
+ param->testflag= 0;
+ _ma_check_print_error(param, errmsg);
}
DBUG_RETURN(error);
#else
@@ -1864,17 +1864,17 @@ int ha_maria::preload_keys(THD * thd, HA_CHECK_OPT *check_opt)
errmsg= buf;
}
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
- if (!&param)
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
+ if (!param)
return HA_ADMIN_INTERNAL_ERROR;
- maria_chk_init(&param);
- param.thd= thd;
- param.op_name= "preload_keys";
- param.db_name= table->s->db.str;
- param.table_name= table->s->table_name.str;
- param.testflag= 0;
- _ma_check_print_error(&param, "%s", errmsg);
+ maria_chk_init(param);
+ param->thd= thd;
+ param->op_name= "preload_keys";
+ param->db_name= table->s->db.str;
+ param->table_name= table->s->table_name.str;
+ param->testflag= 0;
+ _ma_check_print_error(param, "%s", errmsg);
DBUG_RETURN(HA_ADMIN_FAILED);
}
DBUG_RETURN(HA_ADMIN_OK);
@@ -1975,25 +1975,25 @@ int ha_maria::enable_indexes(uint mode)
else if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE)
{
THD *thd= table->in_use;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
- if (!&param)
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
+ if (!param)
return HA_ADMIN_INTERNAL_ERROR;
const char *save_proc_info= thd_proc_info(thd, "Creating index");
- maria_chk_init(&param);
- param.op_name= "recreating_index";
- param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
+ maria_chk_init(param);
+ param->op_name= "recreating_index";
+ param->testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
T_CREATE_MISSING_KEYS | T_SAFE_REPAIR);
/*
Don't lock and unlock table if it's locked.
Normally table should be locked. This test is mostly for safety.
*/
if (likely(file->lock_type != F_UNLCK))
- param.testflag|= T_NO_LOCKS;
+ param->testflag|= T_NO_LOCKS;
if (file->create_unique_index_by_sort)
- param.testflag|= T_CREATE_UNIQUE_BY_SORT;
+ param->testflag|= T_CREATE_UNIQUE_BY_SORT;
if (bulk_insert_single_undo == BULK_INSERT_SINGLE_UNDO_AND_NO_REPAIR)
{
@@ -2002,23 +2002,23 @@ int ha_maria::enable_indexes(uint mode)
Don't bump create_rename_lsn, because UNDO_BULK_INSERT
should not be skipped in case of crash during repair.
*/
- param.testflag|= T_NO_CREATE_RENAME_LSN;
+ param->testflag|= T_NO_CREATE_RENAME_LSN;
}
- param.myf_rw &= ~MY_WAIT_IF_FULL;
- param.sort_buffer_length= THDVAR(thd,sort_buffer_size);
- param.stats_method= (enum_handler_stats_method)THDVAR(thd,stats_method);
- param.tmpdir= &mysql_tmpdir_list;
- if ((error= (repair(thd, &param, 0) != HA_ADMIN_OK)) && param.retry_repair)
+ param->myf_rw &= ~MY_WAIT_IF_FULL;
+ param->sort_buffer_length= THDVAR(thd,sort_buffer_size);
+ param->stats_method= (enum_handler_stats_method)THDVAR(thd,stats_method);
+ param->tmpdir= &mysql_tmpdir_list;
+ if ((error= (repair(thd, param, 0) != HA_ADMIN_OK)) && param->retry_repair)
{
sql_print_warning("Warning: Enabling keys got errno %d on %s.%s, "
"retrying",
- my_errno, param.db_name, param.table_name);
+ my_errno, param->db_name, param->table_name);
/* This should never fail normally */
DBUG_ASSERT(thd->killed != 0);
/* Repairing by sort failed. Now try standard repair method. */
- param.testflag &= ~T_REP_BY_SORT;
- error= (repair(thd, &param, 0) != HA_ADMIN_OK);
+ param->testflag &= ~T_REP_BY_SORT;
+ error= (repair(thd, param, 0) != HA_ADMIN_OK);
/*
If the standard repair succeeded, clear all error messages which
might have been set by the first repair. They can still be seen
@@ -3597,10 +3597,6 @@ static int ha_maria_init(void *p)
maria_pagecache->extra_debug= 1;
maria_assert_if_crashed_table= debug_assert_if_crashed_table;
-#if defined(HAVE_REALPATH) && !defined(HAVE_valgrind) && !defined(HAVE_BROKEN_REALPATH)
- /* We can only test for sub paths if my_symlink.c is using realpath */
- maria_test_invalid_symlink= test_if_data_home_dir;
-#endif
if (res)
maria_hton= 0;
diff --git a/storage/maria/ma_check.c b/storage/maria/ma_check.c
index 8424817bc6b..4461dd40ae3 100644
--- a/storage/maria/ma_check.c
+++ b/storage/maria/ma_check.c
@@ -2837,7 +2837,7 @@ int maria_repair(HA_CHECK *param, register MARIA_HA *info,
(param->testflag & T_BACKUP_DATA ?
MYF(MY_REDEL_MAKE_BACKUP): MYF(0)) |
sync_dir) ||
- _ma_open_datafile(info, share, NullS, -1))
+ _ma_open_datafile(info, share))
{
goto err;
}
@@ -3998,7 +3998,7 @@ int maria_repair_by_sort(HA_CHECK *param, register MARIA_HA *info,
(param->testflag & T_BACKUP_DATA ?
MYF(MY_REDEL_MAKE_BACKUP): MYF(0)) |
sync_dir) ||
- _ma_open_datafile(info, share, NullS, -1))
+ _ma_open_datafile(info, share))
{
_ma_check_print_error(param, "Couldn't change to new data file");
goto err;
@@ -4638,7 +4638,7 @@ err:
MYF((param->testflag & T_BACKUP_DATA ?
MY_REDEL_MAKE_BACKUP : 0) |
sync_dir)) ||
- _ma_open_datafile(info,share, NullS, -1))
+ _ma_open_datafile(info,share))
got_error=1;
}
}
diff --git a/storage/maria/ma_create.c b/storage/maria/ma_create.c
index 0680b5d568e..5f7ac333d5d 100644
--- a/storage/maria/ma_create.c
+++ b/storage/maria/ma_create.c
@@ -54,7 +54,8 @@ int maria_create(const char *name, enum data_file_type datafile_type,
uint max_field_lengths, extra_header_size, column_nr;
uint internal_table= flags & HA_CREATE_INTERNAL_TABLE;
ulong reclength, real_reclength,min_pack_length;
- char filename[FN_REFLEN], linkname[FN_REFLEN], *linkname_ptr;
+ char kfilename[FN_REFLEN], klinkname[FN_REFLEN], *klinkname_ptr;
+ char dfilename[FN_REFLEN], dlinkname[FN_REFLEN], *dlinkname_ptr;
ulong pack_reclength;
ulonglong tot_length,max_rows, tmp;
enum en_fieldtype type;
@@ -846,19 +847,19 @@ int maria_create(const char *name, enum data_file_type datafile_type,
/* chop off the table name, tempory tables use generated name */
if ((path= strrchr(ci->index_file_name, FN_LIBCHAR)))
*path= '\0';
- fn_format(filename, name, ci->index_file_name, MARIA_NAME_IEXT,
+ fn_format(kfilename, name, ci->index_file_name, MARIA_NAME_IEXT,
MY_REPLACE_DIR | MY_UNPACK_FILENAME |
MY_RETURN_REAL_PATH | MY_APPEND_EXT);
}
else
{
- fn_format(filename, ci->index_file_name, "", MARIA_NAME_IEXT,
+ fn_format(kfilename, ci->index_file_name, "", MARIA_NAME_IEXT,
MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
(have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
}
- fn_format(linkname, name, "", MARIA_NAME_IEXT,
+ fn_format(klinkname, name, "", MARIA_NAME_IEXT,
MY_UNPACK_FILENAME|MY_APPEND_EXT);
- linkname_ptr= linkname;
+ klinkname_ptr= klinkname;
/*
Don't create the table if the link or file exists to ensure that one
doesn't accidently destroy another table.
@@ -872,10 +873,10 @@ int maria_create(const char *name, enum data_file_type datafile_type,
{
char *iext= strrchr(name, '.');
int have_iext= iext && !strcmp(iext, MARIA_NAME_IEXT);
- fn_format(filename, name, "", MARIA_NAME_IEXT,
+ fn_format(kfilename, name, "", MARIA_NAME_IEXT,
MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
(have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
- linkname_ptr= NullS;
+ klinkname_ptr= NullS;
/*
Replace the current file.
Don't sync dir now if the data file has the same path.
@@ -895,7 +896,7 @@ int maria_create(const char *name, enum data_file_type datafile_type,
NOTE: The filename is compared against unique_file_name of every
open table. Hence we need a real path here.
*/
- if (!internal_table && _ma_test_if_reopen(filename))
+ if (!internal_table && _ma_test_if_reopen(kfilename))
{
my_printf_error(HA_ERR_TABLE_EXIST, "Aria table '%s' is in use "
"(most likely by a MERGE table). Try FLUSH TABLES.",
@@ -904,8 +905,8 @@ int maria_create(const char *name, enum data_file_type datafile_type,
goto err;
}
- if ((file= mysql_file_create_with_symlink(key_file_kfile, linkname_ptr,
- filename, 0, create_mode,
+ if ((file= mysql_file_create_with_symlink(key_file_kfile, klinkname_ptr,
+ kfilename, 0, create_mode,
MYF(MY_WME|create_flag))) < 0)
goto err;
errpos=1;
@@ -1165,30 +1166,30 @@ int maria_create(const char *name, enum data_file_type datafile_type,
/* chop off the table name, tempory tables use generated name */
if ((path= strrchr(ci->data_file_name, FN_LIBCHAR)))
*path= '\0';
- fn_format(filename, name, ci->data_file_name, MARIA_NAME_DEXT,
+ fn_format(dfilename, name, ci->data_file_name, MARIA_NAME_DEXT,
MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT);
}
else
{
- fn_format(filename, ci->data_file_name, "", MARIA_NAME_DEXT,
+ fn_format(dfilename, ci->data_file_name, "", MARIA_NAME_DEXT,
MY_UNPACK_FILENAME |
(have_dext ? MY_REPLACE_EXT : MY_APPEND_EXT));
}
- fn_format(linkname, name, "",MARIA_NAME_DEXT,
+ fn_format(dlinkname, name, "",MARIA_NAME_DEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT);
- linkname_ptr= linkname;
+ dlinkname_ptr= dlinkname;
create_flag=0;
}
else
{
- fn_format(filename,name,"", MARIA_NAME_DEXT,
+ fn_format(dfilename,name,"", MARIA_NAME_DEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT);
- linkname_ptr= NullS;
+ dlinkname_ptr= NullS;
create_flag= (flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
}
if ((dfile=
- mysql_file_create_with_symlink(key_file_dfile, linkname_ptr,
- filename, 0, create_mode,
+ mysql_file_create_with_symlink(key_file_dfile, dlinkname_ptr,
+ dfilename, 0, create_mode,
MYF(MY_WME | create_flag | sync_dir))) < 0)
goto err;
errpos=3;
@@ -1239,19 +1240,21 @@ err_no_lock:
mysql_file_close(dfile, MYF(0));
/* fall through */
case 2:
- if (! (flags & HA_DONT_TOUCH_DATA))
- mysql_file_delete_with_symlink(key_file_dfile,
- fn_format(filename,name,"",MARIA_NAME_DEXT,
- MY_UNPACK_FILENAME | MY_APPEND_EXT),
- sync_dir);
+ if (! (flags & HA_DONT_TOUCH_DATA))
+ {
+ mysql_file_delete(key_file_dfile, dfilename, MYF(sync_dir));
+ if (dlinkname_ptr)
+ mysql_file_delete(key_file_dfile, dlinkname_ptr, MYF(sync_dir));
+ }
/* fall through */
case 1:
mysql_file_close(file, MYF(0));
if (! (flags & HA_DONT_TOUCH_DATA))
- mysql_file_delete_with_symlink(key_file_kfile,
- fn_format(filename,name,"",MARIA_NAME_IEXT,
- MY_UNPACK_FILENAME | MY_APPEND_EXT),
- sync_dir);
+ {
+ mysql_file_delete(key_file_kfile, kfilename, MYF(sync_dir));
+ if (klinkname_ptr)
+ mysql_file_delete(key_file_kfile, klinkname_ptr, MYF(sync_dir));
+ }
}
ma_crypt_free(&share);
my_free(log_data);
diff --git a/storage/maria/ma_delete_table.c b/storage/maria/ma_delete_table.c
index ec68902485b..970ac792623 100644
--- a/storage/maria/ma_delete_table.c
+++ b/storage/maria/ma_delete_table.c
@@ -84,25 +84,15 @@ int maria_delete_table(const char *name)
int maria_delete_table_files(const char *name, my_bool temporary, myf sync_dir)
{
- char from[FN_REFLEN];
DBUG_ENTER("maria_delete_table_files");
- fn_format(from,name,"",MARIA_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
- if (mysql_file_delete_with_symlink(key_file_kfile, from,
- MYF(MY_WME | sync_dir)))
- DBUG_RETURN(my_errno);
- fn_format(from,name,"",MARIA_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT);
- if (mysql_file_delete_with_symlink(key_file_dfile, from,
- MYF(MY_WME | sync_dir)))
+ if (my_handler_delete_with_symlink(key_file_kfile, name, MARIA_NAME_IEXT, MYF(MY_WME | sync_dir)) ||
+ my_handler_delete_with_symlink(key_file_dfile, name, MARIA_NAME_DEXT, MYF(MY_WME | sync_dir)))
DBUG_RETURN(my_errno);
- // optional files from maria_pack:
- if (!temporary)
- {
- fn_format(from,name,"",".TMD",MY_UNPACK_FILENAME|MY_APPEND_EXT);
- mysql_file_delete_with_symlink(key_file_dfile, from, MYF(0));
- fn_format(from,name,"",".OLD",MY_UNPACK_FILENAME|MY_APPEND_EXT);
- mysql_file_delete_with_symlink(key_file_dfile, from, MYF(0));
+ if (!temporary) {
+ my_handler_delete_with_symlink(key_file_dfile, name, ".TMD", MYF(0));
+ my_handler_delete_with_symlink(key_file_dfile, name, ".OLD", MYF(0));
}
DBUG_RETURN(0);
}
diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c
index 4e97c6b43b9..0cf4140941a 100644
--- a/storage/maria/ma_open.c
+++ b/storage/maria/ma_open.c
@@ -87,7 +87,7 @@ MARIA_HA *_ma_test_if_reopen(const char *filename)
*/
-static MARIA_HA *maria_clone_internal(MARIA_SHARE *share, const char *name,
+static MARIA_HA *maria_clone_internal(MARIA_SHARE *share,
int mode, File data_file,
uint internal_table)
{
@@ -107,7 +107,7 @@ static MARIA_HA *maria_clone_internal(MARIA_SHARE *share, const char *name,
}
if (data_file >= 0)
info.dfile.file= data_file;
- else if (_ma_open_datafile(&info, share, name, -1))
+ else if (_ma_open_datafile(&info, share))
goto err;
errpos= 5;
@@ -253,7 +253,7 @@ MARIA_HA *maria_clone(MARIA_SHARE *share, int mode)
{
MARIA_HA *new_info;
mysql_mutex_lock(&THR_LOCK_maria);
- new_info= maria_clone_internal(share, NullS, mode,
+ new_info= maria_clone_internal(share, mode,
share->data_file_type == BLOCK_RECORD ?
share->bitmap.file.file : -1, 0);
mysql_mutex_unlock(&THR_LOCK_maria);
@@ -299,8 +299,13 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
realpath_err= my_realpath(name_buff, fn_format(org_name, name, "",
MARIA_NAME_IEXT,
MY_UNPACK_FILENAME),MYF(0));
+ if (realpath_err > 0) /* File not found, no point in looking further. */
+ {
+ DBUG_RETURN(NULL);
+ }
+
if (my_is_symlink(org_name) &&
- (realpath_err || (*maria_test_invalid_symlink)(name_buff)))
+ (realpath_err || mysys_test_invalid_symlink(name_buff)))
{
my_errno= HA_WRONG_CREATE_OPTION;
DBUG_RETURN(0);
@@ -325,13 +330,16 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
my_errno= HA_ERR_CRASHED;
goto err;
});
+ DEBUG_SYNC_C("mi_open_kfile");
if ((kfile=mysql_file_open(key_file_kfile, name_buff,
- (open_mode=O_RDWR) | O_SHARE,MYF(0))) < 0)
+ (open_mode=O_RDWR) | O_SHARE | O_NOFOLLOW,
+ MYF(MY_NOSYMLINKS))) < 0)
{
if ((errno != EROFS && errno != EACCES) ||
mode != O_RDONLY ||
(kfile=mysql_file_open(key_file_kfile, name_buff,
- (open_mode=O_RDONLY) | O_SHARE,MYF(0))) < 0)
+ (open_mode=O_RDONLY) | O_SHARE | O_NOFOLLOW,
+ MYF(MY_NOSYMLINKS))) < 0)
goto err;
}
share->mode=open_mode;
@@ -376,7 +384,18 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
(void) strmov(index_name, org_name);
*strrchr(org_name, FN_EXTCHAR)= '\0';
(void) fn_format(data_name,org_name,"",MARIA_NAME_DEXT,
- MY_APPEND_EXT|MY_UNPACK_FILENAME|MY_RESOLVE_SYMLINKS);
+ MY_APPEND_EXT|MY_UNPACK_FILENAME);
+ if (my_is_symlink(data_name))
+ {
+ if (my_realpath(data_name, data_name, MYF(0)))
+ goto err;
+ if (mysys_test_invalid_symlink(data_name))
+ {
+ my_errno= HA_WRONG_CREATE_OPTION;
+ goto err;
+ }
+ share->mode|= O_NOFOLLOW; /* all symlinks are resolved by realpath() */
+ }
info_length=mi_uint2korr(share->state.header.header_length);
base_pos= mi_uint2korr(share->state.header.base_pos);
@@ -853,7 +872,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
if ((share->data_file_type == BLOCK_RECORD ||
share->data_file_type == COMPRESSED_RECORD))
{
- if (_ma_open_datafile(&info, share, name, -1))
+ if (_ma_open_datafile(&info, share))
goto err;
data_file= info.dfile.file;
}
@@ -1025,7 +1044,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags)
data_file= share->bitmap.file.file; /* Only opened once */
}
- if (!(m_info= maria_clone_internal(share, name, mode, data_file,
+ if (!(m_info= maria_clone_internal(share, mode, data_file,
internal_table)))
goto err;
@@ -1913,35 +1932,15 @@ void _ma_set_index_pagecache_callbacks(PAGECACHE_FILE *file,
Open data file
We can't use dup() here as the data file descriptors need to have different
active seek-positions.
-
- The argument file_to_dup is here for the future if there would on some OS
- exist a dup()-like call that would give us two different file descriptors.
*************************************************************************/
-int _ma_open_datafile(MARIA_HA *info, MARIA_SHARE *share, const char *org_name,
- File file_to_dup __attribute__((unused)))
+int _ma_open_datafile(MARIA_HA *info, MARIA_SHARE *share)
{
- char *data_name= share->data_file_name.str;
- char real_data_name[FN_REFLEN];
-
- if (org_name)
- {
- fn_format(real_data_name, org_name, "", MARIA_NAME_DEXT, 4);
- if (my_is_symlink(real_data_name))
- {
- if (my_realpath(real_data_name, real_data_name, MYF(0)) ||
- (*maria_test_invalid_symlink)(real_data_name))
- {
- my_errno= HA_WRONG_CREATE_OPTION;
- return 1;
- }
- data_name= real_data_name;
- }
- }
-
+ myf flags= MY_WME | (share->mode & O_NOFOLLOW ? MY_NOSYMLINKS : 0);
+ DEBUG_SYNC_C("mi_open_datafile");
info->dfile.file= share->bitmap.file.file=
- mysql_file_open(key_file_dfile, data_name,
- share->mode | O_SHARE, MYF(MY_WME));
+ mysql_file_open(key_file_dfile, share->data_file_name.str,
+ share->mode | O_SHARE, MYF(flags));
return info->dfile.file >= 0 ? 0 : 1;
}
@@ -1955,8 +1954,8 @@ int _ma_open_keyfile(MARIA_SHARE *share)
mysql_mutex_lock(&share->intern_lock);
share->kfile.file= mysql_file_open(key_file_kfile,
share->unique_file_name.str,
- share->mode | O_SHARE,
- MYF(MY_WME));
+ share->mode | O_SHARE | O_NOFOLLOW,
+ MYF(MY_WME | MY_NOSYMLINKS));
mysql_mutex_unlock(&share->intern_lock);
return (share->kfile.file < 0);
}
diff --git a/storage/maria/ma_static.c b/storage/maria/ma_static.c
index 2877f05c8dc..d7caf9edc88 100644
--- a/storage/maria/ma_static.c
+++ b/storage/maria/ma_static.c
@@ -107,12 +107,6 @@ uint32 maria_readnext_vec[]=
SEARCH_BIGGER, SEARCH_SMALLER, SEARCH_SMALLER
};
-static int always_valid(const char *filename __attribute__((unused)))
-{
- return 0;
-}
-
-int (*maria_test_invalid_symlink)(const char *filename)= always_valid;
my_bool (*ma_killed)(MARIA_HA *)= ma_killed_standalone;
#ifdef HAVE_PSI_INTERFACE
diff --git a/storage/maria/maria_chk.c b/storage/maria/maria_chk.c
index 0c1c56dfa94..66bf0f5eeb2 100644
--- a/storage/maria/maria_chk.c
+++ b/storage/maria/maria_chk.c
@@ -1275,7 +1275,7 @@ static int maria_chk(HA_CHECK *param, char *filename)
mysql_file_close(info->dfile.file, MYF(MY_WME)); /* Close new file */
error|=maria_change_to_newfile(filename,MARIA_NAME_DEXT,DATA_TMP_EXT,
0, MYF(0));
- if (_ma_open_datafile(info,info->s, NullS, -1))
+ if (_ma_open_datafile(info, info->s))
error=1;
param->out_flag&= ~O_NEW_DATA; /* We are using new datafile */
param->read_cache.file= info->dfile.file;
diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h
index d216384a2aa..154d60a6164 100644
--- a/storage/maria/maria_def.h
+++ b/storage/maria/maria_def.h
@@ -1346,8 +1346,7 @@ int _ma_def_scan_restore_pos(MARIA_HA *info, MARIA_RECORD_POS lastpos);
extern MARIA_HA *_ma_test_if_reopen(const char *filename);
my_bool _ma_check_table_is_closed(const char *name, const char *where);
-int _ma_open_datafile(MARIA_HA *info, MARIA_SHARE *share, const char *org_name,
- File file_to_dup);
+int _ma_open_datafile(MARIA_HA *info, MARIA_SHARE *share);
int _ma_open_keyfile(MARIA_SHARE *share);
void _ma_setup_functions(register MARIA_SHARE *share);
my_bool _ma_dynmap_file(MARIA_HA *info, my_off_t size);
diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
index 60764bfd696..e363fb1da2b 100644
--- a/storage/myisam/ha_myisam.cc
+++ b/storage/myisam/ha_myisam.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2012, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, SkySQL Ab.
+ Copyright (c) 2009, 2017, MariaDB Corporation.
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
@@ -870,59 +870,59 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
{
if (!file) return HA_ADMIN_INTERNAL_ERROR;
int error;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
MYISAM_SHARE* share = file->s;
const char *old_proc_info=thd->proc_info;
- if (!&param)
+ if (!param)
return HA_ADMIN_INTERNAL_ERROR;
thd_proc_info(thd, "Checking table");
- myisamchk_init(&param);
- param.thd = thd;
- param.op_name = "check";
- param.db_name= table->s->db.str;
- param.table_name= table->alias.c_ptr();
- param.testflag = check_opt->flags | T_CHECK | T_SILENT;
- param.stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
+ myisamchk_init(param);
+ param->thd = thd;
+ param->op_name = "check";
+ param->db_name= table->s->db.str;
+ param->table_name= table->alias.c_ptr();
+ param->testflag = check_opt->flags | T_CHECK | T_SILENT;
+ param->stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
if (!(table->db_stat & HA_READ_ONLY))
- param.testflag|= T_STATISTICS;
- param.using_global_keycache = 1;
+ param->testflag|= T_STATISTICS;
+ param->using_global_keycache = 1;
if (!mi_is_crashed(file) &&
- (((param.testflag & T_CHECK_ONLY_CHANGED) &&
+ (((param->testflag & T_CHECK_ONLY_CHANGED) &&
!(share->state.changed & (STATE_CHANGED | STATE_CRASHED |
STATE_CRASHED_ON_REPAIR)) &&
share->state.open_count == 0) ||
- ((param.testflag & T_FAST) && (share->state.open_count ==
+ ((param->testflag & T_FAST) && (share->state.open_count ==
(uint) (share->global_changed ? 1 : 0)))))
return HA_ADMIN_ALREADY_DONE;
- error = chk_status(&param, file); // Not fatal
- error = chk_size(&param, file);
+ error = chk_status(param, file); // Not fatal
+ error = chk_size(param, file);
if (!error)
- error |= chk_del(&param, file, param.testflag);
+ error |= chk_del(param, file, param->testflag);
if (!error)
- error = chk_key(&param, file);
+ error = chk_key(param, file);
if (!error)
{
- if ((!(param.testflag & T_QUICK) &&
+ if ((!(param->testflag & T_QUICK) &&
((share->options &
(HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ||
- (param.testflag & (T_EXTEND | T_MEDIUM)))) ||
+ (param->testflag & (T_EXTEND | T_MEDIUM)))) ||
mi_is_crashed(file))
{
- ulonglong old_testflag= param.testflag;
- param.testflag|=T_MEDIUM;
- if (!(error= init_io_cache(&param.read_cache, file->dfile,
+ ulonglong old_testflag= param->testflag;
+ param->testflag|=T_MEDIUM;
+ if (!(error= init_io_cache(&param->read_cache, file->dfile,
my_default_record_cache_size, READ_CACHE,
share->pack.header_length, 1, MYF(MY_WME))))
{
- error= chk_data_link(&param, file, MY_TEST(param.testflag & T_EXTEND));
- end_io_cache(&(param.read_cache));
+ error= chk_data_link(param, file, MY_TEST(param->testflag & T_EXTEND));
+ end_io_cache(&param->read_cache);
}
- param.testflag= old_testflag;
+ param->testflag= old_testflag;
}
}
if (!error)
@@ -930,7 +930,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
if ((share->state.changed & (STATE_CHANGED |
STATE_CRASHED_ON_REPAIR |
STATE_CRASHED | STATE_NOT_ANALYZED)) ||
- (param.testflag & T_STATISTICS) ||
+ (param->testflag & T_STATISTICS) ||
mi_is_crashed(file))
{
file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
@@ -938,7 +938,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
STATE_CRASHED_ON_REPAIR);
if (!(table->db_stat & HA_READ_ONLY))
- error=update_state_info(&param,file,UPDATE_TIME | UPDATE_OPEN_COUNT |
+ error=update_state_info(param,file,UPDATE_TIME | UPDATE_OPEN_COUNT |
UPDATE_STAT);
mysql_mutex_unlock(&share->intern_lock);
info(HA_STATUS_NO_LOCK | HA_STATUS_TIME | HA_STATUS_VARIABLE |
@@ -965,30 +965,30 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
{
int error=0;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
MYISAM_SHARE* share = file->s;
- if (!&param)
+ if (!param)
return HA_ADMIN_INTERNAL_ERROR;
- myisamchk_init(&param);
- param.thd = thd;
- param.op_name= "analyze";
- param.db_name= table->s->db.str;
- param.table_name= table->alias.c_ptr();
- param.testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
+ myisamchk_init(param);
+ param->thd = thd;
+ param->op_name= "analyze";
+ param->db_name= table->s->db.str;
+ param->table_name= table->alias.c_ptr();
+ param->testflag= (T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
T_DONT_CHECK_CHECKSUM);
- param.using_global_keycache = 1;
- param.stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
+ param->using_global_keycache = 1;
+ param->stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
if (!(share->state.changed & STATE_NOT_ANALYZED))
return HA_ADMIN_ALREADY_DONE;
- error = chk_key(&param, file);
+ error = chk_key(param, file);
if (!error)
{
mysql_mutex_lock(&share->intern_lock);
- error=update_state_info(&param,file,UPDATE_STAT);
+ error=update_state_info(param,file,UPDATE_STAT);
mysql_mutex_unlock(&share->intern_lock);
}
else if (!mi_is_crashed(file) && !thd->killed)
@@ -1000,38 +1000,38 @@ int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt)
{
int error;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
ha_rows start_records;
- if (!file || !&param) return HA_ADMIN_INTERNAL_ERROR;
+ if (!file || !param) return HA_ADMIN_INTERNAL_ERROR;
- myisamchk_init(&param);
- param.thd = thd;
- param.op_name= "repair";
- param.testflag= ((check_opt->flags & ~(T_EXTEND)) |
+ myisamchk_init(param);
+ param->thd = thd;
+ param->op_name= "repair";
+ param->testflag= ((check_opt->flags & ~(T_EXTEND)) |
T_SILENT | T_FORCE_CREATE | T_CALC_CHECKSUM |
(check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT));
- param.tmpfile_createflag= O_RDWR | O_TRUNC;
- param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
- param.backup_time= check_opt->start_time;
+ param->tmpfile_createflag= O_RDWR | O_TRUNC;
+ param->sort_buffer_length= THDVAR(thd, sort_buffer_size);
+ param->backup_time= check_opt->start_time;
start_records=file->state->records;
- while ((error=repair(thd,param,0)) && param.retry_repair)
+ while ((error=repair(thd,*param,0)) && param->retry_repair)
{
- param.retry_repair=0;
- if (test_all_bits(param.testflag,
+ param->retry_repair=0;
+ if (test_all_bits(param->testflag,
(uint) (T_RETRY_WITHOUT_QUICK | T_QUICK)))
{
- param.testflag&= ~(T_RETRY_WITHOUT_QUICK | T_QUICK);
+ param->testflag&= ~(T_RETRY_WITHOUT_QUICK | T_QUICK);
/* Ensure we don't loose any rows when retrying without quick */
- param.testflag|= T_SAFE_REPAIR;
+ param->testflag|= T_SAFE_REPAIR;
sql_print_information("Retrying repair of: '%s' including modifying data file",
table->s->path.str);
continue;
}
- param.testflag&= ~T_QUICK;
- if ((param.testflag & T_REP_BY_SORT))
+ param->testflag&= ~T_QUICK;
+ if ((param->testflag & T_REP_BY_SORT))
{
- param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP;
+ param->testflag= (param->testflag & ~T_REP_BY_SORT) | T_REP;
sql_print_information("Retrying repair of: '%s' with keycache",
table->s->path.str);
continue;
@@ -1053,23 +1053,23 @@ int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt)
int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt)
{
int error;
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
- if (!file || !&param) return HA_ADMIN_INTERNAL_ERROR;
+ if (!file || !param) return HA_ADMIN_INTERNAL_ERROR;
- myisamchk_init(&param);
- param.thd = thd;
- param.op_name= "optimize";
- param.testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE |
+ myisamchk_init(param);
+ param->thd = thd;
+ param->op_name= "optimize";
+ param->testflag= (check_opt->flags | T_SILENT | T_FORCE_CREATE |
T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX);
- param.tmpfile_createflag= O_RDWR | O_TRUNC;
- param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
- if ((error= repair(thd,param,1)) && param.retry_repair)
+ param->tmpfile_createflag= O_RDWR | O_TRUNC;
+ param->sort_buffer_length= THDVAR(thd, sort_buffer_size);
+ if ((error= repair(thd,*param,1)) && param->retry_repair)
{
sql_print_warning("Warning: Optimize table got errno %d on %s.%s, retrying",
- my_errno, param.db_name, param.table_name);
- param.testflag&= ~T_REP_BY_SORT;
- error= repair(thd,param,1);
+ my_errno, param->db_name, param->table_name);
+ param->testflag&= ~T_REP_BY_SORT;
+ error= repair(thd,*param,1);
}
return error;
}
@@ -1274,17 +1274,17 @@ int ha_myisam::assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt)
if (error != HA_ADMIN_OK)
{
/* Send error to user */
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
- if (!&param)
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
+ if (!param)
return HA_ADMIN_INTERNAL_ERROR;
- myisamchk_init(&param);
- param.thd= thd;
- param.op_name= "assign_to_keycache";
- param.db_name= table->s->db.str;
- param.table_name= table->s->table_name.str;
- param.testflag= 0;
- mi_check_print_error(&param, errmsg);
+ myisamchk_init(param);
+ param->thd= thd;
+ param->op_name= "assign_to_keycache";
+ param->db_name= table->s->db.str;
+ param->table_name= table->s->table_name.str;
+ param->testflag= 0;
+ mi_check_print_error(param, errmsg);
}
DBUG_RETURN(error);
}
@@ -1341,16 +1341,16 @@ int ha_myisam::preload_keys(THD* thd, HA_CHECK_OPT *check_opt)
err:
{
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
- if (!&param)
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
+ if (!param)
return HA_ADMIN_INTERNAL_ERROR;
- myisamchk_init(&param);
- param.thd= thd;
- param.op_name= "preload_keys";
- param.db_name= table->s->db.str;
- param.table_name= table->s->table_name.str;
- param.testflag= 0;
- mi_check_print_error(&param, errmsg);
+ myisamchk_init(param);
+ param->thd= thd;
+ param->op_name= "preload_keys";
+ param->db_name= table->s->db.str;
+ param->table_name= table->s->table_name.str;
+ param->testflag= 0;
+ mi_check_print_error(param, errmsg);
DBUG_RETURN(error);
}
}
@@ -1455,45 +1455,45 @@ int ha_myisam::enable_indexes(uint mode)
{
THD *thd= table->in_use;
int was_error= thd->is_error();
- HA_CHECK &param= *(HA_CHECK*) thd->alloc(sizeof(param));
+ HA_CHECK *param= (HA_CHECK*) thd->alloc(sizeof *param);
const char *save_proc_info=thd->proc_info;
- if (!&param)
+ if (!param)
DBUG_RETURN(HA_ADMIN_INTERNAL_ERROR);
thd_proc_info(thd, "Creating index");
- myisamchk_init(&param);
- param.op_name= "recreating_index";
- param.testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
+ myisamchk_init(param);
+ param->op_name= "recreating_index";
+ param->testflag= (T_SILENT | T_REP_BY_SORT | T_QUICK |
T_CREATE_MISSING_KEYS);
/*
Don't lock and unlock table if it's locked.
Normally table should be locked. This test is mostly for safety.
*/
if (likely(file->lock_type != F_UNLCK))
- param.testflag|= T_NO_LOCKS;
-
+ param->testflag|= T_NO_LOCKS;
+
if (file->create_unique_index_by_sort)
- param.testflag|= T_CREATE_UNIQUE_BY_SORT;
+ param->testflag|= T_CREATE_UNIQUE_BY_SORT;
- param.myf_rw&= ~MY_WAIT_IF_FULL;
- param.sort_buffer_length= THDVAR(thd, sort_buffer_size);
- param.stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
- param.tmpdir=&mysql_tmpdir_list;
- if ((error= (repair(thd,param,0) != HA_ADMIN_OK)) && param.retry_repair)
+ param->myf_rw&= ~MY_WAIT_IF_FULL;
+ param->sort_buffer_length= THDVAR(thd, sort_buffer_size);
+ param->stats_method= (enum_handler_stats_method)THDVAR(thd, stats_method);
+ param->tmpdir=&mysql_tmpdir_list;
+ if ((error= (repair(thd,*param,0) != HA_ADMIN_OK)) && param->retry_repair)
{
sql_print_warning("Warning: Enabling keys got errno %d on %s.%s, retrying",
- my_errno, param.db_name, param.table_name);
+ my_errno, param->db_name, param->table_name);
/*
Repairing by sort failed. Now try standard repair method.
Still we want to fix only index file. If data file corruption
was detected (T_RETRY_WITHOUT_QUICK), we shouldn't do much here.
Let implicit repair do this job.
*/
- if (!(param.testflag & T_RETRY_WITHOUT_QUICK))
+ if (!(param->testflag & T_RETRY_WITHOUT_QUICK))
{
- param.testflag&= ~T_REP_BY_SORT;
- error= (repair(thd,param,0) != HA_ADMIN_OK);
+ param->testflag&= ~T_REP_BY_SORT;
+ error= (repair(thd,*param,0) != HA_ADMIN_OK);
}
/*
If the standard repair succeeded, clear all error messages which
@@ -1896,15 +1896,22 @@ int ha_myisam::info(uint flag)
Set data_file_name and index_file_name to point at the symlink value
if table is symlinked (Ie; Real name is not same as generated name)
*/
+ char buf[FN_REFLEN];
data_file_name= index_file_name= 0;
fn_format(name_buff, file->filename, "", MI_NAME_DEXT,
MY_APPEND_EXT | MY_UNPACK_FILENAME);
- if (strcmp(name_buff, misam_info.data_file_name))
- data_file_name=misam_info.data_file_name;
+ if (my_is_symlink(name_buff))
+ {
+ my_readlink(buf, name_buff, MYF(0));
+ data_file_name= ha_thd()->strdup(buf);
+ }
fn_format(name_buff, file->filename, "", MI_NAME_IEXT,
MY_APPEND_EXT | MY_UNPACK_FILENAME);
- if (strcmp(name_buff, misam_info.index_file_name))
- index_file_name=misam_info.index_file_name;
+ if (my_is_symlink(name_buff))
+ {
+ my_readlink(buf, name_buff, MYF(0));
+ index_file_name= ha_thd()->strdup(buf);
+ }
}
if (flag & HA_STATUS_ERRKEY)
{
diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c
index bab0ad2b6a4..3d6e6297d3b 100644
--- a/storage/myisam/mi_check.c
+++ b/storage/myisam/mi_check.c
@@ -75,7 +75,7 @@ static int sort_delete_record(MI_SORT_PARAM *sort_param);
static SORT_KEY_BLOCKS *alloc_key_blocks(HA_CHECK *, uint, uint);
static ha_checksum mi_byte_checksum(const uchar *buf, uint length);
static void set_data_file_type(MI_SORT_INFO *sort_info, MYISAM_SHARE *share);
-static int replace_data_file(HA_CHECK *, MI_INFO *, const char *, File);
+static int replace_data_file(HA_CHECK *param, MI_INFO *info, File new_file);
void myisamchk_init(HA_CHECK *param)
{
@@ -1708,7 +1708,7 @@ err:
/* Replace the actual file with the temporary file */
if (new_file >= 0)
{
- got_error= replace_data_file(param, info, name, new_file);
+ got_error= replace_data_file(param, info, new_file);
new_file= -1;
param->retry_repair= 0;
}
@@ -2522,7 +2522,7 @@ err:
/* Replace the actual file with the temporary file */
if (new_file >= 0)
{
- got_error= replace_data_file(param, info, name, new_file);
+ got_error= replace_data_file(param, info, new_file);
new_file= -1;
}
}
@@ -2536,7 +2536,7 @@ err:
(void) mysql_file_delete(mi_key_file_datatmp,
param->temp_filename, MYF(MY_WME));
if (info->dfile == new_file) /* Retry with key cache */
- if (unlikely(mi_open_datafile(info, share, name, -1)))
+ if (unlikely(mi_open_datafile(info, share)))
param->retry_repair= 0; /* Safety */
}
mi_mark_crashed_on_repair(info);
@@ -3061,7 +3061,7 @@ err:
/* Replace the actual file with the temporary file */
if (new_file >= 0)
{
- got_error= replace_data_file(param, info, name, new_file);
+ got_error= replace_data_file(param, info, new_file);
new_file= -1;
}
}
@@ -3075,7 +3075,7 @@ err:
(void) mysql_file_delete(mi_key_file_datatmp,
param->temp_filename, MYF(MY_WME));
if (info->dfile == new_file) /* Retry with key cache */
- if (unlikely(mi_open_datafile(info, share, name, -1)))
+ if (unlikely(mi_open_datafile(info, share)))
param->retry_repair= 0; /* Safety */
}
mi_mark_crashed_on_repair(info);
@@ -4757,8 +4757,7 @@ int mi_make_backup_of_index(MI_INFO *info, time_t backup_time, myf flags)
}
-static int replace_data_file(HA_CHECK *param, MI_INFO *info,
- const char *name, File new_file)
+static int replace_data_file(HA_CHECK *param, MI_INFO *info, File new_file)
{
MYISAM_SHARE *share=info->s;
@@ -4794,7 +4793,7 @@ static int replace_data_file(HA_CHECK *param, MI_INFO *info,
DATA_TMP_EXT, param->backup_time,
(param->testflag & T_BACKUP_DATA ?
MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
- mi_open_datafile(info, share, name, -1))
+ mi_open_datafile(info, share))
return 1;
return 0;
}
diff --git a/storage/myisam/mi_create.c b/storage/myisam/mi_create.c
index c781538dc0f..8c02674fba5 100644
--- a/storage/myisam/mi_create.c
+++ b/storage/myisam/mi_create.c
@@ -46,7 +46,8 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
uint aligned_key_start, block_length, res;
uint internal_table= flags & HA_CREATE_INTERNAL_TABLE;
ulong reclength, real_reclength,min_pack_length;
- char filename[FN_REFLEN],linkname[FN_REFLEN], *linkname_ptr;
+ char kfilename[FN_REFLEN],klinkname[FN_REFLEN], *klinkname_ptr;
+ char dfilename[FN_REFLEN],dlinkname[FN_REFLEN], *dlinkname_ptr;
ulong pack_reclength;
ulonglong tot_length,max_rows, tmp;
enum en_fieldtype type;
@@ -594,19 +595,19 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
/* chop off the table name, tempory tables use generated name */
if ((path= strrchr(ci->index_file_name, FN_LIBCHAR)))
*path= '\0';
- fn_format(filename, name, ci->index_file_name, MI_NAME_IEXT,
+ fn_format(kfilename, name, ci->index_file_name, MI_NAME_IEXT,
MY_REPLACE_DIR | MY_UNPACK_FILENAME |
MY_RETURN_REAL_PATH | MY_APPEND_EXT);
}
else
{
- fn_format(filename, ci->index_file_name, "", MI_NAME_IEXT,
+ fn_format(kfilename, ci->index_file_name, "", MI_NAME_IEXT,
MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
(have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
}
- fn_format(linkname, name, "", MI_NAME_IEXT,
+ fn_format(klinkname, name, "", MI_NAME_IEXT,
MY_UNPACK_FILENAME|MY_APPEND_EXT);
- linkname_ptr=linkname;
+ klinkname_ptr= klinkname;
/*
Don't create the table if the link or file exists to ensure that one
doesn't accidently destroy another table.
@@ -617,10 +618,10 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
{
char *iext= strrchr(name, '.');
int have_iext= iext && !strcmp(iext, MI_NAME_IEXT);
- fn_format(filename, name, "", MI_NAME_IEXT,
+ fn_format(kfilename, name, "", MI_NAME_IEXT,
MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH |
(have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT));
- linkname_ptr=0;
+ klinkname_ptr= 0;
/* Replace the current file */
create_flag=(flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
}
@@ -635,7 +636,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
NOTE: The filename is compared against unique_file_name of every
open table. Hence we need a real path here.
*/
- if (!internal_table && test_if_reopen(filename))
+ if (!internal_table && test_if_reopen(kfilename))
{
my_printf_error(HA_ERR_TABLE_EXIST, "MyISAM table '%s' is in use "
"(most likely by a MERGE table). Try FLUSH TABLES.",
@@ -645,7 +646,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
}
if ((file= mysql_file_create_with_symlink(mi_key_file_kfile,
- linkname_ptr, filename, 0,
+ klinkname_ptr, kfilename, 0,
create_mode,
MYF(MY_WME | create_flag))) < 0)
goto err;
@@ -665,31 +666,31 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs,
/* chop off the table name, tempory tables use generated name */
if ((path= strrchr(ci->data_file_name, FN_LIBCHAR)))
*path= '\0';
- fn_format(filename, name, ci->data_file_name, MI_NAME_DEXT,
+ fn_format(dfilename, name, ci->data_file_name, MI_NAME_DEXT,
MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT);
}
else
{
- fn_format(filename, ci->data_file_name, "", MI_NAME_DEXT,
+ fn_format(dfilename, ci->data_file_name, "", MI_NAME_DEXT,
MY_UNPACK_FILENAME |
(have_dext ? MY_REPLACE_EXT : MY_APPEND_EXT));
}
- fn_format(linkname, name, "",MI_NAME_DEXT,
+ fn_format(dlinkname, name, "",MI_NAME_DEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT);
- linkname_ptr=linkname;
+ dlinkname_ptr= dlinkname;
create_flag=0;
}
else
{
- fn_format(filename,name,"", MI_NAME_DEXT,
+ fn_format(dfilename,name,"", MI_NAME_DEXT,
MY_UNPACK_FILENAME | MY_APPEND_EXT);
- linkname_ptr=0;
+ dlinkname_ptr= 0;
create_flag=(flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD;
}
if ((dfile=
mysql_file_create_with_symlink(mi_key_file_dfile,
- linkname_ptr, filename, 0,
+ dlinkname_ptr, dfilename, 0,
create_mode,
MYF(MY_WME | create_flag))) < 0)
goto err;
@@ -842,19 +843,21 @@ err_no_lock:
(void) mysql_file_close(dfile, MYF(0));
/* fall through */
case 2:
- if (! (flags & HA_DONT_TOUCH_DATA))
- mysql_file_delete_with_symlink(mi_key_file_dfile,
- fn_format(filename, name, "", MI_NAME_DEXT,
- MY_UNPACK_FILENAME | MY_APPEND_EXT),
- MYF(0));
+ if (! (flags & HA_DONT_TOUCH_DATA))
+ {
+ mysql_file_delete(mi_key_file_dfile, dfilename, MYF(0));
+ if (dlinkname_ptr)
+ mysql_file_delete(mi_key_file_dfile, dlinkname_ptr, MYF(0));
+ }
/* fall through */
case 1:
(void) mysql_file_close(file, MYF(0));
if (! (flags & HA_DONT_TOUCH_DATA))
- mysql_file_delete_with_symlink(mi_key_file_kfile,
- fn_format(filename, name, "", MI_NAME_IEXT,
- MY_UNPACK_FILENAME | MY_APPEND_EXT),
- MYF(0));
+ {
+ mysql_file_delete(mi_key_file_kfile, kfilename, MYF(0));
+ if (klinkname_ptr)
+ mysql_file_delete(mi_key_file_kfile, klinkname_ptr, MYF(0));
+ }
}
my_free(rec_per_key_part);
DBUG_RETURN(my_errno=save_errno); /* return the fatal errno */
diff --git a/storage/myisam/mi_delete_table.c b/storage/myisam/mi_delete_table.c
index 7da960011ca..3422e6b045d 100644
--- a/storage/myisam/mi_delete_table.c
+++ b/storage/myisam/mi_delete_table.c
@@ -26,47 +26,22 @@
#define mi_key_file_dfile 0
#endif
-static int delete_one_file(const char *name, const char *ext,
- PSI_file_key pskey __attribute__((unused)),
- myf flags)
-{
- char from[FN_REFLEN];
- DBUG_ENTER("delete_one_file");
- fn_format(from,name, "", ext, MY_UNPACK_FILENAME | MY_APPEND_EXT);
- if (my_is_symlink(from) && (*myisam_test_invalid_symlink)(from))
- {
- /*
- Symlink is pointing to file in data directory.
- Remove symlink, keep file.
- */
- if (mysql_file_delete(pskey, from, flags))
- DBUG_RETURN(my_errno);
- }
- else
- {
- if (mysql_file_delete_with_symlink(pskey, from, flags))
- DBUG_RETURN(my_errno);
- }
- DBUG_RETURN(0);
-}
-
int mi_delete_table(const char *name)
{
- int res;
DBUG_ENTER("mi_delete_table");
#ifdef EXTRA_DEBUG
check_table_is_closed(name,"delete");
#endif
- if ((res= delete_one_file(name, MI_NAME_IEXT, mi_key_file_kfile, MYF(MY_WME))))
- DBUG_RETURN(res);
- if ((res= delete_one_file(name, MI_NAME_DEXT, mi_key_file_dfile, MYF(MY_WME))))
- DBUG_RETURN(res);
+ if (my_handler_delete_with_symlink(mi_key_file_kfile, name, MI_NAME_IEXT, MYF(MY_WME)) ||
+ my_handler_delete_with_symlink(mi_key_file_dfile, name, MI_NAME_DEXT, MYF(MY_WME)))
+ DBUG_RETURN(my_errno);
+
// optionally present:
- delete_one_file(name, ".OLD", mi_key_file_dfile, MYF(0));
- delete_one_file(name, ".TMD", mi_key_file_dfile, MYF(0));
+ my_handler_delete_with_symlink(mi_key_file_dfile, name, ".OLD", MYF(0));
+ my_handler_delete_with_symlink(mi_key_file_dfile, name, ".TMD", MYF(0));
DBUG_RETURN(0);
}
diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c
index 2d794f611d7..354fb0a8e04 100644
--- a/storage/myisam/mi_open.c
+++ b/storage/myisam/mi_open.c
@@ -104,8 +104,13 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
realpath_err= my_realpath(name_buff,
fn_format(org_name,name,"",MI_NAME_IEXT,4),MYF(0));
+ if (realpath_err > 0) /* File not found, no point in looking further. */
+ {
+ DBUG_RETURN(NULL);
+ }
+
if (my_is_symlink(org_name) &&
- (realpath_err || (*myisam_test_invalid_symlink)(name_buff)))
+ (realpath_err || mysys_test_invalid_symlink(name_buff)))
{
my_errno= HA_WRONG_CREATE_OPTION;
DBUG_RETURN (NULL);
@@ -131,15 +136,17 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
my_errno= HA_ERR_CRASHED;
goto err;
});
- if ((kfile= mysql_file_open(mi_key_file_kfile,
- name_buff,
- (open_mode= O_RDWR) | O_SHARE, MYF(0))) < 0)
+
+ DEBUG_SYNC_C("mi_open_kfile");
+ if ((kfile= mysql_file_open(mi_key_file_kfile, name_buff,
+ (open_mode= O_RDWR) | O_SHARE | O_NOFOLLOW,
+ MYF(MY_NOSYMLINKS))) < 0)
{
if ((errno != EROFS && errno != EACCES) ||
mode != O_RDONLY ||
- (kfile= mysql_file_open(mi_key_file_kfile,
- name_buff,
- (open_mode= O_RDONLY) | O_SHARE, MYF(0))) < 0)
+ (kfile= mysql_file_open(mi_key_file_kfile, name_buff,
+ (open_mode= O_RDONLY) | O_SHARE| O_NOFOLLOW,
+ MYF(MY_NOSYMLINKS))) < 0)
goto err;
}
share->mode=open_mode;
@@ -183,7 +190,18 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
(void) strmov(index_name, org_name);
*strrchr(org_name, '.')= '\0';
(void) fn_format(data_name,org_name,"",MI_NAME_DEXT,
- MY_APPEND_EXT|MY_UNPACK_FILENAME|MY_RESOLVE_SYMLINKS);
+ MY_APPEND_EXT|MY_UNPACK_FILENAME);
+ if (my_is_symlink(data_name))
+ {
+ if (my_realpath(data_name, data_name, MYF(0)))
+ goto err;
+ if (mysys_test_invalid_symlink(data_name))
+ {
+ my_errno= HA_WRONG_CREATE_OPTION;
+ goto err;
+ }
+ share->mode|= O_NOFOLLOW; /* all symlinks are resolved by realpath() */
+ }
info_length=mi_uint2korr(share->state.header.header_length);
base_pos=mi_uint2korr(share->state.header.base_pos);
@@ -497,7 +515,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
lock_error=1; /* Database unlocked */
}
- if (mi_open_datafile(&info, share, name, -1))
+ if (mi_open_datafile(&info, share))
goto err;
errpos=5;
@@ -578,7 +596,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags)
my_errno=EACCES; /* Can't open in write mode */
goto err;
}
- if (mi_open_datafile(&info, share, name, old_info->dfile))
+ if (mi_open_datafile(&info, share))
goto err;
errpos=5;
have_rtree= old_info->rtree_recursion_state != NULL;
@@ -1248,33 +1266,14 @@ uchar *mi_recinfo_read(uchar *ptr, MI_COLUMNDEF *recinfo)
Open data file.
We can't use dup() here as the data file descriptors need to have different
active seek-positions.
-
-The argument file_to_dup is here for the future if there would on some OS
-exist a dup()-like call that would give us two different file descriptors.
*************************************************************************/
-int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *org_name,
- File file_to_dup __attribute__((unused)))
+int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share)
{
- char *data_name= share->data_file_name;
- char real_data_name[FN_REFLEN];
-
- if (org_name)
- {
- fn_format(real_data_name,org_name,"",MI_NAME_DEXT,4);
- if (my_is_symlink(real_data_name))
- {
- if (my_realpath(real_data_name, real_data_name, MYF(0)) ||
- (*myisam_test_invalid_symlink)(real_data_name))
- {
- my_errno= HA_WRONG_CREATE_OPTION;
- return 1;
- }
- data_name= real_data_name;
- }
- }
- info->dfile= mysql_file_open(mi_key_file_dfile,
- data_name, share->mode | O_SHARE, MYF(MY_WME));
+ myf flags= MY_WME | (share->mode & O_NOFOLLOW ? MY_NOSYMLINKS: 0);
+ DEBUG_SYNC_C("mi_open_datafile");
+ info->dfile= mysql_file_open(mi_key_file_dfile, share->data_file_name,
+ share->mode | O_SHARE, MYF(flags));
return info->dfile >= 0 ? 0 : 1;
}
@@ -1283,8 +1282,8 @@ int mi_open_keyfile(MYISAM_SHARE *share)
{
if ((share->kfile= mysql_file_open(mi_key_file_kfile,
share->unique_file_name,
- share->mode | O_SHARE,
- MYF(MY_WME))) < 0)
+ share->mode | O_SHARE | O_NOFOLLOW,
+ MYF(MY_NOSYMLINKS | MY_WME))) < 0)
return 1;
return 0;
}
diff --git a/storage/myisam/mi_static.c b/storage/myisam/mi_static.c
index d77f4f6b8e2..49019fb861c 100644
--- a/storage/myisam/mi_static.c
+++ b/storage/myisam/mi_static.c
@@ -42,14 +42,6 @@ ulong myisam_data_pointer_size=4;
ulonglong myisam_mmap_size= SIZE_T_MAX, myisam_mmap_used= 0;
my_bool (*mi_killed)(MI_INFO *)= mi_killed_standalone;
-static int always_valid(const char *filename __attribute__((unused)))
-{
- return 0;
-}
-
-int (*myisam_test_invalid_symlink)(const char *filename)= always_valid;
-
-
/*
read_vec[] is used for converting between P_READ_KEY.. and SEARCH_
Position is , == , >= , <= , > , <
diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c
index 7835ab83531..edbe235e190 100644
--- a/storage/myisam/myisamchk.c
+++ b/storage/myisam/myisamchk.c
@@ -1047,7 +1047,7 @@ static int myisamchk(HA_CHECK *param, char * filename)
MYF(MY_WME)); /* Close new file */
error|=change_to_newfile(filename, MI_NAME_DEXT, DATA_TMP_EXT,
0, MYF(0));
- if (mi_open_datafile(info,info->s, NULL, -1))
+ if (mi_open_datafile(info, info->s))
error=1;
param->out_flag&= ~O_NEW_DATA; /* We are using new datafile */
param->read_cache.file=info->dfile;
diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h
index 336f1170d29..9d94a26d30c 100644
--- a/storage/myisam/myisamdef.h
+++ b/storage/myisam/myisamdef.h
@@ -713,8 +713,7 @@ void mi_disable_indexes_for_rebuild(MI_INFO *info, ha_rows rows,
my_bool all_keys);
extern MI_INFO *test_if_reopen(char *filename);
my_bool check_table_is_closed(const char *name, const char *where);
-int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *orn_name,
- File file_to_dup);
+int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share);
int mi_open_keyfile(MYISAM_SHARE *share);
void mi_setup_functions(register MYISAM_SHARE *share);
diff --git a/storage/tokudb/CMakeLists.txt b/storage/tokudb/CMakeLists.txt
index 50147575b94..041adc51abc 100644
--- a/storage/tokudb/CMakeLists.txt
+++ b/storage/tokudb/CMakeLists.txt
@@ -1,4 +1,4 @@
-SET(TOKUDB_VERSION 5.6.34-79.1)
+SET(TOKUDB_VERSION 5.6.35-80.0)
# PerconaFT only supports x86-64 and cmake-2.8.9+
IF(CMAKE_VERSION VERSION_LESS "2.8.9")
MESSAGE(STATUS "CMake 2.8.9 or higher is required by TokuDB")
diff --git a/storage/tokudb/PerconaFT/ft/ft-ops.cc b/storage/tokudb/PerconaFT/ft/ft-ops.cc
index 30a8710d7aa..ad9ecb1d074 100644
--- a/storage/tokudb/PerconaFT/ft/ft-ops.cc
+++ b/storage/tokudb/PerconaFT/ft/ft-ops.cc
@@ -651,10 +651,8 @@ void toku_ftnode_clone_callback(void *value_data,
// set new pair attr if necessary
if (node->height == 0) {
*new_attr = make_ftnode_pair_attr(node);
- for (int i = 0; i < node->n_children; i++) {
- BLB(node, i)->logical_rows_delta = 0;
- BLB(cloned_node, i)->logical_rows_delta = 0;
- }
+ node->logical_rows_delta = 0;
+ cloned_node->logical_rows_delta = 0;
} else {
new_attr->is_valid = false;
}
@@ -702,6 +700,10 @@ void toku_ftnode_flush_callback(CACHEFILE UU(cachefile),
if (ftnode->height == 0) {
FT_STATUS_INC(FT_FULL_EVICTIONS_LEAF, 1);
FT_STATUS_INC(FT_FULL_EVICTIONS_LEAF_BYTES, node_size);
+ if (!ftnode->dirty) {
+ toku_ft_adjust_logical_row_count(
+ ft, -ftnode->logical_rows_delta);
+ }
} else {
FT_STATUS_INC(FT_FULL_EVICTIONS_NONLEAF, 1);
FT_STATUS_INC(FT_FULL_EVICTIONS_NONLEAF_BYTES, node_size);
@@ -714,11 +716,12 @@ void toku_ftnode_flush_callback(CACHEFILE UU(cachefile),
BASEMENTNODE bn = BLB(ftnode, i);
toku_ft_decrease_stats(&ft->in_memory_stats,
bn->stat64_delta);
- if (!ftnode->dirty)
- toku_ft_adjust_logical_row_count(
- ft, -bn->logical_rows_delta);
}
}
+ if (!ftnode->dirty) {
+ toku_ft_adjust_logical_row_count(
+ ft, -ftnode->logical_rows_delta);
+ }
}
}
toku_ftnode_free(&ftnode);
@@ -944,8 +947,6 @@ int toku_ftnode_pe_callback(void *ftnode_pv,
basements_to_destroy[num_basements_to_destroy++] = bn;
toku_ft_decrease_stats(&ft->in_memory_stats,
bn->stat64_delta);
- toku_ft_adjust_logical_row_count(ft,
- -bn->logical_rows_delta);
set_BNULL(node, i);
BP_STATE(node, i) = PT_ON_DISK;
num_partial_evictions++;
@@ -2652,7 +2653,7 @@ static std::unique_ptr<char[], decltype(&toku_free)> toku_file_get_parent_dir(
return result;
}
-static bool toku_create_subdirs_if_needed(const char *path) {
+bool toku_create_subdirs_if_needed(const char *path) {
static const mode_t dir_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH;
@@ -4563,6 +4564,8 @@ int toku_ft_rename_iname(DB_TXN *txn,
bs_new_name);
}
+ if (!toku_create_subdirs_if_needed(new_iname_full.get()))
+ return get_error_errno();
r = toku_os_rename(old_iname_full.get(), new_iname_full.get());
if (r != 0)
return r;
diff --git a/storage/tokudb/PerconaFT/ft/ft-ops.h b/storage/tokudb/PerconaFT/ft/ft-ops.h
index 70cf045d43c..df8ffe287df 100644
--- a/storage/tokudb/PerconaFT/ft/ft-ops.h
+++ b/storage/tokudb/PerconaFT/ft/ft-ops.h
@@ -288,3 +288,8 @@ void toku_ft_set_direct_io(bool direct_io_on);
void toku_ft_set_compress_buffers_before_eviction(bool compress_buffers);
void toku_note_deserialized_basement_node(bool fixed_key_size);
+
+// Creates all directories for the path if necessary,
+// returns true if all dirs are created successfully or
+// all dirs exist, false otherwise.
+bool toku_create_subdirs_if_needed(const char* path);
diff --git a/storage/tokudb/PerconaFT/ft/logger/recover.cc b/storage/tokudb/PerconaFT/ft/logger/recover.cc
index a9c30c0e37a..9eaa56bdc53 100644
--- a/storage/tokudb/PerconaFT/ft/logger/recover.cc
+++ b/storage/tokudb/PerconaFT/ft/logger/recover.cc
@@ -987,7 +987,8 @@ static int toku_recover_frename(struct logtype_frename *l, RECOVER_ENV renv) {
return 1;
if (old_exist && !new_exist &&
- (toku_os_rename(old_iname_full.get(), new_iname_full.get()) == -1 ||
+ (!toku_create_subdirs_if_needed(new_iname_full.get()) ||
+ toku_os_rename(old_iname_full.get(), new_iname_full.get()) == -1 ||
toku_fsync_directory(old_iname_full.get()) == -1 ||
toku_fsync_directory(new_iname_full.get()) == -1))
return 1;
diff --git a/storage/tokudb/PerconaFT/ft/node.cc b/storage/tokudb/PerconaFT/ft/node.cc
index 12e5fda226e..07309ff7f94 100644
--- a/storage/tokudb/PerconaFT/ft/node.cc
+++ b/storage/tokudb/PerconaFT/ft/node.cc
@@ -386,7 +386,8 @@ static void bnc_apply_messages_to_basement_node(
const pivot_bounds &
bounds, // contains pivot key bounds of this basement node
txn_gc_info *gc_info,
- bool *msgs_applied) {
+ bool *msgs_applied,
+ int64_t* logical_rows_delta) {
int r;
NONLEAF_CHILDINFO bnc = BNC(ancestor, childnum);
@@ -394,7 +395,6 @@ static void bnc_apply_messages_to_basement_node(
// apply messages from this buffer
STAT64INFO_S stats_delta = {0, 0};
uint64_t workdone_this_ancestor = 0;
- int64_t logical_rows_delta = 0;
uint32_t stale_lbi, stale_ube;
if (!bn->stale_ancestor_messages_applied) {
@@ -470,7 +470,7 @@ static void bnc_apply_messages_to_basement_node(
gc_info,
&workdone_this_ancestor,
&stats_delta,
- &logical_rows_delta);
+ logical_rows_delta);
}
} else if (stale_lbi == stale_ube) {
// No stale messages to apply, we just apply fresh messages, and mark
@@ -482,7 +482,7 @@ static void bnc_apply_messages_to_basement_node(
.gc_info = gc_info,
.workdone = &workdone_this_ancestor,
.stats_to_update = &stats_delta,
- .logical_rows_delta = &logical_rows_delta};
+ .logical_rows_delta = logical_rows_delta};
if (fresh_ube - fresh_lbi > 0)
*msgs_applied = true;
r = bnc->fresh_message_tree
@@ -503,7 +503,7 @@ static void bnc_apply_messages_to_basement_node(
.gc_info = gc_info,
.workdone = &workdone_this_ancestor,
.stats_to_update = &stats_delta,
- .logical_rows_delta = &logical_rows_delta};
+ .logical_rows_delta = logical_rows_delta};
r = bnc->stale_message_tree
.iterate_on_range<struct iterate_do_bn_apply_msg_extra,
@@ -521,8 +521,6 @@ static void bnc_apply_messages_to_basement_node(
if (stats_delta.numbytes || stats_delta.numrows) {
toku_ft_update_stats(&t->ft->in_memory_stats, stats_delta);
}
- toku_ft_adjust_logical_row_count(t->ft, logical_rows_delta);
- bn->logical_rows_delta += logical_rows_delta;
}
static void
@@ -536,6 +534,7 @@ apply_ancestors_messages_to_bn(
bool* msgs_applied
)
{
+ int64_t logical_rows_delta = 0;
BASEMENTNODE curr_bn = BLB(node, childnum);
const pivot_bounds curr_bounds = bounds.next_bounds(node, childnum);
for (ANCESTORS curr_ancestors = ancestors; curr_ancestors; curr_ancestors = curr_ancestors->next) {
@@ -548,13 +547,16 @@ apply_ancestors_messages_to_bn(
curr_ancestors->childnum,
curr_bounds,
gc_info,
- msgs_applied
+ msgs_applied,
+ &logical_rows_delta
);
// We don't want to check this ancestor node again if the
// next time we query it, the msn hasn't changed.
curr_bn->max_msn_applied = curr_ancestors->node->max_msn_applied_to_node_on_disk;
}
}
+ toku_ft_adjust_logical_row_count(t->ft, logical_rows_delta);
+ node->logical_rows_delta += logical_rows_delta;
// At this point, we know all the stale messages above this
// basement node have been applied, and any new messages will be
// fresh, so we don't need to look at stale messages for this
diff --git a/storage/tokudb/PerconaFT/ft/node.h b/storage/tokudb/PerconaFT/ft/node.h
index 52eefec0936..db189e36d59 100644
--- a/storage/tokudb/PerconaFT/ft/node.h
+++ b/storage/tokudb/PerconaFT/ft/node.h
@@ -157,36 +157,49 @@ private:
// TODO: class me up
struct ftnode {
- MSN max_msn_applied_to_node_on_disk; // max_msn_applied that will be written to disk
+ // max_msn_applied that will be written to disk
+ MSN max_msn_applied_to_node_on_disk;
unsigned int flags;
- BLOCKNUM blocknum; // Which block number is this node?
- int layout_version; // What version of the data structure?
- int layout_version_original; // different (<) from layout_version if upgraded from a previous version (useful for debugging)
- int layout_version_read_from_disk; // transient, not serialized to disk, (useful for debugging)
- uint32_t build_id; // build_id (svn rev number) of software that wrote this node to disk
- int height; /* height is always >= 0. 0 for leaf, >0 for nonleaf. */
- int dirty;
+ // Which block number is this node?
+ BLOCKNUM blocknum;
+ // What version of the data structure?
+ int layout_version;
+ // different (<) from layout_version if upgraded from a previous version
+ // (useful for debugging)
+ int layout_version_original;
+ // transient, not serialized to disk, (useful for debugging)
+ int layout_version_read_from_disk;
+ // build_id (svn rev number) of software that wrote this node to disk
+ uint32_t build_id;
+ // height is always >= 0. 0 for leaf, >0 for nonleaf.
+ int height;
+ int dirty;
uint32_t fullhash;
+ // current count of rows add or removed as a result of message application
+ // to this node as a basement, irrelevant for internal nodes, gets reset
+ // when node is undirtied. Used to back out tree scoped LRC id node is
+ // evicted but not persisted
+ int64_t logical_rows_delta;
- // for internal nodes, if n_children==fanout+1 then the tree needs to be rebalanced.
- // for leaf nodes, represents number of basement nodes
+ // for internal nodes, if n_children==fanout+1 then the tree needs to be
+ // rebalanced. for leaf nodes, represents number of basement nodes
int n_children;
ftnode_pivot_keys pivotkeys;
- // What's the oldest referenced xid that this node knows about? The real oldest
- // referenced xid might be younger, but this is our best estimate. We use it
- // as a heuristic to transition provisional mvcc entries from provisional to
- // committed (from implicity committed to really committed).
+ // What's the oldest referenced xid that this node knows about? The real
+ // oldest referenced xid might be younger, but this is our best estimate.
+ // We use it as a heuristic to transition provisional mvcc entries from
+ // provisional to committed (from implicity committed to really committed).
//
- // A better heuristic would be the oldest live txnid, but we use this since it
- // still works well most of the time, and its readily available on the inject
- // code path.
+ // A better heuristic would be the oldest live txnid, but we use this since
+ // it still works well most of the time, and its readily available on the
+ // inject code path.
TXNID oldest_referenced_xid_known;
// array of size n_children, consisting of ftnode partitions
- // each one is associated with a child
- // for internal nodes, the ith partition corresponds to the ith message buffer
- // for leaf nodes, the ith partition corresponds to the ith basement node
+ // each one is associated with a child for internal nodes, the ith
+ // partition corresponds to the ith message buffer for leaf nodes, the ith
+ // partition corresponds to the ith basement node
struct ftnode_partition *bp;
struct ctpair *ct_pair;
};
@@ -199,7 +212,6 @@ struct ftnode_leaf_basement_node {
MSN max_msn_applied; // max message sequence number applied
bool stale_ancestor_messages_applied;
STAT64INFO_S stat64_delta; // change in stat64 counters since basement was last written to disk
- int64_t logical_rows_delta;
};
typedef struct ftnode_leaf_basement_node *BASEMENTNODE;
diff --git a/storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc b/storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc
index 5914f8a1050..56876b474d4 100644
--- a/storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc
+++ b/storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc
@@ -996,7 +996,6 @@ BASEMENTNODE toku_clone_bn(BASEMENTNODE orig_bn) {
bn->seqinsert = orig_bn->seqinsert;
bn->stale_ancestor_messages_applied = orig_bn->stale_ancestor_messages_applied;
bn->stat64_delta = orig_bn->stat64_delta;
- bn->logical_rows_delta = orig_bn->logical_rows_delta;
bn->data_buffer.clone(&orig_bn->data_buffer);
return bn;
}
@@ -1007,7 +1006,6 @@ BASEMENTNODE toku_create_empty_bn_no_buffer(void) {
bn->seqinsert = 0;
bn->stale_ancestor_messages_applied = false;
bn->stat64_delta = ZEROSTATS;
- bn->logical_rows_delta = 0;
bn->data_buffer.init_zero();
return bn;
}
@@ -1432,6 +1430,7 @@ static FTNODE alloc_ftnode_for_deserialize(uint32_t fullhash, BLOCKNUM blocknum)
node->fullhash = fullhash;
node->blocknum = blocknum;
node->dirty = 0;
+ node->logical_rows_delta = 0;
node->bp = nullptr;
node->oldest_referenced_xid_known = TXNID_NONE;
return node;
diff --git a/storage/tokudb/PerconaFT/ft/txn/roll.cc b/storage/tokudb/PerconaFT/ft/txn/roll.cc
index 9f3977743a0..4f374d62173 100644
--- a/storage/tokudb/PerconaFT/ft/txn/roll.cc
+++ b/storage/tokudb/PerconaFT/ft/txn/roll.cc
@@ -227,7 +227,8 @@ int toku_rollback_frename(BYTESTRING old_iname,
return 1;
if (!old_exist && new_exist &&
- (toku_os_rename(new_iname_full.get(), old_iname_full.get()) == -1 ||
+ (!toku_create_subdirs_if_needed(old_iname_full.get()) ||
+ toku_os_rename(new_iname_full.get(), old_iname_full.get()) == -1 ||
toku_fsync_directory(new_iname_full.get()) == -1 ||
toku_fsync_directory(old_iname_full.get()) == -1))
return 1;
diff --git a/storage/tokudb/PerconaFT/util/dmt.h b/storage/tokudb/PerconaFT/util/dmt.h
index 71cde8814ab..99be296d0e9 100644
--- a/storage/tokudb/PerconaFT/util/dmt.h
+++ b/storage/tokudb/PerconaFT/util/dmt.h
@@ -589,7 +589,6 @@ private:
void convert_from_tree_to_array(void);
- __attribute__((nonnull(2,5)))
void delete_internal(subtree *const subtreep, const uint32_t idx, subtree *const subtree_replace, subtree **const rebalance_subtree);
template<typename iterate_extra_t,
@@ -627,16 +626,12 @@ private:
__attribute__((nonnull))
void rebalance(subtree *const subtree);
- __attribute__((nonnull(3)))
static void copyout(uint32_t *const outlen, dmtdata_t *const out, const dmt_node *const n);
- __attribute__((nonnull(3)))
static void copyout(uint32_t *const outlen, dmtdata_t **const out, dmt_node *const n);
- __attribute__((nonnull(4)))
static void copyout(uint32_t *const outlen, dmtdata_t *const out, const uint32_t len, const dmtdata_t *const stored_value_ptr);
- __attribute__((nonnull(4)))
static void copyout(uint32_t *const outlen, dmtdata_t **const out, const uint32_t len, dmtdata_t *const stored_value_ptr);
template<typename dmtcmp_t,
diff --git a/storage/tokudb/PerconaFT/util/omt.h b/storage/tokudb/PerconaFT/util/omt.h
index 799ed0eae7c..c7ed2ca546f 100644
--- a/storage/tokudb/PerconaFT/util/omt.h
+++ b/storage/tokudb/PerconaFT/util/omt.h
@@ -284,7 +284,6 @@ public:
* By taking ownership of the array, we save a malloc and memcpy,
* and possibly a free (if the caller is done with the array).
*/
- __attribute__((nonnull))
void create_steal_sorted_array(omtdata_t **const values, const uint32_t numvalues, const uint32_t new_capacity);
/**
@@ -667,7 +666,6 @@ private:
void set_at_internal(const subtree &subtree, const omtdata_t &value, const uint32_t idx);
- __attribute__((nonnull(2,5)))
void delete_internal(subtree *const subtreep, const uint32_t idx, omt_node *const copyn, subtree **const rebalance_subtree);
template<typename iterate_extra_t,
diff --git a/storage/tokudb/ha_tokudb.cc b/storage/tokudb/ha_tokudb.cc
index 54dff2a4b3b..53dc2d20bb1 100644
--- a/storage/tokudb/ha_tokudb.cc
+++ b/storage/tokudb/ha_tokudb.cc
@@ -29,6 +29,7 @@ Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
#include "tokudb_status.h"
#include "tokudb_card.h"
#include "ha_tokudb.h"
+#include "sql_db.h"
#if TOKU_INCLUDE_EXTENDED_KEYS
@@ -6122,8 +6123,6 @@ int ha_tokudb::info(uint flag) {
stats.deleted = 0;
if (!(flag & HA_STATUS_NO_LOCK)) {
uint64_t num_rows = 0;
- TOKU_DB_FRAGMENTATION_S frag_info;
- memset(&frag_info, 0, sizeof frag_info);
error = txn_begin(db_env, NULL, &txn, DB_READ_UNCOMMITTED, ha_thd());
if (error) {
@@ -6140,11 +6139,6 @@ int ha_tokudb::info(uint flag) {
} else {
goto cleanup;
}
- error = share->file->get_fragmentation(share->file, &frag_info);
- if (error) {
- goto cleanup;
- }
- stats.delete_length = frag_info.unused_bytes;
DB_BTREE_STAT64 dict_stats;
error = share->file->stat64(share->file, txn, &dict_stats);
@@ -6156,6 +6150,7 @@ int ha_tokudb::info(uint flag) {
stats.update_time = dict_stats.bt_modify_time_sec;
stats.check_time = dict_stats.bt_verify_time_sec;
stats.data_file_length = dict_stats.bt_dsize;
+ stats.delete_length = dict_stats.bt_fsize - dict_stats.bt_dsize;
if (hidden_primary_key) {
//
// in this case, we have a hidden primary key, do not
@@ -6191,30 +6186,21 @@ int ha_tokudb::info(uint flag) {
//
// this solution is much simpler than trying to maintain an
// accurate number of valid keys at the handlerton layer.
- uint curr_num_DBs = table->s->keys + tokudb_test(hidden_primary_key);
+ uint curr_num_DBs =
+ table->s->keys + tokudb_test(hidden_primary_key);
for (uint i = 0; i < curr_num_DBs; i++) {
// skip the primary key, skip dropped indexes
if (i == primary_key || share->key_file[i] == NULL) {
continue;
}
- error =
- share->key_file[i]->stat64(
- share->key_file[i],
- txn,
- &dict_stats);
+ error = share->key_file[i]->stat64(
+ share->key_file[i], txn, &dict_stats);
if (error) {
goto cleanup;
}
stats.index_file_length += dict_stats.bt_dsize;
-
- error =
- share->file->get_fragmentation(
- share->file,
- &frag_info);
- if (error) {
- goto cleanup;
- }
- stats.delete_length += frag_info.unused_bytes;
+ stats.delete_length +=
+ dict_stats.bt_fsize - dict_stats.bt_dsize;
}
}
@@ -7651,6 +7637,27 @@ int ha_tokudb::delete_table(const char *name) {
TOKUDB_HANDLER_DBUG_RETURN(error);
}
+static bool tokudb_check_db_dir_exist_from_table_name(const char *table_name) {
+ DBUG_ASSERT(table_name);
+ bool mysql_dir_exists;
+ char db_name[FN_REFLEN];
+ const char *db_name_begin = strchr(table_name, FN_LIBCHAR);
+ const char *db_name_end = strrchr(table_name, FN_LIBCHAR);
+ DBUG_ASSERT(db_name_begin);
+ DBUG_ASSERT(db_name_end);
+ DBUG_ASSERT(db_name_begin != db_name_end);
+
+ ++db_name_begin;
+ size_t db_name_size = db_name_end - db_name_begin;
+
+ DBUG_ASSERT(db_name_size < FN_REFLEN);
+
+ memcpy(db_name, db_name_begin, db_name_size);
+ db_name[db_name_size] = '\0';
+ mysql_dir_exists = (check_db_dir_existence(db_name) == 0);
+
+ return mysql_dir_exists;
+}
//
// renames table from "from" to "to"
@@ -7673,15 +7680,33 @@ int ha_tokudb::rename_table(const char *from, const char *to) {
TOKUDB_SHARE::drop_share(share);
}
int error;
- error = delete_or_rename_table(from, to, false);
- if (TOKUDB_LIKELY(TOKUDB_DEBUG_FLAGS(TOKUDB_DEBUG_HIDE_DDL_LOCK_ERRORS) == 0) &&
- error == DB_LOCK_NOTGRANTED) {
+ bool to_db_dir_exist = tokudb_check_db_dir_exist_from_table_name(to);
+ if (!to_db_dir_exist) {
sql_print_error(
- "Could not rename table from %s to %s because another transaction "
- "has accessed the table. To rename the table, make sure no "
- "transactions touch the table.",
+ "Could not rename table from %s to %s because "
+ "destination db does not exist",
from,
to);
+#ifndef __WIN__
+ /* Small hack. tokudb_check_db_dir_exist_from_table_name calls
+ * my_access, which sets my_errno on Windows, but doesn't on
+ * unix. Set it for unix too.
+ */
+ my_errno= errno;
+#endif
+ error= my_errno;
+ }
+ else {
+ error = delete_or_rename_table(from, to, false);
+ if (TOKUDB_LIKELY(TOKUDB_DEBUG_FLAGS(TOKUDB_DEBUG_HIDE_DDL_LOCK_ERRORS) == 0) &&
+ error == DB_LOCK_NOTGRANTED) {
+ sql_print_error(
+ "Could not rename table from %s to %s because another transaction "
+ "has accessed the table. To rename the table, make sure no "
+ "transactions touch the table.",
+ from,
+ to);
+ }
}
TOKUDB_HANDLER_DBUG_RETURN(error);
}
diff --git a/storage/tokudb/ha_tokudb.h b/storage/tokudb/ha_tokudb.h
index 3d7a3a7fa05..4a7e395d0d1 100644
--- a/storage/tokudb/ha_tokudb.h
+++ b/storage/tokudb/ha_tokudb.h
@@ -816,6 +816,8 @@ public:
int index_first(uchar * buf);
int index_last(uchar * buf);
+ bool has_gap_locks() const { return true; }
+
int rnd_init(bool scan);
int rnd_end();
int rnd_next(uchar * buf);
diff --git a/storage/tokudb/mysql-test/tokudb/r/dir_per_db_rename_to_nonexisting_schema.result b/storage/tokudb/mysql-test/tokudb/r/dir_per_db_rename_to_nonexisting_schema.result
new file mode 100644
index 00000000000..74148bd4e74
--- /dev/null
+++ b/storage/tokudb/mysql-test/tokudb/r/dir_per_db_rename_to_nonexisting_schema.result
@@ -0,0 +1,46 @@
+SET GLOBAL tokudb_dir_per_db=true;
+######
+# Tokudb and mysql data dirs are the same, rename to existent db
+###
+CREATE DATABASE new_db;
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb;
+ALTER TABLE test.t1 RENAME new_db.t1;
+The content of "test" directory:
+The content of "new_db" directory:
+db.opt
+t1.frm
+t1_main_id.tokudb
+t1_status_id.tokudb
+DROP DATABASE new_db;
+######
+# Tokudb and mysql data dirs are the same, rename to nonexistent db
+###
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb;
+CALL mtr.add_suppression("because destination db does not exist");
+ALTER TABLE test.t1 RENAME foo.t1;
+ERROR HY000: Error on rename of './test/t1' to './foo/t1' (errno: 2 "No such file or directory")
+DROP TABLE t1;
+SELECT @@tokudb_data_dir;
+@@tokudb_data_dir
+NULL
+SELECT @@tokudb_dir_per_db;
+@@tokudb_dir_per_db
+0
+######
+# Tokudb and mysql data dirs are different, rename to existent db
+###
+CREATE DATABASE new_db;
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb;
+ALTER TABLE test.t1 RENAME new_db.t1;
+The content of "test" direcotry:
+The content of "new_db" directory:
+DROP DATABASE new_db;
+######
+# Tokudb and mysql data dirs are different, rename to nonexistent db
+###
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb;
+CALL mtr.add_suppression("because destination db does not exist");
+ALTER TABLE test.t1 RENAME foo.t1;
+ERROR HY000: Error on rename of './test/t1' to './foo/t1' (errno: 2 "No such file or directory")
+DROP TABLE t1;
+SET GLOBAL tokudb_dir_per_db=default;
diff --git a/storage/tokudb/mysql-test/tokudb/r/gap_lock_error.result b/storage/tokudb/mysql-test/tokudb/r/gap_lock_error.result
new file mode 100644
index 00000000000..41e294f7d8d
--- /dev/null
+++ b/storage/tokudb/mysql-test/tokudb/r/gap_lock_error.result
@@ -0,0 +1,469 @@
+CREATE TABLE gap1 (id1 INT, id2 INT, id3 INT, c1 INT, value INT,
+PRIMARY KEY (id1, id2, id3),
+INDEX i (c1)) ENGINE=tokudb;
+CREATE TABLE gap2 like gap1;
+CREATE TABLE gap3 (id INT, value INT,
+PRIMARY KEY (id),
+UNIQUE KEY ui(value)) ENGINE=tokudb;
+CREATE TABLE gap4 (id INT, value INT,
+PRIMARY KEY (id)) ENGINE=tokudb
+PARTITION BY HASH(id) PARTITIONS 2;
+insert into gap3 values (1,1), (2,2),(3,3),(4,4),(5,5);
+insert into gap4 values (1,1), (2,2),(3,3),(4,4),(5,5);
+set session autocommit=0;
+select * from gap1 limit 1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where value != 100 limit 1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where id1=1 for update;
+id1 id2 id3 c1 value
+1 0 2 2 2
+1 0 3 3 3
+select * from gap1 where id1=1 and id2= 1 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3 != 1 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3
+between 1 and 3 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 asc
+limit 1 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 desc
+limit 1 for update;
+id1 id2 id3 c1 value
+select * from gap1 order by id1 asc limit 1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 desc limit 1 for update;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 order by id1 desc, id2 desc, id3 desc
+limit 1 for update;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 force index(i) where c1=1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap3 force index(ui) where value=1 for update;
+id value
+1 1
+select * from gap1 where id1=1 and id2=1 and id3=1 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3=1 and value=1
+order by c1 for update;
+id1 id2 id3 c1 value
+select * from gap3 where id=1 for update;
+id value
+1 1
+select * from gap4 where id=1 for update;
+id value
+1 1
+select * from gap4 where id in (1, 2, 3) for update;
+id value
+1 1
+2 2
+3 3
+select * from gap4 for update;
+id value
+2 2
+4 4
+1 1
+3 3
+5 5
+select * from gap4 where id between 3 and 7 for update;
+id value
+3 3
+4 4
+5 5
+set session autocommit=1;
+select * from gap1 limit 1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where value != 100 limit 1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where id1=1 for update;
+id1 id2 id3 c1 value
+1 0 2 2 2
+1 0 3 3 3
+select * from gap1 where id1=1 and id2= 1 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3 != 1 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3
+between 1 and 3 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 asc
+limit 1 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 desc
+limit 1 for update;
+id1 id2 id3 c1 value
+select * from gap1 order by id1 asc limit 1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 desc limit 1 for update;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 order by id1 desc, id2 desc, id3 desc
+limit 1 for update;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 force index(i) where c1=1 for update;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap3 force index(ui) where value=1 for update;
+id value
+1 1
+select * from gap1 where id1=1 and id2=1 and id3=1 for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) for update;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3=1 and value=1
+order by c1 for update;
+id1 id2 id3 c1 value
+select * from gap3 where id=1 for update;
+id value
+1 1
+select * from gap4 where id=1 for update;
+id value
+1 1
+select * from gap4 where id in (1, 2, 3) for update;
+id value
+1 1
+2 2
+3 3
+select * from gap4 for update;
+id value
+2 2
+4 4
+1 1
+3 3
+5 5
+select * from gap4 where id between 3 and 7 for update;
+id value
+3 3
+4 4
+5 5
+set session autocommit=0;
+select * from gap1 limit 1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where value != 100 limit 1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where id1=1 lock in share mode;
+id1 id2 id3 c1 value
+1 0 2 2 2
+1 0 3 3 3
+select * from gap1 where id1=1 and id2= 1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3 != 1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3
+between 1 and 3 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 asc
+limit 1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 desc
+limit 1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 order by id1 asc limit 1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 desc limit 1 lock in share mode;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 order by id1 desc, id2 desc, id3 desc
+limit 1 lock in share mode;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 force index(i) where c1=1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap3 force index(ui) where value=1 lock in share mode;
+id value
+1 1
+select * from gap1 where id1=1 and id2=1 and id3=1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3=1 and value=1
+order by c1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap3 where id=1 lock in share mode;
+id value
+1 1
+select * from gap4 where id=1 lock in share mode;
+id value
+1 1
+select * from gap4 where id in (1, 2, 3) lock in share mode;
+id value
+1 1
+2 2
+3 3
+select * from gap4 lock in share mode;
+id value
+2 2
+4 4
+1 1
+3 3
+5 5
+select * from gap4 where id between 3 and 7 lock in share mode;
+id value
+3 3
+4 4
+5 5
+set session autocommit=1;
+select * from gap1 limit 1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where value != 100 limit 1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where id1=1 lock in share mode;
+id1 id2 id3 c1 value
+1 0 2 2 2
+1 0 3 3 3
+select * from gap1 where id1=1 and id2= 1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3 != 1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3
+between 1 and 3 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 asc
+limit 1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 desc
+limit 1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 order by id1 asc limit 1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 desc limit 1 lock in share mode;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 order by id1 desc, id2 desc, id3 desc
+limit 1 lock in share mode;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 force index(i) where c1=1 lock in share mode;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap3 force index(ui) where value=1 lock in share mode;
+id value
+1 1
+select * from gap1 where id1=1 and id2=1 and id3=1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) lock in share mode;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3=1 and value=1
+order by c1 lock in share mode;
+id1 id2 id3 c1 value
+select * from gap3 where id=1 lock in share mode;
+id value
+1 1
+select * from gap4 where id=1 lock in share mode;
+id value
+1 1
+select * from gap4 where id in (1, 2, 3) lock in share mode;
+id value
+1 1
+2 2
+3 3
+select * from gap4 lock in share mode;
+id value
+2 2
+4 4
+1 1
+3 3
+5 5
+select * from gap4 where id between 3 and 7 lock in share mode;
+id value
+3 3
+4 4
+5 5
+set session autocommit=0;
+select * from gap1 limit 1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where value != 100 limit 1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where id1=1 ;
+id1 id2 id3 c1 value
+1 0 2 2 2
+1 0 3 3 3
+select * from gap1 where id1=1 and id2= 1 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3 != 1 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3
+between 1 and 3 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 asc
+limit 1 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 desc
+limit 1 ;
+id1 id2 id3 c1 value
+select * from gap1 order by id1 asc limit 1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 desc limit 1 ;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 order by id1 desc, id2 desc, id3 desc
+limit 1 ;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 force index(i) where c1=1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap3 force index(ui) where value=1 ;
+id value
+1 1
+select * from gap1 where id1=1 and id2=1 and id3=1 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3=1 and value=1
+order by c1 ;
+id1 id2 id3 c1 value
+select * from gap3 where id=1 ;
+id value
+1 1
+select * from gap4 where id=1 ;
+id value
+1 1
+select * from gap4 where id in (1, 2, 3) ;
+id value
+1 1
+2 2
+3 3
+select * from gap4 ;
+id value
+2 2
+4 4
+1 1
+3 3
+5 5
+select * from gap4 where id between 3 and 7 ;
+id value
+3 3
+4 4
+5 5
+set session autocommit=1;
+select * from gap1 limit 1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where value != 100 limit 1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 where id1=1 ;
+id1 id2 id3 c1 value
+1 0 2 2 2
+1 0 3 3 3
+select * from gap1 where id1=1 and id2= 1 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3 != 1 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 and id3
+between 1 and 3 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 asc
+limit 1 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2= 1 order by id3 desc
+limit 1 ;
+id1 id2 id3 c1 value
+select * from gap1 order by id1 asc limit 1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap1 order by id1 desc limit 1 ;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 order by id1 desc, id2 desc, id3 desc
+limit 1 ;
+id1 id2 id3 c1 value
+500 100 1000 1000 1000
+select * from gap1 force index(i) where c1=1 ;
+id1 id2 id3 c1 value
+0 0 1 1 1
+select * from gap3 force index(ui) where value=1 ;
+id value
+1 1
+select * from gap1 where id1=1 and id2=1 and id3=1 ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) ;
+id1 id2 id3 c1 value
+select * from gap1 where id1=1 and id2=1 and id3=1 and value=1
+order by c1 ;
+id1 id2 id3 c1 value
+select * from gap3 where id=1 ;
+id value
+1 1
+select * from gap4 where id=1 ;
+id value
+1 1
+select * from gap4 where id in (1, 2, 3) ;
+id value
+1 1
+2 2
+3 3
+select * from gap4 ;
+id value
+2 2
+4 4
+1 1
+3 3
+5 5
+select * from gap4 where id between 3 and 7 ;
+id value
+3 3
+4 4
+5 5
+set session autocommit=0;
+insert into gap1 (id1, id2, id3) values (-1,-1,-1);
+insert into gap1 (id1, id2, id3) values (-1,-1,-1)
+on duplicate key update value=100;
+update gap1 set value=100 where id1=1;
+update gap1 set value=100 where id1=1 and id2=1 and id3=1;
+delete from gap1 where id1=2;
+delete from gap1 where id1=-1 and id2=-1 and id3=-1;
+commit;
+set session autocommit=1;
+insert into gap1 (id1, id2, id3) values (-1,-1,-1);
+insert into gap1 (id1, id2, id3) values (-1,-1,-1)
+on duplicate key update value=100;
+update gap1 set value=100 where id1=1;
+update gap1 set value=100 where id1=1 and id2=1 and id3=1;
+delete from gap1 where id1=2;
+delete from gap1 where id1=-1 and id2=-1 and id3=-1;
+commit;
+drop table gap1, gap2, gap3, gap4;
diff --git a/storage/tokudb/mysql-test/tokudb/r/percona_kill_idle_trx_tokudb.result b/storage/tokudb/mysql-test/tokudb/r/percona_kill_idle_trx_tokudb.result
new file mode 100644
index 00000000000..089d1d2b136
--- /dev/null
+++ b/storage/tokudb/mysql-test/tokudb/r/percona_kill_idle_trx_tokudb.result
@@ -0,0 +1,43 @@
+SET default_storage_engine=TokuDB;
+#
+# Test kill_idle_transaction_timeout feature with TokuDB
+#
+CREATE TABLE t1 (a INT);
+SET GLOBAL kill_idle_transaction= 1;
+BEGIN;
+INSERT INTO t1 VALUES (1),(2);
+COMMIT;
+SELECT * FROM t1;
+a
+1
+2
+BEGIN;
+INSERT INTO t1 VALUES (3);
+# Current connection idle transaction killed, reconnecting
+SELECT * FROM t1;
+a
+1
+2
+#
+# Test that row locks are released on idle transaction kill
+#
+SET GLOBAL kill_idle_transaction= 2;
+# Take row locks in connection conn1
+BEGIN;
+SELECT * FROM t1 FOR UPDATE;
+a
+1
+2
+# Take row locks in connection default
+UPDATE t1 SET a=4;
+SELECT * FROM t1;
+a
+4
+4
+# Show that connection conn1 has been killed
+SELECT * FROM t1;
+ERROR HY000: MySQL server has gone away
+# connection default
+# Cleanup
+DROP TABLE t1;
+SET GLOBAL kill_idle_transaction= saved_kill_idle_transaction;
diff --git a/storage/tokudb/mysql-test/tokudb/t/dir_per_db_rename_to_nonexisting_schema.test b/storage/tokudb/mysql-test/tokudb/t/dir_per_db_rename_to_nonexisting_schema.test
new file mode 100644
index 00000000000..17fe0188a6e
--- /dev/null
+++ b/storage/tokudb/mysql-test/tokudb/t/dir_per_db_rename_to_nonexisting_schema.test
@@ -0,0 +1,64 @@
+--source include/have_tokudb.inc
+
+SET GLOBAL tokudb_dir_per_db=true;
+--let DATADIR=`SELECT @@datadir`
+
+--echo ######
+--echo # Tokudb and mysql data dirs are the same, rename to existent db
+--echo ###
+CREATE DATABASE new_db;
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb;
+ALTER TABLE test.t1 RENAME new_db.t1;
+--echo The content of "test" directory:
+--source include/table_files_replace_pattern.inc
+--sorted_result
+--list_files $DATADIR/test
+--echo The content of "new_db" directory:
+--source include/table_files_replace_pattern.inc
+--sorted_result
+--list_files $DATADIR/new_db
+DROP DATABASE new_db;
+
+--echo ######
+--echo # Tokudb and mysql data dirs are the same, rename to nonexistent db
+--echo ###
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb;
+CALL mtr.add_suppression("because destination db does not exist");
+--error ER_ERROR_ON_RENAME
+ALTER TABLE test.t1 RENAME foo.t1;
+DROP TABLE t1;
+
+--let $custom_tokudb_data_dir=$MYSQL_TMP_DIR/custom_tokudb_data_dir
+--mkdir $custom_tokudb_data_dir
+--replace_result $custom_tokudb_data_dir CUSTOM_TOKUDB_DATA_DIR
+--source include/restart_mysqld.inc
+
+--replace_result $custom_tokudb_data_dir CUSTOM_TOKUDB_DATA_DIR
+SELECT @@tokudb_data_dir;
+SELECT @@tokudb_dir_per_db;
+
+--echo ######
+--echo # Tokudb and mysql data dirs are different, rename to existent db
+--echo ###
+CREATE DATABASE new_db;
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb;
+ALTER TABLE test.t1 RENAME new_db.t1;
+--echo The content of "test" direcotry:
+--source include/table_files_replace_pattern.inc
+--sorted_result
+--echo The content of "new_db" directory:
+--source include/table_files_replace_pattern.inc
+--sorted_result
+DROP DATABASE new_db;
+
+--echo ######
+--echo # Tokudb and mysql data dirs are different, rename to nonexistent db
+--echo ###
+CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb;
+CALL mtr.add_suppression("because destination db does not exist");
+--error ER_ERROR_ON_RENAME
+ALTER TABLE test.t1 RENAME foo.t1;
+DROP TABLE t1;
+
+SET GLOBAL tokudb_dir_per_db=default;
+
diff --git a/storage/tokudb/mysql-test/tokudb/t/gap_lock_error.test b/storage/tokudb/mysql-test/tokudb/t/gap_lock_error.test
new file mode 100644
index 00000000000..8c52cef9e27
--- /dev/null
+++ b/storage/tokudb/mysql-test/tokudb/t/gap_lock_error.test
@@ -0,0 +1,5 @@
+--source include/have_tokudb.inc
+
+let $engine=tokudb;
+let $expect_gap_lock_errors=0;
+--source include/gap_lock_error_all.inc
diff --git a/storage/tokudb/mysql-test/tokudb/t/percona_kill_idle_trx_tokudb.test b/storage/tokudb/mysql-test/tokudb/t/percona_kill_idle_trx_tokudb.test
new file mode 100644
index 00000000000..743fb9a55a7
--- /dev/null
+++ b/storage/tokudb/mysql-test/tokudb/t/percona_kill_idle_trx_tokudb.test
@@ -0,0 +1,5 @@
+--source include/have_tokudb.inc
+--skip MariaDB doesn't support kill_idle_trx variable for all SE
+
+SET default_storage_engine=TokuDB;
+--source include/percona_kill_idle_trx.inc
diff --git a/storage/tokudb/mysql-test/tokudb_backup/t/suite.opt b/storage/tokudb/mysql-test/tokudb_backup/t/suite.opt
index 0d80cf85a91..5d4cb245e27 100644
--- a/storage/tokudb/mysql-test/tokudb_backup/t/suite.opt
+++ b/storage/tokudb/mysql-test/tokudb_backup/t/suite.opt
@@ -1 +1 @@
-$TOKUDB_OPT $TOKUDB_LOAD_ADD $TOKUDB_BACKUP_OPT $TOKUDB_BACKUP_LOAD_ADD --loose-tokudb-check-jemalloc=0 --loose-tokudb-cache-size=512M --loose-tokudb-block-size=1M
+$TOKUDB_OPT $TOKUDB_LOAD_ADD_PATH $TOKUDB_BACKUP_OPT $TOKUDB_BACKUP_LOAD_ADD_PATH --loose-tokudb-check-jemalloc=0 --loose-tokudb-cache-size=512M --loose-tokudb-block-size=1M
diff --git a/storage/xtradb/btr/btr0btr.cc b/storage/xtradb/btr/btr0btr.cc
index bce81f95ead..417eeb2c367 100644
--- a/storage/xtradb/btr/btr0btr.cc
+++ b/storage/xtradb/btr/btr0btr.cc
@@ -3571,8 +3571,6 @@ btr_level_list_remove_func(
ulint prev_page_no;
ulint next_page_no;
- ut_ad(page != NULL);
- ut_ad(mtr != NULL);
ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX));
ut_ad(space == page_get_space_id(page));
/* Get the previous and next page numbers of page */
diff --git a/storage/xtradb/btr/btr0cur.cc b/storage/xtradb/btr/btr0cur.cc
index afe2c181594..512b791f212 100644
--- a/storage/xtradb/btr/btr0cur.cc
+++ b/storage/xtradb/btr/btr0cur.cc
@@ -1843,7 +1843,7 @@ btr_cur_pessimistic_insert(
/*************************************************************//**
For an update, checks the locks and does the undo logging.
@return DB_SUCCESS, DB_WAIT_LOCK, or error number */
-UNIV_INLINE MY_ATTRIBUTE((warn_unused_result, nonnull(2,3,6,7)))
+UNIV_INLINE MY_ATTRIBUTE((warn_unused_result))
dberr_t
btr_cur_upd_lock_and_undo(
/*======================*/
@@ -2073,7 +2073,6 @@ btr_cur_update_alloc_zip_func(
const page_t* page = page_cur_get_page(cursor);
ut_ad(page_zip == page_cur_get_page_zip(cursor));
- ut_ad(page_zip);
ut_ad(!dict_index_is_ibuf(index));
ut_ad(rec_offs_validate(page_cur_get_rec(cursor), index, offsets));
@@ -3940,19 +3939,42 @@ inexact:
return(n_rows);
}
-/*******************************************************************//**
-Estimates the number of rows in a given index range.
-@return estimated number of rows */
-UNIV_INTERN
-ib_int64_t
-btr_estimate_n_rows_in_range(
-/*=========================*/
- dict_index_t* index, /*!< in: index */
- const dtuple_t* tuple1, /*!< in: range start, may also be empty tuple */
- ulint mode1, /*!< in: search mode for range start */
- const dtuple_t* tuple2, /*!< in: range end, may also be empty tuple */
- ulint mode2, /*!< in: search mode for range end */
- trx_t* trx) /*!< in: trx */
+/** If the tree gets changed too much between the two dives for the left
+and right boundary then btr_estimate_n_rows_in_range_low() will retry
+that many times before giving up and returning the value stored in
+rows_in_range_arbitrary_ret_val. */
+static const unsigned rows_in_range_max_retries = 4;
+
+/** We pretend that a range has that many records if the tree keeps changing
+for rows_in_range_max_retries retries while we try to estimate the records
+in a given range. */
+static const int64_t rows_in_range_arbitrary_ret_val = 10;
+
+/** Estimates the number of rows in a given index range.
+@param[in] index index
+@param[in] tuple1 range start, may also be empty tuple
+@param[in] mode1 search mode for range start
+@param[in] tuple2 range end, may also be empty tuple
+@param[in] mode2 search mode for range end
+@param[in] trx trx
+@param[in] nth_attempt if the tree gets modified too much while
+we are trying to analyze it, then we will retry (this function will call
+itself, incrementing this parameter)
+@return estimated number of rows; if after rows_in_range_max_retries
+retries the tree keeps changing, then we will just return
+rows_in_range_arbitrary_ret_val as a result (if
+nth_attempt >= rows_in_range_max_retries and the tree is modified between
+the two dives). */
+static
+int64_t
+btr_estimate_n_rows_in_range_low(
+ dict_index_t* index,
+ const dtuple_t* tuple1,
+ ulint mode1,
+ const dtuple_t* tuple2,
+ ulint mode2,
+ trx_t* trx,
+ unsigned nth_attempt)
{
btr_path_t path1[BTR_PATH_ARRAY_N_SLOTS];
btr_path_t path2[BTR_PATH_ARRAY_N_SLOTS];
@@ -3990,6 +4012,12 @@ btr_estimate_n_rows_in_range(
mtr_start_trx(&mtr, trx);
+#ifdef UNIV_DEBUG
+ if (!strcmp(index->name, "iC")) {
+ DEBUG_SYNC_C("btr_estimate_n_rows_in_range_between_dives");
+ }
+#endif
+
cursor.path_arr = path2;
if (dtuple_get_n_fields(tuple2) > 0) {
@@ -4056,6 +4084,33 @@ btr_estimate_n_rows_in_range(
if (!diverged && slot1->nth_rec != slot2->nth_rec) {
+ /* If both slots do not point to the same page or if
+ the paths have crossed and the same page on both
+ apparently contains a different number of records,
+ this means that the tree must have changed between
+ the dive for slot1 and the dive for slot2 at the
+ beginning of this function. */
+ if (slot1->page_no != slot2->page_no
+ || slot1->page_level != slot2->page_level
+ || (slot1->nth_rec >= slot2->nth_rec
+ && slot1->n_recs != slot2->n_recs)) {
+
+ /* If the tree keeps changing even after a
+ few attempts, then just return some arbitrary
+ number. */
+ if (nth_attempt >= rows_in_range_max_retries) {
+ return(rows_in_range_arbitrary_ret_val);
+ }
+
+ const int64_t ret =
+ btr_estimate_n_rows_in_range_low(
+ index, tuple1, mode1,
+ tuple2, mode2, trx,
+ nth_attempt + 1);
+
+ return(ret);
+ }
+
diverged = TRUE;
if (slot1->nth_rec < slot2->nth_rec) {
@@ -4074,7 +4129,7 @@ btr_estimate_n_rows_in_range(
in this case slot1->nth_rec will point
to the supr record and slot2->nth_rec
will point to 6 */
- n_rows = 0;
+ return(0);
}
} else if (diverged && !diverged_lot) {
@@ -4105,6 +4160,30 @@ btr_estimate_n_rows_in_range(
}
}
+/** Estimates the number of rows in a given index range.
+@param[in] index index
+@param[in] tuple1 range start, may also be empty tuple
+@param[in] mode1 search mode for range start
+@param[in] tuple2 range end, may also be empty tuple
+@param[in] mode2 search mode for range end
+@param[in] trx trx
+@return estimated number of rows */
+int64_t
+btr_estimate_n_rows_in_range(
+ dict_index_t* index,
+ const dtuple_t* tuple1,
+ ulint mode1,
+ const dtuple_t* tuple2,
+ ulint mode2,
+ trx_t* trx)
+{
+ const int64_t ret = btr_estimate_n_rows_in_range_low(
+ index, tuple1, mode1, tuple2, mode2, trx,
+ 1 /* first attempt */);
+
+ return(ret);
+}
+
/*******************************************************************//**
Record the number of non_null key values in a given index for
each n-column prefix of the index where 1 <= n <= dict_index_get_n_unique(index).
@@ -4567,7 +4646,6 @@ btr_cur_disown_inherited_fields(
ut_ad(rec_offs_validate(rec, index, offsets));
ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec));
ut_ad(rec_offs_any_extern(offsets));
- ut_ad(mtr);
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
if (rec_offs_nth_extern(offsets, i)
@@ -4630,9 +4708,6 @@ btr_push_update_extern_fields(
ulint n;
const upd_field_t* uf;
- ut_ad(tuple);
- ut_ad(update);
-
uf = update->fields;
n = upd_get_n_fields(update);
@@ -4816,7 +4891,6 @@ btr_store_big_rec_extern_fields(
ut_ad(rec_offs_validate(rec, index, offsets));
ut_ad(rec_offs_any_extern(offsets));
- ut_ad(btr_mtr);
ut_ad(mtr_memo_contains(btr_mtr, dict_index_get_lock(index),
MTR_MEMO_X_LOCK));
ut_ad(mtr_memo_contains(btr_mtr, rec_block, MTR_MEMO_PAGE_X_FIX));
diff --git a/storage/xtradb/buf/buf0buddy.cc b/storage/xtradb/buf/buf0buddy.cc
index 8cb880c1169..2ee39c6c992 100644
--- a/storage/xtradb/buf/buf0buddy.cc
+++ b/storage/xtradb/buf/buf0buddy.cc
@@ -485,7 +485,6 @@ buf_buddy_alloc_low(
{
buf_block_t* block;
- ut_ad(lru);
ut_ad(mutex_own(&buf_pool->LRU_list_mutex));
ut_ad(!mutex_own(&buf_pool->zip_mutex));
ut_ad(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN));
diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc
index 496cf63d939..b1156b4c646 100644
--- a/storage/xtradb/buf/buf0buf.cc
+++ b/storage/xtradb/buf/buf0buf.cc
@@ -3895,15 +3895,6 @@ buf_page_init(
/* Set the state of the block */
buf_block_set_file_page(block, space, offset);
-#ifdef UNIV_DEBUG_VALGRIND
- if (!space) {
- /* Silence valid Valgrind warnings about uninitialized
- data being written to data files. There are some unused
- bytes on some pages that InnoDB does not initialize. */
- UNIV_MEM_VALID(block->frame, UNIV_PAGE_SIZE);
- }
-#endif /* UNIV_DEBUG_VALGRIND */
-
buf_block_init_low(block);
block->lock_hash_val = lock_rec_hash(space, offset);
diff --git a/storage/xtradb/buf/buf0lru.cc b/storage/xtradb/buf/buf0lru.cc
index 579166753c4..dff67c0fad6 100644
--- a/storage/xtradb/buf/buf0lru.cc
+++ b/storage/xtradb/buf/buf0lru.cc
@@ -1301,6 +1301,71 @@ buf_LRU_check_size_of_non_data_objects(
}
}
+/** Diagnose failure to get a free page and request InnoDB monitor output in
+the error log if more than two seconds have been spent already.
+@param[in] n_iterations how many buf_LRU_get_free_page iterations
+ already completed
+@param[in] started_ms timestamp in ms of when the attempt to get the
+ free page started
+@param[in] flush_failures how many times single-page flush, if allowed,
+ has failed
+@param[out] mon_value_was previous srv_print_innodb_monitor value
+@param[out] started_monitor whether InnoDB monitor print has been requested
+*/
+static
+void
+buf_LRU_handle_lack_of_free_blocks(ulint n_iterations, ulint started_ms,
+ ulint flush_failures,
+ ibool *mon_value_was,
+ ibool *started_monitor)
+{
+ static ulint last_printout_ms = 0;
+
+ /* Legacy algorithm started warning after at least 2 seconds, we
+ emulate this. */
+ const ulint current_ms = ut_time_ms();
+
+ if ((current_ms > started_ms + 2000)
+ && (current_ms > last_printout_ms + 2000)) {
+
+ ut_print_timestamp(stderr);
+ fprintf(stderr,
+ " InnoDB: Warning: difficult to find free blocks in\n"
+ "InnoDB: the buffer pool (%lu search iterations)!\n"
+ "InnoDB: %lu failed attempts to flush a page!"
+ " Consider\n"
+ "InnoDB: increasing the buffer pool size.\n"
+ "InnoDB: It is also possible that"
+ " in your Unix version\n"
+ "InnoDB: fsync is very slow, or"
+ " completely frozen inside\n"
+ "InnoDB: the OS kernel. Then upgrading to"
+ " a newer version\n"
+ "InnoDB: of your operating system may help."
+ " Look at the\n"
+ "InnoDB: number of fsyncs in diagnostic info below.\n"
+ "InnoDB: Pending flushes (fsync) log: %lu;"
+ " buffer pool: %lu\n"
+ "InnoDB: %lu OS file reads, %lu OS file writes,"
+ " %lu OS fsyncs\n"
+ "InnoDB: Starting InnoDB Monitor to print further\n"
+ "InnoDB: diagnostics to the standard output.\n",
+ (ulong) n_iterations,
+ (ulong) flush_failures,
+ (ulong) fil_n_pending_log_flushes,
+ (ulong) fil_n_pending_tablespace_flushes,
+ (ulong) os_n_file_reads, (ulong) os_n_file_writes,
+ (ulong) os_n_fsyncs);
+
+ last_printout_ms = current_ms;
+ *mon_value_was = srv_print_innodb_monitor;
+ *started_monitor = TRUE;
+ srv_print_innodb_monitor = TRUE;
+ os_event_set(lock_sys->timeout_event);
+ }
+
+}
+
/** The maximum allowed backoff sleep time duration, microseconds */
#define MAX_FREE_LIST_BACKOFF_SLEEP 10000
@@ -1348,6 +1413,7 @@ buf_LRU_get_free_block(
ulint flush_failures = 0;
ibool mon_value_was = FALSE;
ibool started_monitor = FALSE;
+ ulint started_ms = 0;
ut_ad(!mutex_own(&buf_pool->LRU_list_mutex));
@@ -1356,7 +1422,24 @@ loop:
buf_LRU_check_size_of_non_data_objects(buf_pool);
/* If there is a block in the free list, take it */
- block = buf_LRU_get_free_only(buf_pool);
+ if (DBUG_EVALUATE_IF("simulate_lack_of_pages", true, false)) {
+
+ block = NULL;
+
+ if (srv_debug_monitor_printed)
+ DBUG_SET("-d,simulate_lack_of_pages");
+
+ } else if (DBUG_EVALUATE_IF("simulate_recovery_lack_of_pages",
+ recv_recovery_on, false)) {
+
+ block = NULL;
+
+ if (srv_debug_monitor_printed)
+ DBUG_SUICIDE();
+ } else {
+
+ block = buf_LRU_get_free_only(buf_pool);
+ }
if (block) {
@@ -1371,6 +1454,9 @@ loop:
return(block);
}
+ if (!started_ms)
+ started_ms = ut_time_ms();
+
if (srv_empty_free_list_algorithm == SRV_EMPTY_FREE_LIST_BACKOFF
&& buf_lru_manager_is_active
&& (srv_shutdown_state == SRV_SHUTDOWN_NONE
@@ -1408,11 +1494,17 @@ loop:
: FREE_LIST_BACKOFF_LOW_PRIO_DIVIDER));
}
- /* In case of backoff, do not ever attempt single page flushes
- and wait for the cleaner to free some pages instead. */
+ buf_LRU_handle_lack_of_free_blocks(n_iterations, started_ms,
+ flush_failures,
+ &mon_value_was,
+ &started_monitor);
n_iterations++;
+ srv_stats.buf_pool_wait_free.add(n_iterations, 1);
+
+ /* In case of backoff, do not ever attempt single page flushes
+ and wait for the cleaner to free some pages instead. */
goto loop;
} else {
@@ -1444,6 +1536,12 @@ loop:
mutex_exit(&buf_pool->flush_state_mutex);
+ if (DBUG_EVALUATE_IF("simulate_recovery_lack_of_pages", true, false)
+ || DBUG_EVALUATE_IF("simulate_lack_of_pages", true, false)) {
+
+ buf_pool->try_LRU_scan = false;
+ }
+
freed = FALSE;
if (buf_pool->try_LRU_scan || n_iterations > 0) {
@@ -1469,41 +1567,9 @@ loop:
}
- if (n_iterations > 20) {
- ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: Warning: difficult to find free blocks in\n"
- "InnoDB: the buffer pool (%lu search iterations)!\n"
- "InnoDB: %lu failed attempts to flush a page!"
- " Consider\n"
- "InnoDB: increasing the buffer pool size.\n"
- "InnoDB: It is also possible that"
- " in your Unix version\n"
- "InnoDB: fsync is very slow, or"
- " completely frozen inside\n"
- "InnoDB: the OS kernel. Then upgrading to"
- " a newer version\n"
- "InnoDB: of your operating system may help."
- " Look at the\n"
- "InnoDB: number of fsyncs in diagnostic info below.\n"
- "InnoDB: Pending flushes (fsync) log: %lu;"
- " buffer pool: %lu\n"
- "InnoDB: %lu OS file reads, %lu OS file writes,"
- " %lu OS fsyncs\n"
- "InnoDB: Starting InnoDB Monitor to print further\n"
- "InnoDB: diagnostics to the standard output.\n",
- (ulong) n_iterations,
- (ulong) flush_failures,
- (ulong) fil_n_pending_log_flushes,
- (ulong) fil_n_pending_tablespace_flushes,
- (ulong) os_n_file_reads, (ulong) os_n_file_writes,
- (ulong) os_n_fsyncs);
-
- mon_value_was = srv_print_innodb_monitor;
- started_monitor = TRUE;
- srv_print_innodb_monitor = TRUE;
- os_event_set(srv_monitor_event);
- }
+ buf_LRU_handle_lack_of_free_blocks(n_iterations, started_ms,
+ flush_failures, &mon_value_was,
+ &started_monitor);
/* If we have scanned the whole LRU and still are unable to
find a free block then we should sleep here to let the
diff --git a/storage/xtradb/dict/dict0dict.cc b/storage/xtradb/dict/dict0dict.cc
index 90936f6667b..49de1cf7ef8 100644
--- a/storage/xtradb/dict/dict0dict.cc
+++ b/storage/xtradb/dict/dict0dict.cc
@@ -6214,7 +6214,6 @@ dict_set_corrupted(
row_mysql_lock_data_dictionary(trx);
}
- ut_ad(index);
ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(!dict_table_is_comp(dict_sys->sys_tables));
ut_ad(!dict_table_is_comp(dict_sys->sys_indexes));
diff --git a/storage/xtradb/dict/dict0stats.cc b/storage/xtradb/dict/dict0stats.cc
index c13d4583fef..6a28f3cdf8f 100644
--- a/storage/xtradb/dict/dict0stats.cc
+++ b/storage/xtradb/dict/dict0stats.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (c) 2009, 2015, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2009, 2016, Oracle and/or its affiliates. All Rights Reserved.
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
@@ -1161,7 +1161,8 @@ dict_stats_analyze_index_level(
them away) which brings non-determinism. We skip only
leaf-level delete marks because delete marks on
non-leaf level do not make sense. */
- if (level == 0 &&
+
+ if (level == 0 && srv_stats_include_delete_marked? 0:
rec_get_deleted_flag(
rec,
page_is_comp(btr_pcur_get_page(&pcur)))) {
@@ -1347,8 +1348,12 @@ enum page_scan_method_t {
the given page and count the number of
distinct ones, also ignore delete marked
records */
- QUIT_ON_FIRST_NON_BORING/* quit when the first record that differs
+ QUIT_ON_FIRST_NON_BORING,/* quit when the first record that differs
from its right neighbor is found */
+ COUNT_ALL_NON_BORING_INCLUDE_DEL_MARKED/* scan all records on
+ the given page and count the number of
+ distinct ones, include delete marked
+ records */
};
/* @} */
@@ -1624,6 +1629,8 @@ dict_stats_analyze_index_below_cur(
offsets_rec = dict_stats_scan_page(
&rec, offsets1, offsets2, index, page, n_prefix,
+ srv_stats_include_delete_marked ?
+ COUNT_ALL_NON_BORING_INCLUDE_DEL_MARKED:
COUNT_ALL_NON_BORING_AND_SKIP_DEL_MARKED, n_diff,
n_external_pages);
diff --git a/storage/xtradb/dyn/dyn0dyn.cc b/storage/xtradb/dyn/dyn0dyn.cc
index 3ef5297a7c9..dd1f6863c14 100644
--- a/storage/xtradb/dyn/dyn0dyn.cc
+++ b/storage/xtradb/dyn/dyn0dyn.cc
@@ -40,7 +40,6 @@ dyn_array_add_block(
mem_heap_t* heap;
dyn_block_t* block;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
if (arr->heap == NULL) {
diff --git a/storage/xtradb/fsp/fsp0fsp.cc b/storage/xtradb/fsp/fsp0fsp.cc
index dfaef5fdd5f..3043e780268 100644
--- a/storage/xtradb/fsp/fsp0fsp.cc
+++ b/storage/xtradb/fsp/fsp0fsp.cc
@@ -133,7 +133,7 @@ fsp_fill_free_list(
ulint space, /*!< in: space */
fsp_header_t* header, /*!< in/out: space header */
mtr_t* mtr) /*!< in/out: mini-transaction */
- UNIV_COLD MY_ATTRIBUTE((nonnull));
+ UNIV_COLD;
/**********************************************************************//**
Allocates a single free page from a segment. This function implements
the intelligent allocation strategy which tries to minimize file space
@@ -162,7 +162,7 @@ fseg_alloc_free_page_low(
in which the page should be initialized.
If init_mtr!=mtr, but the page is already
latched in mtr, do not initialize the page. */
- MY_ATTRIBUTE((warn_unused_result, nonnull));
+ MY_ATTRIBUTE((warn_unused_result));
#endif /* !UNIV_HOTBACKUP */
/**********************************************************************//**
@@ -1074,8 +1074,6 @@ fsp_fill_free_list(
ulint i;
mtr_t ibuf_mtr;
- ut_ad(header != NULL);
- ut_ad(mtr != NULL);
ut_ad(page_offset(header) == FSP_HEADER_OFFSET);
/* Check if we can fill free list from above the free list limit */
@@ -1338,7 +1336,7 @@ Allocates a single free page from a space. The page is marked as used.
@retval block, rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded
(init_mtr == mtr, or the page was not previously freed in mtr)
@retval block (not allocated or initialized) otherwise */
-static MY_ATTRIBUTE((nonnull, warn_unused_result))
+static MY_ATTRIBUTE((warn_unused_result))
buf_block_t*
fsp_alloc_free_page(
/*================*/
@@ -1358,9 +1356,6 @@ fsp_alloc_free_page(
ulint page_no;
ulint space_size;
- ut_ad(mtr);
- ut_ad(init_mtr);
-
header = fsp_get_space_header(space, zip_size, mtr);
/* Get the hinted descriptor */
@@ -2379,7 +2374,6 @@ fseg_alloc_free_page_low(
ibool success;
ulint n;
- ut_ad(mtr);
ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR));
ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N)
== FSEG_MAGIC_N_VALUE);
diff --git a/storage/xtradb/fts/fts0opt.cc b/storage/xtradb/fts/fts0opt.cc
index ed882d33548..cb30122adcb 100644
--- a/storage/xtradb/fts/fts0opt.cc
+++ b/storage/xtradb/fts/fts0opt.cc
@@ -579,9 +579,6 @@ fts_zip_read_word(
fts_zip_t* zip, /*!< in: Zip state + data */
fts_string_t* word) /*!< out: uncompressed word */
{
-#ifdef UNIV_DEBUG
- ulint i;
-#endif
short len = 0;
void* null = NULL;
byte* ptr = word->f_str;
@@ -656,10 +653,9 @@ fts_zip_read_word(
}
}
-#ifdef UNIV_DEBUG
/* All blocks must be freed at end of inflate. */
if (zip->status != Z_OK) {
- for (i = 0; i < ib_vector_size(zip->blocks); ++i) {
+ for (ulint i = 0; i < ib_vector_size(zip->blocks); ++i) {
if (ib_vector_getp(zip->blocks, i)) {
ut_free(ib_vector_getp(zip->blocks, i));
ib_vector_set(zip->blocks, i, &null);
@@ -670,7 +666,6 @@ fts_zip_read_word(
if (ptr != NULL) {
ut_ad(word->f_len == strlen((char*) ptr));
}
-#endif /* UNIV_DEBUG */
return(zip->status == Z_OK || zip->status == Z_STREAM_END ? ptr : NULL);
}
diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc
index 4e5f223bc1e..5a10492fa62 100644
--- a/storage/xtradb/handler/ha_innodb.cc
+++ b/storage/xtradb/handler/ha_innodb.cc
@@ -1743,6 +1743,7 @@ static bool innobase_purge_archive_logs(
}
#endif
+
/*************************************************************//**
Check for a valid value of innobase_commit_concurrency.
@return 0 for valid innodb_commit_concurrency */
@@ -4343,14 +4344,9 @@ innobase_change_buffering_inited_ok:
innobase_commit_concurrency_init_default();
-#ifndef EXTENDED_FOR_KILLIDLE
- srv_kill_idle_transaction = 0;
-#endif
-
#ifdef HAVE_POSIX_FALLOCATE
srv_use_posix_fallocate = (ibool) innobase_use_fallocate;
#endif
-
/* Do not enable backoff algorithm for small buffer pool. */
if (!innodb_empty_free_list_algorithm_backoff_allowed(
static_cast<srv_empty_free_list_t>(
@@ -14096,9 +14092,13 @@ ha_innobase::info_low(
/* If this table is already queued for
background analyze, remove it from the
queue as we are about to do the same */
- dict_mutex_enter_for_mysql();
- dict_stats_recalc_pool_del(ib_table);
- dict_mutex_exit_for_mysql();
+ if (!srv_read_only_mode) {
+
+ dict_mutex_enter_for_mysql();
+ dict_stats_recalc_pool_del(
+ ib_table);
+ dict_mutex_exit_for_mysql();
+ }
opt = DICT_STATS_RECALC_PERSISTENT;
} else {
@@ -16637,6 +16637,37 @@ ha_innobase::get_auto_increment(
ulonglong col_max_value = innobase_get_int_col_max_value(
table->next_number_field);
+ /** The following logic is needed to avoid duplicate key error
+ for autoincrement column.
+
+ (1) InnoDB gives the current autoincrement value with respect
+ to increment and offset value.
+
+ (2) Basically it does compute_next_insert_id() logic inside InnoDB
+ to avoid the current auto increment value changed by handler layer.
+
+ (3) It is restricted only for insert operations. */
+
+ if (increment > 1 && thd_sql_command(user_thd) != SQLCOM_ALTER_TABLE
+ && autoinc < col_max_value) {
+
+ ulonglong prev_auto_inc = autoinc;
+
+ autoinc = ((autoinc - 1) + increment - offset)/ increment;
+
+ autoinc = autoinc * increment + offset;
+
+ /* If autoinc exceeds the col_max_value then reset
+ to old autoinc value. Because in case of non-strict
+ sql mode, boundary value is not considered as error. */
+
+ if (autoinc >= col_max_value) {
+ autoinc = prev_auto_inc;
+ }
+
+ ut_ad(autoinc > 0);
+ }
+
/* Called for the first time ? */
if (trx->n_autoinc_rows == 0) {
@@ -19149,32 +19180,6 @@ innobase_fts_retrieve_ranking(
}
/***********************************************************************
-functions for kill session of idle transaction */
-ibool
-innobase_thd_is_idle(
-/*=================*/
- const void* thd) /*!< in: thread handle (THD*) */
-{
-#ifdef EXTENDED_FOR_KILLIDLE
- return(thd_command((const THD*) thd) == COM_SLEEP);
-#else
- return(FALSE);
-#endif
-}
-
-ib_int64_t
-innobase_thd_get_start_time(
-/*========================*/
- const void* thd) /*!< in: thread handle (THD*) */
-{
-#ifdef EXTENDED_FOR_KILLIDLE
- return((ib_int64_t)thd_start_time((const THD*) thd));
-#else
- return(0); /*dummy value*/
-#endif
-}
-
-/***********************************************************************
Free the memory for the FTS handler */
UNIV_INTERN
void
@@ -19193,19 +19198,6 @@ innobase_fts_close_ranking(
return;
}
-UNIV_INTERN
-void
-innobase_thd_kill(
-/*==============*/
- ulong thd_id)
-{
-#ifdef EXTENDED_FOR_KILLIDLE
- thd_kill(thd_id);
-#else
- return;
-#endif
-}
-
/***********************************************************************
Find and Retrieve the FTS Relevance Ranking result for doc with doc_id
of prebuilt->fts_doc_id
@@ -19403,16 +19395,6 @@ innobase_fts_retrieve_docid(
}
-ulong
-innobase_thd_get_thread_id(
-/*=======================*/
- const void* thd)
-{
- return(thd_get_thread_id((const THD*) thd));
-}
-
-
-
/***********************************************************************
Find and retrieve the size of the current result
@return number of matching rows */
@@ -20067,6 +20049,12 @@ static MYSQL_SYSVAR_BOOL(doublewrite, innobase_use_doublewrite,
"Disable with --skip-innodb-doublewrite.",
NULL, NULL, TRUE);
+static MYSQL_SYSVAR_BOOL(stats_include_delete_marked,
+ srv_stats_include_delete_marked,
+ PLUGIN_VAR_OPCMDARG,
+ "Scan delete marked records for persistent stat",
+ NULL, NULL, FALSE);
+
static MYSQL_SYSVAR_BOOL(use_atomic_writes, innobase_use_atomic_writes,
PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
"Prevent partial page writes, via atomic writes (beta). "
@@ -21418,6 +21406,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(data_file_path),
MYSQL_SYSVAR(data_home_dir),
MYSQL_SYSVAR(doublewrite),
+ MYSQL_SYSVAR(stats_include_delete_marked),
MYSQL_SYSVAR(api_enable_binlog),
MYSQL_SYSVAR(api_enable_mdl),
MYSQL_SYSVAR(api_disable_rowlock),
diff --git a/storage/xtradb/handler/ha_innodb.h b/storage/xtradb/handler/ha_innodb.h
index 783077ceaf1..f91c0fb4703 100644
--- a/storage/xtradb/handler/ha_innodb.h
+++ b/storage/xtradb/handler/ha_innodb.h
@@ -167,6 +167,8 @@ class ha_innobase: public handler
int index_first(uchar * buf);
int index_last(uchar * buf);
+ bool has_gap_locks() const { return true; }
+
int rnd_init(bool scan);
int rnd_end();
int rnd_next(uchar *buf);
diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc
index 740f15c2bd5..aede923d22f 100644
--- a/storage/xtradb/handler/handler0alter.cc
+++ b/storage/xtradb/handler/handler0alter.cc
@@ -1868,6 +1868,7 @@ innobase_fts_check_doc_id_index_in_def(
return(FTS_NOT_EXIST_DOC_ID_INDEX);
}
+
/*******************************************************************//**
Create an index table where indexes are ordered as follows:
@@ -1936,26 +1937,11 @@ innobase_create_key_defs(
(only prefix/part of the column is indexed), MySQL will treat the
index as a PRIMARY KEY unless the table already has one. */
- if (n_add > 0 && !new_primary && got_default_clust
- && (key_info[*add].flags & HA_NOSAME)
- && !(key_info[*add].flags & HA_KEY_HAS_PART_KEY_SEG)) {
- uint key_part = key_info[*add].user_defined_key_parts;
-
- new_primary = true;
+ ut_ad(altered_table->s->primary_key == 0
+ || altered_table->s->primary_key == MAX_KEY);
- while (key_part--) {
- const uint maybe_null
- = key_info[*add].key_part[key_part].key_type
- & FIELDFLAG_MAYBE_NULL;
- DBUG_ASSERT(!maybe_null
- == !key_info[*add].key_part[key_part].
- field->real_maybe_null());
-
- if (maybe_null) {
- new_primary = false;
- break;
- }
- }
+ if (got_default_clust && !new_primary) {
+ new_primary = (altered_table->s->primary_key != MAX_KEY);
}
const bool rebuild = new_primary || add_fts_doc_id
@@ -1974,8 +1960,14 @@ innobase_create_key_defs(
ulint primary_key_number;
if (new_primary) {
- DBUG_ASSERT(n_add > 0);
- primary_key_number = *add;
+ if (n_add == 0) {
+ DBUG_ASSERT(got_default_clust);
+ DBUG_ASSERT(altered_table->s->primary_key
+ == 0);
+ primary_key_number = 0;
+ } else {
+ primary_key_number = *add;
+ }
} else if (got_default_clust) {
/* Create the GEN_CLUST_INDEX */
index_def_t* index = indexdef++;
@@ -3097,6 +3089,8 @@ prepare_inplace_alter_table_dict(
ctx->add_cols = add_cols;
} else {
DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info, old_table));
+ DBUG_ASSERT(old_table->s->primary_key
+ == altered_table->s->primary_key);
if (!ctx->new_table->fts
&& innobase_fulltext_exist(altered_table)) {
@@ -4142,6 +4136,27 @@ found_col:
add_fts_doc_id_idx, prebuilt));
}
+/** Get the name of an erroneous key.
+@param[in] error_key_num InnoDB number of the erroneus key
+@param[in] ha_alter_info changes that were being performed
+@param[in] table InnoDB table
+@return the name of the erroneous key */
+static
+const char*
+get_error_key_name(
+ ulint error_key_num,
+ const Alter_inplace_info* ha_alter_info,
+ const dict_table_t* table)
+{
+ if (error_key_num == ULINT_UNDEFINED) {
+ return(FTS_DOC_ID_INDEX_NAME);
+ } else if (ha_alter_info->key_count == 0) {
+ return(dict_table_get_first_index(table)->name);
+ } else {
+ return(ha_alter_info->key_info_buffer[error_key_num].name);
+ }
+}
+
/** Alter the table structure in-place with operations
specified using Alter_inplace_info.
The level of concurrency allowed during this operation depends
@@ -4264,17 +4279,13 @@ oom:
case DB_ONLINE_LOG_TOO_BIG:
DBUG_ASSERT(ctx->online);
my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
- (prebuilt->trx->error_key_num == ULINT_UNDEFINED)
- ? FTS_DOC_ID_INDEX_NAME
- : ha_alter_info->key_info_buffer[
- prebuilt->trx->error_key_num].name);
+ get_error_key_name(prebuilt->trx->error_key_num,
+ ha_alter_info, prebuilt->table));
break;
case DB_INDEX_CORRUPT:
my_error(ER_INDEX_CORRUPT, MYF(0),
- (prebuilt->trx->error_key_num == ULINT_UNDEFINED)
- ? FTS_DOC_ID_INDEX_NAME
- : ha_alter_info->key_info_buffer[
- prebuilt->trx->error_key_num].name);
+ get_error_key_name(prebuilt->trx->error_key_num,
+ ha_alter_info, prebuilt->table));
break;
case DB_DECRYPTION_FAILED: {
String str;
@@ -5094,7 +5105,6 @@ innobase_update_foreign_cache(
"Foreign key constraints for table '%s'"
" are loaded with charset check off",
user_table->name);
-
}
}
@@ -5194,14 +5204,13 @@ commit_try_rebuild(
DBUG_RETURN(true);
case DB_ONLINE_LOG_TOO_BIG:
my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0),
- ha_alter_info->key_info_buffer[0].name);
+ get_error_key_name(err_key, ha_alter_info,
+ rebuilt_table));
DBUG_RETURN(true);
case DB_INDEX_CORRUPT:
my_error(ER_INDEX_CORRUPT, MYF(0),
- (err_key == ULINT_UNDEFINED)
- ? FTS_DOC_ID_INDEX_NAME
- : ha_alter_info->key_info_buffer[err_key]
- .name);
+ get_error_key_name(err_key, ha_alter_info,
+ rebuilt_table));
DBUG_RETURN(true);
default:
my_error_innodb(error, table_name, user_table->flags);
diff --git a/storage/xtradb/handler/i_s.cc b/storage/xtradb/handler/i_s.cc
index 420dff83a40..97d2bd36912 100644
--- a/storage/xtradb/handler/i_s.cc
+++ b/storage/xtradb/handler/i_s.cc
@@ -8324,29 +8324,7 @@ i_s_innodb_changed_pages_fill(
while(log_online_bitmap_iterator_next(&i) &&
(!srv_max_changed_pages ||
- output_rows_num < srv_max_changed_pages) &&
- /*
- There is no need to compare both start LSN and end LSN fields
- with maximum value. It's enough to compare only start LSN.
- Example:
-
- max_lsn = 100
- \\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\ - Query 1
- I------I I-------I I-------------I I----I
- ////////////////// | - Query 2
- 1 2 3 4
-
- Query 1:
- SELECT * FROM INNODB_CHANGED_PAGES WHERE start_lsn < 100
- will select 1,2,3 bitmaps
- Query 2:
- SELECT * FROM INNODB_CHANGED_PAGES WHERE end_lsn < 100
- will select 1,2 bitmaps
-
- The condition start_lsn <= 100 will be false after reading
- 1,2,3 bitmaps which suits for both cases.
- */
- LOG_BITMAP_ITERATOR_START_LSN(i) <= max_lsn)
+ output_rows_num < srv_max_changed_pages))
{
if (!LOG_BITMAP_ITERATOR_PAGE_CHANGED(i))
continue;
diff --git a/storage/xtradb/include/btr0cur.h b/storage/xtradb/include/btr0cur.h
index f485d072c4c..960bd55d3d9 100644
--- a/storage/xtradb/include/btr0cur.h
+++ b/storage/xtradb/include/btr0cur.h
@@ -294,11 +294,7 @@ btr_cur_update_alloc_zip_func(
false=update-in-place */
mtr_t* mtr, /*!< in/out: mini-transaction */
trx_t* trx) /*!< in: NULL or transaction */
-#ifdef UNIV_DEBUG
- MY_ATTRIBUTE((nonnull (1, 2, 3, 4, 7), warn_unused_result));
-#else
- MY_ATTRIBUTE((nonnull (1, 2, 3, 6), warn_unused_result));
-#endif
+ MY_ATTRIBUTE((warn_unused_result));
#ifdef UNIV_DEBUG
# define btr_cur_update_alloc_zip(page_zip,cursor,index,offsets,len,cr,mtr,trx) \
@@ -428,7 +424,7 @@ btr_cur_del_mark_set_clust_rec(
const ulint* offsets,/*!< in: rec_get_offsets(rec) */
que_thr_t* thr, /*!< in: query thread */
mtr_t* mtr) /*!< in/out: mini-transaction */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/***********************************************************//**
Sets a secondary index record delete mark to TRUE or FALSE.
@return DB_SUCCESS, DB_LOCK_WAIT, or error number */
@@ -441,7 +437,7 @@ btr_cur_del_mark_set_sec_rec(
ibool val, /*!< in: value to set */
que_thr_t* thr, /*!< in: query thread */
mtr_t* mtr) /*!< in/out: mini-transaction */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/*************************************************************//**
Tries to compress a page of the tree if it seems useful. It is assumed
that mtr holds an x-latch on the tree and on the cursor page. To avoid
@@ -609,8 +605,7 @@ btr_cur_disown_inherited_fields(
dict_index_t* index, /*!< in: index of the page */
const ulint* offsets,/*!< in: array returned by rec_get_offsets() */
const upd_t* update, /*!< in: update vector */
- mtr_t* mtr) /*!< in/out: mini-transaction */
- MY_ATTRIBUTE((nonnull(2,3,4,5,6)));
+ mtr_t* mtr); /*!< in/out: mini-transaction */
/** Operation code for btr_store_big_rec_extern_fields(). */
enum blob_op {
@@ -655,7 +650,7 @@ btr_store_big_rec_extern_fields(
mtr_t* btr_mtr, /*!< in: mtr containing the
latches to the clustered index */
enum blob_op op) /*! in: operation code */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/*******************************************************************//**
Frees the space in an externally stored field to the file space
@@ -751,8 +746,7 @@ btr_push_update_extern_fields(
/*==========================*/
dtuple_t* tuple, /*!< in/out: data tuple */
const upd_t* update, /*!< in: update vector */
- mem_heap_t* heap) /*!< in: memory heap */
- MY_ATTRIBUTE((nonnull));
+ mem_heap_t* heap); /*!< in: memory heap */
/***********************************************************//**
Sets a secondary index record's delete mark to the given value. This
function is only used by the insert buffer merge mechanism. */
diff --git a/storage/xtradb/include/btr0sea.h b/storage/xtradb/include/btr0sea.h
index 8f438bf640e..bfe2c43defb 100644
--- a/storage/xtradb/include/btr0sea.h
+++ b/storage/xtradb/include/btr0sea.h
@@ -200,7 +200,7 @@ hash_table_t*
btr_search_get_hash_table(
/*======================*/
const dict_index_t* index) /*!< in: index */
- MY_ATTRIBUTE((pure,warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//**
Returns the adaptive hash index latch for a given index key.
@@ -210,7 +210,7 @@ prio_rw_lock_t*
btr_search_get_latch(
/*=================*/
const dict_index_t* index) /*!< in: index */
- MY_ATTRIBUTE((pure,warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/*********************************************************************//**
Returns the AHI partition number corresponding to a given index ID. */
@@ -227,8 +227,7 @@ UNIV_INLINE
void
btr_search_index_init(
/*===============*/
- dict_index_t* index) /*!< in: index */
- MY_ATTRIBUTE((nonnull));
+ dict_index_t* index); /*!< in: index */
/********************************************************************//**
Latches all adaptive hash index latches in exclusive mode. */
diff --git a/storage/xtradb/include/btr0sea.ic b/storage/xtradb/include/btr0sea.ic
index 3cbcff75f31..e963d8a8449 100644
--- a/storage/xtradb/include/btr0sea.ic
+++ b/storage/xtradb/include/btr0sea.ic
@@ -90,7 +90,6 @@ btr_search_get_hash_table(
/*======================*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->search_table);
return(index->search_table);
@@ -105,7 +104,6 @@ btr_search_get_latch(
/*=================*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->search_latch >= btr_search_latch_arr &&
index->search_latch < btr_search_latch_arr +
btr_search_index_num);
@@ -132,8 +130,6 @@ btr_search_index_init(
/*===============*/
dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
-
index->search_latch =
&btr_search_latch_arr[btr_search_get_key(index->id)];
index->search_table =
diff --git a/storage/xtradb/include/buf0buddy.ic b/storage/xtradb/include/buf0buddy.ic
index 9bc8e9e8762..a5fb510dd19 100644
--- a/storage/xtradb/include/buf0buddy.ic
+++ b/storage/xtradb/include/buf0buddy.ic
@@ -50,7 +50,7 @@ buf_buddy_alloc_low(
allocated from the LRU list and
buf_pool->LRU_list_mutex was
temporarily released */
- MY_ATTRIBUTE((malloc, nonnull));
+ MY_ATTRIBUTE((malloc));
/**********************************************************************//**
Deallocate a block. */
diff --git a/storage/xtradb/include/buf0buf.h b/storage/xtradb/include/buf0buf.h
index 6e705e311d5..1774d9445ff 100644
--- a/storage/xtradb/include/buf0buf.h
+++ b/storage/xtradb/include/buf0buf.h
@@ -243,8 +243,7 @@ buf_relocate(
buf_page_t* bpage, /*!< in/out: control block being relocated;
buf_page_get_state(bpage) must be
BUF_BLOCK_ZIP_DIRTY or BUF_BLOCK_ZIP_PAGE */
- buf_page_t* dpage) /*!< in/out: destination control block */
- MY_ATTRIBUTE((nonnull));
+ buf_page_t* dpage); /*!< in/out: destination control block */
/*********************************************************************//**
Gets the current size of buffer buf_pool in bytes.
@return size in bytes */
@@ -791,7 +790,7 @@ buf_page_print(
ulint flags) /*!< in: 0 or
BUF_PAGE_PRINT_NO_CRASH or
BUF_PAGE_PRINT_NO_FULL */
- UNIV_COLD MY_ATTRIBUTE((nonnull));
+ UNIV_COLD;
/********************************************************************//**
Decompress a block.
@return TRUE if successful */
diff --git a/storage/xtradb/include/dict0dict.h b/storage/xtradb/include/dict0dict.h
index cfaf3e12e82..1622b927a76 100644
--- a/storage/xtradb/include/dict0dict.h
+++ b/storage/xtradb/include/dict0dict.h
@@ -762,7 +762,7 @@ ulint
dict_index_is_clust(
/*================*/
const dict_index_t* index) /*!< in: index */
- MY_ATTRIBUTE((nonnull, pure, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//**
Check whether the index is unique.
@return nonzero for unique index, zero for other indexes */
@@ -771,7 +771,7 @@ ulint
dict_index_is_unique(
/*=================*/
const dict_index_t* index) /*!< in: index */
- MY_ATTRIBUTE((nonnull, pure, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//**
Check whether the index is the insert buffer tree.
@return nonzero for insert buffer, zero for other indexes */
@@ -780,7 +780,7 @@ ulint
dict_index_is_ibuf(
/*===============*/
const dict_index_t* index) /*!< in: index */
- MY_ATTRIBUTE((nonnull, pure, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//**
Check whether the index is a secondary index or the insert buffer tree.
@return nonzero for insert buffer, zero for other indexes */
@@ -789,7 +789,7 @@ ulint
dict_index_is_sec_or_ibuf(
/*======================*/
const dict_index_t* index) /*!< in: index */
- MY_ATTRIBUTE((nonnull, pure, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/************************************************************************
Gets the all the FTS indexes for the table. NOTE: must not be called for
@@ -811,7 +811,7 @@ ulint
dict_table_get_n_user_cols(
/*=======================*/
const dict_table_t* table) /*!< in: table */
- MY_ATTRIBUTE((nonnull, pure, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//**
Gets the number of system columns in a table in the dictionary cache.
@return number of system (e.g., ROW_ID) columns of a table */
@@ -830,7 +830,7 @@ ulint
dict_table_get_n_cols(
/*==================*/
const dict_table_t* table) /*!< in: table */
- MY_ATTRIBUTE((nonnull, pure, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//**
Gets the approximately estimated number of rows in the table.
@return estimated number of rows */
@@ -1784,7 +1784,7 @@ ulint
dict_index_is_corrupted(
/*====================*/
const dict_index_t* index) /*!< in: index */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
#endif /* !UNIV_HOTBACKUP */
/**********************************************************************//**
@@ -1797,7 +1797,7 @@ dict_set_corrupted(
dict_index_t* index, /*!< in/out: index */
trx_t* trx, /*!< in/out: transaction */
const char* ctx) /*!< in: context */
- UNIV_COLD MY_ATTRIBUTE((nonnull));
+ UNIV_COLD;
/**********************************************************************//**
Flags an index corrupted in the data dictionary cache only. This
@@ -1808,8 +1808,7 @@ void
dict_set_corrupted_index_cache_only(
/*================================*/
dict_index_t* index, /*!< in/out: index */
- dict_table_t* table) /*!< in/out: table */
- MY_ATTRIBUTE((nonnull));
+ dict_table_t* table); /*!< in/out: table */
/**********************************************************************//**
Flags a table with specified space_id corrupted in the table dictionary
diff --git a/storage/xtradb/include/dict0dict.ic b/storage/xtradb/include/dict0dict.ic
index 2b63ddea51d..81da2fa5580 100644
--- a/storage/xtradb/include/dict0dict.ic
+++ b/storage/xtradb/include/dict0dict.ic
@@ -267,7 +267,6 @@ dict_index_is_clust(
/*================*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
return(index->type & DICT_CLUSTERED);
@@ -281,7 +280,6 @@ dict_index_is_unique(
/*=================*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
return(index->type & DICT_UNIQUE);
@@ -296,7 +294,6 @@ dict_index_is_ibuf(
/*===============*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
return(index->type & DICT_IBUF);
@@ -328,7 +325,6 @@ dict_index_is_sec_or_ibuf(
{
ulint type;
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
type = index->type;
@@ -346,7 +342,6 @@ dict_table_get_n_user_cols(
/*=======================*/
const dict_table_t* table) /*!< in: table */
{
- ut_ad(table);
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
return(table->n_cols - DATA_N_SYS_COLS);
@@ -378,7 +373,6 @@ dict_table_get_n_cols(
/*==================*/
const dict_table_t* table) /*!< in: table */
{
- ut_ad(table);
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);
return(table->n_cols);
@@ -1550,7 +1544,6 @@ dict_index_is_corrupted(
/*====================*/
const dict_index_t* index) /*!< in: index */
{
- ut_ad(index);
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N);
return((index->type & DICT_CORRUPT)
diff --git a/storage/xtradb/include/dyn0dyn.h b/storage/xtradb/include/dyn0dyn.h
index 1bd10b6bf58..20963a1472b 100644
--- a/storage/xtradb/include/dyn0dyn.h
+++ b/storage/xtradb/include/dyn0dyn.h
@@ -46,9 +46,8 @@ UNIV_INLINE
dyn_array_t*
dyn_array_create(
/*=============*/
- dyn_array_t* arr) /*!< in/out memory buffer of
+ dyn_array_t* arr); /*!< in/out memory buffer of
size sizeof(dyn_array_t) */
- MY_ATTRIBUTE((nonnull));
/************************************************************//**
Frees a dynamic array. */
UNIV_INLINE
@@ -69,7 +68,7 @@ dyn_array_open(
dyn_array_t* arr, /*!< in: dynamic array */
ulint size) /*!< in: size in bytes of the buffer; MUST be
smaller than DYN_ARRAY_DATA_SIZE! */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/*********************************************************************//**
Closes the buffer returned by dyn_array_open. */
UNIV_INLINE
@@ -77,8 +76,7 @@ void
dyn_array_close(
/*============*/
dyn_array_t* arr, /*!< in: dynamic array */
- const byte* ptr) /*!< in: end of used space */
- MY_ATTRIBUTE((nonnull));
+ const byte* ptr); /*!< in: end of used space */
/*********************************************************************//**
Makes room on top of a dyn array and returns a pointer to
the added element. The caller must copy the element to
@@ -90,7 +88,7 @@ dyn_array_push(
/*===========*/
dyn_array_t* arr, /*!< in/out: dynamic array */
ulint size) /*!< in: size in bytes of the element */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/************************************************************//**
Returns pointer to an element in dyn array.
@return pointer to element */
@@ -101,7 +99,7 @@ dyn_array_get_element(
const dyn_array_t* arr, /*!< in: dyn array */
ulint pos) /*!< in: position of element
in bytes from array start */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/************************************************************//**
Returns the size of stored data in a dyn array.
@return data size in bytes */
@@ -110,7 +108,7 @@ ulint
dyn_array_get_data_size(
/*====================*/
const dyn_array_t* arr) /*!< in: dyn array */
- MY_ATTRIBUTE((nonnull, warn_unused_result, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/************************************************************//**
Gets the first block in a dyn array.
@param arr dyn array
@@ -144,7 +142,7 @@ ulint
dyn_block_get_used(
/*===============*/
const dyn_block_t* block) /*!< in: dyn array block */
- MY_ATTRIBUTE((nonnull, warn_unused_result, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//**
Gets pointer to the start of data in a dyn array block.
@return pointer to data */
@@ -153,7 +151,7 @@ byte*
dyn_block_get_data(
/*===============*/
const dyn_block_t* block) /*!< in: dyn array block */
- MY_ATTRIBUTE((nonnull, warn_unused_result, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************//**
Pushes n bytes to a dyn array. */
UNIV_INLINE
diff --git a/storage/xtradb/include/dyn0dyn.ic b/storage/xtradb/include/dyn0dyn.ic
index f18f2e6dff9..6e97649245e 100644
--- a/storage/xtradb/include/dyn0dyn.ic
+++ b/storage/xtradb/include/dyn0dyn.ic
@@ -36,7 +36,7 @@ dyn_block_t*
dyn_array_add_block(
/*================*/
dyn_array_t* arr) /*!< in/out: dyn array */
- MY_ATTRIBUTE((nonnull, warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
/********************************************************************//**
Gets the number of used bytes in a dyn array block.
@@ -47,8 +47,6 @@ dyn_block_get_used(
/*===============*/
const dyn_block_t* block) /*!< in: dyn array block */
{
- ut_ad(block);
-
return((block->used) & ~DYN_BLOCK_FULL_FLAG);
}
@@ -76,7 +74,6 @@ dyn_array_create(
dyn_array_t* arr) /*!< in/out: memory buffer of
size sizeof(dyn_array_t) */
{
- ut_ad(arr);
#if DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG
# error "DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG"
#endif
@@ -119,7 +116,6 @@ dyn_array_push(
dyn_block_t* block;
ulint used;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
ut_ad(size <= DYN_ARRAY_DATA_SIZE);
ut_ad(size);
@@ -159,7 +155,6 @@ dyn_array_open(
{
dyn_block_t* block;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
ut_ad(size <= DYN_ARRAY_DATA_SIZE);
ut_ad(size);
@@ -195,7 +190,6 @@ dyn_array_close(
{
dyn_block_t* block;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
block = dyn_array_get_last_block(arr);
@@ -222,7 +216,6 @@ dyn_array_get_element(
{
const dyn_block_t* block;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
/* Get the first array block */
@@ -260,7 +253,6 @@ dyn_array_get_data_size(
const dyn_block_t* block;
ulint sum = 0;
- ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
if (arr->heap == NULL) {
diff --git a/storage/xtradb/include/log0online.h b/storage/xtradb/include/log0online.h
index 5706f3af4b0..722336dd6b4 100644
--- a/storage/xtradb/include/log0online.h
+++ b/storage/xtradb/include/log0online.h
@@ -38,19 +38,25 @@ log_online_bitmap_file_range_t;
/** An iterator over changed page info */
typedef struct log_bitmap_iterator_struct log_bitmap_iterator_t;
-/*********************************************************************//**
-Initializes the online log following subsytem. */
+/** Initialize the constant part of the log tracking subsystem */
+UNIV_INTERN
+void
+log_online_init(void);
+
+/** Initialize the dynamic part of the log tracking subsystem */
UNIV_INTERN
void
log_online_read_init(void);
-/*=======================*/
-/*********************************************************************//**
-Shuts down the online log following subsystem. */
+/** Shut down the dynamic part of the log tracking subsystem */
UNIV_INTERN
void
log_online_read_shutdown(void);
-/*===========================*/
+
+/** Shut down the constant part of the log tracking subsystem */
+UNIV_INTERN
+void
+log_online_shutdown(void);
/*********************************************************************//**
Reads and parses the redo log up to last checkpoint LSN to build the changed
@@ -147,6 +153,8 @@ struct log_online_bitmap_file_range_struct {
/** Struct for an iterator through all bits of changed pages bitmap blocks */
struct log_bitmap_iterator_struct
{
+ lsn_t max_lsn; /*!< End LSN of the
+ range */
ibool failed; /*!< Has the iteration
stopped prematurely */
log_online_bitmap_file_range_t in_files; /*!< The bitmap files
diff --git a/storage/xtradb/include/log0recv.h b/storage/xtradb/include/log0recv.h
index e93ec2666af..e7b6a937f01 100644
--- a/storage/xtradb/include/log0recv.h
+++ b/storage/xtradb/include/log0recv.h
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, MariaDB Corporation.
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
@@ -300,20 +301,12 @@ void
recv_sys_var_init(void);
/*===================*/
#endif /* !UNIV_HOTBACKUP */
-/*******************************************************************//**
-Empties the hash table of stored log records, applying them to appropriate
-pages. */
+/** Apply the hash table of stored log records to persistent data pages.
+@param[in] last_batch whether the change buffer merge will be
+ performed as part of the operation */
UNIV_INTERN
-dberr_t
-recv_apply_hashed_log_recs(
-/*=======================*/
- ibool allow_ibuf); /*!< in: if TRUE, also ibuf operations are
- allowed during the application; if FALSE,
- no ibuf operations are allowed, and after
- the application all file pages are flushed to
- disk and invalidated in buffer pool: this
- alternative means that no new log records
- can be generated during the application */
+void
+recv_apply_hashed_log_recs(bool last_batch);
#ifdef UNIV_HOTBACKUP
/*******************************************************************//**
Applies log records in the hash table to a backup. */
@@ -439,6 +432,8 @@ struct recv_sys_t{
scan find a corrupt log block, or a corrupt
log record, or there is a log parsing
buffer overflow */
+ /** the time when progress was last reported */
+ ib_time_t progress_time;
#ifdef UNIV_LOG_ARCHIVE
log_group_t* archive_group;
/*!< in archive recovery: the log group whose
@@ -451,6 +446,20 @@ struct recv_sys_t{
addresses in the hash table */
recv_dblwr_t dblwr;
+
+ /** Determine whether redo log recovery progress should be reported.
+ @param[in] time the current time
+ @return whether progress should be reported
+ (the last report was at least 15 seconds ago) */
+ bool report(ib_time_t time)
+ {
+ if (time - progress_time < 15) {
+ return false;
+ }
+
+ progress_time = time;
+ return true;
+ }
};
/** The recovery system */
diff --git a/storage/xtradb/include/mach0data.h b/storage/xtradb/include/mach0data.h
index 9859def0adc..2e16634a6c2 100644
--- a/storage/xtradb/include/mach0data.h
+++ b/storage/xtradb/include/mach0data.h
@@ -53,7 +53,7 @@ ulint
mach_read_from_1(
/*=============*/
const byte* b) /*!< in: pointer to byte */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/*******************************************************//**
The following function is used to store data in two consecutive
bytes. We store the most significant byte to the lower address. */
@@ -114,7 +114,7 @@ ulint
mach_read_from_3(
/*=============*/
const byte* b) /*!< in: pointer to 3 bytes */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/*******************************************************//**
The following function is used to store data in four consecutive
bytes. We store the most significant byte to the lowest address. */
@@ -133,7 +133,7 @@ ulint
mach_read_from_4(
/*=============*/
const byte* b) /*!< in: pointer to four bytes */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/*********************************************************//**
Writes a ulint in a compressed form (1..5 bytes).
@return stored size in bytes */
@@ -160,7 +160,7 @@ ulint
mach_read_compressed(
/*=================*/
const byte* b) /*!< in: pointer to memory from where to read */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/*******************************************************//**
The following function is used to store data in 6 consecutive
bytes. We store the most significant byte to the lowest address. */
@@ -179,7 +179,7 @@ ib_uint64_t
mach_read_from_6(
/*=============*/
const byte* b) /*!< in: pointer to 6 bytes */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/*******************************************************//**
The following function is used to store data in 7 consecutive
bytes. We store the most significant byte to the lowest address. */
@@ -198,7 +198,7 @@ ib_uint64_t
mach_read_from_7(
/*=============*/
const byte* b) /*!< in: pointer to 7 bytes */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/*******************************************************//**
The following function is used to store data in 8 consecutive
bytes. We store the most significant byte to the lowest address. */
@@ -243,7 +243,7 @@ ib_uint64_t
mach_ull_read_compressed(
/*=====================*/
const byte* b) /*!< in: pointer to memory from where to read */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/*********************************************************//**
Writes a 64-bit integer in a compressed form (1..11 bytes).
@return size in bytes */
@@ -270,7 +270,7 @@ ib_uint64_t
mach_ull_read_much_compressed(
/*==========================*/
const byte* b) /*!< in: pointer to memory from where to read */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/*********************************************************//**
Reads a ulint in a compressed form if the log record fully contains it.
@return pointer to end of the stored field, NULL if not complete */
diff --git a/storage/xtradb/include/mach0data.ic b/storage/xtradb/include/mach0data.ic
index bf2c735b0da..3904d96c09f 100644
--- a/storage/xtradb/include/mach0data.ic
+++ b/storage/xtradb/include/mach0data.ic
@@ -52,7 +52,6 @@ mach_read_from_1(
/*=============*/
const byte* b) /*!< in: pointer to byte */
{
- ut_ad(b);
return((ulint)(b[0]));
}
@@ -132,7 +131,6 @@ mach_read_from_3(
/*=============*/
const byte* b) /*!< in: pointer to 3 bytes */
{
- ut_ad(b);
return( ((ulint)(b[0]) << 16)
| ((ulint)(b[1]) << 8)
| (ulint)(b[2])
@@ -182,7 +180,6 @@ mach_read_from_4(
/*=============*/
const byte* b) /*!< in: pointer to four bytes */
{
- ut_ad(b);
return( ((ulint)(b[0]) << 24)
| ((ulint)(b[1]) << 16)
| ((ulint)(b[2]) << 8)
@@ -261,8 +258,6 @@ mach_read_compressed(
{
ulint flag;
- ut_ad(b);
-
flag = mach_read_from_1(b);
if (flag < 0x80UL) {
@@ -339,8 +334,6 @@ mach_read_from_7(
/*=============*/
const byte* b) /*!< in: pointer to 7 bytes */
{
- ut_ad(b);
-
return(ut_ull_create(mach_read_from_3(b), mach_read_from_4(b + 3)));
}
@@ -370,8 +363,6 @@ mach_read_from_6(
/*=============*/
const byte* b) /*!< in: pointer to 6 bytes */
{
- ut_ad(b);
-
return(ut_ull_create(mach_read_from_2(b), mach_read_from_4(b + 2)));
}
@@ -419,8 +410,6 @@ mach_ull_read_compressed(
ib_uint64_t n;
ulint size;
- ut_ad(b);
-
n = (ib_uint64_t) mach_read_compressed(b);
size = mach_get_compressed_size((ulint) n);
@@ -486,8 +475,6 @@ mach_ull_read_much_compressed(
ib_uint64_t n;
ulint size;
- ut_ad(b);
-
if (*b != (byte)0xFF) {
n = 0;
size = 0;
diff --git a/storage/xtradb/include/mtr0mtr.h b/storage/xtradb/include/mtr0mtr.h
index 23992598f2e..ef6cd61719d 100644
--- a/storage/xtradb/include/mtr0mtr.h
+++ b/storage/xtradb/include/mtr0mtr.h
@@ -235,8 +235,7 @@ UNIV_INTERN
void
mtr_commit(
/*=======*/
- mtr_t* mtr) /*!< in/out: mini-transaction */
- MY_ATTRIBUTE((nonnull));
+ mtr_t* mtr); /*!< in/out: mini-transaction */
/**********************************************************//**
Sets and returns a savepoint in mtr.
@return savepoint */
@@ -354,7 +353,7 @@ mtr_memo_contains(
mtr_t* mtr, /*!< in: mtr */
const void* object, /*!< in: object to search */
ulint type) /*!< in: type of object */
- MY_ATTRIBUTE((warn_unused_result, nonnull));
+ MY_ATTRIBUTE((warn_unused_result));
/**********************************************************//**
Checks if memo contains the given page.
diff --git a/storage/xtradb/include/os0file.h b/storage/xtradb/include/os0file.h
index f590c5f16ce..d6f0ecfb69c 100644
--- a/storage/xtradb/include/os0file.h
+++ b/storage/xtradb/include/os0file.h
@@ -557,9 +557,10 @@ os_file_create_simple_no_error_handling_func(
value */
__attribute__((nonnull, warn_unused_result));
/****************************************************************//**
-Tries to disable OS caching on an opened file descriptor. */
+Tries to disable OS caching on an opened file descriptor.
+@return true if operation is success and false otherwise */
UNIV_INTERN
-void
+bool
os_file_set_nocache(
/*================*/
os_file_t fd, /*!< in: file descriptor to alter */
diff --git a/storage/xtradb/include/os0thread.h b/storage/xtradb/include/os0thread.h
index 671b9b7dc3f..7865358b0f7 100644
--- a/storage/xtradb/include/os0thread.h
+++ b/storage/xtradb/include/os0thread.h
@@ -131,11 +131,9 @@ os_thread_create_func(
os_thread_id_t* thread_id); /*!< out: id of the created
thread, or NULL */
-/**
-Waits until the specified thread completes and joins it. Its return value is
-ignored.
-
-@param thread thread to join */
+/** Waits until the specified thread completes and joins it.
+Its return value is ignored.
+@param[in,out] thread thread to join */
UNIV_INTERN
void
os_thread_join(
diff --git a/storage/xtradb/include/page0page.h b/storage/xtradb/include/page0page.h
index cb43c937757..eefa0fa4c5b 100644
--- a/storage/xtradb/include/page0page.h
+++ b/storage/xtradb/include/page0page.h
@@ -235,8 +235,7 @@ ulint
page_header_get_offs(
/*=================*/
const page_t* page, /*!< in: page */
- ulint field) /*!< in: PAGE_FREE, ... */
- MY_ATTRIBUTE((nonnull, pure));
+ ulint field); /*!< in: PAGE_FREE, ... */
/*************************************************************//**
Returns the pointer stored in the given header field, or NULL. */
@@ -528,7 +527,7 @@ bool
page_is_leaf(
/*=========*/
const page_t* page) /*!< in: page */
- MY_ATTRIBUTE((nonnull, pure));
+ MY_ATTRIBUTE((warn_unused_result));
/************************************************************//**
Determine whether the page is empty.
@return true if the page is empty (PAGE_N_RECS = 0) */
@@ -849,8 +848,7 @@ page_copy_rec_list_end(
buf_block_t* block, /*!< in: index page containing rec */
rec_t* rec, /*!< in: record on page */
dict_index_t* index, /*!< in: record descriptor */
- mtr_t* mtr) /*!< in: mtr */
- MY_ATTRIBUTE((nonnull));
+ mtr_t* mtr); /*!< in: mtr */
/*************************************************************//**
Copies records from page to new_page, up to the given record, NOT
including that record. Infimum and supremum records are not copied.
@@ -871,8 +869,7 @@ page_copy_rec_list_start(
buf_block_t* block, /*!< in: index page containing rec */
rec_t* rec, /*!< in: record on page */
dict_index_t* index, /*!< in: record descriptor */
- mtr_t* mtr) /*!< in: mtr */
- MY_ATTRIBUTE((nonnull));
+ mtr_t* mtr); /*!< in: mtr */
/*************************************************************//**
Deletes records from a page from a given record onward, including that record.
The infimum and supremum records are not deleted. */
@@ -921,8 +918,7 @@ page_move_rec_list_end(
buf_block_t* block, /*!< in: index page from where to move */
rec_t* split_rec, /*!< in: first record to move */
dict_index_t* index, /*!< in: record descriptor */
- mtr_t* mtr) /*!< in: mtr */
- MY_ATTRIBUTE((nonnull(1, 2, 4, 5)));
+ mtr_t* mtr); /*!< in: mtr */
/*************************************************************//**
Moves record list start to another page. Moved records do not include
split_rec.
@@ -952,8 +948,7 @@ page_dir_split_slot(
page_t* page, /*!< in: index page */
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
uncompressed part will be written, or NULL */
- ulint slot_no)/*!< in: the directory slot */
- MY_ATTRIBUTE((nonnull(1)));
+ ulint slot_no);/*!< in: the directory slot */
/*************************************************************//**
Tries to balance the given directory slot with too few records
with the upper neighbor, so that there are at least the minimum number
@@ -965,8 +960,7 @@ page_dir_balance_slot(
/*==================*/
page_t* page, /*!< in/out: index page */
page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
- ulint slot_no)/*!< in: the directory slot */
- MY_ATTRIBUTE((nonnull(1)));
+ ulint slot_no);/*!< in: the directory slot */
/**********************************************************//**
Parses a log record of a record list end or start deletion.
@return end of log record or NULL */
diff --git a/storage/xtradb/include/page0page.ic b/storage/xtradb/include/page0page.ic
index 5cf92fd5d8d..364536b86f8 100644
--- a/storage/xtradb/include/page0page.ic
+++ b/storage/xtradb/include/page0page.ic
@@ -156,7 +156,6 @@ page_header_get_offs(
{
ulint offs;
- ut_ad(page);
ut_ad((field == PAGE_FREE)
|| (field == PAGE_LAST_INSERT)
|| (field == PAGE_HEAP_TOP));
diff --git a/storage/xtradb/include/page0zip.h b/storage/xtradb/include/page0zip.h
index 81068e7bd29..adafaa6d8b6 100644
--- a/storage/xtradb/include/page0zip.h
+++ b/storage/xtradb/include/page0zip.h
@@ -132,7 +132,7 @@ page_zip_compress(
dict_index_t* index, /*!< in: index of the B-tree node */
ulint level, /*!< in: compression level */
mtr_t* mtr) /*!< in: mini-transaction, or NULL */
- MY_ATTRIBUTE((nonnull(1,2,3)));
+ MY_ATTRIBUTE((warn_unused_result));
/**********************************************************************//**
Decompress a page. This function should tolerate errors on the compressed
@@ -424,8 +424,7 @@ page_zip_reorganize(
out: data, n_blobs,
m_start, m_end, m_nonempty */
dict_index_t* index, /*!< in: index of the B-tree node */
- mtr_t* mtr) /*!< in: mini-transaction */
- MY_ATTRIBUTE((nonnull));
+ mtr_t* mtr); /*!< in: mini-transaction */
#ifndef UNIV_HOTBACKUP
/**********************************************************************//**
Copy the records of a page byte for byte. Do not copy the page header
@@ -458,7 +457,7 @@ page_zip_parse_compress(
byte* end_ptr,/*!< in: buffer end */
page_t* page, /*!< out: uncompressed page */
page_zip_des_t* page_zip)/*!< out: compressed page */
- MY_ATTRIBUTE((nonnull(1,2)));
+ MY_ATTRIBUTE((warn_unused_result));
#endif /* !UNIV_INNOCHECKSUM */
diff --git a/storage/xtradb/include/rem0rec.h b/storage/xtradb/include/rem0rec.h
index d72f2760a8c..9baf0ab380a 100644
--- a/storage/xtradb/include/rem0rec.h
+++ b/storage/xtradb/include/rem0rec.h
@@ -747,8 +747,7 @@ rec_copy(
/*=====*/
void* buf, /*!< in: buffer */
const rec_t* rec, /*!< in: physical record */
- const ulint* offsets)/*!< in: array returned by rec_get_offsets() */
- MY_ATTRIBUTE((nonnull));
+ const ulint* offsets);/*!< in: array returned by rec_get_offsets() */
#ifndef UNIV_HOTBACKUP
/**********************************************************//**
Determines the size of a data tuple prefix in a temporary file.
diff --git a/storage/xtradb/include/row0upd.h b/storage/xtradb/include/row0upd.h
index e59ec58b63c..4312fcf7339 100644
--- a/storage/xtradb/include/row0upd.h
+++ b/storage/xtradb/include/row0upd.h
@@ -248,9 +248,8 @@ row_upd_index_replace_new_col_vals_index_pos(
/*!< in: if TRUE, limit the replacement to
ordering fields of index; note that this
does not work for non-clustered indexes. */
- mem_heap_t* heap) /*!< in: memory heap for allocating and
+ mem_heap_t* heap); /*!< in: memory heap for allocating and
copying the new values */
- MY_ATTRIBUTE((nonnull));
/***********************************************************//**
Replaces the new column values stored in the update vector to the index entry
given. */
@@ -311,7 +310,7 @@ row_upd_changes_ord_field_binary_func(
compile time */
const row_ext_t*ext) /*!< NULL, or prefixes of the externally
stored columns in the old row */
- MY_ATTRIBUTE((nonnull(1,2), warn_unused_result));
+ MY_ATTRIBUTE((warn_unused_result));
#ifdef UNIV_DEBUG
# define row_upd_changes_ord_field_binary(index,update,thr,row,ext) \
row_upd_changes_ord_field_binary_func(index,update,thr,row,ext)
diff --git a/storage/xtradb/include/srv0srv.h b/storage/xtradb/include/srv0srv.h
index 8713322a008..a2ea6509852 100644
--- a/storage/xtradb/include/srv0srv.h
+++ b/storage/xtradb/include/srv0srv.h
@@ -526,6 +526,7 @@ extern unsigned long long srv_stats_transient_sample_pages;
extern my_bool srv_stats_persistent;
extern unsigned long long srv_stats_persistent_sample_pages;
extern my_bool srv_stats_auto_recalc;
+extern my_bool srv_stats_include_delete_marked;
extern unsigned long long srv_stats_modified_counter;
extern my_bool srv_stats_sample_traditional;
@@ -1321,4 +1322,12 @@ wsrep_srv_conc_cancel_wait(
thread */
#endif /* WITH_WSREP */
+#ifndef DBUG_OFF
+/** false before InnoDB monitor has been printed at least once, true
+afterwards */
+extern bool srv_debug_monitor_printed;
+#else
+#define srv_debug_monitor_printed false
+#endif
+
#endif
diff --git a/storage/xtradb/include/trx0trx.h b/storage/xtradb/include/trx0trx.h
index 24bbff30986..fc710a86d74 100644
--- a/storage/xtradb/include/trx0trx.h
+++ b/storage/xtradb/include/trx0trx.h
@@ -107,7 +107,7 @@ void
trx_free_prepared(
/*==============*/
trx_t* trx) /*!< in, own: trx object */
- UNIV_COLD MY_ATTRIBUTE((nonnull));
+ UNIV_COLD;
/********************************************************************//**
Frees a transaction object for MySQL. */
UNIV_INTERN
diff --git a/storage/xtradb/include/univ.i b/storage/xtradb/include/univ.i
index ad0565a0290..1e375ba2c09 100644
--- a/storage/xtradb/include/univ.i
+++ b/storage/xtradb/include/univ.i
@@ -45,10 +45,10 @@ Created 1/20/1994 Heikki Tuuri
#define INNODB_VERSION_MAJOR 5
#define INNODB_VERSION_MINOR 6
-#define INNODB_VERSION_BUGFIX 34
+#define INNODB_VERSION_BUGFIX 35
#ifndef PERCONA_INNODB_VERSION
-#define PERCONA_INNODB_VERSION 79.1
+#define PERCONA_INNODB_VERSION 80.0
#endif
/* Enable UNIV_LOG_ARCHIVE in XtraDB */
diff --git a/storage/xtradb/log/log0log.cc b/storage/xtradb/log/log0log.cc
index e0259ef80a6..4c654f5272d 100644
--- a/storage/xtradb/log/log0log.cc
+++ b/storage/xtradb/log/log0log.cc
@@ -2,7 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2009, Google Inc.
-Copyright (c) 2014, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2014, 2017, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -48,6 +48,10 @@ Created 12/9/1995 Heikki Tuuri
#endif
#ifndef UNIV_HOTBACKUP
+#if MYSQL_VERSION_ID < 100200
+# include <my_systemd.h> /* sd_notifyf() */
+#endif
+
#include "mem0mem.h"
#include "buf0buf.h"
#include "buf0flu.h"
@@ -1876,7 +1880,7 @@ log_preflush_pool_modified_pages(
and we could not make a new checkpoint on the basis of the
info on the buffer pool only. */
- recv_apply_hashed_log_recs(TRUE);
+ recv_apply_hashed_log_recs(true);
}
if (!buf_page_cleaner_is_active
@@ -2251,7 +2255,7 @@ log_checkpoint(
ut_ad(!srv_read_only_mode);
if (recv_recovery_is_on()) {
- recv_apply_hashed_log_recs(TRUE);
+ recv_apply_hashed_log_recs(true);
}
if (srv_unix_file_flush_method != SRV_UNIX_NOSYNC &&
@@ -2625,6 +2629,13 @@ loop:
start_lsn += len;
buf += len;
+ if (recv_sys->report(ut_time())) {
+ ib_logf(IB_LOG_LEVEL_INFO, "Read redo log up to LSN=" LSN_PF,
+ start_lsn);
+ sd_notifyf(0, "STATUS=Read redo log up to LSN=" LSN_PF,
+ start_lsn);
+ }
+
if (start_lsn != end_lsn) {
if (release_mutex) {
diff --git a/storage/xtradb/log/log0online.cc b/storage/xtradb/log/log0online.cc
index 4e6ad65a906..74f2e2360a8 100644
--- a/storage/xtradb/log/log0online.cc
+++ b/storage/xtradb/log/log0online.cc
@@ -77,12 +77,14 @@ struct log_bitmap_struct {
both the correct type and the tree does
not mind its overwrite during
rbt_next() tree traversal. */
- ib_mutex_t mutex; /*!< mutex protecting all the fields.*/
};
/* The log parsing and bitmap output struct instance */
static struct log_bitmap_struct* log_bmp_sys;
+/* Mutex protecting log_bmp_sys */
+static ib_mutex_t log_bmp_sys_mutex;
+
/** File name stem for bitmap files. */
static const char* bmp_file_name_stem = "ib_modified_log_";
@@ -174,28 +176,24 @@ log_online_set_page_bit(
ulint space, /*!<in: log record space id */
ulint page_no)/*!<in: log record page id */
{
- ulint block_start_page;
- ulint block_pos;
- uint bit_pos;
- ib_rbt_bound_t tree_search_pos;
- byte search_page[MODIFIED_PAGE_BLOCK_SIZE];
- byte *page_ptr;
-
- ut_ad(mutex_own(&log_bmp_sys->mutex));
+ ut_ad(mutex_own(&log_bmp_sys_mutex));
ut_a(space != ULINT_UNDEFINED);
ut_a(page_no != ULINT_UNDEFINED);
- block_start_page = page_no / MODIFIED_PAGE_BLOCK_ID_COUNT
+ ulint block_start_page = page_no / MODIFIED_PAGE_BLOCK_ID_COUNT
* MODIFIED_PAGE_BLOCK_ID_COUNT;
- block_pos = block_start_page ? (page_no % block_start_page / 8)
+ ulint block_pos = block_start_page ? (page_no % block_start_page / 8)
: (page_no / 8);
- bit_pos = page_no % 8;
+ uint bit_pos = page_no % 8;
+ byte search_page[MODIFIED_PAGE_BLOCK_SIZE];
mach_write_to_4(search_page + MODIFIED_PAGE_SPACE_ID, space);
mach_write_to_4(search_page + MODIFIED_PAGE_1ST_PAGE_ID,
block_start_page);
+ byte *page_ptr;
+ ib_rbt_bound_t tree_search_pos;
if (!rbt_search(log_bmp_sys->modified_pages, &tree_search_pos,
search_page)) {
page_ptr = rbt_value(byte, tree_search_pos.last);
@@ -594,12 +592,19 @@ log_online_is_bitmap_file(
&& (!strcmp(stem, bmp_file_name_stem)));
}
-/*********************************************************************//**
-Initialize the online log following subsytem. */
+/** Initialize the constant part of the log tracking subsystem */
+UNIV_INTERN
+void
+log_online_init(void)
+{
+ mutex_create(log_bmp_sys_mutex_key, &log_bmp_sys_mutex,
+ SYNC_LOG_ONLINE);
+}
+
+/** Initialize the dynamic part of the log tracking subsystem */
UNIV_INTERN
void
log_online_read_init(void)
-/*======================*/
{
ibool success;
lsn_t tracking_start_lsn
@@ -623,9 +628,6 @@ log_online_read_init(void)
log_bmp_sys->read_buf = static_cast<byte *>
(ut_align(log_bmp_sys->read_buf_ptr, OS_FILE_LOG_BLOCK_SIZE));
- mutex_create(log_bmp_sys_mutex_key, &log_bmp_sys->mutex,
- SYNC_LOG_ONLINE);
-
/* Initialize bitmap file directory from srv_data_home and add a path
separator if needed. */
srv_data_home_len = strlen(srv_data_home);
@@ -760,13 +762,15 @@ log_online_read_init(void)
log_set_tracked_lsn(tracking_start_lsn);
}
-/*********************************************************************//**
-Shut down the online log following subsystem. */
+/** Shut down the dynamic part of the log tracking subsystem */
UNIV_INTERN
void
log_online_read_shutdown(void)
-/*==========================*/
{
+ mutex_enter(&log_bmp_sys_mutex);
+
+ srv_track_changed_pages = FALSE;
+
ib_rbt_node_t *free_list_node = log_bmp_sys->page_free_list;
if (log_bmp_sys->out.file != os_file_invalid) {
@@ -782,10 +786,21 @@ log_online_read_shutdown(void)
free_list_node = next;
}
- mutex_free(&log_bmp_sys->mutex);
-
ut_free(log_bmp_sys->read_buf_ptr);
ut_free(log_bmp_sys);
+ log_bmp_sys = NULL;
+
+ srv_redo_log_thread_started = false;
+
+ mutex_exit(&log_bmp_sys_mutex);
+}
+
+/** Shut down the constant part of the log tracking subsystem */
+UNIV_INTERN
+void
+log_online_shutdown(void)
+{
+ mutex_free(&log_bmp_sys_mutex);
}
/*********************************************************************//**
@@ -831,13 +846,12 @@ void
log_online_parse_redo_log(void)
/*===========================*/
{
+ ut_ad(mutex_own(&log_bmp_sys_mutex));
+
byte *ptr = log_bmp_sys->parse_buf;
byte *end = log_bmp_sys->parse_buf_end;
-
ulint len = 0;
- ut_ad(mutex_own(&log_bmp_sys->mutex));
-
while (ptr != end
&& log_bmp_sys->next_parse_lsn < log_bmp_sys->end_lsn) {
@@ -919,6 +933,8 @@ log_online_add_to_parse_buf(
ulint skip_len) /*!< in: how much of log data to
skip */
{
+ ut_ad(mutex_own(&log_bmp_sys_mutex));
+
ulint start_offset = skip_len ? skip_len : LOG_BLOCK_HDR_SIZE;
ulint end_offset
= (data_len == OS_FILE_LOG_BLOCK_SIZE)
@@ -927,8 +943,6 @@ log_online_add_to_parse_buf(
ulint actual_data_len = (end_offset >= start_offset)
? end_offset - start_offset : 0;
- ut_ad(mutex_own(&log_bmp_sys->mutex));
-
ut_memcpy(log_bmp_sys->parse_buf_end, log_block + start_offset,
actual_data_len);
@@ -951,11 +965,9 @@ log_online_parse_redo_log_block(
log data should be skipped as
they were parsed before */
{
- ulint block_data_len;
-
- ut_ad(mutex_own(&log_bmp_sys->mutex));
+ ut_ad(mutex_own(&log_bmp_sys_mutex));
- block_data_len = log_block_get_data_len(log_block);
+ ulint block_data_len = log_block_get_data_len(log_block);
ut_ad(block_data_len % OS_FILE_LOG_BLOCK_SIZE == 0
|| block_data_len < OS_FILE_LOG_BLOCK_SIZE);
@@ -975,14 +987,14 @@ log_online_follow_log_seg(
lsn_t block_start_lsn, /*!< in: the LSN to read from */
lsn_t block_end_lsn) /*!< in: the LSN to read to */
{
+ ut_ad(mutex_own(&log_bmp_sys_mutex));
+
/* Pointer to the current OS_FILE_LOG_BLOCK-sized chunk of the read log
data to parse */
byte* log_block = log_bmp_sys->read_buf;
byte* log_block_end = log_bmp_sys->read_buf
+ (block_end_lsn - block_start_lsn);
- ut_ad(mutex_own(&log_bmp_sys->mutex));
-
mutex_enter(&log_sys->mutex);
log_group_read_log_seg(LOG_RECOVER, log_bmp_sys->read_buf,
group, block_start_lsn, block_end_lsn, TRUE);
@@ -1042,11 +1054,11 @@ log_online_follow_log_group(
lsn_t contiguous_lsn) /*!< in: the LSN of log block start
containing the log_parse_start_lsn */
{
+ ut_ad(mutex_own(&log_bmp_sys_mutex));
+
lsn_t block_start_lsn = contiguous_lsn;
lsn_t block_end_lsn;
- ut_ad(mutex_own(&log_bmp_sys->mutex));
-
log_bmp_sys->next_parse_lsn = log_bmp_sys->start_lsn;
log_bmp_sys->parse_buf_end = log_bmp_sys->parse_buf;
@@ -1083,21 +1095,29 @@ log_online_write_bitmap_page(
/*=========================*/
const byte *block) /*!< in: block to write */
{
- ibool success;
-
- ut_ad(srv_track_changed_pages);
- ut_ad(mutex_own(&log_bmp_sys->mutex));
+ ut_ad(mutex_own(&log_bmp_sys_mutex));
/* Simulate a write error */
DBUG_EXECUTE_IF("bitmap_page_write_error",
- ib_logf(IB_LOG_LEVEL_ERROR,
- "simulating bitmap write error in "
- "log_online_write_bitmap_page");
- return FALSE;);
-
- success = os_file_write(log_bmp_sys->out.name, log_bmp_sys->out.file,
- block, log_bmp_sys->out.offset,
- MODIFIED_PAGE_BLOCK_SIZE);
+ {
+ ulint space_id
+ = mach_read_from_4(block
+ + MODIFIED_PAGE_SPACE_ID);
+ if (space_id > 0) {
+ ib_logf(IB_LOG_LEVEL_ERROR,
+ "simulating bitmap write "
+ "error in "
+ "log_online_write_bitmap_page "
+ "for space ID %lu",
+ space_id);
+ return FALSE;
+ }
+ });
+
+ ibool success = os_file_write(log_bmp_sys->out.name,
+ log_bmp_sys->out.file, block,
+ log_bmp_sys->out.offset,
+ MODIFIED_PAGE_BLOCK_SIZE);
if (UNIV_UNLIKELY(!success)) {
/* The following call prints an error message */
@@ -1136,11 +1156,7 @@ ibool
log_online_write_bitmap(void)
/*=========================*/
{
- ib_rbt_node_t *bmp_tree_node;
- const ib_rbt_node_t *last_bmp_tree_node;
- ibool success = TRUE;
-
- ut_ad(mutex_own(&log_bmp_sys->mutex));
+ ut_ad(mutex_own(&log_bmp_sys_mutex));
if (log_bmp_sys->out.offset >= srv_max_bitmap_file_size) {
if (!log_online_rotate_bitmap_file(log_bmp_sys->start_lsn)) {
@@ -1148,9 +1164,12 @@ log_online_write_bitmap(void)
}
}
- bmp_tree_node = (ib_rbt_node_t *)
- rbt_first(log_bmp_sys->modified_pages);
- last_bmp_tree_node = rbt_last(log_bmp_sys->modified_pages);
+ ib_rbt_node_t *bmp_tree_node
+ = (ib_rbt_node_t *)rbt_first(log_bmp_sys->modified_pages);
+ const ib_rbt_node_t * const last_bmp_tree_node
+ = rbt_last(log_bmp_sys->modified_pages);
+
+ ibool success = TRUE;
while (bmp_tree_node) {
@@ -1183,9 +1202,11 @@ log_online_write_bitmap(void)
rbt_next(log_bmp_sys->modified_pages, bmp_tree_node);
DBUG_EXECUTE_IF("bitmap_page_2_write_error",
- ut_ad(bmp_tree_node); /* 2nd page must exist */
- DBUG_SET("+d,bitmap_page_write_error");
- DBUG_SET("-d,bitmap_page_2_write_error"););
+ if (bmp_tree_node)
+ {
+ DBUG_SET("+d,bitmap_page_write_error");
+ DBUG_SET("-d,bitmap_page_2_write_error");
+ });
}
rbt_reset(log_bmp_sys->modified_pages);
@@ -1206,10 +1227,19 @@ log_online_follow_redo_log(void)
log_group_t* group;
ibool result;
- ut_ad(srv_track_changed_pages);
ut_ad(!srv_read_only_mode);
- mutex_enter(&log_bmp_sys->mutex);
+ if (!srv_track_changed_pages)
+ return TRUE;
+
+ DEBUG_SYNC_C("log_online_follow_redo_log");
+
+ mutex_enter(&log_bmp_sys_mutex);
+
+ if (!srv_track_changed_pages) {
+ mutex_exit(&log_bmp_sys_mutex);
+ return TRUE;
+ }
/* Grab the LSN of the last checkpoint, we will parse up to it */
mutex_enter(&(log_sys->mutex));
@@ -1217,7 +1247,7 @@ log_online_follow_redo_log(void)
mutex_exit(&(log_sys->mutex));
if (log_bmp_sys->end_lsn == log_bmp_sys->start_lsn) {
- mutex_exit(&log_bmp_sys->mutex);
+ mutex_exit(&log_bmp_sys_mutex);
return TRUE;
}
@@ -1240,7 +1270,7 @@ log_online_follow_redo_log(void)
log_bmp_sys->start_lsn = log_bmp_sys->end_lsn;
log_set_tracked_lsn(log_bmp_sys->start_lsn);
- mutex_exit(&log_bmp_sys->mutex);
+ mutex_exit(&log_bmp_sys_mutex);
return result;
}
@@ -1587,6 +1617,8 @@ log_online_bitmap_iterator_init(
{
ut_a(i);
+ i->max_lsn = max_lsn;
+
if (UNIV_UNLIKELY(min_lsn > max_lsn)) {
/* Empty range */
@@ -1695,6 +1727,9 @@ log_online_bitmap_iterator_next(
return TRUE;
}
+ if (i->end_lsn >= i->max_lsn && i->last_page_in_run)
+ return FALSE;
+
while (!checksum_ok)
{
while (i->in.size < MODIFIED_PAGE_BLOCK_SIZE
@@ -1790,15 +1825,21 @@ log_online_purge_changed_page_bitmaps(
lsn = LSN_MAX;
}
+ bool log_bmp_sys_inited = false;
if (srv_redo_log_thread_started) {
/* User requests might happen with both enabled and disabled
tracking */
- mutex_enter(&log_bmp_sys->mutex);
+ log_bmp_sys_inited = true;
+ mutex_enter(&log_bmp_sys_mutex);
+ if (!srv_redo_log_thread_started) {
+ log_bmp_sys_inited = false;
+ mutex_exit(&log_bmp_sys_mutex);
+ }
}
if (!log_online_setup_bitmap_file_range(&bitmap_files, 0, LSN_MAX)) {
- if (srv_redo_log_thread_started) {
- mutex_exit(&log_bmp_sys->mutex);
+ if (log_bmp_sys_inited) {
+ mutex_exit(&log_bmp_sys_mutex);
}
return TRUE;
}
@@ -1836,7 +1877,7 @@ log_online_purge_changed_page_bitmaps(
}
}
- if (srv_redo_log_thread_started) {
+ if (log_bmp_sys_inited) {
if (lsn > log_bmp_sys->end_lsn) {
lsn_t new_file_lsn;
if (lsn == LSN_MAX) {
@@ -1852,7 +1893,7 @@ log_online_purge_changed_page_bitmaps(
}
}
- mutex_exit(&log_bmp_sys->mutex);
+ mutex_exit(&log_bmp_sys_mutex);
}
free(bitmap_files.files);
diff --git a/storage/xtradb/log/log0recv.cc b/storage/xtradb/log/log0recv.cc
index 6405a87a2bd..a20cb8dec4d 100644
--- a/storage/xtradb/log/log0recv.cc
+++ b/storage/xtradb/log/log0recv.cc
@@ -2,7 +2,7 @@
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
-Copyright (c) 2013, 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2013, 2017, MariaDB Corporation.
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
@@ -85,7 +85,7 @@ this must be less than UNIV_PAGE_SIZE as it is stored in the buffer pool */
#define RECV_READ_AHEAD_AREA 32
/** The recovery system */
-UNIV_INTERN recv_sys_t* recv_sys = NULL;
+UNIV_INTERN recv_sys_t* recv_sys;
/** TRUE when applying redo log records during crash recovery; FALSE
otherwise. Note that this is FALSE while a background thread is
rolling back incomplete transactions. */
@@ -137,9 +137,6 @@ UNIV_INTERN ibool recv_is_making_a_backup = FALSE;
UNIV_INTERN ibool recv_is_from_backup = FALSE;
# define buf_pool_get_curr_size() (5 * 1024 * 1024)
#endif /* !UNIV_HOTBACKUP */
-/** The following counter is used to decide when to print info on
-log scan */
-static ulint recv_scan_print_counter;
/** The type of the previous parsed redo log record */
static ulint recv_previous_parsed_rec_type;
@@ -310,8 +307,6 @@ recv_sys_var_init(void)
recv_no_ibuf_operations = FALSE;
- recv_scan_print_counter = 0;
-
recv_previous_parsed_rec_type = 999999;
recv_previous_parsed_rec_offset = 0;
@@ -422,6 +417,7 @@ recv_sys_init(
recv_sys->last_block_buf_start, OS_FILE_LOG_BLOCK_SIZE));
recv_sys->found_corrupt_log = FALSE;
+ recv_sys->progress_time = ut_time();
recv_max_page_lsn = 0;
@@ -431,33 +427,18 @@ recv_sys_init(
mutex_exit(&(recv_sys->mutex));
}
-/********************************************************//**
-Empties the hash table when it has been fully processed.
-@return DB_SUCCESS when successfull or DB_ERROR when fails. */
+/** Empty a fully processed hash table. */
static
-dberr_t
-recv_sys_empty_hash(void)
-/*=====================*/
+void
+recv_sys_empty_hash()
{
ut_ad(mutex_own(&(recv_sys->mutex)));
-
- if (recv_sys->n_addrs != 0) {
- fprintf(stderr,
- "InnoDB: Error: %lu pages with log records"
- " were left unprocessed!\n"
- "InnoDB: Maximum page number with"
- " log records on it %lu\n",
- (ulong) recv_sys->n_addrs,
- (ulong) recv_max_parsed_page_no);
- return DB_ERROR;
- }
+ ut_a(recv_sys->n_addrs == 0);
hash_table_free(recv_sys->addr_hash);
mem_heap_empty(recv_sys->heap);
recv_sys->addr_hash = hash_create(buf_pool_get_curr_size() / 512);
-
- return DB_SUCCESS;
}
#ifndef UNIV_HOTBACKUP
@@ -1804,6 +1785,8 @@ recv_recover_page_func(
mtr_commit(&mtr);
+ ib_time_t time = ut_time();
+
mutex_enter(&(recv_sys->mutex));
if (recv_max_page_lsn < page_lsn) {
@@ -1812,11 +1795,17 @@ recv_recover_page_func(
recv_addr->state = RECV_PROCESSED;
- ut_a(recv_sys->n_addrs);
- recv_sys->n_addrs--;
-
- mutex_exit(&(recv_sys->mutex));
+ ut_a(recv_sys->n_addrs > 0);
+ if (ulint n = --recv_sys->n_addrs) {
+ if (recv_sys->report(time)) {
+ ib_logf(IB_LOG_LEVEL_INFO,
+ "To recover: " ULINTPF " pages from log", n);
+ sd_notifyf(0, "STATUS=To recover: " ULINTPF
+ " pages from log", n);
+ }
+ }
+ mutex_exit(&recv_sys->mutex);
}
#ifndef UNIV_HOTBACKUP
@@ -1862,62 +1851,50 @@ recv_read_in_area(
}
buf_read_recv_pages(FALSE, space, zip_size, page_nos, n);
- /*
- fprintf(stderr, "Recv pages at %lu n %lu\n", page_nos[0], n);
- */
return(n);
}
-/*******************************************************************//**
-Empties the hash table of stored log records, applying them to appropriate
-pages.
-@return DB_SUCCESS when successfull or DB_ERROR when fails. */
+/** Apply the hash table of stored log records to persistent data pages.
+@param[in] last_batch whether the change buffer merge will be
+ performed as part of the operation */
UNIV_INTERN
-dberr_t
-recv_apply_hashed_log_recs(
-/*=======================*/
- ibool allow_ibuf) /*!< in: if TRUE, also ibuf operations are
- allowed during the application; if FALSE,
- no ibuf operations are allowed, and after
- the application all file pages are flushed to
- disk and invalidated in buffer pool: this
- alternative means that no new log records
- can be generated during the application;
- the caller must in this case own the log
- mutex */
+void
+recv_apply_hashed_log_recs(bool last_batch)
{
- recv_addr_t* recv_addr;
- ulint i;
- ibool has_printed = FALSE;
- ulong progress;
- mtr_t mtr;
- dberr_t err = DB_SUCCESS;
-loop:
- mutex_enter(&(recv_sys->mutex));
-
- if (recv_sys->apply_batch_on) {
+ for (;;) {
+ mutex_enter(&recv_sys->mutex);
- mutex_exit(&(recv_sys->mutex));
+ if (!recv_sys->apply_batch_on) {
+ break;
+ }
+ mutex_exit(&recv_sys->mutex);
os_thread_sleep(500000);
-
- goto loop;
}
- ut_ad((allow_ibuf == 0) == (mutex_own(&log_sys->mutex) != 0));
+ ut_ad(!last_batch == mutex_own(&log_sys->mutex));
- if (!allow_ibuf) {
+ if (!last_batch) {
recv_no_ibuf_operations = TRUE;
}
+ if (ulint n = recv_sys->n_addrs) {
+ const char* msg = last_batch
+ ? "Starting final batch to recover "
+ : "Starting a batch to recover ";
+ ib_logf(IB_LOG_LEVEL_INFO,
+ "%s" ULINTPF " pages from redo log", msg, n);
+ sd_notifyf(0, "STATUS=%s" ULINTPF " pages from redo log",
+ msg, n);
+ }
+
recv_sys->apply_log_recs = TRUE;
recv_sys->apply_batch_on = TRUE;
- for (i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) {
-
- for (recv_addr = static_cast<recv_addr_t*>(
- HASH_GET_FIRST(recv_sys->addr_hash, i));
- recv_addr != 0;
+ for (ulint i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) {
+ for (recv_addr_t* recv_addr = static_cast<recv_addr_t*>(
+ HASH_GET_FIRST(recv_sys->addr_hash, i));
+ recv_addr;
recv_addr = static_cast<recv_addr_t*>(
HASH_GET_NEXT(addr_hash, recv_addr))) {
@@ -1926,24 +1903,12 @@ loop:
ulint page_no = recv_addr->page_no;
if (recv_addr->state == RECV_NOT_PROCESSED) {
- if (!has_printed) {
- ib_logf(IB_LOG_LEVEL_INFO,
- "Starting an apply batch"
- " of log records"
- " to the database...");
- fputs("InnoDB: Progress in percent: ",
- stderr);
- has_printed = TRUE;
- }
-
- mutex_exit(&(recv_sys->mutex));
+ mutex_exit(&recv_sys->mutex);
if (buf_page_peek(space, page_no)) {
- buf_block_t* block;
-
+ mtr_t mtr;
mtr_start(&mtr);
-
- block = buf_page_get(
+ buf_block_t* block = buf_page_get(
space, zip_size, page_no,
RW_X_LATCH, &mtr);
buf_block_dbg_add_level(
@@ -1956,21 +1921,9 @@ loop:
page_no);
}
- mutex_enter(&(recv_sys->mutex));
+ mutex_enter(&recv_sys->mutex);
}
}
-
- progress = (ulong) (i * 100)
- / hash_get_n_cells(recv_sys->addr_hash);
- if (has_printed
- && progress
- != ((i + 1) * 100)
- / hash_get_n_cells(recv_sys->addr_hash)) {
-
- fprintf(stderr, "%lu ", progress);
- sd_notifyf(0, "STATUS=Applying batch of log records for"
- " InnoDB: Progress %lu", progress);
- }
}
/* Wait until all the pages have been processed */
@@ -1984,12 +1937,7 @@ loop:
mutex_enter(&(recv_sys->mutex));
}
- if (has_printed) {
-
- fprintf(stderr, "\n");
- }
-
- if (!allow_ibuf) {
+ if (!last_batch) {
bool success;
/* Flush all the file pages to disk and invalidate them in
@@ -2027,16 +1975,9 @@ loop:
recv_sys->apply_log_recs = FALSE;
recv_sys->apply_batch_on = FALSE;
- err = recv_sys_empty_hash();
-
- if (has_printed) {
- fprintf(stderr, "InnoDB: Apply batch completed\n");
- sd_notify(0, "STATUS=InnoDB: Apply batch completed");
- }
-
- mutex_exit(&(recv_sys->mutex));
+ recv_sys_empty_hash();
- return err;
+ mutex_exit(&recv_sys->mutex);
}
#else /* !UNIV_HOTBACKUP */
/*******************************************************************//**
@@ -2059,11 +2000,6 @@ recv_apply_log_recs_for_backup(void)
block = back_block1;
- ib_logf(IB_LOG_LEVEL_INFO,
- "Starting an apply batch of log records to the database...");
-
- fputs("InnoDB: Progress in percent: ", stderr);
-
n_hash_cells = hash_get_n_cells(recv_sys->addr_hash);
for (i = 0; i < n_hash_cells; i++) {
@@ -2177,16 +2113,6 @@ recv_apply_log_recs_for_backup(void)
skip_this_recv_addr:
recv_addr = HASH_GET_NEXT(addr_hash, recv_addr);
}
-
- if ((100 * i) / n_hash_cells
- != (100 * (i + 1)) / n_hash_cells) {
- fprintf(stderr, "%lu ",
- (ulong) ((100 * i) / n_hash_cells));
- fflush(stderr);
- sd_notifyf(0, "STATUS=Applying batch of log records for"
- " backup InnoDB: Progress %lu",
- (ulong) (100 * i) / n_hash_cells);
- }
}
sd_notify(0, "STATUS=InnoDB: Apply batch for backup completed");
@@ -2889,11 +2815,10 @@ recv_scan_log_recs(
#ifndef UNIV_HOTBACKUP
if (recv_log_scan_is_startup_type
&& !recv_needed_recovery) {
-
if (!srv_read_only_mode) {
ib_logf(IB_LOG_LEVEL_INFO,
- "Log scan progressed past the "
- "checkpoint lsn " LSN_PF "",
+ "Starting crash recovery from "
+ "checkpoint LSN=" LSN_PF,
recv_sys->scanned_lsn);
recv_init_crash_recovery();
@@ -2953,19 +2878,6 @@ recv_scan_log_recs(
*group_scanned_lsn = scanned_lsn;
- if (recv_needed_recovery
- || (recv_is_from_backup && !recv_is_making_a_backup)) {
- recv_scan_print_counter++;
-
- if (finished || (recv_scan_print_counter % 80 == 0)) {
-
- fprintf(stderr,
- "InnoDB: Doing recovery: scanned up to"
- " log sequence number " LSN_PF "\n",
- *group_scanned_lsn);
- }
- }
-
if (more_data && !recv_sys->found_corrupt_log) {
/* Try to parse more log records */
@@ -2985,12 +2897,7 @@ recv_scan_log_recs(
log yet: they would be produced by ibuf
operations */
- *err = recv_apply_hashed_log_recs(FALSE);
-
- if (*err != DB_SUCCESS) {
- /* Finish processing because of error */
- return (TRUE);
- }
+ recv_apply_hashed_log_recs(false);
}
#endif /* !UNIV_HOTBACKUP */
@@ -3074,11 +2981,6 @@ recv_init_crash_recovery(void)
recv_needed_recovery = TRUE;
- ib_logf(IB_LOG_LEVEL_INFO, "Database was not shutdown normally!");
- ib_logf(IB_LOG_LEVEL_INFO, "Starting crash recovery.");
- ib_logf(IB_LOG_LEVEL_INFO,
- "Reading tablespace information from the .ibd files...");
-
fil_load_single_table_tablespaces();
/* If we are using the doublewrite method, we will
@@ -3089,9 +2991,7 @@ recv_init_crash_recovery(void)
if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {
ib_logf(IB_LOG_LEVEL_INFO,
- "Restoring possible half-written data pages ");
-
- ib_logf(IB_LOG_LEVEL_INFO,
+ "Restoring possible half-written data pages "
"from the doublewrite buffer...");
buf_dblwr_process();
diff --git a/storage/xtradb/mach/mach0data.cc b/storage/xtradb/mach/mach0data.cc
index 206434dc5ab..feeedb01609 100644
--- a/storage/xtradb/mach/mach0data.cc
+++ b/storage/xtradb/mach/mach0data.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (c) 1995, 2009, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
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
@@ -55,7 +55,6 @@ mach_parse_compressed(
if (flag < 0x80UL) {
*val = flag;
return(ptr + 1);
-
}
/* Workaround GCC bug
@@ -64,7 +63,11 @@ mach_parse_compressed(
function, causing and out-of-bounds read if we are reading a short
integer close to the end of buffer. */
#if defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__clang__)
- asm volatile("": : :"memory");
+#define DEPLOY_FENCE
+#endif
+
+#ifdef DEPLOY_FENCE
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
#endif
if (flag < 0xC0UL) {
@@ -75,8 +78,13 @@ mach_parse_compressed(
*val = mach_read_from_2(ptr) & 0x7FFFUL;
return(ptr + 2);
+ }
- } else if (flag < 0xE0UL) {
+#ifdef DEPLOY_FENCE
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+#endif
+
+ if (flag < 0xE0UL) {
if (end_ptr < ptr + 3) {
return(NULL);
}
@@ -84,7 +92,13 @@ mach_parse_compressed(
*val = mach_read_from_3(ptr) & 0x3FFFFFUL;
return(ptr + 3);
- } else if (flag < 0xF0UL) {
+ }
+
+#ifdef DEPLOY_FENCE
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+#endif
+
+ if (flag < 0xF0UL) {
if (end_ptr < ptr + 4) {
return(NULL);
}
@@ -92,14 +106,20 @@ mach_parse_compressed(
*val = mach_read_from_4(ptr) & 0x1FFFFFFFUL;
return(ptr + 4);
- } else {
- ut_ad(flag == 0xF0UL);
+ }
- if (end_ptr < ptr + 5) {
- return(NULL);
- }
+#ifdef DEPLOY_FENCE
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+#endif
- *val = mach_read_from_4(ptr + 1);
- return(ptr + 5);
+#undef DEPLOY_FENCE
+
+ ut_ad(flag == 0xF0UL);
+
+ if (end_ptr < ptr + 5) {
+ return(NULL);
}
+
+ *val = mach_read_from_4(ptr + 1);
+ return(ptr + 5);
}
diff --git a/storage/xtradb/mtr/mtr0mtr.cc b/storage/xtradb/mtr/mtr0mtr.cc
index a1d7261e43c..e564b270d00 100644
--- a/storage/xtradb/mtr/mtr0mtr.cc
+++ b/storage/xtradb/mtr/mtr0mtr.cc
@@ -312,7 +312,6 @@ mtr_commit(
/*=======*/
mtr_t* mtr) /*!< in: mini-transaction */
{
- ut_ad(mtr);
ut_ad(mtr->magic_n == MTR_MAGIC_N);
ut_ad(mtr->state == MTR_ACTIVE);
ut_ad(!mtr->inside_ibuf);
diff --git a/storage/xtradb/os/os0file.cc b/storage/xtradb/os/os0file.cc
index c5be6d45c0e..cdc3df5e851 100644
--- a/storage/xtradb/os/os0file.cc
+++ b/storage/xtradb/os/os0file.cc
@@ -1234,50 +1234,15 @@ next_file:
char* full_path;
int ret;
struct stat statinfo;
-#ifdef HAVE_READDIR_R
- char dirent_buf[sizeof(struct dirent)
- + _POSIX_PATH_MAX + 100];
- /* In /mysys/my_lib.c, _POSIX_PATH_MAX + 1 is used as
- the max file name len; but in most standards, the
- length is NAME_MAX; we add 100 to be even safer */
-#endif
next_file:
-#ifdef HAVE_READDIR_R
- ret = readdir_r(dir, (struct dirent*) dirent_buf, &ent);
-
- if (ret != 0
-#ifdef UNIV_AIX
- /* On AIX, only if we got non-NULL 'ent' (result) value and
- a non-zero 'ret' (return) value, it indicates a failed
- readdir_r() call. An NULL 'ent' with an non-zero 'ret'
- would indicate the "end of the directory" is reached. */
- && ent != NULL
-#endif
- ) {
- fprintf(stderr,
- "InnoDB: cannot read directory %s, error %lu\n",
- dirname, (ulong) ret);
-
- return(-1);
- }
-
- if (ent == NULL) {
- /* End of directory */
-
- return(1);
- }
-
- ut_a(strlen(ent->d_name) < _POSIX_PATH_MAX + 100 - 1);
-#else
ent = readdir(dir);
if (ent == NULL) {
return(1);
}
-#endif
ut_a(strlen(ent->d_name) < OS_FILE_MAX_PATH);
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
@@ -1605,9 +1570,13 @@ os_file_set_nocache_if_needed(os_file_t file, const char* name,
if (srv_unix_file_flush_method == SRV_UNIX_ALL_O_DIRECT
|| (type == OS_DATA_FILE
&& (srv_unix_file_flush_method == SRV_UNIX_O_DIRECT
- || (srv_unix_file_flush_method == SRV_UNIX_O_DIRECT_NO_FSYNC)))) {
- os_file_set_nocache(file, name, mode_str);
- }
+ || (srv_unix_file_flush_method
+ == SRV_UNIX_O_DIRECT_NO_FSYNC))))
+ /* Do fsync() on log files when setting O_DIRECT fails.
+ See log_io_complete() */
+ if (!os_file_set_nocache(file, name, mode_str)
+ && srv_unix_file_flush_method == SRV_UNIX_ALL_O_DIRECT)
+ srv_unix_file_flush_method = SRV_UNIX_O_DIRECT;
}
/****************************************************************//**
@@ -1815,9 +1784,10 @@ os_file_create_simple_no_error_handling_func(
}
/****************************************************************//**
-Tries to disable OS caching on an opened file descriptor. */
+Tries to disable OS caching on an opened file descriptor.
+@return TRUE if operation is success and FALSE otherwise */
UNIV_INTERN
-void
+bool
os_file_set_nocache(
/*================*/
os_file_t fd /*!< in: file descriptor to alter */
@@ -1838,6 +1808,7 @@ os_file_set_nocache(
"Failed to set DIRECTIO_ON on file %s: %s: %s, "
"continuing anyway.",
file_name, operation_name, strerror(errno_save));
+ return false;
}
#elif defined(O_DIRECT)
if (fcntl(fd, F_SETFL, O_DIRECT) == -1) {
@@ -1868,8 +1839,10 @@ short_warning:
"continuing anyway.",
file_name, operation_name, strerror(errno_save));
}
+ return false;
}
#endif /* defined(UNIV_SOLARIS) && defined(DIRECTIO_ON) */
+ return true;
}
diff --git a/storage/xtradb/os/os0thread.cc b/storage/xtradb/os/os0thread.cc
index 5ddc40b0eeb..8baf06b9bb7 100644
--- a/storage/xtradb/os/os0thread.cc
+++ b/storage/xtradb/os/os0thread.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
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
@@ -206,29 +206,32 @@ os_thread_create_func(
#endif
}
-/**
-Waits until the specified thread completes and joins it. Its return value is
-ignored.
-
-@param thread thread to join */
+/** Waits until the specified thread completes and joins it.
+Its return value is ignored.
+@param[in,out] thread thread to join */
UNIV_INTERN
void
os_thread_join(
os_thread_t thread)
{
- /*This function is currently only used to workaround glibc bug
+ /* This function is currently only used to workaround glibc bug
described in http://bugs.mysql.com/bug.php?id=82886
On Windows, no workarounds are necessary, all threads
are "detached" upon thread exit (handle is closed), so we do
nothing.
*/
-#ifndef _WIN32
- int ret MY_ATTRIBUTE((unused)) = pthread_join(thread, NULL);
+#ifdef __WIN__
+ /* Do nothing. */
+#else
+#ifdef UNIV_DEBUG
+ const int ret MY_ATTRIBUTE((unused)) =
+#endif /* UNIV_DEBUG */
+ pthread_join(thread, NULL);
- /* Waiting on already-quit threads is allowed */
+ /* Waiting on already-quit threads is allowed. */
ut_ad(ret == 0 || ret == ESRCH);
-#endif
+#endif /* __WIN__ */
}
/*****************************************************************//**
@@ -257,8 +260,9 @@ os_thread_exit(
#ifdef __WIN__
ExitThread((DWORD) exit_value);
#else
- if (detach)
+ if (detach) {
pthread_detach(pthread_self());
+ }
pthread_exit(exit_value);
#endif
}
diff --git a/storage/xtradb/page/page0page.cc b/storage/xtradb/page/page0page.cc
index a6fba4074ef..3f8e47adafd 100644
--- a/storage/xtradb/page/page0page.cc
+++ b/storage/xtradb/page/page0page.cc
@@ -1455,7 +1455,6 @@ page_dir_split_slot(
ulint i;
ulint n_owned;
- ut_ad(page);
ut_ad(!page_zip || page_is_comp(page));
ut_ad(slot_no > 0);
@@ -1517,7 +1516,6 @@ page_dir_balance_slot(
rec_t* old_rec;
rec_t* new_rec;
- ut_ad(page);
ut_ad(!page_zip || page_is_comp(page));
ut_ad(slot_no > 0);
diff --git a/storage/xtradb/page/page0zip.cc b/storage/xtradb/page/page0zip.cc
index 04340c0f3d2..32e76fb44e6 100644
--- a/storage/xtradb/page/page0zip.cc
+++ b/storage/xtradb/page/page0zip.cc
@@ -4810,8 +4810,6 @@ page_zip_parse_compress(
ulint size;
ulint trailer_size;
- ut_ad(ptr != NULL);
- ut_ad(end_ptr != NULL);
ut_ad(!page == !page_zip);
if (UNIV_UNLIKELY(ptr + (2 + 2) > end_ptr)) {
diff --git a/storage/xtradb/rem/rem0rec.cc b/storage/xtradb/rem/rem0rec.cc
index b9496b7f620..6770748c38b 100644
--- a/storage/xtradb/rem/rem0rec.cc
+++ b/storage/xtradb/rem/rem0rec.cc
@@ -789,7 +789,7 @@ rec_get_nth_field_offs_old(
/**********************************************************//**
Determines the size of a data tuple prefix in ROW_FORMAT=COMPACT.
@return total size */
-UNIV_INLINE MY_ATTRIBUTE((warn_unused_result, nonnull(1,2)))
+UNIV_INLINE MY_ATTRIBUTE((warn_unused_result))
ulint
rec_get_converted_size_comp_prefix_low(
/*===================================*/
diff --git a/storage/xtradb/row/row0merge.cc b/storage/xtradb/row/row0merge.cc
index 2a4805a4714..7bf5c98ee5f 100644
--- a/storage/xtradb/row/row0merge.cc
+++ b/storage/xtradb/row/row0merge.cc
@@ -1073,14 +1073,8 @@ row_merge_read_rec(
ulint data_size;
ulint avail_size;
- ut_ad(block);
- ut_ad(buf);
ut_ad(b >= &block[0]);
ut_ad(b < &block[srv_sort_buf_size]);
- ut_ad(index);
- ut_ad(foffs);
- ut_ad(mrec);
- ut_ad(offsets);
ut_ad(*offsets == 1 + REC_OFFS_HEADER_SIZE
+ dict_index_get_n_fields(index));
@@ -4172,8 +4166,8 @@ wait_again:
for (j = 0; j < FTS_NUM_AUX_INDEX;
j++) {
- os_thread_join(merge_info[j]
- .thread_hdl);
+ os_thread_join(merge_info[j]
+ .thread_hdl);
}
}
} else {
diff --git a/storage/xtradb/row/row0mysql.cc b/storage/xtradb/row/row0mysql.cc
index c81b10b93f1..3ddb8654f69 100644
--- a/storage/xtradb/row/row0mysql.cc
+++ b/storage/xtradb/row/row0mysql.cc
@@ -1373,6 +1373,8 @@ run_again:
row_ins_step(thr);
+ DEBUG_SYNC_C("ib_after_row_insert_step");
+
err = trx->error_state;
if (err != DB_SUCCESS) {
diff --git a/storage/xtradb/row/row0purge.cc b/storage/xtradb/row/row0purge.cc
index bc2e0b0e1cb..35b3520749b 100644
--- a/storage/xtradb/row/row0purge.cc
+++ b/storage/xtradb/row/row0purge.cc
@@ -897,7 +897,7 @@ row_purge_record_func(
Fetches an undo log record and does the purge for the recorded operation.
If none left, or the current purge completed, returns the control to the
parent node, which is always a query thread node. */
-static MY_ATTRIBUTE((nonnull))
+static
void
row_purge(
/*======*/
diff --git a/storage/xtradb/row/row0upd.cc b/storage/xtradb/row/row0upd.cc
index 6288251da77..a203872e3bb 100644
--- a/storage/xtradb/row/row0upd.cc
+++ b/storage/xtradb/row/row0upd.cc
@@ -1285,8 +1285,6 @@ row_upd_index_replace_new_col_vals_index_pos(
ulint n_fields;
const ulint zip_size = dict_table_zip_size(index->table);
- ut_ad(index);
-
dtuple_set_info_bits(entry, update->info_bits);
if (order_only) {
@@ -1471,8 +1469,6 @@ row_upd_changes_ord_field_binary_func(
ulint i;
const dict_index_t* clust_index;
- ut_ad(index);
- ut_ad(update);
ut_ad(thr);
ut_ad(thr->graph);
ut_ad(thr->graph->trx);
diff --git a/storage/xtradb/srv/srv0srv.cc b/storage/xtradb/srv/srv0srv.cc
index 3865332efbe..5389b7e9bc4 100644
--- a/storage/xtradb/srv/srv0srv.cc
+++ b/storage/xtradb/srv/srv0srv.cc
@@ -79,12 +79,6 @@ Created 10/8/1995 Heikki Tuuri
#include <my_rdtsc.h>
#include "btr0scrub.h"
-/* prototypes of new functions added to ha_innodb.cc for kill_idle_transaction */
-ibool innobase_thd_is_idle(const void* thd);
-ib_int64_t innobase_thd_get_start_time(const void* thd);
-void innobase_thd_kill(ulong thd_id);
-ulong innobase_thd_get_thread_id(const void* thd);
-
/* prototypes for new functions added to ha_innodb.cc */
ibool innobase_get_slow_log();
@@ -506,6 +500,7 @@ this many index pages, there are 2 ways to calculate statistics:
table/index are not found in the innodb database */
UNIV_INTERN unsigned long long srv_stats_transient_sample_pages = 8;
UNIV_INTERN my_bool srv_stats_persistent = TRUE;
+UNIV_INTERN my_bool srv_stats_include_delete_marked = FALSE;
UNIV_INTERN unsigned long long srv_stats_persistent_sample_pages = 20;
UNIV_INTERN my_bool srv_stats_auto_recalc = TRUE;
@@ -1480,22 +1475,26 @@ srv_printf_innodb_monitor(
low level 135. Therefore we can reserve the latter mutex here without
a danger of a deadlock of threads. */
- mutex_enter(&dict_foreign_err_mutex);
+ if (!recv_recovery_on) {
- if (!srv_read_only_mode && ftell(dict_foreign_err_file) != 0L) {
- fputs("------------------------\n"
- "LATEST FOREIGN KEY ERROR\n"
- "------------------------\n", file);
- ut_copy_file(file, dict_foreign_err_file);
- }
+ mutex_enter(&dict_foreign_err_mutex);
+
+ if (!srv_read_only_mode
+ && ftell(dict_foreign_err_file) != 0L) {
+ fputs("------------------------\n"
+ "LATEST FOREIGN KEY ERROR\n"
+ "------------------------\n", file);
+ ut_copy_file(file, dict_foreign_err_file);
+ }
- mutex_exit(&dict_foreign_err_mutex);
+ mutex_exit(&dict_foreign_err_mutex);
+ }
/* Only if lock_print_info_summary proceeds correctly,
before we call the lock_print_info_all_transactions
to print all the lock information. IMPORTANT NOTE: This
function acquires the lock mutex on success. */
- ret = lock_print_info_summary(file, nowait);
+ ret = recv_recovery_on ? FALSE : lock_print_info_summary(file, nowait);
if (ret) {
if (trx_start_pos) {
@@ -1528,10 +1527,13 @@ srv_printf_innodb_monitor(
"--------\n", file);
os_aio_print(file);
- fputs("-------------------------------------\n"
- "INSERT BUFFER AND ADAPTIVE HASH INDEX\n"
- "-------------------------------------\n", file);
- ibuf_print(file);
+ if (!recv_recovery_on) {
+
+ fputs("-------------------------------------\n"
+ "INSERT BUFFER AND ADAPTIVE HASH INDEX\n"
+ "-------------------------------------\n", file);
+ ibuf_print(file);
+ }
fprintf(file,
@@ -1543,10 +1545,13 @@ srv_printf_innodb_monitor(
btr_cur_n_sea_old = btr_cur_n_sea;
btr_cur_n_non_sea_old = btr_cur_n_non_sea;
- fputs("---\n"
- "LOG\n"
- "---\n", file);
- log_print(file);
+ if (!recv_recovery_on) {
+
+ fputs("---\n"
+ "LOG\n"
+ "---\n", file);
+ log_print(file);
+ }
fputs("----------------------\n"
"BUFFER POOL AND MEMORY\n"
@@ -1641,8 +1646,9 @@ srv_printf_innodb_monitor(
? (recv_sys->addr_hash->n_cells * sizeof(hash_cell_t)) : 0),
recv_sys_subtotal);
+
fprintf(file, "Dictionary memory allocated " ULINTPF "\n",
- dict_sys->size);
+ dict_sys ? dict_sys->size : 0);
buf_print_io(file);
@@ -1750,6 +1756,10 @@ srv_printf_innodb_monitor(
mutex_exit(&srv_innodb_monitor_mutex);
fflush(file);
+#ifndef DBUG_OFF
+ srv_debug_monitor_printed = true;
+#endif
+
return(ret);
}
@@ -2118,6 +2128,12 @@ srv_export_innodb_status(void)
mutex_exit(&srv_innodb_monitor_mutex);
}
+#ifndef DBUG_OFF
+/** false before InnoDB monitor has been printed at least once, true
+afterwards */
+bool srv_debug_monitor_printed = false;
+#endif
+
/*********************************************************************//**
A thread which prints the info output by various InnoDB monitors.
@return a dummy parameter */
@@ -2391,36 +2407,6 @@ loop:
old_sema = sema;
}
- if (srv_kill_idle_transaction && trx_sys) {
- trx_t* trx;
- time_t now;
-rescan_idle:
- now = time(NULL);
- mutex_enter(&trx_sys->mutex);
- trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list);
- while (trx) {
- if (trx->state == TRX_STATE_ACTIVE
- && trx->mysql_thd
- && innobase_thd_is_idle(trx->mysql_thd)) {
- ib_int64_t start_time = innobase_thd_get_start_time(trx->mysql_thd);
- ulong thd_id = innobase_thd_get_thread_id(trx->mysql_thd);
-
- if (trx->last_stmt_start != start_time) {
- trx->idle_start = now;
- trx->last_stmt_start = start_time;
- } else if (difftime(now, trx->idle_start)
- > srv_kill_idle_transaction) {
- /* kill the session */
- mutex_exit(&trx_sys->mutex);
- innobase_thd_kill(thd_id);
- goto rescan_idle;
- }
- }
- trx = UT_LIST_GET_NEXT(mysql_trx_list, trx);
- }
- mutex_exit(&trx_sys->mutex);
- }
-
/* Flush stderr so that a database user gets the output
to possible MySQL error file */
@@ -2542,10 +2528,8 @@ DECLARE_THREAD(srv_redo_log_follow_thread)(
} while (srv_shutdown_state < SRV_SHUTDOWN_LAST_PHASE);
- srv_track_changed_pages = FALSE;
log_online_read_shutdown();
os_event_set(srv_redo_log_tracked_event);
- srv_redo_log_thread_started = false; /* Defensive, not required */
my_thread_end();
os_thread_exit(NULL);
diff --git a/storage/xtradb/srv/srv0start.cc b/storage/xtradb/srv/srv0start.cc
index 679913959c9..5b6ca38951e 100644
--- a/storage/xtradb/srv/srv0start.cc
+++ b/storage/xtradb/srv/srv0start.cc
@@ -2120,6 +2120,7 @@ innobase_start_or_create_for_mysql(void)
fsp_init();
log_init();
+ log_online_init();
lock_sys_create(srv_lock_table_size);
@@ -2513,6 +2514,23 @@ files_checked:
and there must be no page in the buf_flush list. */
buf_pool_invalidate();
+ /* Start monitor thread early enough so that e.g. crash
+ recovery failing to find free pages in the buffer pool is
+ diagnosed. */
+ if (!srv_read_only_mode)
+ {
+ /* Create the thread which prints InnoDB monitor
+ info */
+ srv_monitor_active = true;
+ thread_handles[4 + SRV_MAX_N_IO_THREADS] =
+ os_thread_create(
+ srv_monitor_thread,
+ NULL,
+ thread_ids + 4 + SRV_MAX_N_IO_THREADS);
+
+ thread_started[4 + SRV_MAX_N_IO_THREADS] = true;
+ }
+
/* We always try to do a recovery, even if the database had
been shut down normally: this is the normal startup path */
@@ -2533,7 +2551,7 @@ files_checked:
return(err);
}
- /* This must precede recv_apply_hashed_log_recs(TRUE). */
+ /* This must precede recv_apply_hashed_log_recs(true). */
ib_bh = trx_sys_init_at_db_start();
if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {
@@ -2541,12 +2559,8 @@ files_checked:
respective file pages, for the last batch of
recv_group_scan_log_recs(). */
- err = recv_apply_hashed_log_recs(TRUE);
+ recv_apply_hashed_log_recs(true);
DBUG_PRINT("ib_log", ("apply completed"));
-
- if (err != DB_SUCCESS) {
- return(err);
- }
}
if (!srv_read_only_mode) {
@@ -2860,11 +2874,14 @@ files_checked:
thread_started[3 + SRV_MAX_N_IO_THREADS] = true;
/* Create the thread which prints InnoDB monitor info */
- srv_monitor_active = true;
- thread_handles[4 + SRV_MAX_N_IO_THREADS] = os_thread_create(
- srv_monitor_thread,
- NULL, thread_ids + 4 + SRV_MAX_N_IO_THREADS);
- thread_started[4 + SRV_MAX_N_IO_THREADS] = true;
+ if (!thread_started[4 + SRV_MAX_N_IO_THREADS]) {
+ /* srv_monitor_thread not yet started */
+ srv_monitor_active = true;
+ thread_handles[4 + SRV_MAX_N_IO_THREADS] = os_thread_create(
+ srv_monitor_thread,
+ NULL, thread_ids + 4 + SRV_MAX_N_IO_THREADS);
+ thread_started[4 + SRV_MAX_N_IO_THREADS] = true;
+ }
}
/* Create the SYS_FOREIGN and SYS_FOREIGN_COLS system tables */
@@ -3235,6 +3252,7 @@ innobase_shutdown_for_mysql(void)
btr_search_disable();
ibuf_close();
+ log_online_shutdown();
log_shutdown();
trx_sys_file_format_close();
trx_sys_close();
diff --git a/storage/xtradb/sync/sync0sync.cc b/storage/xtradb/sync/sync0sync.cc
index f3516958d89..6692eef9fb0 100644
--- a/storage/xtradb/sync/sync0sync.cc
+++ b/storage/xtradb/sync/sync0sync.cc
@@ -2,7 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
-Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
+Copyright (c) 2017, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
diff --git a/storage/xtradb/trx/trx0sys.cc b/storage/xtradb/trx/trx0sys.cc
index 45315d35c42..1b4d4f00074 100644
--- a/storage/xtradb/trx/trx0sys.cc
+++ b/storage/xtradb/trx/trx0sys.cc
@@ -1383,6 +1383,33 @@ trx_sys_close(void)
trx_sys = NULL;
}
+/** @brief Convert an undo log to TRX_UNDO_PREPARED state on shutdown.
+
+If any prepared ACTIVE transactions exist, and their rollback was
+prevented by innodb_force_recovery, we convert these transactions to
+XA PREPARE state in the main-memory data structures, so that shutdown
+will proceed normally. These transactions will again recover as ACTIVE
+on the next restart, and they will be rolled back unless
+innodb_force_recovery prevents it again.
+
+@param[in] trx transaction
+@param[in,out] undo undo log to convert to TRX_UNDO_PREPARED */
+static
+void
+trx_undo_fake_prepared(
+ const trx_t* trx,
+ trx_undo_t* undo)
+{
+ ut_ad(srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
+ ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
+ ut_ad(trx->is_recovered);
+
+ if (undo != NULL) {
+ ut_ad(undo->state == TRX_UNDO_ACTIVE);
+ undo->state = TRX_UNDO_PREPARED;
+ }
+}
+
/*********************************************************************
Check if there are any active (non-prepared) transactions.
@return total number of active transactions or 0 if none */
@@ -1391,15 +1418,42 @@ ulint
trx_sys_any_active_transactions(void)
/*=================================*/
{
- ulint total_trx = 0;
-
mutex_enter(&trx_sys->mutex);
- total_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list)
- + UT_LIST_GET_LEN(trx_sys->mysql_trx_list);
+ ulint total_trx = UT_LIST_GET_LEN(trx_sys->mysql_trx_list);
+
+ if (total_trx == 0) {
+ total_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
+ ut_a(total_trx >= trx_sys->n_prepared_trx);
+
+ if (total_trx > trx_sys->n_prepared_trx
+ && srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO) {
+ for (trx_t* trx = UT_LIST_GET_FIRST(
+ trx_sys->rw_trx_list);
+ trx != NULL;
+ trx = UT_LIST_GET_NEXT(trx_list, trx)) {
+ if (!trx_state_eq(trx, TRX_STATE_ACTIVE)
+ || !trx->is_recovered) {
+ continue;
+ }
+ /* This was a recovered transaction
+ whose rollback was disabled by
+ the innodb_force_recovery setting.
+ Pretend that it is in XA PREPARE
+ state so that shutdown will work. */
+ trx_undo_fake_prepared(
+ trx, trx->insert_undo);
+ trx_undo_fake_prepared(
+ trx, trx->update_undo);
+ trx->state = TRX_STATE_PREPARED;
+ trx_sys->n_prepared_trx++;
+ trx_sys->n_prepared_recovered_trx++;
+ }
+ }
- ut_a(total_trx >= trx_sys->n_prepared_trx);
- total_trx -= trx_sys->n_prepared_trx;
+ ut_a(total_trx >= trx_sys->n_prepared_trx);
+ total_trx -= trx_sys->n_prepared_trx;
+ }
mutex_exit(&trx_sys->mutex);
diff --git a/support-files/build-tags b/support-files/build-tags
index 87f320ba5f5..03b243ee8cc 100755
--- a/support-files/build-tags
+++ b/support-files/build-tags
@@ -8,9 +8,9 @@ then
echo client storage dbug libmysql sql-common \
sql extra mysys mysys_ssl strings regex pcre vio include \
tools unittest plugin libmysqld | \
- xargs -n1 git ls-files | \
+ xargs -n1 git ls-files | grep -v '\.jar$' | \
xargs etags -o TAGS --append
else
- find . -type f |
+ find . -type f ! -name "*.jar" |
xargs etags -o TAGS --append
fi
diff --git a/support-files/rpm/server-postin.sh b/support-files/rpm/server-postin.sh
index f6b7a9ba8b6..08b046dc272 100644
--- a/support-files/rpm/server-postin.sh
+++ b/support-files/rpm/server-postin.sh
@@ -74,36 +74,9 @@ fi
SETARGETDIR=/etc/selinux/targeted/src/policy
SEDOMPROG=$SETARGETDIR/domains/program
SECONPROG=$SETARGETDIR/file_contexts/program
-if [ -f /etc/redhat-release ] ; then
- if grep '\(Red Hat Enterprise Linux ..\|CentOS\) release 4' \
- /etc/redhat-release >/dev/null 2>&1; then
- echo
- echo
- echo 'Notes regarding SELinux on this platform:'
- echo '========================================='
- echo
- echo 'The default policy might cause server startup to fail because it is '
- echo 'not allowed to access critical files. In this case, please update '
- echo 'your installation. '
- echo
- echo 'The default policy might also cause inavailability of SSL related '
- echo 'features because the server is not allowed to access /dev/random '
- echo 'and /dev/urandom. If this is a problem, please do the following: '
- echo
- echo ' 1) install selinux-policy-targeted-sources from your OS vendor'
- echo ' 2) add the following two lines to '$SEDOMPROG/mysqld.te':'
- echo ' allow mysqld_t random_device_t:chr_file read;'
- echo ' allow mysqld_t urandom_device_t:chr_file read;'
- echo ' 3) cd to '$SETARGETDIR' and issue the following command:'
- echo ' make load'
- echo
- echo
- fi
- if grep 'CentOS release 6' /etc/redhat-release >/dev/null 2>&1; then
- if [ -x /usr/sbin/semodule ] ; then
- /usr/sbin/semodule -i /usr/share/mysql/policy/selinux/mariadb.pp
- fi
- fi
+
+if [ -x /usr/sbin/semodule ] ; then
+ /usr/sbin/semodule -i /usr/share/mysql/policy/selinux/mariadb.pp
fi
if [ -x sbin/restorecon ] ; then
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index ad8933118cc..0eb8aef95c4 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -1,5 +1,5 @@
-/* Copyright (c) 2002, 2012, Oracle and/or its affiliates.
- Copyright (c) 2008, 2013, Monty Program Ab
+/* Copyright (c) 2002, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2008, 2017, MariaDB
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
@@ -19344,6 +19344,49 @@ static void test_mdev4326()
}
+/**
+ BUG#17512527: LIST HANDLING INCORRECT IN MYSQL_PRUNE_STMT_LIST()
+*/
+static void test_bug17512527()
+{
+ MYSQL *conn;
+ MYSQL_STMT *stmt1, *stmt2;
+ unsigned long thread_id;
+ char query[MAX_TEST_QUERY_LENGTH];
+ int rc;
+
+ conn= client_connect(0, MYSQL_PROTOCOL_SOCKET, 1);
+
+ stmt1 = mysql_stmt_init(conn);
+ check_stmt(stmt1);
+ rc= mysql_stmt_prepare(stmt1, STRING_WITH_LEN("SELECT 1"));
+ check_execute(stmt1, rc);
+
+ stmt2 = mysql_stmt_init(conn);
+ check_stmt(stmt2);
+
+ thread_id= mysql_thread_id(conn);
+ sprintf(query, "KILL %lu", thread_id);
+ if (thread_query(query))
+ exit(1);
+
+ rc= mysql_stmt_prepare(stmt2, STRING_WITH_LEN("SELECT 2"));
+ check_execute(stmt2, rc);
+
+ rc= mysql_stmt_execute(stmt1);
+ check_execute_r(stmt1, rc);
+
+ rc= mysql_stmt_execute(stmt2);
+ check_execute(stmt2, rc);
+
+ mysql_close(conn);
+
+ mysql_stmt_close(stmt2);
+ mysql_stmt_close(stmt1);
+}
+
+
+
/*
Check compressed protocol
*/
@@ -19715,6 +19758,9 @@ static struct my_tests_st my_tests[]= {
{ "test_bug13001491", test_bug13001491 },
{ "test_mdev4326", test_mdev4326 },
{ "test_ps_sp_out_params", test_ps_sp_out_params },
+#ifndef _WIN32
+ { "test_bug17512527", test_bug17512527},
+#endif
{ "test_compressed_protocol", test_compressed_protocol },
{ "test_big_packet", test_big_packet },
{ 0, 0 }