From 0248fb2e8a3c6a02f443140cfcf68a0190354e23 Mon Sep 17 00:00:00 2001 From: Chaithra Gopalareddy Date: Thu, 18 Aug 2016 09:56:48 +0530 Subject: [PATCH 01/73] Bug #23135667: CRASH AFTER DEEPLY NESTED BUILD_EQUAL_ITEMS_FOR_COND Problem: When build_equal_items_for_cond gets called for a big query recursively, the specified thread_stack_size exceeds. But optimizer does not handle this condition. As a result, server exits. Solution: Check if we exceed specified stack size and if yes exit gracefully by throwing an error. --- sql/sql_select.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index fb705e9ba6ad2..80d4b87e91690 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8154,6 +8154,9 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond, COND_EQUAL cond_equal; cond_equal.upper_levels= inherited; + if (check_stack_overrun(thd, STACK_MIN_SIZE, NULL)) + return cond; // Fatal error flag is set! + if (cond->type() == Item::COND_ITEM) { List eq_list; From 04bad164e7d4bad2b2ed63485ea6361029392d68 Mon Sep 17 00:00:00 2001 From: "mysql-builder@oracle.com" <> Date: Thu, 18 Aug 2016 12:12:09 +0530 Subject: [PATCH 02/73] From 8b1f4d84cafe393e92f942278f9f020a62ceb5b9 Mon Sep 17 00:00:00 2001 From: Terje Rosten Date: Fri, 12 Aug 2016 12:38:20 +0200 Subject: [PATCH 03/73] Bug#24464380 PRIVILEGE ESCALATION USING MYSQLD_SAFE Argument to malloc-lib must be included in restricted list of directories, symlink guards added, and mysqld and mysqld-version options restricted to command line only. Don't redirect errors to stderr. --- packaging/rpm-oel/mysql.init | 2 +- packaging/rpm-sles/mysql.init | 2 +- scripts/mysqld_safe.sh | 79 +++++++++++++++++++++-------------- support-files/mysql.server.sh | 2 +- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/packaging/rpm-oel/mysql.init b/packaging/rpm-oel/mysql.init index 262d0582f68b4..aaea498d15339 100644 --- a/packaging/rpm-oel/mysql.init +++ b/packaging/rpm-oel/mysql.init @@ -102,7 +102,7 @@ start(){ # alarms, per bug #547485 $exec --datadir="$datadir" --socket="$socketfile" \ --pid-file="$mypidfile" \ - --basedir=/usr --user=mysql >/dev/null 2>&1 & + --basedir=/usr --user=mysql >/dev/null & safe_pid=$! # Spin for a maximum of N seconds waiting for the server to come up; # exit the loop immediately if mysqld_safe process disappears. diff --git a/packaging/rpm-sles/mysql.init b/packaging/rpm-sles/mysql.init index 50ca4c9033c75..dda0bebba5658 100644 --- a/packaging/rpm-sles/mysql.init +++ b/packaging/rpm-sles/mysql.init @@ -137,7 +137,7 @@ start () { rc_failed 6 ; rc_status -v ; rc_exit fi - $PROG --basedir=/usr --datadir="$datadir" --pid-file="$pidfile" >/dev/null 2>&1 & + $PROG --basedir=/usr --datadir="$datadir" --pid-file="$pidfile" >/dev/null & if pinger $! ; then echo -n "Starting service MySQL:" touch $lockfile diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index f705953059e0b..11b692ec92874 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -208,8 +208,17 @@ parse_arguments() { --core-file-size=*) core_file_size="$val" ;; --ledir=*) ledir="$val" ;; --malloc-lib=*) set_malloc_lib "$val" ;; - --mysqld=*) MYSQLD="$val" ;; + --mysqld=*) + if [ -z "$pick_args" ]; then + log_error "--mysqld option can only be used as command line option, found in config file" + exit 1 + fi + MYSQLD="$val" ;; --mysqld-version=*) + if [ -z "$pick_args" ]; then + log_error "--mysqld-version option can only be used as command line option, found in config file" + exit 1 + fi if test -n "$val" then MYSQLD="mysqld-$val" @@ -297,38 +306,22 @@ mysqld_ld_preload_text() { echo "$text" } - -mysql_config= -get_mysql_config() { - if [ -z "$mysql_config" ]; then - mysql_config=`echo "$0" | sed 's,/[^/][^/]*$,/mysql_config,'` - if [ ! -x "$mysql_config" ]; then - log_error "Can not run mysql_config $@ from '$mysql_config'" - exit 1 - fi - fi - - "$mysql_config" "$@" -} - - # set_malloc_lib LIB # - If LIB is empty, do nothing and return -# - If LIB is 'tcmalloc', look for tcmalloc shared library in /usr/lib -# then pkglibdir. tcmalloc is part of the Google perftools project. +# - If LIB is 'tcmalloc', look for tcmalloc shared library in $malloc_dirs. +# tcmalloc is part of the Google perftools project. # - If LIB is an absolute path, assume it is a malloc shared library # # Put LIB in mysqld_ld_preload, which will be added to LD_PRELOAD when # running mysqld. See ld.so for details. set_malloc_lib() { + # This list is kept intentionally simple. + malloc_dirs="/usr/lib /usr/lib64 /usr/lib/i386-linux-gnu /usr/lib/x86_64-linux-gnu" malloc_lib="$1" if [ "$malloc_lib" = tcmalloc ]; then - pkglibdir=`get_mysql_config --variable=pkglibdir` malloc_lib= - # This list is kept intentionally simple. Simply set --malloc-lib - # to a full path if another location is desired. - for libdir in /usr/lib "$pkglibdir" "$pkglibdir/mysql"; do + for libdir in $(echo $malloc_dirs); do for flavor in _minimal '' _and_profiler _debug; do tmp="$libdir/libtcmalloc$flavor.so" #log_notice "DEBUG: Checking for malloc lib '$tmp'" @@ -339,7 +332,7 @@ set_malloc_lib() { done if [ -z "$malloc_lib" ]; then - log_error "no shared library for --malloc-lib=tcmalloc found in /usr/lib or $pkglibdir" + log_error "no shared library for --malloc-lib=tcmalloc found in $malloc_dirs" exit 1 fi fi @@ -350,9 +343,21 @@ set_malloc_lib() { case "$malloc_lib" in /*) if [ ! -r "$malloc_lib" ]; then - log_error "--malloc-lib '$malloc_lib' can not be read and will not be used" + log_error "--malloc-lib can not be read and will not be used" exit 1 fi + + # Restrict to a the list in $malloc_dirs above + case "$(dirname "$malloc_lib")" in + /usr/lib) ;; + /usr/lib64) ;; + /usr/lib/i386-linux-gnu) ;; + /usr/lib/x86_64-linux-gnu) ;; + *) + log_error "--malloc-lib must be located in one of the directories: $malloc_dirs" + exit 1 + ;; + esac ;; *) log_error "--malloc-lib must be an absolute path or 'tcmalloc'; " \ @@ -569,7 +574,7 @@ then log_notice "Logging to '$err_log'." logging=file - if [ ! -f "$err_log" ]; then # if error log already exists, + if [ ! -f "$err_log" -a ! -h "$err_log" ]; then # if error log already exists, touch "$err_log" # we just append. otherwise, chmod "$fmode" "$err_log" # fix the permissions here! fi @@ -594,7 +599,7 @@ then USER_OPTION="--user=$user" fi # Change the err log to the right user, if it is in use - if [ $want_syslog -eq 0 ]; then + if [ $want_syslog -eq 0 -a ! -h "$err_log" ]; then touch "$err_log" chown $user "$err_log" fi @@ -614,9 +619,11 @@ safe_mysql_unix_port=${mysql_unix_port:-${MYSQL_UNIX_PORT:-@MYSQL_UNIX_ADDR@}} mysql_unix_port_dir=`dirname $safe_mysql_unix_port` if [ ! -d $mysql_unix_port_dir ] then - mkdir $mysql_unix_port_dir - chown $user $mysql_unix_port_dir - chmod 755 $mysql_unix_port_dir + if [ ! -h $mysql_unix_port_dir ]; then + mkdir $mysql_unix_port_dir + chown $user $mysql_unix_port_dir + chmod 755 $mysql_unix_port_dir + fi fi # If the user doesn't specify a binary, we assume name "mysqld" @@ -728,7 +735,9 @@ then exit 1 fi fi - rm -f "$pid_file" + if [ ! -h "$pid_file" ]; then + rm -f "$pid_file" + fi if test -f "$pid_file" then log_error "Fatal error: Can't remove the pid file: @@ -779,13 +788,19 @@ have_sleep=1 while true do - rm -f $safe_mysql_unix_port "$pid_file" # Some extra safety + # Some extra safety + if [ ! -h "$safe_mysql_unix_port" ]; then + rm -f "$safe_mysql_unix_port" + fi + if [ ! -h "$pid_file" ]; then + rm -f "$pid_file" + fi start_time=`date +%M%S` eval_log_error "$cmd" - if [ $want_syslog -eq 0 -a ! -f "$err_log" ]; then + if [ $want_syslog -eq 0 -a ! -f "$err_log" -a ! -h "$err_log" ]; then touch "$err_log" # hypothetical: log was renamed but not chown $user "$err_log" # flushed yet. we'd recreate it with chmod "$fmode" "$err_log" # wrong owner next time we log, so set diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index 7487d5acc0f9d..909d33f877085 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -280,7 +280,7 @@ case "$mode" in then # Give extra arguments to mysqld with the my.cnf file. This script # may be overwritten at next upgrade. - $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 & + $bindir/mysqld_safe --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null & wait_for_pid created "$!" "$mysqld_pid_file_path"; return_value=$? # Make lock for RedHat / SuSE From 033b11912121ad2c1dbd4a93202eeac196124801 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Tue, 16 Aug 2016 15:35:19 +0200 Subject: [PATCH 04/73] Bug#24388746: PRIVILEGE ESCALATION AND RACE CONDITION USING CREATE TABLE During REPAIR TABLE of a MyISAM table, a temporary data file (.TMD) is created. When repair finishes, this file is renamed to the original .MYD file. The problem was that during this rename, we copied the stats from the old file to the new file with chmod/chown. If a user managed to replace the temporary file before chmod/chown was executed, it was possible to get an arbitrary file with the privileges of the mysql user. This patch fixes the problem by not copying stats from the old file to the new file. This is not needed as the new file was created with the correct stats. This fix only changes server behavior - external utilities such as myisamchk still does chmod/chown. No test case provided since the problem involves synchronization with file system operations. --- include/my_sys.h | 3 ++- include/myisam.h | 11 +++++----- mysys/my_redel.c | 12 ++++++++--- storage/myisam/ha_myisam.cc | 26 ++++++++++++++++++----- storage/myisam/mi_check.c | 41 ++++++++++++++++++++++++++----------- storage/myisam/myisamchk.c | 16 +++++++++------ 6 files changed, 77 insertions(+), 32 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index b1b8bf15be328..472c2ba5ca069 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 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 @@ -83,6 +83,7 @@ typedef struct my_aio_result { #define MY_RESOLVE_LINK 128 /* my_realpath(); Only resolve links */ #define MY_HOLD_ORIGINAL_MODES 128 /* my_copy() holds to file modes */ #define MY_REDEL_MAKE_BACKUP 256 +#define MY_REDEL_NO_COPY_STAT 512 /* my_redel() doesn't call my_copystat() */ #define MY_SEEK_NOT_DONE 32 /* my_lock may have to do a seek */ #define MY_DONT_WAIT 64 /* my_lock() don't wait if can't lock */ #define MY_ZEROFILL 32 /* my_malloc(), fill array with zero */ diff --git a/include/myisam.h b/include/myisam.h index 85d37a81bc699..a9fcd7e436975 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 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 @@ -426,12 +426,13 @@ int chk_size(MI_CHECK *param, MI_INFO *info); int chk_key(MI_CHECK *param, MI_INFO *info); int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend); int mi_repair(MI_CHECK *param, register MI_INFO *info, - char * name, int rep_quick); -int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name); + char * name, int rep_quick, my_bool no_copy_stat); +int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name, + my_bool no_copy_stat); int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, - const char * name, int rep_quick); + const char * name, int rep_quick, my_bool no_copy_stat); int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, - const char * name, int rep_quick); + const char * name, int rep_quick, my_bool no_copy_stat); int change_to_newfile(const char * filename, const char * old_ext, const char * new_ext, myf myflags); int lock_file(MI_CHECK *param, File file, my_off_t start, int lock_type, diff --git a/mysys/my_redel.c b/mysys/my_redel.c index a47df8265c808..25391cd4e8fa0 100644 --- a/mysys/my_redel.c +++ b/mysys/my_redel.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 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 @@ -35,6 +35,9 @@ struct utimbuf { if MY_REDEL_MAKE_COPY is given, then the orginal file is renamed to org_name-'current_time'.BAK + + if MY_REDEL_NO_COPY_STAT is given, stats are not copied + from org_name to tmp_name. */ #define REDEL_EXT ".BAK" @@ -46,8 +49,11 @@ int my_redel(const char *org_name, const char *tmp_name, myf MyFlags) DBUG_PRINT("my",("org_name: '%s' tmp_name: '%s' MyFlags: %d", org_name,tmp_name,MyFlags)); - if (my_copystat(org_name,tmp_name,MyFlags) < 0) - goto end; + if (!(MyFlags & MY_REDEL_NO_COPY_STAT)) + { + if (my_copystat(org_name,tmp_name,MyFlags) < 0) + goto end; + } if (MyFlags & MY_REDEL_MAKE_BACKUP) { char name_buff[FN_REFLEN+20]; diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 602a0ae6cc11f..21cbef32188ef 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 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 @@ -1091,24 +1091,36 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool do_optimize) /* TODO: respect myisam_repair_threads variable */ my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map)); thd_proc_info(thd, buf); + /* + The new file is created with the right stats, so we can skip + copying file stats from old to new. + */ error = mi_repair_parallel(¶m, file, fixed_name, - param.testflag & T_QUICK); + param.testflag & T_QUICK, TRUE); thd_proc_info(thd, "Repair done"); // to reset proc_info, as // it was pointing to local buffer } else { thd_proc_info(thd, "Repair by sorting"); + /* + The new file is created with the right stats, so we can skip + copying file stats from old to new. + */ error = mi_repair_by_sort(¶m, file, fixed_name, - param.testflag & T_QUICK); + param.testflag & T_QUICK, TRUE); } } else { thd_proc_info(thd, "Repair with keycache"); param.testflag &= ~T_REP_BY_SORT; + /* + The new file is created with the right stats, so we can skip + copying file stats from old to new. + */ error= mi_repair(¶m, file, fixed_name, - param.testflag & T_QUICK); + param.testflag & T_QUICK, TRUE); } #ifdef HAVE_MMAP if (remap) @@ -1124,7 +1136,11 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool do_optimize) { optimize_done=1; thd_proc_info(thd, "Sorting index"); - error=mi_sort_index(¶m,file,fixed_name); + /* + The new file is created with the right stats, so we can skip + copying file stats from old to new. + */ + error=mi_sort_index(¶m,file,fixed_name, TRUE); } if (!statistics_done && (local_testflag & T_STATISTICS)) { diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index ba1f975549a1c..fe0d4c9c30b47 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 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 @@ -1512,7 +1512,7 @@ static int mi_drop_all_indexes(MI_CHECK *param, MI_INFO *info, my_bool force) /* Save new datafile-name in temp_filename */ int mi_repair(MI_CHECK *param, register MI_INFO *info, - char * name, int rep_quick) + char * name, int rep_quick, my_bool no_copy_stat) { int error,got_error; ha_rows start_records,new_header_length; @@ -1726,6 +1726,11 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info, /* Replace the actual file with the temporary file */ if (new_file >= 0) { + myf flags= 0; + if (param->testflag & T_BACKUP_DATA) + flags |= MY_REDEL_MAKE_BACKUP; + if (no_copy_stat) + flags |= MY_REDEL_NO_COPY_STAT; mysql_file_close(new_file, MYF(0)); info->dfile=new_file= -1; /* @@ -1744,8 +1749,7 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info, info->s->file_map= NULL; } if (change_to_newfile(share->data_file_name, MI_NAME_DEXT, DATA_TMP_EXT, - (param->testflag & T_BACKUP_DATA ? - MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) || + flags) || mi_open_datafile(info,share,name,-1)) got_error=1; @@ -1933,7 +1937,8 @@ int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, File file) /* Sort index for more efficent reads */ -int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name) +int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name, + my_bool no_copy_stat) { reg2 uint key; reg1 MI_KEYDEF *keyinfo; @@ -2004,7 +2009,7 @@ int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name) share->kfile = -1; (void) mysql_file_close(new_file, MYF(MY_WME)); if (change_to_newfile(share->index_file_name, MI_NAME_IEXT, INDEX_TMP_EXT, - MYF(0)) || + no_copy_stat ? MYF(MY_REDEL_NO_COPY_STAT) : MYF(0)) || mi_open_keyfile(share)) goto err2; info->lock_type= F_UNLCK; /* Force mi_readinfo to lock */ @@ -2209,6 +2214,8 @@ int filecopy(MI_CHECK *param, File to,File from,my_off_t start, info MyISAM handler to repair name Name of table (for warnings) rep_quick set to <> 0 if we should not change data file + no_copy_stat Don't copy file stats from old to new file, + assume that new file was created with correct stats RESULT 0 ok @@ -2216,7 +2223,7 @@ int filecopy(MI_CHECK *param, File to,File from,my_off_t start, */ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, - const char * name, int rep_quick) + const char * name, int rep_quick, my_bool no_copy_stat) { int got_error; uint i; @@ -2543,11 +2550,15 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, /* Replace the actual file with the temporary file */ if (new_file >= 0) { + myf flags= 0; + if (param->testflag & T_BACKUP_DATA) + flags |= MY_REDEL_MAKE_BACKUP; + if (no_copy_stat) + flags |= MY_REDEL_NO_COPY_STAT; mysql_file_close(new_file, MYF(0)); info->dfile=new_file= -1; if (change_to_newfile(share->data_file_name,MI_NAME_DEXT, DATA_TMP_EXT, - (param->testflag & T_BACKUP_DATA ? - MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) || + flags) || mi_open_datafile(info,share,name,-1)) got_error=1; } @@ -2595,6 +2606,8 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, info MyISAM handler to repair name Name of table (for warnings) rep_quick set to <> 0 if we should not change data file + no_copy_stat Don't copy file stats from old to new file, + assume that new file was created with correct stats DESCRIPTION Same as mi_repair_by_sort but do it multithreaded @@ -2629,7 +2642,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, */ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, - const char * name, int rep_quick) + const char * name, int rep_quick, my_bool no_copy_stat) { int got_error; uint i,key, total_key_length, istep; @@ -3076,11 +3089,15 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, /* Replace the actual file with the temporary file */ if (new_file >= 0) { + myf flags= 0; + if (param->testflag & T_BACKUP_DATA) + flags |= MY_REDEL_MAKE_BACKUP; + if (no_copy_stat) + flags |= MY_REDEL_NO_COPY_STAT; mysql_file_close(new_file, MYF(0)); info->dfile=new_file= -1; if (change_to_newfile(share->data_file_name, MI_NAME_DEXT, DATA_TMP_EXT, - (param->testflag & T_BACKUP_DATA ? - MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) || + flags) || mi_open_datafile(info,share,name,-1)) got_error=1; } diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c index 8606bd7c74868..9360a0548726a 100644 --- a/storage/myisam/myisamchk.c +++ b/storage/myisam/myisamchk.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 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 @@ -993,14 +993,18 @@ static int myisamchk(MI_CHECK *param, char * filename) info->s->state.key_map, param->force_sort)) { + /* + The new file might not be created with the right stats depending + on how myisamchk is run, so we must copy file stats from old to new. + */ if (param->testflag & T_REP_BY_SORT) - error=mi_repair_by_sort(param,info,filename,rep_quick); + error= mi_repair_by_sort(param, info, filename, rep_quick, FALSE); else - error=mi_repair_parallel(param,info,filename,rep_quick); + error= mi_repair_parallel(param, info, filename, rep_quick, FALSE); state_updated=1; } else if (param->testflag & T_REP_ANY) - error=mi_repair(param, info,filename,rep_quick); + error= mi_repair(param, info, filename, rep_quick, FALSE); } if (!error && param->testflag & T_SORT_RECORDS) { @@ -1040,12 +1044,12 @@ static int myisamchk(MI_CHECK *param, char * filename) { if (param->verbose) puts("Table had a compressed index; We must now recreate the index"); - error=mi_repair_by_sort(param,info,filename,1); + error= mi_repair_by_sort(param, info, filename, 1, FALSE); } } } if (!error && param->testflag & T_SORT_INDEX) - error=mi_sort_index(param,info,filename); + error= mi_sort_index(param, info, filename, FALSE); if (!error) share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED | STATE_CRASHED_ON_REPAIR); From 8dc642112c83c73969f37dbb12b9fe8f546fd42a Mon Sep 17 00:00:00 2001 From: Sivert Sorumgard Date: Mon, 22 Aug 2016 14:30:02 +0200 Subject: [PATCH 05/73] Bug#24388753: PRIVILEGE ESCALATION USING MYSQLD_SAFE [This is the 5.5/5.6 version of the bugfix]. The problem was that it was possible to write log files ending in .ini/.cnf that later could be parsed as an options file. This made it possible for users to specify startup options without the permissions to do so. This patch fixes the problem by disallowing general query log and slow query log to be written to files ending in .ini and .cnf. --- sql/log.cc | 89 +++++++++++++++++++++++++++++++++++++++++++++++-- sql/log.h | 10 ++++++ sql/mysqld.cc | 18 +++++++++- sql/sys_vars.cc | 25 +++++++++----- 4 files changed, 131 insertions(+), 11 deletions(-) diff --git a/sql/log.cc b/sql/log.cc index 50d7762af6d7a..493aae8f2ff17 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2293,6 +2293,77 @@ bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name, } +bool is_valid_log_name(const char *name, size_t len) +{ + if (len > 3) + { + const char *tail= name + len - 4; + if (my_strcasecmp(system_charset_info, tail, ".ini") == 0 || + my_strcasecmp(system_charset_info, tail, ".cnf") == 0) + { + return false; + } + } + return true; +} + + +/** + Get the real log file name, and possibly reopen file. + + Use realpath() to get the path with symbolic links + expanded. Then, close the file, and reopen the real path using the + O_NOFOLLOW flag. This will reject following symbolic links. + + @param file File descriptor. + @param log_file_key Key for P_S instrumentation. + @param open_flags Flags to use for opening the file. + @param opened_file_name Name of the open fd. + + @retval file descriptor to open file with 'real_file_name', or '-1' + in case of errors. +*/ + +#ifndef _WIN32 +static File mysql_file_real_name_reopen(File file, +#ifdef HAVE_PSI_INTERFACE + PSI_file_key log_file_key, +#endif + int open_flags, + const char *opened_file_name) +{ + DBUG_ASSERT(file); + DBUG_ASSERT(opened_file_name); + + /* Buffer for realpath must have capacity for PATH_MAX. */ + char real_file_name[PATH_MAX]; + + /* Get realpath, validate, open realpath with O_NOFOLLOW. */ + if (realpath(opened_file_name, real_file_name) == NULL) + { + (void) mysql_file_close(file, MYF(0)); + return -1; + } + + if (mysql_file_close(file, MYF(0))) + return -1; + + if (strlen(real_file_name) > FN_REFLEN) + return -1; + + if (!is_valid_log_name(real_file_name, strlen(real_file_name))) + { + sql_print_error("Invalid log file name after expanding symlinks: '%s'", + real_file_name); + return -1; + } + + return mysql_file_open(log_file_key, real_file_name, + open_flags | O_NOFOLLOW, + MYF(MY_WME | ME_WAITTANG)); +} +#endif // _WIN32 + /* Open a (new) log file. @@ -2358,8 +2429,22 @@ bool MYSQL_LOG::open( if ((file= mysql_file_open(log_file_key, log_file_name, open_flags, - MYF(MY_WME | ME_WAITTANG))) < 0 || - init_io_cache(&log_file, file, IO_SIZE, io_cache_type, + MYF(MY_WME | ME_WAITTANG))) < 0) + goto err; + +#ifndef _WIN32 + /* Reopen and validate path. */ + if ((log_type_arg == LOG_UNKNOWN || log_type_arg == LOG_NORMAL) && + (file= mysql_file_real_name_reopen(file, +#ifdef HAVE_PSI_INTERFACE + log_file_key, +#endif + open_flags, + log_file_name)) < 0) + goto err; +#endif // _WIN32 + + if (init_io_cache(&log_file, file, IO_SIZE, io_cache_type, mysql_file_tell(file, MYF(MY_WME)), 0, MYF(MY_WME | MY_NABP | ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0)))) diff --git a/sql/log.h b/sql/log.h index b5e751386a60d..d3ecba419645f 100644 --- a/sql/log.h +++ b/sql/log.h @@ -717,6 +717,16 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, char *make_log_name(char *buff, const char *name, const char* log_ext); +/** + Check given log name against certain blacklisted names/extensions. + + @param name Log name to check + @param len Length of log name + + @returns true if name is valid, false otherwise. +*/ +bool is_valid_log_name(const char *name, size_t len); + extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log; extern LOGGER logger; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a2532ceddd396..e979ea1b731a9 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify @@ -3512,6 +3512,22 @@ static int init_common_variables() "--log-slow-queries option, log tables are used. " "To enable logging to files use the --log-output=file option."); + if (opt_logname && + !is_valid_log_name(opt_logname, strlen(opt_logname))) + { + sql_print_error("Invalid value for --general_log_file: %s", + opt_logname); + return 1; + } + + if (opt_slow_logname && + !is_valid_log_name(opt_slow_logname, strlen(opt_slow_logname))) + { + sql_print_error("Invalid value for --slow_query_log_file: %s", + opt_slow_logname); + return 1; + } + #define FIX_LOG_VAR(VAR, ALT) \ if (!VAR || !*VAR) \ { \ diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index b0fa7f9a341fb..d08cb4f8ca838 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2002, 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 @@ -2810,6 +2810,14 @@ static bool check_log_path(sys_var *self, THD *thd, set_var *var) if (!var->save_result.string_value.str) return true; + if (!is_valid_log_name(var->save_result.string_value.str, + var->save_result.string_value.length)) + { + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), + self->name.str, var->save_result.string_value.str); + return true; + } + if (var->save_result.string_value.length > FN_REFLEN) { // path is too long my_error(ER_PATH_LENGTH, MYF(0), self->name.str); @@ -2856,7 +2864,7 @@ static bool check_log_path(sys_var *self, THD *thd, set_var *var) return false; } static bool fix_log(char** logname, const char* default_logname, - const char*ext, bool enabled, void (*reopen)(char*)) + const char*ext, bool enabled, bool (*reopen)(char*)) { if (!*logname) // SET ... = DEFAULT { @@ -2868,16 +2876,17 @@ static bool fix_log(char** logname, const char* default_logname, } logger.lock_exclusive(); mysql_mutex_unlock(&LOCK_global_system_variables); + bool error= false; if (enabled) - reopen(*logname); + error= reopen(*logname); logger.unlock(); mysql_mutex_lock(&LOCK_global_system_variables); - return false; + return error; } -static void reopen_general_log(char* name) +static bool reopen_general_log(char* name) { logger.get_log_file_handler()->close(0); - logger.get_log_file_handler()->open_query_log(name); + return logger.get_log_file_handler()->open_query_log(name); } static bool fix_general_log_file(sys_var *self, THD *thd, enum_var_type type) { @@ -2890,10 +2899,10 @@ static Sys_var_charptr Sys_general_log_path( IN_FS_CHARSET, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_log_path), ON_UPDATE(fix_general_log_file)); -static void reopen_slow_log(char* name) +static bool reopen_slow_log(char* name) { logger.get_slow_log_file_handler()->close(0); - logger.get_slow_log_file_handler()->open_slow_log(name); + return logger.get_slow_log_file_handler()->open_slow_log(name); } static bool fix_slow_log_file(sys_var *self, THD *thd, enum_var_type type) { From 55a2babcefc9a9f46534f8e6e4b5ca12d94c1105 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Fri, 19 Aug 2016 12:06:16 +0200 Subject: [PATCH 06/73] Bug#24400628: DEBUG ASSETION KICKS IN WHEN LONG SUBPARTITION NAME IS USED IN CREATE TABLE The problem was that using a very long subpartition name could lead to the server exiting abnormally. This patch fixes the problem by reporting ER_TOO_LONG_IDENT if a name with more than 64 characters are used as partition and subpartition name. --- sql/sql_yacc.yy | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index b8ddc8bd49fc5..2ca36e23652b4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4655,6 +4655,12 @@ part_name: { partition_info *part_info= Lex->part_info; partition_element *p_elem= part_info->curr_part_elem; + if (check_string_char_length(&$1, "", NAME_CHAR_LEN, + system_charset_info, true)) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), $1.str); + MYSQL_YYABORT; + } p_elem->partition_name= $1.str; } ; @@ -4949,7 +4955,15 @@ sub_part_definition: sub_name: ident_or_text - { Lex->part_info->curr_part_elem->partition_name= $1.str; } + { + if (check_string_char_length(&$1, "", NAME_CHAR_LEN, + system_charset_info, true)) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), $1.str); + MYSQL_YYABORT; + } + Lex->part_info->curr_part_elem->partition_name= $1.str; + } ; opt_part_options: From 97fad8518bdce19938fdf55cbb5858e31e9ac464 Mon Sep 17 00:00:00 2001 From: Kailasnath Nagarkar Date: Fri, 26 Aug 2016 11:11:27 +0530 Subject: [PATCH 07/73] Bug #23303485 : HANDLE_FATAL_SIGNAL (SIG=11) IN SUBSELECT_UNION_ENGINE::NO_ROWS This patch is specific for mysql-5.5 ISSUE: When max_join_size is used and union query results in evaluation of tuples greater than max_join_size, the join object is not created, and is set to NULL. However, this join object is further dereferenced by union logic to determine if query resulted in any number of rows being returned. Since, the object is NULL, it results in program terminating abnormally. SOLUTION: Added check to verify if join object is created. If join object is created, it will be used to determine if query resulted in any number of rows. Else, when join object is not created, we return 'false' indicating that there were no rows for the query. --- sql/item_subselect.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 43af0b5a3f638..21c897da2beab 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2002, 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 @@ -1789,8 +1789,12 @@ bool subselect_union_engine::is_executed() const bool subselect_union_engine::no_rows() { + bool rows_present= false; + /* Check if we got any rows when reading UNION result from temp. table: */ - return test(!unit->fake_select_lex->join->send_records); + if (unit->fake_select_lex->join) + rows_present= test(!unit->fake_select_lex->join->send_records); + return rows_present; } void subselect_uniquesubquery_engine::cleanup() From 7603ac53c86ea1a31a1511b5d630a24faf5de58c Mon Sep 17 00:00:00 2001 From: Terje Rosten Date: Fri, 26 Aug 2016 11:25:40 +0200 Subject: [PATCH 08/73] Bug#24464380 PRIVILEGE ESCALATION USING MYSQLD_SAFE Post push fix: Solaris 10 /bin/sh don't understand $(). --- scripts/mysqld_safe.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 11b692ec92874..1b30a3bb15ba2 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -321,7 +321,7 @@ set_malloc_lib() { if [ "$malloc_lib" = tcmalloc ]; then malloc_lib= - for libdir in $(echo $malloc_dirs); do + for libdir in `echo $malloc_dirs`; do for flavor in _minimal '' _and_profiler _debug; do tmp="$libdir/libtcmalloc$flavor.so" #log_notice "DEBUG: Checking for malloc lib '$tmp'" @@ -348,7 +348,7 @@ set_malloc_lib() { fi # Restrict to a the list in $malloc_dirs above - case "$(dirname "$malloc_lib")" in + case "`dirname "$malloc_lib"`" in /usr/lib) ;; /usr/lib64) ;; /usr/lib/i386-linux-gnu) ;; From aeab9d6b417871a2893df710c690be0de53e0c7a Mon Sep 17 00:00:00 2001 From: Arun Kuruvila Date: Mon, 29 Aug 2016 11:41:50 +0530 Subject: [PATCH 09/73] Bug#23303391: HANDLE_FATAL_SIGNAL (SIG=11) IN ALLOC_QUERY USING CHARACTER-SET-SERVER=UTF16 This is a backport of Bug#15985752 to mysql-5.5 --- sql/mysqld.cc | 21 ++++++++++++++++++--- sql/sql_acl.cc | 10 ++++++++-- sql/sql_connect.cc | 10 +++++++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e979ea1b731a9..d8edbe4b637a6 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3482,9 +3482,24 @@ static int init_common_variables() /* Set collactions that depends on the default collation */ global_system_variables.collation_server= default_charset_info; global_system_variables.collation_database= default_charset_info; - global_system_variables.collation_connection= default_charset_info; - global_system_variables.character_set_results= default_charset_info; - global_system_variables.character_set_client= default_charset_info; + + if (is_supported_parser_charset(default_charset_info)) + { + global_system_variables.collation_connection= default_charset_info; + global_system_variables.character_set_results= default_charset_info; + global_system_variables.character_set_client= default_charset_info; + } + else + { + sql_print_information("'%s' can not be used as client character set. " + "'%s' will be used as default client character set.", + default_charset_info->csname, + my_charset_latin1.csname); + global_system_variables.collation_connection= &my_charset_latin1; + global_system_variables.character_set_results= &my_charset_latin1; + global_system_variables.character_set_client= &my_charset_latin1; + } + if (!(character_set_filesystem= get_charset_by_csname(character_set_filesystem_name, MY_CS_PRIMARY, MYF(MY_WME)))) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 5ff6f38d18db0..99394878a55d2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -8789,7 +8789,10 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, { mpvio->client_capabilities= uint4korr(end); mpvio->max_client_packet_length= 0xfffff; - charset_code= default_charset_info->number; + charset_code= global_system_variables.character_set_client->number; + sql_print_warning("Client failed to provide its character set. " + "'%s' will be used as client character set.", + global_system_variables.character_set_client->csname); if (mpvio->charset_adapter->init_client_charset(charset_code)) return packet_error; goto skip_to_ssl; @@ -8826,7 +8829,10 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, Old clients didn't have their own charset. Instead the assumption was that they used what ever the server used. */ - charset_code= default_charset_info->number; + charset_code= global_system_variables.character_set_client->number; + sql_print_warning("Client failed to provide its character set. " + "'%s' will be used as client character set.", + global_system_variables.character_set_client->csname); } DBUG_EXECUTE_IF("host_error_charset", { diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index ed0b20a0e3477..f1ee34f3d09d1 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2007, 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 @@ -431,6 +431,14 @@ bool thd_init_client_charset(THD *thd, uint cs_number) global_system_variables.character_set_client->name, cs->name)) { + if (!is_supported_parser_charset( + global_system_variables.character_set_client)) + { + /* Disallow non-supported parser character sets: UCS2, UTF16, UTF32 */ + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client", + global_system_variables.character_set_client->csname); + return true; + } thd->variables.character_set_client= global_system_variables.character_set_client; thd->variables.collation_connection= From 91ddaff991d745d6b2308342a2555d302a13a481 Mon Sep 17 00:00:00 2001 From: Kailasnath Nagarkar Date: Fri, 2 Sep 2016 15:13:52 +0530 Subject: [PATCH 10/73] Bug #24489302 : ZEROFILL CAUSE MEMORY-CORRUPTION AND CRASH ISSUE: Heap corruption occurs and hence mysql server terminates abnormally in String variable destructor when ZEROFILL is used for a column. Though the abnormal termination is observed in the String destructor, heap corruption occurs at earlier stage when function Field_num::prepend_zeros() is called. This function, prepends zeros to the actual data and works on entire field length. Since the allocated memory could be less than the field length, heap corruption occurs. Later, when String destructor tries to free heap, the server terminates abnormally since the heap is corrupt. SOLUTION: In Field_num::prepend_zeros() function, if allocated memory is less than the field length, re-allocate memory enough to hold field length size data. --- sql/field.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index 3ca072e777164..15571afefb8ee 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1130,12 +1130,15 @@ void Field_num::prepend_zeros(String *value) int diff; if ((diff= (int) (field_length - value->length())) > 0) { - bmove_upp((uchar*) value->ptr()+field_length, - (uchar*) value->ptr()+value->length(), - value->length()); - bfill((uchar*) value->ptr(),diff,'0'); - value->length(field_length); - (void) value->c_ptr_quick(); // Avoid warnings in purify + const bool error= value->realloc(field_length); + if (!error) + { + bmove_upp((uchar*) value->ptr()+field_length, + (uchar*) value->ptr()+value->length(), + value->length()); + bfill((uchar*) value->ptr(),diff,'0'); + value->length(field_length); + } } } From 0d43e570ba66618dbe8078e11844b11e53af22f1 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 1 Sep 2016 13:30:44 +0300 Subject: [PATCH 11/73] Bug #24496214: MISLEADING ERROR EXECUTING MYSQLADMIN SHUTDOWN AGAINST A SERVER RUNNING FIREWALL mysqladmin shutdown will try to extract the server's pid file before executing the actual shutdown command. It will do that by executing a SHOW VARIABLES query and processing the result. However if that query fails it print a (somewhat confusing) error mesasage and will still continue to do the shutdown command. If that passes then the mysqladmin user will get an error but the shutdown will still be successful. This is confusing so the error message text is changed to say that this is a non-fatal error and execution continues. No test case added since it'd require a selective query failure device that's not available in 5.5. --- client/mysqladmin.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index f0ae2c121375a..c03b37ab16515 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -1438,8 +1438,10 @@ static my_bool get_pidfile(MYSQL *mysql, char *pidfile) if (mysql_query(mysql, "SHOW VARIABLES LIKE 'pid_file'")) { - my_printf_error(0, "query failed; error: '%s'", error_flags, - mysql_error(mysql)); + my_printf_error(mysql_errno(mysql), + "The query to get the server's pid file failed," + " error: '%s'. Continuing.", error_flags, + mysql_error(mysql)); } result = mysql_store_result(mysql); if (result) From d933b881d4509b30899fee4b4eaa4466c38efeca Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Mon, 26 Sep 2016 14:42:56 +0200 Subject: [PATCH 12/73] Raise version number after cloning 5.5.53 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d44c8b2800612..4f1ecb3a1972b 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=53 +MYSQL_VERSION_PATCH=54 MYSQL_VERSION_EXTRA= From 99c0fdb5a8af4104efed6ea47df850abf4c59530 Mon Sep 17 00:00:00 2001 From: Robert Golebiowski Date: Tue, 27 Sep 2016 11:17:38 +0200 Subject: [PATCH 13/73] Bug #24740291: YASSL UPDATE TO 2.4.2 --- extra/yassl/README | 18 +++ extra/yassl/certs/dsa-cert.pem | 38 ++--- extra/yassl/include/openssl/ssl.h | 2 +- extra/yassl/src/ssl.cpp | 60 +++++--- extra/yassl/taocrypt/include/aes.hpp | 58 ++++++++ extra/yassl/taocrypt/include/integer.hpp | 3 + extra/yassl/taocrypt/src/aes.cpp | 172 ++++++++++++++--------- extra/yassl/taocrypt/src/asn.cpp | 24 ++-- extra/yassl/taocrypt/src/dsa.cpp | 16 ++- extra/yassl/taocrypt/test/test.cpp | 3 + extra/yassl/testsuite/test.hpp | 2 +- 11 files changed, 274 insertions(+), 122 deletions(-) diff --git a/extra/yassl/README b/extra/yassl/README index b5eb88824fb0d..a3d4f60f56128 100644 --- a/extra/yassl/README +++ b/extra/yassl/README @@ -12,6 +12,24 @@ before calling SSL_new(); *** end Note *** +yaSSL Release notes, version 2.4.2 (9/22/2016) + This release of yaSSL fixes a medium security vulnerability. A fix for + potential AES side channel leaks is included that a local user monitoring + the same CPU core cache could exploit. VM users, hyper-threading users, + and users where potential attackers have access to the CPU cache will need + to update if they utilize AES. + + DSA padding fixes for unusual sizes is included as well. Users with DSA + certficiates should update. + +yaSSL Release notes, version 2.4.0 (5/20/2016) + This release of yaSSL fixes the OpenSSL compatibility function + SSL_CTX_load_verify_locations() when using the path directory to allow + unlimited path sizes. Minor Windows build fixes are included. + No high level security fixes in this version but we always recommend + updating. + + yaSSL Release notes, version 2.3.9b (2/03/2016) This release of yaSSL fixes the OpenSSL compatibility function X509_NAME_get_index_by_NID() to use the actual index of the common name diff --git a/extra/yassl/certs/dsa-cert.pem b/extra/yassl/certs/dsa-cert.pem index 10d533edc88b0..10794cbee7313 100644 --- a/extra/yassl/certs/dsa-cert.pem +++ b/extra/yassl/certs/dsa-cert.pem @@ -1,22 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDqzCCA2ugAwIBAgIJAMGqrgDU6DyhMAkGByqGSM44BAMwgY4xCzAJBgNVBAYT +MIIDrzCCA2+gAwIBAgIJAK1zRM7YFcNjMAkGByqGSM44BAMwgZAxCzAJBgNVBAYT AlVTMQ8wDQYDVQQIDAZPcmVnb24xETAPBgNVBAcMCFBvcnRsYW5kMRAwDgYDVQQK -DAd3b2xmU1NMMRAwDgYDVQQLDAd0ZXN0aW5nMRYwFAYDVQQDDA13d3cueWFzc2wu -Y29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMB4XDTEzMDQyMjIw -MDk0NFoXDTE2MDExNzIwMDk0NFowgY4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQIDAZP -cmVnb24xETAPBgNVBAcMCFBvcnRsYW5kMRAwDgYDVQQKDAd3b2xmU1NMMRAwDgYD -VQQLDAd0ZXN0aW5nMRYwFAYDVQQDDA13d3cueWFzc2wuY29tMR8wHQYJKoZIhvcN -AQkBFhBpbmZvQHdvbGZzc2wuY29tMIIBuDCCASwGByqGSM44BAEwggEfAoGBAL1R -7koy4IrH6sbh6nDEUUPPKgfhxxLCWCVexF2+qzANEr+hC9M002haJXFOfeS9DyoO -WFbL0qMZOuqv+22CaHnoUWl7q3PjJOAI3JH0P54ZyUPuU1909RzgTdIDp5+ikbr7 -KYjnltL73FQVMbjTZQKthIpPn3MjYcF+4jp2W2zFAhUAkcntYND6MGf+eYzIJDN2 -L7SonHUCgYEAklpxErfqznIZjVvqqHFaq+mgAL5J8QrKVmdhYZh/Y8z4jCjoCA8o -TDoFKxf7s2ZzgaPKvglaEKiYqLqic9qY78DYJswzQMLFvjsF4sFZ+pYCBdWPQI4N -PgxCiznK6Ce+JH9ikSBvMvG+tevjr2UpawDIHX3+AWYaZBZwKADAaboDgYUAAoGB -AJ3LY89yHyvQ/TsQ6zlYbovjbk/ogndsMqPdNUvL4RuPTgJP/caaDDa0XJ7ak6A7 -TJ+QheLNwOXoZPYJC4EGFSDAXpYniGhbWIrVTCGe6lmZDfnx40WXS0kk3m/DHaC0 -3ElLAiybxVGxyqoUfbT3Zv1JwftWMuiqHH5uADhdXuXVo1AwTjAdBgNVHQ4EFgQU -IJjk416o4v8qpH9LBtXlR9v8gccwHwYDVR0jBBgwFoAUIJjk416o4v8qpH9LBtXl -R9v8gccwDAYDVR0TBAUwAwEB/zAJBgcqhkjOOAQDAy8AMCwCFCjGKIdOSV12LcTu -k08owGM6YkO1AhQe+K173VuaO/OsDNsxZlKpyH8+1g== +DAd3b2xmU1NMMRAwDgYDVQQLDAd0ZXN0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz +bC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20wHhcNMTYwOTIy +MjEyMzA0WhcNMjIwMzE1MjEyMzA0WjCBkDELMAkGA1UEBhMCVVMxDzANBgNVBAgM +Bk9yZWdvbjERMA8GA1UEBwwIUG9ydGxhbmQxEDAOBgNVBAoMB3dvbGZTU0wxEDAO +BgNVBAsMB3Rlc3RpbmcxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqG +SIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCAbgwggEsBgcqhkjOOAQBMIIBHwKB +gQC9Ue5KMuCKx+rG4epwxFFDzyoH4ccSwlglXsRdvqswDRK/oQvTNNNoWiVxTn3k +vQ8qDlhWy9KjGTrqr/ttgmh56FFpe6tz4yTgCNyR9D+eGclD7lNfdPUc4E3SA6ef +opG6+ymI55bS+9xUFTG402UCrYSKT59zI2HBfuI6dltsxQIVAJHJ7WDQ+jBn/nmM +yCQzdi+0qJx1AoGBAJJacRK36s5yGY1b6qhxWqvpoAC+SfEKylZnYWGYf2PM+Iwo +6AgPKEw6BSsX+7Nmc4Gjyr4JWhComKi6onPamO/A2CbMM0DCxb47BeLBWfqWAgXV +j0CODT4MQos5yugnviR/YpEgbzLxvrXr469lKWsAyB19/gFmGmQWcCgAwGm6A4GF +AAKBgQCdy2PPch8r0P07EOs5WG6L425P6IJ3bDKj3TVLy+Ebj04CT/3Gmgw2tFye +2pOgO0yfkIXizcDl6GT2CQuBBhUgwF6WJ4hoW1iK1UwhnupZmQ358eNFl0tJJN5v +wx2gtNxJSwIsm8VRscqqFH2092b9ScH7VjLoqhx+bgA4XV7l1aNQME4wHQYDVR0O +BBYEFCCY5ONeqOL/KqR/SwbV5Ufb/IHHMB8GA1UdIwQYMBaAFCCY5ONeqOL/KqR/ +SwbV5Ufb/IHHMAwGA1UdEwQFMAMBAf8wCQYHKoZIzjgEAwMvADAsAhQRYSCVN/Ge +agV3mffU3qNZ92fI0QIUPH7Jp+iASI7U1ocaYDc10qXGaGY= -----END CERTIFICATE----- diff --git a/extra/yassl/include/openssl/ssl.h b/extra/yassl/include/openssl/ssl.h index 83daf3cc81f9e..0609dfc0592f5 100644 --- a/extra/yassl/include/openssl/ssl.h +++ b/extra/yassl/include/openssl/ssl.h @@ -35,7 +35,7 @@ #include "rsa.h" -#define YASSL_VERSION "2.3.9b" +#define YASSL_VERSION "2.4.2" #if defined(__cplusplus) diff --git a/extra/yassl/src/ssl.cpp b/extra/yassl/src/ssl.cpp index cde32df4f43f4..1925e2f759267 100644 --- a/extra/yassl/src/ssl.cpp +++ b/extra/yassl/src/ssl.cpp @@ -161,7 +161,7 @@ int read_file(SSL_CTX* ctx, const char* file, int format, CertType type) TaoCrypt::DSA_PrivateKey dsaKey; dsaKey.Initialize(dsaSource); - if (rsaSource.GetError().What()) { + if (dsaSource.GetError().What()) { // neither worked ret = SSL_FAILURE; } @@ -784,40 +784,67 @@ int SSL_CTX_load_verify_locations(SSL_CTX* ctx, const char* file, WIN32_FIND_DATA FindFileData; HANDLE hFind; - char name[MAX_PATH + 1]; // directory specification - strncpy(name, path, MAX_PATH - 3); - strncat(name, "\\*", 3); + const int DELIMITER_SZ = 2; + const int DELIMITER_STAR_SZ = 3; + int pathSz = (int)strlen(path); + int nameSz = pathSz + DELIMITER_STAR_SZ + 1; // plus 1 for terminator + char* name = NEW_YS char[nameSz]; // directory specification + memset(name, 0, nameSz); + strncpy(name, path, nameSz - DELIMITER_STAR_SZ - 1); + strncat(name, "\\*", DELIMITER_STAR_SZ); hFind = FindFirstFile(name, &FindFileData); - if (hFind == INVALID_HANDLE_VALUE) return SSL_BAD_PATH; + if (hFind == INVALID_HANDLE_VALUE) { + ysArrayDelete(name); + return SSL_BAD_PATH; + } do { - if (FindFileData.dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY) { - strncpy(name, path, MAX_PATH - 2 - HALF_PATH); - strncat(name, "\\", 2); - strncat(name, FindFileData.cFileName, HALF_PATH); + if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + int curSz = (int)strlen(FindFileData.cFileName); + if (pathSz + curSz + DELIMITER_SZ + 1 > nameSz) { + ysArrayDelete(name); + // plus 1 for terminator + nameSz = pathSz + curSz + DELIMITER_SZ + 1; + name = NEW_YS char[nameSz]; + } + memset(name, 0, nameSz); + strncpy(name, path, nameSz - curSz - DELIMITER_SZ - 1); + strncat(name, "\\", DELIMITER_SZ); + strncat(name, FindFileData.cFileName, + nameSz - pathSz - DELIMITER_SZ - 1); ret = read_file(ctx, name, SSL_FILETYPE_PEM, CA); } } while (ret == SSL_SUCCESS && FindNextFile(hFind, &FindFileData)); + ysArrayDelete(name); FindClose(hFind); #else // _WIN32 - - const int MAX_PATH = 260; - DIR* dir = opendir(path); if (!dir) return SSL_BAD_PATH; struct dirent* entry; struct stat buf; - char name[MAX_PATH + 1]; + const int DELIMITER_SZ = 1; + int pathSz = (int)strlen(path); + int nameSz = pathSz + DELIMITER_SZ + 1; //plus 1 for null terminator + char* name = NEW_YS char[nameSz]; // directory specification while (ret == SSL_SUCCESS && (entry = readdir(dir))) { - strncpy(name, path, MAX_PATH - 1 - HALF_PATH); - strncat(name, "/", 1); - strncat(name, entry->d_name, HALF_PATH); + int curSz = (int)strlen(entry->d_name); + if (pathSz + curSz + DELIMITER_SZ + 1 > nameSz) { + ysArrayDelete(name); + nameSz = pathSz + DELIMITER_SZ + curSz + 1; + name = NEW_YS char[nameSz]; + } + memset(name, 0, nameSz); + strncpy(name, path, nameSz - curSz - 1); + strncat(name, "/", DELIMITER_SZ); + strncat(name, entry->d_name, nameSz - pathSz - DELIMITER_SZ - 1); + if (stat(name, &buf) < 0) { + ysArrayDelete(name); closedir(dir); return SSL_BAD_STAT; } @@ -826,6 +853,7 @@ int SSL_CTX_load_verify_locations(SSL_CTX* ctx, const char* file, ret = read_file(ctx, name, SSL_FILETYPE_PEM, CA); } + ysArrayDelete(name); closedir(dir); #endif diff --git a/extra/yassl/taocrypt/include/aes.hpp b/extra/yassl/taocrypt/include/aes.hpp index 017630331560b..bccf6e73fc720 100644 --- a/extra/yassl/taocrypt/include/aes.hpp +++ b/extra/yassl/taocrypt/include/aes.hpp @@ -60,6 +60,7 @@ class AES : public Mode_BASE { static const word32 Te[5][256]; static const word32 Td[5][256]; + static const byte CTd4[256]; static const word32* Te0; static const word32* Te1; @@ -80,11 +81,68 @@ class AES : public Mode_BASE { void ProcessAndXorBlock(const byte*, const byte*, byte*) const; + word32 PreFetchTe() const; + word32 PreFetchTd() const; + word32 PreFetchCTd4() const; + AES(const AES&); // hide copy AES& operator=(const AES&); // and assign }; +#if defined(__x86_64__) || defined(_M_X64) || \ + (defined(__ILP32__) && (__ILP32__ >= 1)) + #define TC_CACHE_LINE_SZ 64 +#else + /* default cache line size */ + #define TC_CACHE_LINE_SZ 32 +#endif + +inline word32 AES::PreFetchTe() const +{ + word32 x = 0; + + /* 4 tables of 256 entries */ + for (int i = 0; i < 4; i++) { + /* each entry is 4 bytes */ + for (int j = 0; j < 256; j += TC_CACHE_LINE_SZ/4) { + x &= Te[i][j]; + } + } + + return x; +} + + +inline word32 AES::PreFetchTd() const +{ + word32 x = 0; + + /* 4 tables of 256 entries */ + for (int i = 0; i < 4; i++) { + /* each entry is 4 bytes */ + for (int j = 0; j < 256; j += TC_CACHE_LINE_SZ/4) { + x &= Td[i][j]; + } + } + + return x; +} + + +inline word32 AES::PreFetchCTd4() const +{ + word32 x = 0; + int i; + + for (i = 0; i < 256; i += TC_CACHE_LINE_SZ) { + x &= CTd4[i]; + } + + return x; +} + + typedef BlockCipher AES_ECB_Encryption; typedef BlockCipher AES_ECB_Decryption; diff --git a/extra/yassl/taocrypt/include/integer.hpp b/extra/yassl/taocrypt/include/integer.hpp index 75a3ee3d3df80..05fe189fd585f 100644 --- a/extra/yassl/taocrypt/include/integer.hpp +++ b/extra/yassl/taocrypt/include/integer.hpp @@ -119,6 +119,9 @@ namespace TaoCrypt { +#ifdef _WIN32 + #undef max // avoid name clash +#endif // general MAX template inline const T& max(const T& a, const T& b) diff --git a/extra/yassl/taocrypt/src/aes.cpp b/extra/yassl/taocrypt/src/aes.cpp index ee4c7a6e8a1d3..3fcf80ac20207 100644 --- a/extra/yassl/taocrypt/src/aes.cpp +++ b/extra/yassl/taocrypt/src/aes.cpp @@ -109,10 +109,10 @@ void AES::SetKey(const byte* userKey, word32 keylen, CipherDir /*dummy*/) { temp = rk[3]; rk[4] = rk[0] ^ - (Te4[GETBYTE(temp, 2)] & 0xff000000) ^ - (Te4[GETBYTE(temp, 1)] & 0x00ff0000) ^ - (Te4[GETBYTE(temp, 0)] & 0x0000ff00) ^ - (Te4[GETBYTE(temp, 3)] & 0x000000ff) ^ + (Te2[GETBYTE(temp, 2)] & 0xff000000) ^ + (Te3[GETBYTE(temp, 1)] & 0x00ff0000) ^ + (Te0[GETBYTE(temp, 0)] & 0x0000ff00) ^ + (Te1[GETBYTE(temp, 3)] & 0x000000ff) ^ rcon_[i]; rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; @@ -128,10 +128,10 @@ void AES::SetKey(const byte* userKey, word32 keylen, CipherDir /*dummy*/) { temp = rk[ 5]; rk[ 6] = rk[ 0] ^ - (Te4[GETBYTE(temp, 2)] & 0xff000000) ^ - (Te4[GETBYTE(temp, 1)] & 0x00ff0000) ^ - (Te4[GETBYTE(temp, 0)] & 0x0000ff00) ^ - (Te4[GETBYTE(temp, 3)] & 0x000000ff) ^ + (Te2[GETBYTE(temp, 2)] & 0xff000000) ^ + (Te3[GETBYTE(temp, 1)] & 0x00ff0000) ^ + (Te0[GETBYTE(temp, 0)] & 0x0000ff00) ^ + (Te1[GETBYTE(temp, 3)] & 0x000000ff) ^ rcon_[i]; rk[ 7] = rk[ 1] ^ rk[ 6]; rk[ 8] = rk[ 2] ^ rk[ 7]; @@ -149,10 +149,10 @@ void AES::SetKey(const byte* userKey, word32 keylen, CipherDir /*dummy*/) { temp = rk[ 7]; rk[ 8] = rk[ 0] ^ - (Te4[GETBYTE(temp, 2)] & 0xff000000) ^ - (Te4[GETBYTE(temp, 1)] & 0x00ff0000) ^ - (Te4[GETBYTE(temp, 0)] & 0x0000ff00) ^ - (Te4[GETBYTE(temp, 3)] & 0x000000ff) ^ + (Te2[GETBYTE(temp, 2)] & 0xff000000) ^ + (Te3[GETBYTE(temp, 1)] & 0x00ff0000) ^ + (Te0[GETBYTE(temp, 0)] & 0x0000ff00) ^ + (Te1[GETBYTE(temp, 3)] & 0x000000ff) ^ rcon_[i]; rk[ 9] = rk[ 1] ^ rk[ 8]; rk[10] = rk[ 2] ^ rk[ 9]; @@ -161,10 +161,10 @@ void AES::SetKey(const byte* userKey, word32 keylen, CipherDir /*dummy*/) break; temp = rk[11]; rk[12] = rk[ 4] ^ - (Te4[GETBYTE(temp, 3)] & 0xff000000) ^ - (Te4[GETBYTE(temp, 2)] & 0x00ff0000) ^ - (Te4[GETBYTE(temp, 1)] & 0x0000ff00) ^ - (Te4[GETBYTE(temp, 0)] & 0x000000ff); + (Te2[GETBYTE(temp, 3)] & 0xff000000) ^ + (Te3[GETBYTE(temp, 2)] & 0x00ff0000) ^ + (Te0[GETBYTE(temp, 1)] & 0x0000ff00) ^ + (Te1[GETBYTE(temp, 0)] & 0x000000ff); rk[13] = rk[ 5] ^ rk[12]; rk[14] = rk[ 6] ^ rk[13]; rk[15] = rk[ 7] ^ rk[14]; @@ -191,25 +191,25 @@ void AES::SetKey(const byte* userKey, word32 keylen, CipherDir /*dummy*/) for (i = 1; i < rounds_; i++) { rk += 4; rk[0] = - Td0[Te4[GETBYTE(rk[0], 3)] & 0xff] ^ - Td1[Te4[GETBYTE(rk[0], 2)] & 0xff] ^ - Td2[Te4[GETBYTE(rk[0], 1)] & 0xff] ^ - Td3[Te4[GETBYTE(rk[0], 0)] & 0xff]; + Td0[Te1[GETBYTE(rk[0], 3)] & 0xff] ^ + Td1[Te1[GETBYTE(rk[0], 2)] & 0xff] ^ + Td2[Te1[GETBYTE(rk[0], 1)] & 0xff] ^ + Td3[Te1[GETBYTE(rk[0], 0)] & 0xff]; rk[1] = - Td0[Te4[GETBYTE(rk[1], 3)] & 0xff] ^ - Td1[Te4[GETBYTE(rk[1], 2)] & 0xff] ^ - Td2[Te4[GETBYTE(rk[1], 1)] & 0xff] ^ - Td3[Te4[GETBYTE(rk[1], 0)] & 0xff]; + Td0[Te1[GETBYTE(rk[1], 3)] & 0xff] ^ + Td1[Te1[GETBYTE(rk[1], 2)] & 0xff] ^ + Td2[Te1[GETBYTE(rk[1], 1)] & 0xff] ^ + Td3[Te1[GETBYTE(rk[1], 0)] & 0xff]; rk[2] = - Td0[Te4[GETBYTE(rk[2], 3)] & 0xff] ^ - Td1[Te4[GETBYTE(rk[2], 2)] & 0xff] ^ - Td2[Te4[GETBYTE(rk[2], 1)] & 0xff] ^ - Td3[Te4[GETBYTE(rk[2], 0)] & 0xff]; + Td0[Te1[GETBYTE(rk[2], 3)] & 0xff] ^ + Td1[Te1[GETBYTE(rk[2], 2)] & 0xff] ^ + Td2[Te1[GETBYTE(rk[2], 1)] & 0xff] ^ + Td3[Te1[GETBYTE(rk[2], 0)] & 0xff]; rk[3] = - Td0[Te4[GETBYTE(rk[3], 3)] & 0xff] ^ - Td1[Te4[GETBYTE(rk[3], 2)] & 0xff] ^ - Td2[Te4[GETBYTE(rk[3], 1)] & 0xff] ^ - Td3[Te4[GETBYTE(rk[3], 0)] & 0xff]; + Td0[Te1[GETBYTE(rk[3], 3)] & 0xff] ^ + Td1[Te1[GETBYTE(rk[3], 2)] & 0xff] ^ + Td2[Te1[GETBYTE(rk[3], 1)] & 0xff] ^ + Td3[Te1[GETBYTE(rk[3], 0)] & 0xff]; } } } @@ -244,6 +244,7 @@ void AES::encrypt(const byte* inBlock, const byte* xorBlock, s2 ^= rk[2]; s3 ^= rk[3]; + s0 |= PreFetchTe(); /* * Nr - 1 full rounds: */ @@ -312,28 +313,28 @@ void AES::encrypt(const byte* inBlock, const byte* xorBlock, */ s0 = - (Te4[GETBYTE(t0, 3)] & 0xff000000) ^ - (Te4[GETBYTE(t1, 2)] & 0x00ff0000) ^ - (Te4[GETBYTE(t2, 1)] & 0x0000ff00) ^ - (Te4[GETBYTE(t3, 0)] & 0x000000ff) ^ + (Te2[GETBYTE(t0, 3)] & 0xff000000) ^ + (Te3[GETBYTE(t1, 2)] & 0x00ff0000) ^ + (Te0[GETBYTE(t2, 1)] & 0x0000ff00) ^ + (Te1[GETBYTE(t3, 0)] & 0x000000ff) ^ rk[0]; s1 = - (Te4[GETBYTE(t1, 3)] & 0xff000000) ^ - (Te4[GETBYTE(t2, 2)] & 0x00ff0000) ^ - (Te4[GETBYTE(t3, 1)] & 0x0000ff00) ^ - (Te4[GETBYTE(t0, 0)] & 0x000000ff) ^ + (Te2[GETBYTE(t1, 3)] & 0xff000000) ^ + (Te3[GETBYTE(t2, 2)] & 0x00ff0000) ^ + (Te0[GETBYTE(t3, 1)] & 0x0000ff00) ^ + (Te1[GETBYTE(t0, 0)] & 0x000000ff) ^ rk[1]; s2 = - (Te4[GETBYTE(t2, 3)] & 0xff000000) ^ - (Te4[GETBYTE(t3, 2)] & 0x00ff0000) ^ - (Te4[GETBYTE(t0, 1)] & 0x0000ff00) ^ - (Te4[GETBYTE(t1, 0)] & 0x000000ff) ^ + (Te2[GETBYTE(t2, 3)] & 0xff000000) ^ + (Te3[GETBYTE(t3, 2)] & 0x00ff0000) ^ + (Te0[GETBYTE(t0, 1)] & 0x0000ff00) ^ + (Te1[GETBYTE(t1, 0)] & 0x000000ff) ^ rk[2]; s3 = - (Te4[GETBYTE(t3, 3)] & 0xff000000) ^ - (Te4[GETBYTE(t0, 2)] & 0x00ff0000) ^ - (Te4[GETBYTE(t1, 1)] & 0x0000ff00) ^ - (Te4[GETBYTE(t2, 0)] & 0x000000ff) ^ + (Te2[GETBYTE(t3, 3)] & 0xff000000) ^ + (Te3[GETBYTE(t0, 2)] & 0x00ff0000) ^ + (Te0[GETBYTE(t1, 1)] & 0x0000ff00) ^ + (Te1[GETBYTE(t2, 0)] & 0x000000ff) ^ rk[3]; @@ -358,6 +359,8 @@ void AES::decrypt(const byte* inBlock, const byte* xorBlock, s2 ^= rk[2]; s3 ^= rk[3]; + s0 |= PreFetchTd(); + /* * Nr - 1 full rounds: */ @@ -423,29 +426,32 @@ void AES::decrypt(const byte* inBlock, const byte* xorBlock, * apply last round and * map cipher state to byte array block: */ + + t0 |= PreFetchCTd4(); + s0 = - (Td4[GETBYTE(t0, 3)] & 0xff000000) ^ - (Td4[GETBYTE(t3, 2)] & 0x00ff0000) ^ - (Td4[GETBYTE(t2, 1)] & 0x0000ff00) ^ - (Td4[GETBYTE(t1, 0)] & 0x000000ff) ^ + ((word32)CTd4[GETBYTE(t0, 3)] << 24) ^ + ((word32)CTd4[GETBYTE(t3, 2)] << 16) ^ + ((word32)CTd4[GETBYTE(t2, 1)] << 8) ^ + ((word32)CTd4[GETBYTE(t1, 0)]) ^ rk[0]; s1 = - (Td4[GETBYTE(t1, 3)] & 0xff000000) ^ - (Td4[GETBYTE(t0, 2)] & 0x00ff0000) ^ - (Td4[GETBYTE(t3, 1)] & 0x0000ff00) ^ - (Td4[GETBYTE(t2, 0)] & 0x000000ff) ^ + ((word32)CTd4[GETBYTE(t1, 3)] << 24) ^ + ((word32)CTd4[GETBYTE(t0, 2)] << 16) ^ + ((word32)CTd4[GETBYTE(t3, 1)] << 8) ^ + ((word32)CTd4[GETBYTE(t2, 0)]) ^ rk[1]; s2 = - (Td4[GETBYTE(t2, 3)] & 0xff000000) ^ - (Td4[GETBYTE(t1, 2)] & 0x00ff0000) ^ - (Td4[GETBYTE(t0, 1)] & 0x0000ff00) ^ - (Td4[GETBYTE(t3, 0)] & 0x000000ff) ^ + ((word32)CTd4[GETBYTE(t2, 3)] << 24 ) ^ + ((word32)CTd4[GETBYTE(t1, 2)] << 16 ) ^ + ((word32)CTd4[GETBYTE(t0, 1)] << 8 ) ^ + ((word32)CTd4[GETBYTE(t3, 0)]) ^ rk[2]; s3 = - (Td4[GETBYTE(t3, 3)] & 0xff000000) ^ - (Td4[GETBYTE(t2, 2)] & 0x00ff0000) ^ - (Td4[GETBYTE(t1, 1)] & 0x0000ff00) ^ - (Td4[GETBYTE(t0, 0)] & 0x000000ff) ^ + ((word32)CTd4[GETBYTE(t3, 3)] << 24) ^ + ((word32)CTd4[GETBYTE(t2, 2)] << 16) ^ + ((word32)CTd4[GETBYTE(t1, 1)] << 8) ^ + ((word32)CTd4[GETBYTE(t0, 0)]) ^ rk[3]; gpBlock::Put(xorBlock, outBlock)(s0)(s1)(s2)(s3); @@ -1826,18 +1832,52 @@ const word32 AES::Td[5][256] = { } }; +const byte AES::CTd4[256] = +{ + 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, +}; + const word32* AES::Te0 = AES::Te[0]; const word32* AES::Te1 = AES::Te[1]; const word32* AES::Te2 = AES::Te[2]; const word32* AES::Te3 = AES::Te[3]; -const word32* AES::Te4 = AES::Te[4]; const word32* AES::Td0 = AES::Td[0]; const word32* AES::Td1 = AES::Td[1]; const word32* AES::Td2 = AES::Td[2]; const word32* AES::Td3 = AES::Td[3]; -const word32* AES::Td4 = AES::Td[4]; diff --git a/extra/yassl/taocrypt/src/asn.cpp b/extra/yassl/taocrypt/src/asn.cpp index a210d805452f3..7ff3c7167d23b 100644 --- a/extra/yassl/taocrypt/src/asn.cpp +++ b/extra/yassl/taocrypt/src/asn.cpp @@ -1209,17 +1209,17 @@ word32 DecodeDSA_Signature(byte* decoded, const byte* encoded, word32 sz) } word32 rLen = GetLength(source); if (rLen != 20) { - if (rLen == 21) { // zero at front, eat + while (rLen > 20 && source.remaining() > 0) { // zero's at front, eat source.next(); --rLen; } - else if (rLen == 19) { // add zero to front so 20 bytes + if (rLen < 20) { // add zero's to front so 20 bytes + word32 tmpLen = rLen; + while (tmpLen < 20) { decoded[0] = 0; decoded++; + tmpLen++; } - else { - source.SetError(DSA_SZ_E); - return 0; } } memcpy(decoded, source.get_buffer() + source.get_index(), rLen); @@ -1232,17 +1232,17 @@ word32 DecodeDSA_Signature(byte* decoded, const byte* encoded, word32 sz) } word32 sLen = GetLength(source); if (sLen != 20) { - if (sLen == 21) { - source.next(); // zero at front, eat + while (sLen > 20 && source.remaining() > 0) { + source.next(); // zero's at front, eat --sLen; } - else if (sLen == 19) { - decoded[rLen] = 0; // add zero to front so 20 bytes + if (sLen < 20) { // add zero's to front so 20 bytes + word32 tmpLen = sLen; + while (tmpLen < 20) { + decoded[rLen] = 0; decoded++; + tmpLen++; } - else { - source.SetError(DSA_SZ_E); - return 0; } } memcpy(decoded + rLen, source.get_buffer() + source.get_index(), sLen); diff --git a/extra/yassl/taocrypt/src/dsa.cpp b/extra/yassl/taocrypt/src/dsa.cpp index bf116d3e48d12..b19fed9235b24 100644 --- a/extra/yassl/taocrypt/src/dsa.cpp +++ b/extra/yassl/taocrypt/src/dsa.cpp @@ -172,6 +172,7 @@ word32 DSA_Signer::Sign(const byte* sha_digest, byte* sig, const Integer& q = key_.GetSubGroupOrder(); const Integer& g = key_.GetSubGroupGenerator(); const Integer& x = key_.GetPrivatePart(); + byte* tmpPtr = sig; // initial signature output Integer k(rng, 1, q - 1); @@ -187,22 +188,23 @@ word32 DSA_Signer::Sign(const byte* sha_digest, byte* sig, return -1; int rSz = r_.ByteCount(); + int tmpSz = rSz; - if (rSz == 19) { - sig[0] = 0; - sig++; + while (tmpSz++ < SHA::DIGEST_SIZE) { + *sig++ = 0; } r_.Encode(sig, rSz); + sig = tmpPtr + SHA::DIGEST_SIZE; // advance sig output to s int sSz = s_.ByteCount(); + tmpSz = sSz; - if (sSz == 19) { - sig[rSz] = 0; - sig++; + while (tmpSz++ < SHA::DIGEST_SIZE) { + *sig++ = 0; } - s_.Encode(sig + rSz, sSz); + s_.Encode(sig, sSz); return 40; } diff --git a/extra/yassl/taocrypt/test/test.cpp b/extra/yassl/taocrypt/test/test.cpp index a7d5cb3e8af35..fc1f0e8762ddc 100644 --- a/extra/yassl/taocrypt/test/test.cpp +++ b/extra/yassl/taocrypt/test/test.cpp @@ -1277,6 +1277,9 @@ int dsa_test() if (!verifier.Verify(digest, decoded)) return -90; + if (!verifier.Verify(digest, signature)) + return -91; + return 0; } diff --git a/extra/yassl/testsuite/test.hpp b/extra/yassl/testsuite/test.hpp index 5c9dc7ce117ef..e2e44c24027df 100644 --- a/extra/yassl/testsuite/test.hpp +++ b/extra/yassl/testsuite/test.hpp @@ -22,7 +22,6 @@ #define yaSSL_TEST_HPP #include "runtime.hpp" -#include "openssl/ssl.h" /* openssl compatibility test */ #include "error.hpp" #include #include @@ -56,6 +55,7 @@ #endif #define SOCKET_T int #endif /* _WIN32 */ +#include "openssl/ssl.h" /* openssl compatibility test */ #ifdef _MSC_VER From ac143744a90d1069f0b4f8a47516cdcca915fbfa Mon Sep 17 00:00:00 2001 From: Arun Kuruvila Date: Wed, 28 Sep 2016 15:52:05 +0530 Subject: [PATCH 14/73] Bug#24707666: DEFAULT SETTING FOR SECURE-FILE-PRIV SHOULD BE RESTRICTED IN ALL GA RELEASES Back port of WL#6782 to 5.5 and 5.6. This also includes back port of Bug#20771331, Bug#20741572 and Bug#20770671. Bug#24695274 and Bug#24679907 are also handled along with this. --- cmake/install_layout.cmake | 256 +++++++++++++++++- config.h.cmake | 4 + mysql-test/include/mtr_warnings.sql | 7 +- mysql-test/include/mysqld--help.inc | 3 +- mysql-test/mysql-test-run.pl | 4 +- mysql-test/r/mysqld--help-notwin.result | 1 - mysql-test/r/mysqld--help-win.result | 1 - .../auth_sec/r/secure_file_priv_error.result | 7 + .../auth_sec/r/secure_file_priv_null.result | 21 ++ .../r/secure_file_priv_warnings.result | 17 ++ .../secure_file_priv_warnings_not_win.result | 9 + .../r/secure_file_priv_warnings_win.result | 8 + .../auth_sec/t/secure_file_priv_error.test | 39 +++ .../t/secure_file_priv_null-master.opt | 1 + .../auth_sec/t/secure_file_priv_null.test | 42 +++ .../t/secure_file_priv_warnings-master.opt | 1 + .../auth_sec/t/secure_file_priv_warnings.test | 47 ++++ .../t/secure_file_priv_warnings_not_win.test | 24 ++ .../t/secure_file_priv_warnings_win.test | 35 +++ packaging/rpm-oel/mysql-systemd-start | 6 + packaging/rpm-oel/mysql.init | 10 +- packaging/rpm-oel/mysql.spec.in | 5 + packaging/rpm-sles/mysql.spec.in | 5 + packaging/solaris/postinstall-solaris.sh | 8 +- sql/mysqld.cc | 244 +++++++++++++++-- sql/sql_class.cc | 2 + sql/sql_class.h | 1 + sql/sys_vars.cc | 8 +- support-files/mysql.spec.sh | 7 +- 29 files changed, 786 insertions(+), 37 deletions(-) create mode 100644 mysql-test/suite/auth_sec/r/secure_file_priv_error.result create mode 100644 mysql-test/suite/auth_sec/r/secure_file_priv_null.result create mode 100644 mysql-test/suite/auth_sec/r/secure_file_priv_warnings.result create mode 100644 mysql-test/suite/auth_sec/r/secure_file_priv_warnings_not_win.result create mode 100644 mysql-test/suite/auth_sec/r/secure_file_priv_warnings_win.result create mode 100644 mysql-test/suite/auth_sec/t/secure_file_priv_error.test create mode 100644 mysql-test/suite/auth_sec/t/secure_file_priv_null-master.opt create mode 100644 mysql-test/suite/auth_sec/t/secure_file_priv_null.test create mode 100644 mysql-test/suite/auth_sec/t/secure_file_priv_warnings-master.opt create mode 100644 mysql-test/suite/auth_sec/t/secure_file_priv_warnings.test create mode 100644 mysql-test/suite/auth_sec/t/secure_file_priv_warnings_not_win.test create mode 100644 mysql-test/suite/auth_sec/t/secure_file_priv_warnings_win.test diff --git a/cmake/install_layout.cmake b/cmake/install_layout.cmake index 4adda0b6eacb9..4fd18b049f238 100644 --- a/cmake/install_layout.cmake +++ b/cmake/install_layout.cmake @@ -1,4 +1,4 @@ -# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2010, 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 @@ -22,7 +22,7 @@ # and relative links. Windows zip uses the same tarball layout but without # the build prefix. # -# RPM +# RPM, SLES # Build as per default RPM layout, with prefix=/usr # Note: The layout for ULN RPMs differs, see the "RPM" section. # @@ -32,10 +32,22 @@ # SVR4 # Solaris package layout suitable for pkg* tools, prefix=/opt/mysql/mysql # +# FREEBSD, GLIBC, OSX, TARGZ +# Build with prefix=/usr/local/mysql, create tarball with install prefix="." +# and relative links. +# +# WIN +# Windows zip : same as tarball layout but without the build prefix +# # To force a directory layout, use -DINSTALL_LAYOUT=. # # The default is STANDALONE. # +# Note : At present, RPM and SLES layouts are similar. This is also true +# for layouts like FREEBSD, GLIBC, OSX, TARGZ. However, they provide +# opportunity to fine-tune deployment for each platform without +# affecting all other types of deployment. +# # There is the possibility to further fine-tune installation directories. # Several variables can be overwritten: # @@ -60,6 +72,7 @@ # - INSTALL_SUPPORTFILESDIR (various extra support files) # # - INSTALL_MYSQLDATADIR (data directory) +# - INSTALL_SECURE_FILE_PRIVDIR (--secure-file-priv directory) # # When changing this page, _please_ do not forget to update public Wiki # http://forge.mysql.com/wiki/CMake#Fine-tuning_installation_paths @@ -69,10 +82,11 @@ IF(NOT INSTALL_LAYOUT) ENDIF() SET(INSTALL_LAYOUT "${DEFAULT_INSTALL_LAYOUT}" -CACHE STRING "Installation directory layout. Options are: STANDALONE (as in zip or tar.gz installer), RPM, DEB, SVR4") +CACHE STRING "Installation directory layout. Options are: TARGZ (as in tar.gz installer), WIN (as in zip installer), STANDALONE, RPM, DEB, SVR4, FREEBSD, GLIBC, OSX, SLES") IF(UNIX) - IF(INSTALL_LAYOUT MATCHES "RPM") + IF(INSTALL_LAYOUT MATCHES "RPM" OR + INSTALL_LAYOUT MATCHES "SLES") SET(default_prefix "/usr") ELSEIF(INSTALL_LAYOUT MATCHES "DEB") SET(default_prefix "/opt/mysql/server-${MYSQL_BASE_VERSION}") @@ -87,7 +101,7 @@ IF(UNIX) SET(CMAKE_INSTALL_PREFIX ${default_prefix} CACHE PATH "install prefix" FORCE) ENDIF() - SET(VALID_INSTALL_LAYOUTS "RPM" "STANDALONE" "DEB" "SVR4") + SET(VALID_INSTALL_LAYOUTS "RPM" "DEB" "SVR4" "FREEBSD" "GLIBC" "OSX" "TARGZ" "SLES" "STANDALONE") LIST(FIND VALID_INSTALL_LAYOUTS "${INSTALL_LAYOUT}" ind) IF(ind EQUAL -1) MESSAGE(FATAL_ERROR "Invalid INSTALL_LAYOUT parameter:${INSTALL_LAYOUT}." @@ -99,6 +113,15 @@ IF(UNIX) MARK_AS_ADVANCED(SYSCONFDIR) ENDIF() +IF(WIN32) + SET(VALID_INSTALL_LAYOUTS "TARGZ" "STANDALONE" "WIN") + LIST(FIND VALID_INSTALL_LAYOUTS "${INSTALL_LAYOUT}" ind) + IF(ind EQUAL -1) + MESSAGE(FATAL_ERROR "Invalid INSTALL_LAYOUT parameter:${INSTALL_LAYOUT}." + " Choose between ${VALID_INSTALL_LAYOUTS}" ) + ENDIF() +ENDIF() + # # plugin_tests's value should not be used by imported plugins, # just use if(INSTALL_PLUGINTESTDIR). @@ -109,6 +132,22 @@ FILE(GLOB plugin_tests ${CMAKE_SOURCE_DIR}/internal/plugin/*/tests ) +# +# DEFAULT_SECURE_FILE_PRIV_DIR/DEFAULT_SECURE_FILE_PRIV_EMBEDDED_DIR +# +IF(INSTALL_LAYOUT MATCHES "STANDALONE" OR + INSTALL_LAYOUT MATCHES "WIN") + SET(secure_file_priv_path "NULL") +ELSEIF(INSTALL_LAYOUT MATCHES "RPM" OR + INSTALL_LAYOUT MATCHES "SLES" OR + INSTALL_LAYOUT MATCHES "SVR4" OR + INSTALL_LAYOUT MATCHES "DEB") + SET(secure_file_priv_path "/var/lib/mysql-files") +ELSE() + SET(secure_file_priv_path "${default_prefix}/mysql-files") +ENDIF() +SET(secure_file_priv_embedded_path "NULL") + # # STANDALONE layout # @@ -134,6 +173,148 @@ SET(INSTALL_SUPPORTFILESDIR_STANDALONE "support-files") # SET(INSTALL_MYSQLDATADIR_STANDALONE "data") SET(INSTALL_PLUGINTESTDIR_STANDALONE ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_STANDALONE ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_STANDALONE ${secure_file_priv_embedded_path}) + +# +# WIN layout +# +SET(INSTALL_BINDIR_WIN "bin") +SET(INSTALL_SBINDIR_WIN "bin") +SET(INSTALL_SCRIPTDIR_WIN "scripts") +# +SET(INSTALL_LIBDIR_WIN "lib") +SET(INSTALL_PLUGINDIR_WIN "lib/plugin") +# +SET(INSTALL_INCLUDEDIR_WIN "include") +# +SET(INSTALL_DOCDIR_WIN "docs") +SET(INSTALL_DOCREADMEDIR_WIN ".") +SET(INSTALL_MANDIR_WIN "man") +SET(INSTALL_INFODIR_WIN "docs") +# +SET(INSTALL_SHAREDIR_WIN "share") +SET(INSTALL_MYSQLSHAREDIR_WIN "share") +SET(INSTALL_MYSQLTESTDIR_WIN "mysql-test") +SET(INSTALL_SQLBENCHDIR_WIN ".") +SET(INSTALL_SUPPORTFILESDIR_WIN "support-files") +# +SET(INSTALL_MYSQLDATADIR_WIN "data") +SET(INSTALL_PLUGINTESTDIR_WIN ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_WIN ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_WIN ${secure_file_priv_embedded_path}) + +# +# FREEBSD layout +# +SET(INSTALL_BINDIR_FREEBSD "bin") +SET(INSTALL_SBINDIR_FREEBSD "bin") +SET(INSTALL_SCRIPTDIR_FREEBSD "scripts") +# +SET(INSTALL_LIBDIR_FREEBSD "lib") +SET(INSTALL_PLUGINDIR_FREEBSD "lib/plugin") +# +SET(INSTALL_INCLUDEDIR_FREEBSD "include") +# +SET(INSTALL_DOCDIR_FREEBSD "docs") +SET(INSTALL_DOCREADMEDIR_FREEBSD ".") +SET(INSTALL_MANDIR_FREEBSD "man") +SET(INSTALL_INFODIR_FREEBSD "docs") +# +SET(INSTALL_SHAREDIR_FREEBSD "share") +SET(INSTALL_MYSQLSHAREDIR_FREEBSD "share") +SET(INSTALL_MYSQLTESTDIR_FREEBSD "mysql-test") +SET(INSTALL_SQLBENCHDIR_FREEBSD ".") +SET(INSTALL_SUPPORTFILESDIR_FREEBSD "support-files") +# +SET(INSTALL_MYSQLDATADIR_FREEBSD "data") +SET(INSTALL_PLUGINTESTDIR_FREEBSD ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_FREEBSD ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_FREEBSD ${secure_file_priv_embedded_path}) + +# +# GLIBC layout +# +SET(INSTALL_BINDIR_GLIBC "bin") +SET(INSTALL_SBINDIR_GLIBC "bin") +SET(INSTALL_SCRIPTDIR_GLIBC "scripts") +# +SET(INSTALL_LIBDIR_GLIBC "lib") +SET(INSTALL_PLUGINDIR_GLIBC "lib/plugin") +# +SET(INSTALL_INCLUDEDIR_GLIBC "include") +# +SET(INSTALL_DOCDIR_GLIBC "docs") +SET(INSTALL_DOCREADMEDIR_GLIBC ".") +SET(INSTALL_MANDIR_GLIBC "man") +SET(INSTALL_INFODIR_GLIBC "docs") +# +SET(INSTALL_SHAREDIR_GLIBC "share") +SET(INSTALL_MYSQLSHAREDIR_GLIBC "share") +SET(INSTALL_MYSQLTESTDIR_GLIBC "mysql-test") +SET(INSTALL_SQLBENCHDIR_GLIBC ".") +SET(INSTALL_SUPPORTFILESDIR_GLIBC "support-files") +# +SET(INSTALL_MYSQLDATADIR_GLIBC "data") +SET(INSTALL_PLUGINTESTDIR_GLIBC ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_GLIBC ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_GLIBC ${secure_file_priv_embedded_path}) + +# +# OSX layout +# +SET(INSTALL_BINDIR_OSX "bin") +SET(INSTALL_SBINDIR_OSX "bin") +SET(INSTALL_SCRIPTDIR_OSX "scripts") +# +SET(INSTALL_LIBDIR_OSX "lib") +SET(INSTALL_PLUGINDIR_OSX "lib/plugin") +# +SET(INSTALL_INCLUDEDIR_OSX "include") +# +SET(INSTALL_DOCDIR_OSX "docs") +SET(INSTALL_DOCREADMEDIR_OSX ".") +SET(INSTALL_MANDIR_OSX "man") +SET(INSTALL_INFODIR_OSX "docs") +# +SET(INSTALL_SHAREDIR_OSX "share") +SET(INSTALL_MYSQLSHAREDIR_OSX "share") +SET(INSTALL_MYSQLTESTDIR_OSX "mysql-test") +SET(INSTALL_SQLBENCHDIR_OSX ".") +SET(INSTALL_SUPPORTFILESDIR_OSX "support-files") +# +SET(INSTALL_MYSQLDATADIR_OSX "data") +SET(INSTALL_PLUGINTESTDIR_OSX ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_OSX ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_OSX ${secure_file_priv_embedded_path}) + +# +# TARGZ layout +# +SET(INSTALL_BINDIR_TARGZ "bin") +SET(INSTALL_SBINDIR_TARGZ "bin") +SET(INSTALL_SCRIPTDIR_TARGZ "scripts") +# +SET(INSTALL_LIBDIR_TARGZ "lib") +SET(INSTALL_PLUGINDIR_TARGZ "lib/plugin") +# +SET(INSTALL_INCLUDEDIR_TARGZ "include") +# +SET(INSTALL_DOCDIR_TARGZ "docs") +SET(INSTALL_DOCREADMEDIR_TARGZ ".") +SET(INSTALL_MANDIR_TARGZ "man") +SET(INSTALL_INFODIR_TARGZ "docs") +# +SET(INSTALL_SHAREDIR_TARGZ "share") +SET(INSTALL_MYSQLSHAREDIR_TARGZ "share") +SET(INSTALL_MYSQLTESTDIR_TARGZ "mysql-test") +SET(INSTALL_SQLBENCHDIR_TARGZ ".") +SET(INSTALL_SUPPORTFILESDIR_TARGZ "support-files") +# +SET(INSTALL_MYSQLDATADIR_TARGZ "data") +SET(INSTALL_PLUGINTESTDIR_TARGZ ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_TARGZ ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_TARGZ ${secure_file_priv_embedded_path}) # # RPM layout @@ -169,6 +350,41 @@ SET(INSTALL_SUPPORTFILESDIR_RPM "share/mysql") # SET(INSTALL_MYSQLDATADIR_RPM "/var/lib/mysql") SET(INSTALL_PLUGINTESTDIR_RPM ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_RPM ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_RPM ${secure_file_priv_embedded_path}) + +# +# SLES layout +# +SET(INSTALL_BINDIR_SLES "bin") +SET(INSTALL_SBINDIR_SLES "sbin") +SET(INSTALL_SCRIPTDIR_SLES "bin") +# +IF(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + SET(INSTALL_LIBDIR_SLES "lib64") + SET(INSTALL_PLUGINDIR_SLES "lib64/mysql/plugin") +ELSE() + SET(INSTALL_LIBDIR_SLES "lib") + SET(INSTALL_PLUGINDIR_SLES "lib/mysql/plugin") +ENDIF() +# +SET(INSTALL_INCLUDEDIR_SLES "include/mysql") +# +#SET(INSTALL_DOCDIR_SLES unset - installed directly by SLES) +#SET(INSTALL_DOCREADMEDIR_SLES unset - installed directly by SLES) +SET(INSTALL_INFODIR_SLES "share/info") +SET(INSTALL_MANDIR_SLES "share/man") +# +SET(INSTALL_SHAREDIR_SLES "share") +SET(INSTALL_MYSQLSHAREDIR_SLES "share/mysql") +SET(INSTALL_MYSQLTESTDIR_SLES "share/mysql-test") +SET(INSTALL_SQLBENCHDIR_SLES "") +SET(INSTALL_SUPPORTFILESDIR_SLES "share/mysql") +# +SET(INSTALL_MYSQLDATADIR_SLES "/var/lib/mysql") +SET(INSTALL_PLUGINTESTDIR_SLES ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_SLES ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_SLES ${secure_file_priv_embedded_path}) # # DEB layout @@ -193,8 +409,10 @@ SET(INSTALL_MYSQLTESTDIR_DEB "mysql-test") SET(INSTALL_SQLBENCHDIR_DEB ".") SET(INSTALL_SUPPORTFILESDIR_DEB "support-files") # -SET(INSTALL_MYSQLDATADIR_DEB "data") +SET(INSTALL_MYSQLDATADIR_DEB "/var/lib/mysql") SET(INSTALL_PLUGINTESTDIR_DEB ${plugin_tests}) +SET(INSTALL_SECURE_FILE_PRIVDIR_DEB ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_DEB ${secure_file_priv_embedded_path}) # # SVR4 layout @@ -221,7 +439,8 @@ SET(INSTALL_SUPPORTFILESDIR_SVR4 "support-files") # SET(INSTALL_MYSQLDATADIR_SVR4 "/var/lib/mysql") SET(INSTALL_PLUGINTESTDIR_SVR4 ${plugin_tests}) - +SET(INSTALL_SECURE_FILE_PRIVDIR_SVR4 ${secure_file_priv_path}) +SET(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR_SVR4 ${secure_file_priv_embedded_path}) # Clear cached variables if install layout was changed IF(OLD_INSTALL_LAYOUT) @@ -235,8 +454,29 @@ SET(OLD_INSTALL_LAYOUT ${INSTALL_LAYOUT} CACHE INTERNAL "") # will be defined as ${INSTALL_BINDIR_STANDALONE} by default if STANDALONE # layout is chosen) FOREACH(var BIN SBIN LIB MYSQLSHARE SHARE PLUGIN INCLUDE SCRIPT DOC MAN - INFO MYSQLTEST SQLBENCH DOCREADME SUPPORTFILES MYSQLDATA PLUGINTEST) + INFO MYSQLTEST SQLBENCH DOCREADME SUPPORTFILES MYSQLDATA PLUGINTEST + SECURE_FILE_PRIV SECURE_FILE_PRIV_EMBEDDED) SET(INSTALL_${var}DIR ${INSTALL_${var}DIR_${INSTALL_LAYOUT}} CACHE STRING "${var} installation directory" ${FORCE}) MARK_AS_ADVANCED(INSTALL_${var}DIR) ENDFOREACH() + +# +# Set DEFAULT_SECURE_FILE_PRIV_DIR +# This is used as default value for --secure-file-priv +# +IF(INSTALL_SECURE_FILE_PRIVDIR) + SET(DEFAULT_SECURE_FILE_PRIV_DIR "\"${INSTALL_SECURE_FILE_PRIVDIR}\"" + CACHE INTERNAL "default --secure-file-priv directory" FORCE) +ELSE() + SET(DEFAULT_SECURE_FILE_PRIV_DIR \"\" + CACHE INTERNAL "default --secure-file-priv directory" FORCE) +ENDIF() + +IF(INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR) + SET(DEFAULT_SECURE_FILE_PRIV_EMBEDDED_DIR "\"${INSTALL_SECURE_FILE_PRIV_EMBEDDEDDIR}\"" + CACHE INTERNAL "default --secure-file-priv directory (for embedded library)" FORCE) +ELSE() + SET(DEFAULT_SECURE_FILE_PRIV_EMBEDDED_DIR "NULL" + CACHE INTERNAL "default --secure-file-priv directory (for embedded library)" FORCE) +ENDIF() diff --git a/config.h.cmake b/config.h.cmake index 4548d0a221f24..c7ed127379a32 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -624,4 +624,8 @@ #cmakedefine SIZEOF_TIME_T @SIZEOF_TIME_T@ #cmakedefine TIME_T_UNSIGNED @TIME_T_UNSIGNED@ +/* For --secure-file-priv */ +#cmakedefine DEFAULT_SECURE_FILE_PRIV_DIR @DEFAULT_SECURE_FILE_PRIV_DIR@ +#cmakedefine DEFAULT_SECURE_FILE_PRIV_EMBEDDED_DIR @DEFAULT_SECURE_FILE_PRIV_EMBEDDED_DIR@ + #endif diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql index 45acbc03b7e84..0a3c3bc60b3e6 100644 --- a/mysql-test/include/mtr_warnings.sql +++ b/mysql-test/include/mtr_warnings.sql @@ -1,4 +1,4 @@ --- Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. +-- Copyright (c) 2008, 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 @@ -204,6 +204,11 @@ INSERT INTO global_suppressions VALUES */ ("Found lock of type 6 that is write and read locked"), + /* + Warnings related to --secure-file-priv + */ + ("Insecure configuration for --secure-file-priv:*"), + ("THE_LAST_SUPPRESSION")|| diff --git a/mysql-test/include/mysqld--help.inc b/mysql-test/include/mysqld--help.inc index 380a7f6c8cfa8..7fa57abbe1ed5 100644 --- a/mysql-test/include/mysqld--help.inc +++ b/mysql-test/include/mysqld--help.inc @@ -18,7 +18,8 @@ perl; # their paths may vary: @skipvars=qw/basedir open-files-limit general-log-file log plugin-dir log-slow-queries pid-file slow-query-log-file - datadir slave-load-tmpdir tmpdir socket/; + datadir slave-load-tmpdir tmpdir socket + secure-file-priv/; # Plugins which may or may not be there: @plugins=qw/innodb ndb archive blackhole federated partition ndbcluster debug temp-pool ssl des-key-file diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 684d262f41094..3eb70c1bdb9b4 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl # -*- cperl -*- -# Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2004, 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 @@ -1823,6 +1823,7 @@ sub collect_mysqld_features { mtr_init_args(\$args); mtr_add_arg($args, "--no-defaults"); mtr_add_arg($args, "--datadir=%s", mixed_path($tmpdir)); + mtr_add_arg($args, "--secure-file-priv=\"\""); mtr_add_arg($args, "--lc-messages-dir=%s", $path_language); mtr_add_arg($args, "--skip-grant-tables"); mtr_add_arg($args, "--verbose"); @@ -3297,6 +3298,7 @@ sub mysql_install_db { mtr_add_arg($args, "--loose-skip-falcon"); mtr_add_arg($args, "--loose-skip-ndbcluster"); mtr_add_arg($args, "--tmpdir=%s", "$opt_vardir/tmp/"); + mtr_add_arg($args, "--secure-file-priv=%s", "$opt_vardir"); mtr_add_arg($args, "--core-file"); if ( $opt_debug ) diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index d527d6cb70279..78dc9ab4d880a 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -923,7 +923,6 @@ report-user (No default value) rpl-recovery-rank 0 safe-user-create FALSE secure-auth FALSE -secure-file-priv (No default value) server-id 0 show-slave-auth-info FALSE skip-grant-tables TRUE diff --git a/mysql-test/r/mysqld--help-win.result b/mysql-test/r/mysqld--help-win.result index 2ce9e763b14ad..1d56da7aa5e6e 100644 --- a/mysql-test/r/mysqld--help-win.result +++ b/mysql-test/r/mysqld--help-win.result @@ -931,7 +931,6 @@ report-user (No default value) rpl-recovery-rank 0 safe-user-create FALSE secure-auth FALSE -secure-file-priv (No default value) server-id 0 shared-memory FALSE shared-memory-base-name MYSQL diff --git a/mysql-test/suite/auth_sec/r/secure_file_priv_error.result b/mysql-test/suite/auth_sec/r/secure_file_priv_error.result new file mode 100644 index 0000000000000..4bb4d87c5f068 --- /dev/null +++ b/mysql-test/suite/auth_sec/r/secure_file_priv_error.result @@ -0,0 +1,7 @@ +#----------------------------------------------------------------------- +# Setup +# Try to restart server with invalid value for --secure-file-priv +# Search for : Failed to access directory for --secure-file-priv. +# Restart completed. +# Restart +#----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/r/secure_file_priv_null.result b/mysql-test/suite/auth_sec/r/secure_file_priv_null.result new file mode 100644 index 0000000000000..e2a5102c627e3 --- /dev/null +++ b/mysql-test/suite/auth_sec/r/secure_file_priv_null.result @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------- +# Setup +#----------------------------------------------------------------------- +# Search for : --secure-file-priv is set to NULL. Operations +# related to importing and exporting data are +# disabled +show variables like 'secure_file_priv'; +Variable_name Value +secure_file_priv null +use test; +drop table if exists secure_file_priv_test_null; +create table secure_file_priv_test_null(c1 int); +insert into secure_file_priv_test_null values (1), (2), (3), (4); +select * from secure_file_priv_test_null into outfile 'blah'; +ERROR HY000: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement +select * from secure_file_priv_test_null into outfile 'null/blah'; +ERROR HY000: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement +drop table secure_file_priv_test_null; +#----------------------------------------------------------------------- +# Clean-up +#----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/r/secure_file_priv_warnings.result b/mysql-test/suite/auth_sec/r/secure_file_priv_warnings.result new file mode 100644 index 0000000000000..3b80cbe8d6fcf --- /dev/null +++ b/mysql-test/suite/auth_sec/r/secure_file_priv_warnings.result @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------- +# Setup +#----------------------------------------------------------------------- +# Search for : Insecure configuration for --secure-file-priv: Current +# value does not restrict location of generated files. +# Consider setting it to a valid, non-empty path. +SHOW VARIABLES LIKE 'secure_file_priv'; +Variable_name Value +secure_file_priv +#----------------------------------------------------------------------- +# Restart completed. +# Search for : Insecure configuration for --secure-file-priv: Plugin +# directory is accessible through --secure-file-priv. +# Consider choosing a different directory. +#----------------------------------------------------------------------- +# Clean-up +#----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/r/secure_file_priv_warnings_not_win.result b/mysql-test/suite/auth_sec/r/secure_file_priv_warnings_not_win.result new file mode 100644 index 0000000000000..84e2f8ac3c214 --- /dev/null +++ b/mysql-test/suite/auth_sec/r/secure_file_priv_warnings_not_win.result @@ -0,0 +1,9 @@ +#----------------------------------------------------------------------- +# Search for : Insecure configuration for --secure-file-priv: Data +# directory is accessible through --secure-file-priv. +# Consider choosing a different directory. +#----------------------------------------------------------------------- +# Search for : Insecure configuration for --secure-file-priv: Location +# is accessible to all OS users. Consider choosing a +# different directory. +#----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/r/secure_file_priv_warnings_win.result b/mysql-test/suite/auth_sec/r/secure_file_priv_warnings_win.result new file mode 100644 index 0000000000000..3beff6c4747fe --- /dev/null +++ b/mysql-test/suite/auth_sec/r/secure_file_priv_warnings_win.result @@ -0,0 +1,8 @@ +#----------------------------------------------------------------------- +# Test 2 : Restarting mysqld with : +# --secure-file-priv=MYSQLTEST_VARDIR/mysqld.1/Data +# Restart completed. +# Search for : Insecure configuration for --secure-file-priv: Data +# directory is accessible through --secure-file-priv. +# Consider choosing a different directory. +#----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/t/secure_file_priv_error.test b/mysql-test/suite/auth_sec/t/secure_file_priv_error.test new file mode 100644 index 0000000000000..9f8d185d8f51c --- /dev/null +++ b/mysql-test/suite/auth_sec/t/secure_file_priv_error.test @@ -0,0 +1,39 @@ +--source include/no_valgrind_without_big.inc +--source include/not_embedded.inc + +--echo #----------------------------------------------------------------------- +--echo # Setup +let restart_log= $MYSQLTEST_VARDIR/log/my_restart.err; +let SEARCH_FILE= $restart_log; +let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; + +--echo # Try to restart server with invalid value for --secure-file-priv +--exec echo "wait" > $restart_file +--shutdown_server +--source include/wait_until_disconnected.inc + +--error 0,1 +--remove_file $restart_log +# Following should fail +--error 1 +--exec $MYSQLD_CMD --secure-file-priv=blahblahblah --loose-console > $restart_log 2>&1 + +--echo # Search for : Failed to access directory for --secure-file-priv. +let SEARCH_PATTERN= Failed to access directory for --secure-file-priv; +--source include/search_pattern_in_file.inc + +--remove_file $restart_log + +--source include/wait_until_disconnected.inc +# Dummy argument for restart +--exec echo "restart:" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc +--disable_reconnect +--echo # Restart completed. + +--echo # Restart +--disable_warnings +--source include/force_restart.inc +--enable_warnings +--echo #----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/t/secure_file_priv_null-master.opt b/mysql-test/suite/auth_sec/t/secure_file_priv_null-master.opt new file mode 100644 index 0000000000000..80d7f3cd46905 --- /dev/null +++ b/mysql-test/suite/auth_sec/t/secure_file_priv_null-master.opt @@ -0,0 +1 @@ +--secure-file-priv=null diff --git a/mysql-test/suite/auth_sec/t/secure_file_priv_null.test b/mysql-test/suite/auth_sec/t/secure_file_priv_null.test new file mode 100644 index 0000000000000..8d394a135895a --- /dev/null +++ b/mysql-test/suite/auth_sec/t/secure_file_priv_null.test @@ -0,0 +1,42 @@ +--source include/no_valgrind_without_big.inc +--source include/not_embedded.inc + +--echo #----------------------------------------------------------------------- +--echo # Setup +let server_log= $MYSQLTEST_VARDIR/log/mysqld.1.err; +let SEARCH_FILE= $server_log; +let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +--echo #----------------------------------------------------------------------- + +--echo # Search for : --secure-file-priv is set to NULL. Operations +--echo # related to importing and exporting data are +--echo # disabled +let SEARCH_PATTERN= --secure-file-priv is set to NULL. Operations related to importing and exporting data are disabled; +--source include/search_pattern_in_file.inc + +connect(test4_con,localhost,root,,,,,); +show variables like 'secure_file_priv'; + +use test; +--disable_warnings +drop table if exists secure_file_priv_test_null; +--enable_warnings +create table secure_file_priv_test_null(c1 int); +insert into secure_file_priv_test_null values (1), (2), (3), (4); +--error 1290 +select * from secure_file_priv_test_null into outfile 'blah'; +--error 1290 +select * from secure_file_priv_test_null into outfile 'null/blah'; +drop table secure_file_priv_test_null; + +connection default; +disconnect test4_con; + +--echo #----------------------------------------------------------------------- + +--echo # Clean-up +--disable_warnings +--source include/force_restart.inc +--enable_warnings + +--echo #----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/t/secure_file_priv_warnings-master.opt b/mysql-test/suite/auth_sec/t/secure_file_priv_warnings-master.opt new file mode 100644 index 0000000000000..22520f0aa9901 --- /dev/null +++ b/mysql-test/suite/auth_sec/t/secure_file_priv_warnings-master.opt @@ -0,0 +1 @@ +--secure-file-priv="" diff --git a/mysql-test/suite/auth_sec/t/secure_file_priv_warnings.test b/mysql-test/suite/auth_sec/t/secure_file_priv_warnings.test new file mode 100644 index 0000000000000..cc7a79d5b3c18 --- /dev/null +++ b/mysql-test/suite/auth_sec/t/secure_file_priv_warnings.test @@ -0,0 +1,47 @@ +--source include/no_valgrind_without_big.inc +--source include/not_embedded.inc + +--echo #----------------------------------------------------------------------- +--echo # Setup +let server_log= $MYSQLTEST_VARDIR/log/mysqld.1.err; +let SEARCH_FILE= $server_log; +let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +let PLUGIN_DIR= $MYSQLTEST_VARDIR/tmp; +--echo #----------------------------------------------------------------------- + +--echo # Search for : Insecure configuration for --secure-file-priv: Current +--echo # value does not restrict location of generated files. +--echo # Consider setting it to a valid, non-empty path. +let SEARCH_PATTERN= Insecure configuration for --secure-file-priv: Current value does not restrict location of generated files. Consider setting it to a valid, non-empty path.; +--source include/search_pattern_in_file.inc + +# Must show empty string +SHOW VARIABLES LIKE 'secure_file_priv'; + +--echo #----------------------------------------------------------------------- + +let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +--exec echo "wait" > $restart_file +--shutdown_server +--source include/wait_until_disconnected.inc +--remove_file $server_log +--exec echo "restart:--plugin-dir=$PLUGIN_DIR --secure-file-priv=$PLUGIN_DIR" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc +--disable_reconnect +--echo # Restart completed. + +--echo # Search for : Insecure configuration for --secure-file-priv: Plugin +--echo # directory is accessible through --secure-file-priv. +--echo # Consider choosing a different directory. +let SEARCH_PATTERN= Insecure configuration for --secure-file-priv: Plugin directory is accessible through --secure-file-priv. Consider choosing a different directory.; +--source include/search_pattern_in_file.inc + +--echo #----------------------------------------------------------------------- + +--echo # Clean-up +--disable_warnings +--source include/force_restart.inc +--enable_warnings + +--echo #----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/t/secure_file_priv_warnings_not_win.test b/mysql-test/suite/auth_sec/t/secure_file_priv_warnings_not_win.test new file mode 100644 index 0000000000000..ec027d4a743f5 --- /dev/null +++ b/mysql-test/suite/auth_sec/t/secure_file_priv_warnings_not_win.test @@ -0,0 +1,24 @@ +--source include/no_valgrind_without_big.inc +--source include/not_windows.inc +--source include/not_embedded.inc + +let server_log= $MYSQLTEST_VARDIR/log/mysqld.1.err; +let SEARCH_FILE= $server_log; + +--echo #----------------------------------------------------------------------- + +--echo # Search for : Insecure configuration for --secure-file-priv: Data +--echo # directory is accessible through --secure-file-priv. +--echo # Consider choosing a different directory. +let SEARCH_PATTERN= Insecure configuration for --secure-file-priv: Data directory is accessible through --secure-file-priv. Consider choosing a different directory.; +--source include/search_pattern_in_file.inc + +--echo #----------------------------------------------------------------------- + +--echo # Search for : Insecure configuration for --secure-file-priv: Location +--echo # is accessible to all OS users. Consider choosing a +--echo # different directory. +let SEARCH_PATTERN= Insecure configuration for --secure-file-priv: Location is accessible to all OS users. Consider choosing a different directory.; +--source include/search_pattern_in_file.inc + +--echo #----------------------------------------------------------------------- diff --git a/mysql-test/suite/auth_sec/t/secure_file_priv_warnings_win.test b/mysql-test/suite/auth_sec/t/secure_file_priv_warnings_win.test new file mode 100644 index 0000000000000..bb175fb40ea3f --- /dev/null +++ b/mysql-test/suite/auth_sec/t/secure_file_priv_warnings_win.test @@ -0,0 +1,35 @@ +--source include/no_valgrind_without_big.inc +--source include/windows.inc +--source include/not_embedded.inc + +let server_log= $MYSQLTEST_VARDIR/log/mysqld.1.err; +let SEARCH_FILE= $server_log; + +--echo #----------------------------------------------------------------------- + +--echo # Test 2 : Restarting mysqld with : +--echo # --secure-file-priv=MYSQLTEST_VARDIR/mysqld.1/Data + +let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +--exec echo "wait" > $restart_file +--shutdown_server +--source include/wait_until_disconnected.inc +--error 0,1 +--remove_file $server_log +--exec echo "restart: --secure-file-priv=$MYSQLTEST_VARDIR/mysqld.1/Data" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc +--disable_reconnect +--echo # Restart completed. + +--echo # Search for : Insecure configuration for --secure-file-priv: Data +--echo # directory is accessible through --secure-file-priv. +--echo # Consider choosing a different directory. +let SEARCH_PATTERN= Insecure configuration for --secure-file-priv: Data directory is accessible through --secure-file-priv. Consider choosing a different directory.; +--source include/search_pattern_in_file.inc + +--disable_warnings +--source include/force_restart.inc +--enable_warnings + +--echo #----------------------------------------------------------------------- diff --git a/packaging/rpm-oel/mysql-systemd-start b/packaging/rpm-oel/mysql-systemd-start index fab7b3627b31a..231a76087ac3e 100644 --- a/packaging/rpm-oel/mysql-systemd-start +++ b/packaging/rpm-oel/mysql-systemd-start @@ -30,6 +30,12 @@ install_db () { if [ -x /usr/sbin/restorecon ]; then /usr/sbin/restorecon "$datadir" /usr/sbin/restorecon $log + for dir in /var/lib/mysql-files ; do + if [ -x /usr/sbin/semanage -a -d /var/lib/mysql -a -d $dir ] ; then + /usr/sbin/semanage fcontext -a -e /var/lib/mysql $dir >/dev/null 2>&1 + /sbin/restorecon $dir + fi + done fi # If special mysql dir is in place, skip db install diff --git a/packaging/rpm-oel/mysql.init b/packaging/rpm-oel/mysql.init index aaea498d15339..75ae672801b77 100644 --- a/packaging/rpm-oel/mysql.init +++ b/packaging/rpm-oel/mysql.init @@ -82,7 +82,15 @@ start(){ fi chown mysql:mysql "$datadir" chmod 0755 "$datadir" - [ -x /sbin/restorecon ] && /sbin/restorecon "$datadir" + if [ -x /sbin/restorecon ]; then + /sbin/restorecon "$datadir" + for dir in /var/lib/mysql-files ; do + if [ -x /usr/sbin/semanage -a -d /var/lib/mysql -a -d $dir ] ; then + /usr/sbin/semanage fcontext -a -e /var/lib/mysql $dir >/dev/null 2>&1 + /sbin/restorecon $dir + fi + done + fi # Now create the database action $"Initializing MySQL database: " /usr/bin/mysql_install_db --rpm --datadir="$datadir" --user=mysql ret=$? diff --git a/packaging/rpm-oel/mysql.spec.in b/packaging/rpm-oel/mysql.spec.in index 409c325b6759f..7ef294ffa8492 100644 --- a/packaging/rpm-oel/mysql.spec.in +++ b/packaging/rpm-oel/mysql.spec.in @@ -560,6 +560,7 @@ MBD=$RPM_BUILD_DIR/%{src_dir} install -d -m 0755 %{buildroot}%{_datadir}/mysql/SELinux/RHEL4 install -d -m 0755 %{buildroot}/var/lib/mysql install -d -m 0755 %{buildroot}/var/run/mysqld +install -d -m 0750 %{buildroot}/var/lib/mysql-files # Install all binaries cd $MBD/release @@ -790,6 +791,7 @@ fi %attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/logrotate.d/mysql %dir %attr(755, mysql, mysql) /var/lib/mysql %dir %attr(755, mysql, mysql) /var/run/mysqld +%dir %attr(750, mysql, mysql) /var/lib/mysql-files %files common %defattr(-, root, root, -) @@ -916,6 +918,9 @@ fi %endif %changelog +* Mon Sep 26 2016 Balasubramanian Kandasamy - 5.5.53-1 +- Include mysql-files directory + * Tue Jul 05 2016 Balasubramanian Kandasamy - 5.5.51-1 - Remove mysql_config from client subpackage diff --git a/packaging/rpm-sles/mysql.spec.in b/packaging/rpm-sles/mysql.spec.in index a11dfff7b70e0..6652cdcccb614 100644 --- a/packaging/rpm-sles/mysql.spec.in +++ b/packaging/rpm-sles/mysql.spec.in @@ -425,6 +425,7 @@ MBD=$RPM_BUILD_DIR/%{src_dir} install -d -m 0755 %{buildroot}/var/lib/mysql install -d -m 0755 %{buildroot}/var/run/mysql install -d -m 0750 %{buildroot}/var/log/mysql +install -d -m 0750 %{buildroot}/var/lib/mysql-files # Install all binaries cd $MBD/release @@ -638,6 +639,7 @@ fi %dir %attr(755, mysql, mysql) /var/lib/mysql %dir %attr(755, mysql, mysql) /var/run/mysql %dir %attr(750, mysql, mysql) /var/log/mysql +%dir %attr(750, mysql, mysql) /var/lib/mysql-files %files common %defattr(-, root, root, -) @@ -783,6 +785,9 @@ fi %attr(755, root, root) %{_libdir}/mysql/libmysqld.so %changelog +* Mon Sep 26 2016 Balasubramanian Kandasamy - 5.5.53-1 +- Include mysql-files directory + * Tue Sep 29 2015 Balasubramanian Kandasamy - 5.5.47-1 - Added conflicts to mysql-connector-c-shared dependencies diff --git a/packaging/solaris/postinstall-solaris.sh b/packaging/solaris/postinstall-solaris.sh index b024d94f15854..a31e151e1bb32 100644 --- a/packaging/solaris/postinstall-solaris.sh +++ b/packaging/solaris/postinstall-solaris.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2008, 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 @@ -26,6 +26,7 @@ mygroup=mysql myuser=mysql mydatadir=/var/lib/mysql basedir=@@basedir@@ +mysecurefiledir=/var/lib/mysql-files if [ -n "$BASEDIR" ] ; then basedir="$BASEDIR" @@ -58,6 +59,11 @@ fi chown -R $myuser:$mygroup $mydatadir +# Create securefile directory +[ -d "$mysecurefiledir" ] || mkdir -p -m 770 "$mysecurefiledir" || exit 1 +chown -R $myuser:$mygroup $mysecurefiledir + + # Solaris patch 119255 (somewhere around revision 42) changes the behaviour # of pkgadd to set TMPDIR internally to a root-owned install directory. This # has the unfortunate side effect of breaking running mysql_install_db with diff --git a/sql/mysqld.cc b/sql/mysqld.cc index d8edbe4b637a6..c969fd8a62a38 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -570,6 +570,7 @@ uint mysql_real_data_home_len, mysql_data_home_len= 1; uint reg_ext_length; const key_map key_map_empty(0); key_map key_map_full(0); // Will be initialized later +char secure_file_real_path[FN_REFLEN]; DATE_TIME_FORMAT global_date_format, global_datetime_format, global_time_format; Time_zone *default_tz; @@ -7613,9 +7614,9 @@ bool is_secure_file_path(char *path) char buff1[FN_REFLEN], buff2[FN_REFLEN]; size_t opt_secure_file_priv_len; /* - All paths are secure if opt_secure_file_path is 0 + All paths are secure if opt_secure_file_priv is 0 */ - if (!opt_secure_file_priv) + if (!opt_secure_file_priv[0]) return TRUE; opt_secure_file_priv_len= strlen(opt_secure_file_priv); @@ -7623,6 +7624,9 @@ bool is_secure_file_path(char *path) if (strlen(path) >= FN_REFLEN) return FALSE; + if (!my_strcasecmp(system_charset_info, opt_secure_file_priv, "NULL")) + return FALSE; + if (my_realpath(buff1, path, 0)) { /* @@ -7655,9 +7659,184 @@ bool is_secure_file_path(char *path) } +/** + check_secure_file_priv_path : Checks path specified through + --secure-file-priv and raises warning in following cases: + 1. If path is empty string or NULL and mysqld is not running + with --bootstrap mode. + 2. If path can access data directory + 3. If path points to a directory which is accessible by + all OS users (non-Windows build only) + + It throws error in following cases: + + 1. If path normalization fails + 2. If it can not get stats of the directory + + @params NONE + + Assumptions : + 1. Data directory path has been normalized + 2. opt_secure_file_priv has been normalized unless it is set + to "NULL". + + @returns Status of validation + @retval true : Validation is successful with/without warnings + @retval false : Validation failed. Error is raised. +*/ + +bool check_secure_file_priv_path() +{ + char datadir_buffer[FN_REFLEN+1]={0}; + char plugindir_buffer[FN_REFLEN+1]={0}; + char whichdir[20]= {0}; + size_t opt_plugindir_len= 0; + size_t opt_datadir_len= 0; + size_t opt_secure_file_priv_len= 0; + bool warn= false; + bool case_insensitive_fs; +#ifndef _WIN32 + MY_STAT dir_stat; +#endif + + if (!opt_secure_file_priv[0]) + { + if (opt_bootstrap) + { + /* + Do not impose --secure-file-priv restriction + in --bootstrap mode + */ + sql_print_information("Ignoring --secure-file-priv value as server is " + "running with --bootstrap."); + } + else + { + sql_print_warning("Insecure configuration for --secure-file-priv: " + "Current value does not restrict location of generated " + "files. Consider setting it to a valid, " + "non-empty path."); + } + return true; + } + + /* + Setting --secure-file-priv to NULL would disable + reading/writing from/to file + */ + if(!my_strcasecmp(system_charset_info, opt_secure_file_priv, "NULL")) + { + sql_print_information("--secure-file-priv is set to NULL. " + "Operations related to importing and exporting " + "data are disabled"); + return true; + } + + /* + Check if --secure-file-priv can access data directory + */ + opt_secure_file_priv_len= strlen(opt_secure_file_priv); + + /* + Adds dir seperator at the end. + This is required in subsequent comparison + */ + convert_dirname(datadir_buffer, mysql_unpacked_real_data_home, NullS); + opt_datadir_len= strlen(datadir_buffer); + + case_insensitive_fs= + (test_if_case_insensitive(datadir_buffer) == 1); + + if (!case_insensitive_fs) + { + if (!strncmp(datadir_buffer, opt_secure_file_priv, + opt_datadir_len < opt_secure_file_priv_len ? + opt_datadir_len : opt_secure_file_priv_len)) + { + warn= true; + strcpy(whichdir, "Data directory"); + } + } + else + { + if (!files_charset_info->coll->strnncoll(files_charset_info, + (uchar *) datadir_buffer, + opt_datadir_len, + (uchar *) opt_secure_file_priv, + opt_secure_file_priv_len, + TRUE)) + { + warn= true; + strcpy(whichdir, "Data directory"); + } + } + + /* + Don't bother comparing --secure-file-priv with --plugin-dir + if we already have a match against --datadir or + --plugin-dir is not pointing to a valid directory. + */ + if (!warn && !my_realpath(plugindir_buffer, opt_plugin_dir, 0)) + { + convert_dirname(plugindir_buffer, plugindir_buffer, NullS); + opt_plugindir_len= strlen(plugindir_buffer); + + if (!case_insensitive_fs) + { + if (!strncmp(plugindir_buffer, opt_secure_file_priv, + opt_plugindir_len < opt_secure_file_priv_len ? + opt_plugindir_len : opt_secure_file_priv_len)) + { + warn= true; + strcpy(whichdir, "Plugin directory"); + } + } + else + { + if (!files_charset_info->coll->strnncoll(files_charset_info, + (uchar *) plugindir_buffer, + opt_plugindir_len, + (uchar *) opt_secure_file_priv, + opt_secure_file_priv_len, + TRUE)) + { + warn= true; + strcpy(whichdir, "Plugin directory"); + } + } + } + + + if (warn) + sql_print_warning("Insecure configuration for --secure-file-priv: " + "%s is accessible through " + "--secure-file-priv. Consider choosing a different " + "directory.", whichdir); + +#ifndef _WIN32 + /* + Check for --secure-file-priv directory's permission + */ + if (!(my_stat(opt_secure_file_priv, &dir_stat, MYF(0)))) + { + sql_print_error("Failed to get stat for directory pointed out " + "by --secure-file-priv"); + return false; + } + + if (dir_stat.st_mode & S_IRWXO) + sql_print_warning("Insecure configuration for --secure-file-priv: " + "Location is accessible to all OS users. " + "Consider choosing a different directory."); +#endif + return true; +} + + static int fix_paths(void) { char buff[FN_REFLEN],*pos; + bool secure_file_priv_nonempty= false; convert_dirname(mysql_home,mysql_home,NullS); /* Resolve symlinks to allow 'mysql_home' to be a relative symlink */ my_realpath(mysql_home,mysql_home,MYF(0)); @@ -7715,29 +7894,56 @@ static int fix_paths(void) Convert the secure-file-priv option to system format, allowing a quick strcmp to check if read or write is in an allowed dir */ - if (opt_secure_file_priv) + if (opt_bootstrap) + opt_secure_file_priv= EMPTY_STR.str; + secure_file_priv_nonempty= opt_secure_file_priv[0] ? true : false; + + if (secure_file_priv_nonempty && strlen(opt_secure_file_priv) > FN_REFLEN) { - if (*opt_secure_file_priv == 0) - { - my_free(opt_secure_file_priv); - opt_secure_file_priv= 0; - } - else + sql_print_warning("Value for --secure-file-priv is longer than maximum " + "limit of %d", FN_REFLEN-1); + return 1; + } + + memset(buff, 0, sizeof(buff)); + if (secure_file_priv_nonempty && + my_strcasecmp(system_charset_info, opt_secure_file_priv, "NULL")) + { + int retval= my_realpath(buff, opt_secure_file_priv, MYF(MY_WME)); + if (!retval) { - if (strlen(opt_secure_file_priv) >= FN_REFLEN) - opt_secure_file_priv[FN_REFLEN-1]= '\0'; - if (my_realpath(buff, opt_secure_file_priv, 0)) + convert_dirname(secure_file_real_path, buff, NullS); +#ifdef WIN32 + MY_DIR *dir= my_dir(secure_file_real_path, MYF(MY_DONT_SORT+MY_WME)); + if (!dir) { - sql_print_warning("Failed to normalize the argument for --secure-file-priv."); - return 1; + retval= 1; } - char *secure_file_real_path= (char *)my_malloc(FN_REFLEN, MYF(MY_FAE)); - convert_dirname(secure_file_real_path, buff, NullS); - my_free(opt_secure_file_priv); - opt_secure_file_priv= secure_file_real_path; + else + { + my_dirend(dir); + } +#endif + } + + if (retval) + { + char err_buffer[FN_REFLEN]; + my_snprintf(err_buffer, FN_REFLEN-1, + "Failed to access directory for --secure-file-priv." + " Please make sure that directory exists and is " + "accessible by MySQL Server. Supplied value : %s", + opt_secure_file_priv); + err_buffer[FN_REFLEN-1]='\0'; + sql_print_error("%s", err_buffer); + return 1; } + opt_secure_file_priv= secure_file_real_path; } - + + if (!check_secure_file_priv_path()) + return 1; + return 0; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0696021cfc09b..d9fda85d8f669 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -68,6 +68,8 @@ char internal_table_name[2]= "*"; char empty_c_string[1]= {0}; /* used for not defined db */ +LEX_STRING EMPTY_STR= { (char *) "", 0 }; + const char * const THD::DEFAULT_WHERE= "field list"; diff --git a/sql/sql_class.h b/sql/sql_class.h index dcc7458ee5043..aa6745e4564ea 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -105,6 +105,7 @@ enum enum_filetype { FILETYPE_CSV, FILETYPE_XML }; extern char internal_table_name[2]; extern char empty_c_string[1]; +extern LEX_STRING EMPTY_STR; extern MYSQL_PLUGIN_IMPORT const char **errmesg; extern bool volatile shutdown_in_progress; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index d08cb4f8ca838..6fd728d638dea 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1941,8 +1941,12 @@ static Sys_var_charptr Sys_secure_file_priv( "secure_file_priv", "Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files " "within specified directory", - PREALLOCATED READ_ONLY GLOBAL_VAR(opt_secure_file_priv), - CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT(0)); + READ_ONLY GLOBAL_VAR(opt_secure_file_priv), +#ifndef EMBEDDED_LIBRARY + CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT(DEFAULT_SECURE_FILE_PRIV_DIR)); +#else + CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT(DEFAULT_SECURE_FILE_PRIV_EMBEDDED_DIR)); +#endif static bool fix_server_id(sys_var *self, THD *thd, enum_var_type type) { diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 5af4783f9195a..211ed4f38881d 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 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 @@ -562,6 +562,7 @@ install -d $RBR%{_includedir} install -d $RBR%{_libdir} install -d $RBR%{_mandir} install -d $RBR%{_sbindir} +install -d $RBR/var/lib/mysql-files mkdir -p $RBR%{_sysconfdir}/my.cnf.d @@ -1141,6 +1142,7 @@ echo "=====" >> $STATUS_HISTORY %attr(755, root, root) %{_sysconfdir}/init.d/mysql %attr(755, root, root) %{_datadir}/mysql/ +%dir %attr(750, mysql, mysql) /var/lib/mysql-files # ---------------------------------------------------------------------------- %files -n MySQL-client%{product_suffix} @@ -1226,6 +1228,9 @@ echo "=====" >> $STATUS_HISTORY # merging BK trees) ############################################################################## %changelog +* Mon Sep 26 2016 Balasubramanian Kandasamy +- Include mysql-files directory + * Wed Jul 02 2014 Bjorn Munch - Disable dtrace unconditionally, breaks after we install Oracle dtrace From da97aa6885959daff4b87360128cdc9952e4759e Mon Sep 17 00:00:00 2001 From: "mysql-builder@oracle.com" <> Date: Thu, 29 Sep 2016 11:02:05 +0530 Subject: [PATCH 15/73] From 65febcce97ebe2da0c9723b76a041e249b053a98 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Tue, 27 Sep 2016 14:09:54 +0300 Subject: [PATCH 16/73] Fix Bug#24707869 GCC 5 AND 6 MISCOMPILE MACH_PARSE_COMPRESSED Prevent GCC from moving a mach_read_from_4() before we have checked that we have 4 bytes to read. The pointer may only point to a 1, 2 or 3 bytes in which case the code should not read 4 bytes. This is a workaround to a GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77673 Patch submitted by: Laurynas Biveinis RB: 14135 Reviewed by: Pawel Olchawa --- storage/innobase/mach/mach0data.c | 53 ++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/storage/innobase/mach/mach0data.c b/storage/innobase/mach/mach0data.c index 95b135b09541e..9669516244de0 100644 --- a/storage/innobase/mach/mach0data.c +++ b/storage/innobase/mach/mach0data.c @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2009, Innobase Oy. 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,8 +55,22 @@ mach_parse_compressed( if (flag < 0x80UL) { *val = flag; return(ptr + 1); + } + + /* Workaround GCC bug + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77673: + the compiler moves mach_read_from_4 right to the beginning of the + 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__) +#define DEPLOY_FENCE +#endif + +#ifdef DEPLOY_FENCE + __atomic_thread_fence(__ATOMIC_ACQUIRE); +#endif - } else if (flag < 0xC0UL) { + if (flag < 0xC0UL) { if (end_ptr < ptr + 2) { return(NULL); } @@ -64,8 +78,13 @@ mach_parse_compressed( *val = mach_read_from_2(ptr) & 0x7FFFUL; return(ptr + 2); + } + +#ifdef DEPLOY_FENCE + __atomic_thread_fence(__ATOMIC_ACQUIRE); +#endif - } else if (flag < 0xE0UL) { + if (flag < 0xE0UL) { if (end_ptr < ptr + 3) { return(NULL); } @@ -73,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); } @@ -81,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 + +#undef DEPLOY_FENCE + + ut_ad(flag == 0xF0UL); - *val = mach_read_from_4(ptr + 1); - return(ptr + 5); + if (end_ptr < ptr + 5) { + return(NULL); } + + *val = mach_read_from_4(ptr + 1); + return(ptr + 5); } From 1f93f4381b60e3a8012ba36a4dec920416073759 Mon Sep 17 00:00:00 2001 From: Terje Rosten Date: Thu, 6 Oct 2016 13:26:16 +0200 Subject: [PATCH 17/73] Bug#24483092 UNSAFE USE OF VARIOUS SHELL UTILITIES - Remove use of touch and chmod. - Restrict usage of chown to cases where target directory is /var/log. - Due to limited feature set in /bin/sh on Solaris, /bin/bash will be used on this platform. - Give error if directory for UNIX socket file is missing. - Privileged user should not log to files owned by different user (mysqld will log as before). --- scripts/CMakeLists.txt | 10 +++- scripts/mysqld_safe.sh | 109 +++++++++++++++++++++++++++++++---------- 2 files changed, 92 insertions(+), 27 deletions(-) diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 05bf8530a2634..920b68543349c 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 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 @@ -105,7 +105,13 @@ ELSE() ENDIF() IF(UNIX) - # FIND_PROC and CHECK_PID are used by mysqld_safe + # SHELL_PATH, FIND_PROC, CHECK_PID are used by mysqld_safe +IF(CMAKE_SYSTEM_NAME MATCHES "SunOS") + SET (SHELL_PATH "/bin/bash") +ELSE() + SET (SHELL_PATH "/bin/sh") +ENDIF() + IF(CMAKE_SYSTEM_NAME MATCHES "Linux") SET (FIND_PROC "ps wwwp $PID | grep -v mysqld_safe | grep -- $MYSQLD > /dev/null") diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 1b30a3bb15ba2..4b103817ab67d 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!@SHELL_PATH@ # Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB # This file is public domain and comes with NO WARRANTY of any kind # @@ -125,7 +125,13 @@ log_generic () { echo "$msg" case $logging in init) ;; # Just echo the message, don't save it anywhere - file) echo "$msg" >> "$err_log" ;; + file) + if [ -w / -o "$USER" = "root" ]; then + true + else + echo "$msg" >> "$err_log" + fi + ;; syslog) logger -t "$syslog_tag_mysqld_safe" -p "$priority" "$*" ;; *) echo "Internal program error (non-fatal):" \ @@ -145,7 +151,13 @@ log_notice () { eval_log_error () { cmd="$1" case $logging in - file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;; + file) + if [ -w / -o "$USER" = "root" ]; then + cmd="$cmd > /dev/null 2>&1" + else + cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" + fi + ;; syslog) # mysqld often prefixes its messages with a timestamp, which is # redundant when logging to syslog (which adds its own timestamp) @@ -571,14 +583,7 @@ then fi # Log to err_log file - log_notice "Logging to '$err_log'." logging=file - - if [ ! -f "$err_log" -a ! -h "$err_log" ]; then # if error log already exists, - touch "$err_log" # we just append. otherwise, - chmod "$fmode" "$err_log" # fix the permissions here! - fi - else if [ -n "$syslog_tag" ] then @@ -591,6 +596,48 @@ else logging=syslog fi +logdir=`dirname "$err_log"` +# Change the err log to the right user, if possible and it is in use +if [ $logging = "file" -o $logging = "both" ]; then + if [ ! -f "$err_log" -a ! -h "$err_log" ]; then + if test -w / -o "$USER" = "root"; then + case $logdir in + /var/log) + ( + umask 0137 + set -o noclobber + > "$err_log" && chown $user "$err_log" + ) ;; + *) ;; + esac + else + ( + umask 0137 + set -o noclobber + > "$err_log" + ) + fi + fi + + if [ -f "$err_log" ]; then # Log to err_log file + log_notice "Logging to '$err_log'." + elif [ "x$user" = "xroot" ]; then # running as root, mysqld can create log file; continue + echo "Logging to '$err_log'." >&2 + else + case $logdir in + # We can't create $err_log, however mysqld can; continue + /tmp|/var/tmp|/var/log/mysql|$DATADIR) + echo "Logging to '$err_log'." >&2 + ;; + # We can't create $err_log and don't know if mysqld can; error out + *) + log_error "error: log-error set to '$err_log', however file don't exists. Create writable for user '$user'." + exit 1 + ;; + esac + fi +fi + USER_OPTION="" if test -w / -o "$USER" = "root" then @@ -598,11 +645,6 @@ then then USER_OPTION="--user=$user" fi - # Change the err log to the right user, if it is in use - if [ $want_syslog -eq 0 -a ! -h "$err_log" ]; then - touch "$err_log" - chown $user "$err_log" - fi if test -n "$open_files" then ulimit -n $open_files @@ -615,15 +657,12 @@ then fi safe_mysql_unix_port=${mysql_unix_port:-${MYSQL_UNIX_PORT:-@MYSQL_UNIX_ADDR@}} -# Make sure that directory for $safe_mysql_unix_port exists +# Check that directory for $safe_mysql_unix_port exists mysql_unix_port_dir=`dirname $safe_mysql_unix_port` if [ ! -d $mysql_unix_port_dir ] then - if [ ! -h $mysql_unix_port_dir ]; then - mkdir $mysql_unix_port_dir - chown $user $mysql_unix_port_dir - chmod 755 $mysql_unix_port_dir - fi + log_error "Directory '$mysql_unix_port_dir' for UNIX socket file don't exists." + exit 1 fi # If the user doesn't specify a binary, we assume name "mysqld" @@ -800,11 +839,31 @@ do eval_log_error "$cmd" + # hypothetical: log was renamed but not + # flushed yet. we'd recreate it with + # wrong owner next time we log, so set + # it up correctly while we can! + if [ $want_syslog -eq 0 -a ! -f "$err_log" -a ! -h "$err_log" ]; then - touch "$err_log" # hypothetical: log was renamed but not - chown $user "$err_log" # flushed yet. we'd recreate it with - chmod "$fmode" "$err_log" # wrong owner next time we log, so set - fi # it up correctly while we can! + if test -w / -o "$USER" = "root"; then + logdir=`dirname "$err_log"` + case $logdir in + /var/log) + ( + umask 0137 + set -o noclobber + > "$err_log" && chown $user "$err_log" + ) ;; + *) ;; + esac + else + ( + umask 0137 + set -o noclobber + > "$err_log" + ) + fi + fi end_time=`date +%M%S` From 149212772804e93983f80b63099ba9e1241ddf4f Mon Sep 17 00:00:00 2001 From: Karthik Kamath Date: Thu, 13 Oct 2016 14:48:45 +0530 Subject: [PATCH 18/73] BUG#23499695: MYSQL SERVER NORMAL SHUTDOWN WITH TIME STAMP 700101 ANALYSIS: ========= To set the time 'start_time' of query in THD, current time is obtained by calling 'gettimeofday()'. On Solaris platform, due to some system level issues, time obtained is invalid i.e. its either greater than 2038 (max signed value to hold microseconds since 1970) or 1970 (0 microseconds since 1970). In these cases, validation checks infer that the 'start_time' is invalid and mysql server initiates the shutdown process. But the reason for shutdown is not logged. FIX: ==== We are now logging appropriate message when shutdown is triggered in the above mentioned scenarios. Now, even if the initial validation checks infer that the 'start_time' is invalid, server shutdown is not initiated immediately. Before initiating the server shutdown, the process of setting 'start_time' and validating it is reiterated (for max 5 times). If correct time is obtained in these 5 iterations then server continues to run. --- sql/sql_parse.cc | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fd3623c6148f7..ac3901997f314 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 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 @@ -889,17 +889,47 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->enable_slow_log= TRUE; thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */ thd->set_time(); - if (!thd->is_valid_time()) + if (thd->is_valid_time() == false) { /* - If the time has got past 2038 we need to shut this server down - We do this by making sure every command is a shutdown and we - have enough privileges to shut the server down - - TODO: remove this when we have full 64 bit my_time_t support + If the time has gone past 2038 we need to shutdown the server. But + there is possibility of getting invalid time value on some platforms. + For example, gettimeofday() might return incorrect value on solaris + platform. Hence validating the current time with 5 iterations before + initiating the normal server shutdown process because of time getting + past 2038. */ - thd->security_ctx->master_access|= SHUTDOWN_ACL; - command= COM_SHUTDOWN; + const int max_tries= 5; + sql_print_warning("Current time has got past year 2038. Validating current " + "time with %d iterations before initiating the normal " + "server shutdown process.", max_tries); + + int tries= 0; + while (++tries <= max_tries) + { + thd->set_time(); + if (thd->is_valid_time() == true) + { + sql_print_warning("Iteration %d: Obtained valid current time from " + "system", tries); + break; + } + sql_print_warning("Iteration %d: Current time obtained from system is " + "greater than 2038", tries); + } + if (tries > max_tries) + { + /* + If the time has got past 2038 we need to shut this server down. + We do this by making sure every command is a shutdown and we + have enough privileges to shut the server down + + TODO: remove this when we have full 64 bit my_time_t support + */ + sql_print_error("This MySQL server doesn't support dates later than 2038"); + thd->security_ctx->master_access|= SHUTDOWN_ACL; + command= COM_SHUTDOWN; + } } thd->set_query_id(next_query_id()); inc_thread_running(); From 63b2c9765068d82fc1ee3932ce21f9330eef4b55 Mon Sep 17 00:00:00 2001 From: Terje Rosten Date: Mon, 24 Oct 2016 13:11:34 +0200 Subject: [PATCH 19/73] Bug#24925181 INCORRECT ISA DETECTION CODE IN OEL RPM SPEC Wrapper for mysql_config used in multilib installs modified to work as intended, added more archs (aarch64, ppc64le, s390x, s390, sparc and sparc64) to lists in fallback mode and use same script for EL and Fedora. Thanks to Alexey Kopytov for report and fix. --- packaging/rpm-oel/mysql_config.sh | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/packaging/rpm-oel/mysql_config.sh b/packaging/rpm-oel/mysql_config.sh index abe46e0ed74de..8044ed441641a 100644 --- a/packaging/rpm-oel/mysql_config.sh +++ b/packaging/rpm-oel/mysql_config.sh @@ -2,22 +2,30 @@ # # Wrapper script for mysql_config to support multilib # -# Only works on OEL6/RHEL6 and similar # -# This command respects setarch +# This command respects setarch, works on OL6/RHEL6 and later bits=$(rpm --eval %__isa_bits) case $bits in - 32|64) status=known ;; - *) status=unknown ;; + 32|64) ;; + *) bits=unknown ;; esac -if [ "$status" = "unknown" ] ; then - echo "$0: error: command 'rpm --eval %__isa_bits' returned unknown value: $bits" - exit 1 +# Try mapping by uname if rpm command failed +if [ "$bits" = "unknown" ] ; then + arch=$(uname -m) + case $arch in + x86_64|ppc64|ppc64le|aarch64|s390x|sparc64) bits=64 ;; + i386|i486|i586|i686|pentium3|pentium4|athlon|ppc|s390|sparc) bits=32 ;; + *) bits=unknown ;; + esac fi +if [ "$bits" == "unknown" ] ; then + echo "$0: error: failed to determine isa bits on your arch." + exit 1 +fi if [ -x /usr/bin/mysql_config-$bits ] ; then /usr/bin/mysql_config-$bits "$@" @@ -25,4 +33,3 @@ else echo "$0: error: needed binary: /usr/bin/mysql_config-$bits is missing. Please check your MySQL installation." exit 1 fi - From 31d8c9221fb9451c4e269be7b0d4d26a882e730e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 25 Oct 2016 16:58:47 +0200 Subject: [PATCH 20/73] 5.6.34 From ae473368feea7dc75f10624495eb210ae10e1c05 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 25 Oct 2016 16:59:57 +0200 Subject: [PATCH 21/73] 5.6.34 From c3cf7f47f0f4a1ec314001aaf0c3d9c1c1f62097 Mon Sep 17 00:00:00 2001 From: Thayumanavar S Date: Fri, 28 Oct 2016 14:45:03 +0200 Subject: [PATCH 22/73] BUG#24487120 - SLAVE'S SLAVE_SQL_RUNNING IS STOPPED DURING LOAD DATA AT MASTER. Revert "BUG#23080148 - BACKPORT BUG 14653594 AND BUG 20683959 TO" This reverts commit 1d31f5b3090d129382b50b95512f2f79305715a1. The commit causes replication incompatibility between minor revisions and based on discussion with Srinivasarao, the patch is reverted. --- mysql-test/r/loaddata.result | 26 +------ mysql-test/std_data/bug20683959loaddata.txt | 1 - mysql-test/t/loaddata.test | 25 +------ sql/sql_load.cc | 77 ++++++++------------- 4 files changed, 30 insertions(+), 99 deletions(-) delete mode 100644 mysql-test/std_data/bug20683959loaddata.txt diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result index 2f2a3579eecf2..2d67d24bedd66 100644 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -507,7 +507,7 @@ DROP TABLE t1; # Bug#11765139 58069: LOAD DATA INFILE: VALGRIND REPORTS INVALID MEMORY READS AND WRITES WITH U # CREATE TABLE t1(f1 INT); -SELECT 0xE1C330 INTO OUTFILE 't1.dat'; +SELECT 0xE1BB30 INTO OUTFILE 't1.dat'; LOAD DATA INFILE 't1.dat' IGNORE INTO TABLE t1 CHARACTER SET utf8; DROP TABLE t1; # @@ -532,27 +532,3 @@ FIELDS TERMINATED BY 't' LINES TERMINATED BY ''; Got one of the listed errors SET @@sql_mode= @old_mode; DROP TABLE t1; - -# -# Bug#23080148 - Backport of Bug#20683959. -# Bug#20683959 LOAD DATA INFILE IGNORES A SPECIFIC ROW SILENTLY -# UNDER DB CHARSET IS UTF8. -# -CREATE DATABASE d1 CHARSET latin1; -USE d1; -CREATE TABLE t1 (val TEXT); -LOAD DATA INFILE '../../std_data/bug20683959loaddata.txt' INTO TABLE t1; -SELECT COUNT(*) FROM t1; -COUNT(*) -1 -SELECT HEX(val) FROM t1; -HEX(val) -C38322525420406E696F757A656368756E3A20E98198E2889AF58081AEE7B99DE4B88AE383A3E7B99DE69690F58087B3E7B9A7EFBDA8E7B99DEFBDB3E7B99DE78999E880B3E7B8BAEFBDAAE7B9A7E89699E296A1E7B8BAE4BBA3EFBD8CE7B8BAEFBDA9E7B8B2E2889AE38184E7B99DEFBDB3E7B99DE4B88AE383A3E7B99DE69690F58087B3E7B9A7EFBDA8E7B99DEFBDB3E7B99DE5B3A8EFBD84E8ABA0EFBDA8E89C89F580948EE599AAE7B8BAEFBDAAE7B8BAE9A198EFBDA9EFBDB1E7B9A7E581B5E289A0E7B8BAEFBDBEE7B9A7E9A194EFBDA9E882B4EFBDA5EFBDB5E980A7F5808B96E28693E99EABE38287E58F99E7B8BAE58AB1E28691E7B8BAF5808B9AE7828AE98095EFBDB1E7B8BAEFBDAFE7B8B2E288ABE6A89FE89EB3E6BA98F58081ADE88EA0EFBDBAE98095E6BA98F58081AEE89D93EFBDBAE8AD9BEFBDACE980A7F5808B96E28693E7B8BAF580918EE288AAE7B8BAE4B88AEFBC9EE7B8BAE4B99DE28691E7B8BAF5808B96EFBCA0E88DB3E6A68AEFBDB9EFBDB3E981B2E5B3A8E296A1E7B8BAE7A4BCE7828AE88DB3E6A68AEFBDB0EFBDBDE7B8BAA0E7B8BAE88B93EFBDBEE5B899EFBC9E -CREATE DATABASE d2 CHARSET utf8; -USE d2; -CREATE TABLE t1 (val TEXT); -LOAD DATA INFILE '../../std_data/bug20683959loaddata.txt' INTO TABLE t1; -ERROR HY000: Invalid utf8 character string: 'Ã"RT @niouzechun: \9058\221A' -DROP TABLE d1.t1, d2.t1; -DROP DATABASE d1; -DROP DATABASE d2; diff --git a/mysql-test/std_data/bug20683959loaddata.txt b/mysql-test/std_data/bug20683959loaddata.txt deleted file mode 100644 index 1878cc7887938..0000000000000 --- a/mysql-test/std_data/bug20683959loaddata.txt +++ /dev/null @@ -1 +0,0 @@ -Ã"RT @niouzechun: é˜âˆšõ€®ç¹ä¸Šãƒ£ç¹æ–õ€‡³ç¹§ï½¨ç¹ï½³ç¹ç‰™è€³ç¸ºï½ªç¹§è–™â–¡ç¸ºä»£ï½Œç¸ºï½©ç¸²âˆšã„ç¹ï½³ç¹ä¸Šãƒ£ç¹æ–õ€‡³ç¹§ï½¨ç¹ï½³ç¹å³¨ï½„諠ィ蜉õ€”Žå™ªç¸ºï½ªç¸ºé¡˜ï½©ï½±ç¹§åµâ‰ ç¸ºï½¾ç¹§é¡”ゥ肴・オ逧õ€‹–↓鞫ょå™ç¸ºåŠ±â†‘縺õ€‹šç‚Šé€•ï½±ç¸ºï½¯ç¸²âˆ«æ¨Ÿèž³æº˜õ€­èŽ ï½ºé€•æº˜õ€®è“コ譛ャ逧õ€‹–↓縺õ€‘Žâˆªç¸ºä¸Šï¼žç¸ºä¹â†‘縺õ€‹–ï¼ è³æ¦Šï½¹ï½³é²å³¨â–¡ç¸ºç¤¼ç‚Šè³æ¦Šï½°ï½½ç¸º ç¸ºè‹“セ帙> diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test index 9a664b848433c..aa7be52484e43 100644 --- a/mysql-test/t/loaddata.test +++ b/mysql-test/t/loaddata.test @@ -610,7 +610,7 @@ disconnect con1; --echo # CREATE TABLE t1(f1 INT); -EVAL SELECT 0xE1C330 INTO OUTFILE 't1.dat'; +EVAL SELECT 0xE1BB30 INTO OUTFILE 't1.dat'; --disable_warnings LOAD DATA INFILE 't1.dat' IGNORE INTO TABLE t1 CHARACTER SET utf8; --enable_warnings @@ -656,26 +656,3 @@ SET @@sql_mode= @old_mode; --remove_file $MYSQLTEST_VARDIR/mysql DROP TABLE t1; ---echo ---echo # ---echo # Bug#23080148 - Backport of Bug#20683959. ---echo # Bug#20683959 LOAD DATA INFILE IGNORES A SPECIFIC ROW SILENTLY ---echo # UNDER DB CHARSET IS UTF8. ---echo # - -CREATE DATABASE d1 CHARSET latin1; -USE d1; -CREATE TABLE t1 (val TEXT); -LOAD DATA INFILE '../../std_data/bug20683959loaddata.txt' INTO TABLE t1; -SELECT COUNT(*) FROM t1; -SELECT HEX(val) FROM t1; - -CREATE DATABASE d2 CHARSET utf8; -USE d2; -CREATE TABLE t1 (val TEXT); ---error ER_INVALID_CHARACTER_STRING -LOAD DATA INFILE '../../std_data/bug20683959loaddata.txt' INTO TABLE t1; - -DROP TABLE d1.t1, d2.t1; -DROP DATABASE d1; -DROP DATABASE d2; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index a46967a24a85b..c084e5e38390b 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1363,8 +1363,8 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, set_if_bigger(length,line_start.length()); stack=stack_pos=(int*) sql_alloc(sizeof(int)*length); - if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(MY_WME)))) - error= true; /* purecov: inspected */ + if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(0)))) + error=1; /* purecov: inspected */ else { end_of_buff=buffer+buff_length; @@ -1556,50 +1556,37 @@ int READ_INFO::read_field() } } #ifdef USE_MB - uint ml= my_mbcharlen(read_charset, chr); - if (ml == 0) - { - *to= '\0'; - my_error(ER_INVALID_CHARACTER_STRING, MYF(0), - read_charset->csname, buffer); - error= true; - return 1; - } + if (my_mbcharlen(read_charset, chr) > 1 && + to + my_mbcharlen(read_charset, chr) <= end_of_buff) + { + uchar* p= to; + int ml, i; + *to++ = chr; - if (ml > 1 && - to + ml <= end_of_buff) - { - uchar* p= to; - *to++ = chr; + ml= my_mbcharlen(read_charset, chr); - for (uint i= 1; i < ml; i++) + for (i= 1; i < ml; i++) + { + chr= GET; + if (chr == my_b_EOF) { - chr= GET; - if (chr == my_b_EOF) - { - /* - Need to back up the bytes already ready from illformed - multi-byte char - */ - to-= i; - goto found_eof; - } - *to++ = chr; + /* + Need to back up the bytes already ready from illformed + multi-byte char + */ + to-= i; + goto found_eof; } - if (my_ismbchar(read_charset, + *to++ = chr; + } + if (my_ismbchar(read_charset, (const char *)p, (const char *)to)) - continue; - for (uint i= 0; i < ml; i++) - PUSH(*--to); - chr= GET; - } - else if (ml > 1) - { - // Buffer is too small, exit while loop, and reallocate. - PUSH(chr); - break; - } + continue; + for (i= 0; i < ml; i++) + PUSH(*--to); + chr= GET; + } #endif *to++ = (uchar) chr; } @@ -1843,15 +1830,7 @@ int READ_INFO::read_value(int delim, String *val) for (chr= GET; my_tospace(chr) != delim && chr != my_b_EOF;) { #ifdef USE_MB - uint ml= my_mbcharlen(read_charset, chr); - if (ml == 0) - { - chr= my_b_EOF; - val->length(0); - return chr; - } - - if (ml > 1) + if (my_mbcharlen(read_charset, chr) > 1) { DBUG_PRINT("read_xml",("multi byte")); int i, ml= my_mbcharlen(read_charset, chr); From 5884aa15d40b4dcc6de5cbcf276200c5fcbac938 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sun, 6 Nov 2016 14:57:27 +0100 Subject: [PATCH 23/73] - Fix MDEV-11234. Escape quoting character. Should be doubled. Now it is also possible to escape it by a backslash. modified: storage/connect/tabfmt.cpp - Prepare making VEC table type support conditional. VEC tables might be unsupported in future versions modified: storage/connect/CMakeLists.txt modified: storage/connect/mycat.cc modified: storage/connect/reldef.cpp modified: storage/connect/xindex.cpp - MDEV-11067 suggested to add configuration support to the Apache wrapper. Was added but commented out until prooved it is really useful. modified: storage/connect/ApacheInterface.java modified: storage/connect/ha_connect.cc modified: storage/connect/jdbccat.h modified: storage/connect/jdbconn.cpp modified: storage/connect/jdbconn.h modified: storage/connect/tabjdbc.cpp modified: storage/connect/tabjdbc.h - Remove useless members. modified: storage/connect/jdbconn.cpp modified: storage/connect/jdbconn.h - New UDF countin. modified: storage/connect/jsonudf.cpp modified: storage/connect/jsonudf.h --- storage/connect/ApacheInterface.java | 5 ++- storage/connect/CMakeLists.txt | 2 +- storage/connect/ha_connect.cc | 7 +++- storage/connect/jdbccat.h | 1 + storage/connect/jdbconn.cpp | 60 ++++++++++++++++------------ storage/connect/jdbconn.h | 6 +-- storage/connect/jsonudf.cpp | 47 ++++++++++++++++++++++ storage/connect/jsonudf.h | 3 ++ storage/connect/mycat.cc | 6 ++- storage/connect/reldef.cpp | 13 ++++-- storage/connect/tabfmt.cpp | 34 +++++++++++----- storage/connect/tabjdbc.cpp | 4 ++ storage/connect/tabjdbc.h | 1 + storage/connect/xindex.cpp | 8 +++- 14 files changed, 147 insertions(+), 50 deletions(-) diff --git a/storage/connect/ApacheInterface.java b/storage/connect/ApacheInterface.java index b4c8a4e98850e..47b46dc05066e 100644 --- a/storage/connect/ApacheInterface.java +++ b/storage/connect/ApacheInterface.java @@ -35,7 +35,10 @@ public int JdbcConnect(String[] parms, int fsize, boolean scrollable) { ds.setPassword(parms[3]); pool.put(url, ds); } // endif ds - + + // if (parms.length > 4 && parms[4] != null) + // ds.setConnectionProperties(parms[4]); + // Get a connection from the data source conn = ds.getConnection(); diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt index 95d8853811953..2122e56134b53 100644 --- a/storage/connect/CMakeLists.txt +++ b/storage/connect/CMakeLists.txt @@ -38,7 +38,7 @@ user_connect.h valblk.h value.h xindex.h xobject.h xtable.h) # Definitions that are shared for all OSes # add_definitions( -DMARIADB -DFORCE_INIT_OF_VARS -Dconnect_EXPORTS) -add_definitions( -DHUGE_SUPPORT -DZIP_SUPPORT -DPIVOT_SUPPORT ) +add_definitions( -DHUGE_SUPPORT -DZIP_SUPPORT -DPIVOT_SUPPORT -DVCT_SUPPORT ) # diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index cf945a73f4610..2222e51b083d3 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -171,7 +171,7 @@ #define JSONMAX 10 // JSON Default max grp size extern "C" { - char version[]= "Version 1.04.0008 August 10, 2016"; + char version[]= "Version 1.04.0008 October 20, 2016"; #if defined(__WIN__) char compver[]= "Version 1.04.0008 " __DATE__ " " __TIME__; char slash= '\\'; @@ -5190,7 +5190,8 @@ static int connect_assisted_discovery(handlerton *, THD* thd, PJPARM sjp= NULL; char *driver= NULL; char *url= NULL; - char *tabtyp = NULL; +//char *prop= NULL; + char *tabtyp= NULL; #endif // JDBC_SUPPORT uint tm, fnc= FNC_NO, supfnc= (FNC_NO | FNC_COL); bool bif, ok= false, dbf= false; @@ -5256,6 +5257,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd, #if defined(JDBC_SUPPORT) driver= GetListOption(g, "Driver", topt->oplist, NULL); // url= GetListOption(g, "URL", topt->oplist, NULL); +// prop = GetListOption(g, "Properties", topt->oplist, NULL); tabtyp = GetListOption(g, "Tabtype", topt->oplist, NULL); #endif // JDBC_SUPPORT mxe= atoi(GetListOption(g,"maxerr", topt->oplist, "0")); @@ -5366,6 +5368,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd, jdef->SetName(create_info->alias); sjp= (PJPARM)PlugSubAlloc(g, NULL, sizeof(JDBCPARM)); sjp->Driver= driver; +// sjp->Properties = prop; sjp->Fsize= 0; sjp->Scrollable= false; diff --git a/storage/connect/jdbccat.h b/storage/connect/jdbccat.h index 37f33d7063d8b..7108aa376cef2 100644 --- a/storage/connect/jdbccat.h +++ b/storage/connect/jdbccat.h @@ -8,6 +8,7 @@ typedef struct jdbc_parms { char *Url; // Driver URL char *User; // User connect info char *Pwd; // Password connect info +//char *Properties; // Connection property list //int Cto; // Connect timeout //int Qto; // Query timeout int Fsize; // Fetch size diff --git a/storage/connect/jdbconn.cpp b/storage/connect/jdbconn.cpp index dca9bd0eac4e5..b31e77bf1ff9e 100644 --- a/storage/connect/jdbconn.cpp +++ b/storage/connect/jdbconn.cpp @@ -525,10 +525,10 @@ JDBConn::JDBConn(PGLOBAL g, TDBJDBC *tdbp) m_Wrap = strcat(strcpy(wn, "wrappers/"), m_Wrap); } // endif m_Wrap - m_Driver = NULL; - m_Url = NULL; - m_User = NULL; - m_Pwd = NULL; +//m_Driver = NULL; +//m_Url = NULL; +//m_User = NULL; +//m_Pwd = NULL; m_Ncol = 0; m_Aff = 0; m_Rows = 0; @@ -772,7 +772,7 @@ bool JDBConn::GetJVM(PGLOBAL g) /***********************************************************************/ int JDBConn::Open(PJPARM sop) { - + int irc = RC_FX; bool err = false; jboolean jt = (trace > 0); PGLOBAL& g = m_G; @@ -865,30 +865,37 @@ int JDBConn::Open(PJPARM sop) switch (rc) { case JNI_OK: strcpy(g->Message, "VM successfully created"); + irc = RC_OK; break; case JNI_ERR: strcpy(g->Message, "Initialising JVM failed: unknown error"); - return RC_FX; + break; case JNI_EDETACHED: strcpy(g->Message, "Thread detached from the VM"); - return RC_FX; + break; case JNI_EVERSION: strcpy(g->Message, "JNI version error"); - return RC_FX; + break; case JNI_ENOMEM: strcpy(g->Message, "Not enough memory"); - return RC_FX; + break; case JNI_EEXIST: strcpy(g->Message, "VM already created"); - return RC_FX; + break; case JNI_EINVAL: strcpy(g->Message, "Invalid arguments"); - return RC_FX; + break; default: - sprintf(g->Message, "Unknown return code %d", rc); - return RC_FX; + sprintf(g->Message, "Unknown return code %d", (int)rc); + break; } // endswitch rc + if (trace) + htrc("%s\n", g->Message); + + if (irc != RC_OK) + return irc; + //=============== Display JVM version =============== jint ver = env->GetVersion(); printf("JVM Version %d.%d\n", ((ver>>16)&0x0f), (ver&0x0f)); @@ -978,10 +985,10 @@ int JDBConn::Open(PJPARM sop) jobjectArray parms = env->NewObjectArray(4, // constructs java array of 4 env->FindClass("java/lang/String"), NULL); // Strings - m_Driver = sop->Driver; - m_Url = sop->Url; - m_User = sop->User; - m_Pwd = sop->Pwd; +//m_Driver = sop->Driver; +//m_Url = sop->Url; +//m_User = sop->User; +//m_Pwd = sop->Pwd; m_Scrollable = sop->Scrollable; m_RowsetSize = sop->Fsize; //m_LoginTimeout = sop->Cto; @@ -989,17 +996,20 @@ int JDBConn::Open(PJPARM sop) //m_UseCnc = sop->UseCnc; // change some elements - if (m_Driver) - env->SetObjectArrayElement(parms, 0, env->NewStringUTF(m_Driver)); + if (sop->Driver) + env->SetObjectArrayElement(parms, 0, env->NewStringUTF(sop->Driver)); + + if (sop->Url) + env->SetObjectArrayElement(parms, 1, env->NewStringUTF(sop->Url)); - if (m_Url) - env->SetObjectArrayElement(parms, 1, env->NewStringUTF(m_Url)); + if (sop->User) + env->SetObjectArrayElement(parms, 2, env->NewStringUTF(sop->User)); - if (m_User) - env->SetObjectArrayElement(parms, 2, env->NewStringUTF(m_User)); + if (sop->Pwd) + env->SetObjectArrayElement(parms, 3, env->NewStringUTF(sop->Pwd)); - if (m_Pwd) - env->SetObjectArrayElement(parms, 3, env->NewStringUTF(m_Pwd)); +//if (sop->Properties) +// env->SetObjectArrayElement(parms, 4, env->NewStringUTF(sop->Properties)); // call method rc = env->CallIntMethod(job, cid, parms, m_RowsetSize, m_Scrollable); diff --git a/storage/connect/jdbconn.h b/storage/connect/jdbconn.h index 0a1c52d4576d1..9d428142839da 100644 --- a/storage/connect/jdbconn.h +++ b/storage/connect/jdbconn.h @@ -180,9 +180,9 @@ class JDBConn : public BLOCK { char *Msg; char *m_Wrap; char m_IDQuoteChar[2]; - PSZ m_Driver; - PSZ m_Url; - PSZ m_User; +//PSZ m_Driver; +//PSZ m_Url; +//PSZ m_User; PSZ m_Pwd; int m_Ncol; int m_Aff; diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index 8bddc68e2aeca..f9034f257399b 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -5264,3 +5264,50 @@ char *envar(UDF_INIT *initid, UDF_ARGS *args, char *result, return str; } // end of envar +/*********************************************************************************/ +/* Returns the distinct number of B occurences in A. */ +/*********************************************************************************/ +my_bool countin_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +{ + if (args->arg_count != 2) { + strcpy(message, "This function must have 2 arguments"); + return true; + } else if (args->arg_type[0] != STRING_RESULT) { + strcpy(message, "First argument must be string"); + return true; + } else if (args->arg_type[1] != STRING_RESULT) { + strcpy(message, "Second argument is not a string"); + return true; + } // endif args + + return false; +} // end of countin_init + +long long countin(UDF_INIT *initid, UDF_ARGS *args, char *result, + unsigned long *res_length, char *is_null, char *) +{ + PSZ str1, str2; + char *s; + long long n = 0; + size_t lg; + + lg = (size_t)args->lengths[0]; + s = str1 = (PSZ)malloc(lg + 1); + memcpy(str1, args->args[0], lg); + str1[lg] = 0; + + lg = (size_t)args->lengths[1]; + str2 = (PSZ)malloc(lg + 1); + memcpy(str2, args->args[1], lg); + str2[lg] = 0; + + while (s = strstr(s, str2)) { + n++; + s += lg; + } // endwhile + + free(str1); + free(str2); + return n; +} // end of countin + diff --git a/storage/connect/jsonudf.h b/storage/connect/jsonudf.h index 1406d9f2f2e94..d2890421c623f 100644 --- a/storage/connect/jsonudf.h +++ b/storage/connect/jsonudf.h @@ -221,6 +221,9 @@ extern "C" { DllExport my_bool envar_init(UDF_INIT*, UDF_ARGS*, char*); DllExport char *envar(UDF_EXEC_ARGS); + + DllExport my_bool countin_init(UDF_INIT*, UDF_ARGS*, char*); + DllExport long long countin(UDF_EXEC_ARGS); } // extern "C" diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc index b4b03e6ba4a22..19c9f62b5bf6d 100644 --- a/storage/connect/mycat.cc +++ b/storage/connect/mycat.cc @@ -64,7 +64,9 @@ #include "filamtxt.h" #include "tabdos.h" #include "tabfmt.h" +#if defined(VCT_SUPPORT) #include "tabvct.h" +#endif // VCT_SUPPORT #include "tabsys.h" #if defined(__WIN__) #include "tabmac.h" @@ -549,7 +551,9 @@ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, PTABLE tablep, LPCSTR am) #if defined(XML_SUPPORT) case TAB_XML: tdp= new(g) XMLDEF; break; #endif // XML_SUPPORT - case TAB_VEC: tdp= new(g) VCTDEF; break; +#if defined(VCT_SUPPORT) + 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 diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp index ac2327212e0fa..a62fcbf941623 100644 --- a/storage/connect/reldef.cpp +++ b/storage/connect/reldef.cpp @@ -40,7 +40,9 @@ #include "tabcol.h" #include "filamap.h" #include "filamfix.h" +#if defined(VCT_SUPPORT) #include "filamvct.h" +#endif // VCT_SUPPORT #if defined(ZIP_SUPPORT) #include "filamzip.h" #endif // ZIP_SUPPORT @@ -683,16 +685,19 @@ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode) txfp = new(g) MPXFAM(defp); else txfp = new(g) FIXFAM(defp); - } else if (rfm == RECFM_VCT) { - assert (Pxdef->GetDefType() == TYPE_AM_VCT); +#if defined(VCT_SUPPORT) + assert(Pxdef->GetDefType() == TYPE_AM_VCT); if (map) txfp = new(g) VCMFAM((PVCTDEF)defp); else txfp = new(g) VCTFAM((PVCTDEF)defp); - - } // endif's +#else // !VCT_SUPPORT + strcpy(g->Message, "VCT no more supported"); + return NULL; +#endif // !VCT_SUPPORT + } // endif's ((PTDBDOS)tdbp)->SetTxfp(txfp); } // endif Txfp diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index 015f8d93b159c..d21a8b977dabb 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -830,8 +830,9 @@ bool TDBCSV::SkipHeader(PGLOBAL g) /***********************************************************************/ int TDBCSV::ReadBuffer(PGLOBAL g) { - char *p1, *p2, *p = NULL; - int i, n, len, rc = Txfp->ReadBuffer(g); + //char *p1, *p2, *p = NULL; + char *p2, *p = NULL; + int i, n, len, rc = Txfp->ReadBuffer(g); bool bad = false; if (trace > 1) @@ -846,14 +847,23 @@ int TDBCSV::ReadBuffer(PGLOBAL g) for (i = 0; i < Fields; i++) { if (!bad) { if (Qot && *p2 == Qot) { // Quoted field - for (n = 0, p1 = ++p2; (p = strchr(p1, Qot)); p1 = p + 2) - if (*(p + 1) == Qot) - n++; // Doubled internal quotes - else - break; // Final quote + //for (n = 0, p1 = ++p2; (p = strchr(p1, Qot)); p1 = p + 2) + // if (*(p + 1) == Qot) + // n++; // Doubled internal quotes + // else + // break; // Final quote + + for (n = 0, p = ++p2; p; p++) + if (*p == Qot || *p == '\\') { + if (*(++p) == Qot) + n++; // Escaped internal quotes + else if (*(p - 1) == Qot) + break; // Final quote + } // endif *p if (p) { - len = p++ - p2; + //len = p++ - p2; + len = p - p2 - 1;; // if (Sep != ' ') // for (; *p == ' '; p++) ; // Skip blanks @@ -873,10 +883,12 @@ int TDBCSV::ReadBuffer(PGLOBAL g) if (n) { int j, k; - // Suppress the double of internal quotes + // Suppress the escape of internal quotes for (j = k = 0; j < len; j++, k++) { - if (p2[j] == Qot) - j++; // skip first one + if (p2[j] == Qot || (p2[j] == '\\' && p2[j + 1] == Qot)) + j++; // skip escape char + else if (p2[j] == '\\') + p2[k++] = p2[j++]; // avoid \\Qot p2[k] = p2[j]; } // endfor i, j diff --git a/storage/connect/tabjdbc.cpp b/storage/connect/tabjdbc.cpp index e398523892f2e..912e6c7d53009 100644 --- a/storage/connect/tabjdbc.cpp +++ b/storage/connect/tabjdbc.cpp @@ -110,6 +110,7 @@ bool JDBCDEF::SetParms(PJPARM sjp) sjp->Url= Url; sjp->User= Username; sjp->Pwd= Password; +//sjp->Properties = Prop; return true; } // end of SetParms @@ -234,6 +235,7 @@ bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) 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); @@ -337,6 +339,7 @@ TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBASE(tdp) Schema = tdp->Tabschema; Ops.User = tdp->Username; Ops.Pwd = tdp->Password; +// Ops.Properties = tdp->Prop; Catalog = tdp->Tabcat; Srcdef = tdp->Srcdef; Qrystr = tdp->Qrystr; @@ -356,6 +359,7 @@ TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBASE(tdp) Ops.Url = NULL; Ops.User = NULL; Ops.Pwd = NULL; +// Ops.Properties = NULL; Catalog = NULL; Srcdef = NULL; Qrystr = NULL; diff --git a/storage/connect/tabjdbc.h b/storage/connect/tabjdbc.h index 7244ebd383228..fee8223abaf43 100644 --- a/storage/connect/tabjdbc.h +++ b/storage/connect/tabjdbc.h @@ -58,6 +58,7 @@ class DllExport JDBCDEF : public TABDEF { /* Logical table description */ 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 */ diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp index 563126302784f..a2cf4e77b801c 100755 --- a/storage/connect/xindex.cpp +++ b/storage/connect/xindex.cpp @@ -45,7 +45,9 @@ //nclude "array.h" #include "filamtxt.h" #include "tabdos.h" +#if defined(VCT_SUPPORT) #include "tabvct.h" +#endif // VCT_SUPPORT /***********************************************************************/ /* Macro or external routine definition */ @@ -293,9 +295,11 @@ bool XINDEX::AddColumns(void) return false; // Not applying to static index else if (IsMul()) return false; // Not done yet for multiple index - else if (Tbxp->GetAmType() == TYPE_AM_VCT && ((PTDBVCT)Tbxp)->IsSplit()) +#if defined(VCT_SUPPORT) + else if (Tbxp->GetAmType() == TYPE_AM_VCT && ((PTDBVCT)Tbxp)->IsSplit()) return false; // This would require to read additional files - else +#endif // VCT_SUPPORT + else return true; } // end of AddColumns From aae67535cc399c92cac24b2b1f44e9a196806c9f Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Mon, 14 Nov 2016 19:20:40 +0100 Subject: [PATCH 24/73] - MDEV-11051 place Java classes ApacheInterface and JdbcInterface into single jar file. Try to fix the INSTALL command. modified: storage/connect/CMakeLists.txt - Make some JDBC tests available on Windows modified: storage/connect/mysql-test/connect/t/jdbc.test modified: storage/connect/mysql-test/connect/t/jdbc_new.test added: storage/connect/mysql-test/connect/t/windows.inc --- storage/connect/CMakeLists.txt | 31 +++-- storage/connect/JavaWrappers.jar | Bin 0 -> 19615 bytes storage/connect/JdbcInterface.java | 13 ++ storage/connect/jdbconn.cpp | 118 +++++++++--------- .../connect/mysql-test/connect/disabled.def | 5 +- .../mysql-test/connect/r/jdbc_new.result | 6 +- .../connect/std_data/JdbcMariaDB.jar | Bin 5993273 -> 6021866 bytes .../connect/mysql-test/connect/t/jdbc.test | 1 + .../mysql-test/connect/t/jdbc_new.test | 1 + .../connect/mysql-test/connect/t/windows.inc | 5 + 10 files changed, 109 insertions(+), 71 deletions(-) create mode 100644 storage/connect/JavaWrappers.jar create mode 100644 storage/connect/mysql-test/connect/t/windows.inc diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt index 2122e56134b53..46c4841ff9702 100644 --- a/storage/connect/CMakeLists.txt +++ b/storage/connect/CMakeLists.txt @@ -20,25 +20,25 @@ SET(CONNECT_SOURCES ha_connect.cc connect.cc user_connect.cc mycat.cc fmdlex.c osutil.c plugutil.c rcmsg.c rcmsg.h array.cpp blkfil.cpp colblk.cpp csort.cpp -filamap.cpp filamdbf.cpp filamfix.cpp filamtxt.cpp filamvct.cpp filamzip.cpp +filamap.cpp filamdbf.cpp filamfix.cpp filamtxt.cpp filamzip.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 -tabvct.cpp tabvir.cpp tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.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 filamtxt.h filamvct.h filamzip.h +engmsg.h filamap.h filamdbf.h filamfix.h filamtxt.h filamzip.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 tabvct.h tabvir.h tabxcl.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) # # Definitions that are shared for all OSes # add_definitions( -DMARIADB -DFORCE_INIT_OF_VARS -Dconnect_EXPORTS) -add_definitions( -DHUGE_SUPPORT -DZIP_SUPPORT -DPIVOT_SUPPORT -DVCT_SUPPORT ) +add_definitions( -DHUGE_SUPPORT -DZIP_SUPPORT -DPIVOT_SUPPORT ) # @@ -89,6 +89,18 @@ ELSE(NOT UNIX) ENDIF(UNIX) +# +# VCT: the VEC format might be not supported in future versions +# + +OPTION(CONNECT_WITH_VCT "Compile CONNECT storage engine with VCT support" ON) + +IF(CONNECT_WITH_VCT) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} filamvct.cpp tabvct.cpp filamvct.h tabvct.h) + add_definitions(-DVCT_SUPPORT) +ENDIF(CONNECT_WITH_VCT) + + # # XML # @@ -236,9 +248,9 @@ ENDIF(CONNECT_WITH_ODBC) # JDBC # IF(APPLE) - OPTION(CONNECT_WITH_JDBC "some comment" OFF) + OPTION(CONNECT_WITH_JDBC "Compile CONNECT storage engine without JDBC support" OFF) ELSE() - OPTION(CONNECT_WITH_JDBC "some comment" ON) + OPTION(CONNECT_WITH_JDBC "Compile CONNECT storage engine with JDBC support" ON) ENDIF() IF(CONNECT_WITH_JDBC) @@ -252,12 +264,15 @@ IF(CONNECT_WITH_JDBC) SET(CONNECT_SOURCES ${CONNECT_SOURCES} jdbconn.cpp tabjdbc.cpp jdbconn.h tabjdbc.h jdbccat.h JdbcInterface.java ApacheInterface.java MariadbInterface.java - MysqlInterface.java OracleInterface.java PostgresqlInterface.java) + MysqlInterface.java OracleInterface.java PostgresqlInterface.java + JavaWrappers.jar) # TODO: Find how to compile and install the java wrapper classes # Find required libraries and include directories SET (JAVA_SOURCES JdbcInterface.java) add_jar(JdbcInterface ${JAVA_SOURCES}) install_jar(JdbcInterface DESTINATION ${INSTALL_PLUGINDIR} COMPONENT connect-engine) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/JavaWrappers.jar + DESTINATION ${INSTALL_PLUGINDIR} COMPONENT connect-engine) add_definitions(-DJDBC_SUPPORT) ELSE() SET(JDBC_LIBRARY "") diff --git a/storage/connect/JavaWrappers.jar b/storage/connect/JavaWrappers.jar new file mode 100644 index 0000000000000000000000000000000000000000..d5353d2cbfd08c449d2c4ddc7a81b1f8c32c1ad1 GIT binary patch literal 19615 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 H7Wn@FuwP?< literal 0 HcmV?d00001 diff --git a/storage/connect/JdbcInterface.java b/storage/connect/JdbcInterface.java index 34af8c4e013c2..07dcaf985b48e 100644 --- a/storage/connect/JdbcInterface.java +++ b/storage/connect/JdbcInterface.java @@ -220,6 +220,19 @@ public void SetTimestampParm(int i, Timestamp t) { } // end of SetTimestampParm + public int SetNullParm(int i, int typ) { + int rc = 0; + + try { + pstmt.setNull(i, typ); + } catch (Exception e) { + SetErrmsg(e); + rc = -1; + } // end try/catch + + return rc; + } // end of SetNullParm + public int ExecutePrep() { int n = -3; diff --git a/storage/connect/jdbconn.cpp b/storage/connect/jdbconn.cpp index b31e77bf1ff9e..a69f84a94a1aa 100644 --- a/storage/connect/jdbconn.cpp +++ b/storage/connect/jdbconn.cpp @@ -55,9 +55,8 @@ #if defined(__WIN__) extern "C" HINSTANCE s_hModule; // Saved module handle -#else // !__WIN__ +#endif // __WIN__ #define nullptr 0 -#endif // !__WIN__ TYPCONV GetTypeConv(); int GetConvSize(); @@ -1442,7 +1441,7 @@ bool JDBConn::SetParam(JDBCCOL *colp) PGLOBAL& g = m_G; bool rc = false; PVAL val = colp->GetValue(); - jint n, i = (jint)colp->GetRank(); + jint n, jrc = 0, i = (jint)colp->GetRank(); jshort s; jlong lg; //jfloat f; @@ -1452,69 +1451,74 @@ bool JDBConn::SetParam(JDBCCOL *colp) jstring jst = nullptr; jmethodID dtc, setid = nullptr; - switch (val->GetType()) { - case TYPE_STRING: - if (gmID(g, setid, "SetStringParm", "(ILjava/lang/String;)V")) + if (val->GetNullable() && val->IsNull()) { + if (gmID(g, setid, "SetNullParm", "(II)I")) return true; - jst = env->NewStringUTF(val->GetCharValue()); - env->CallVoidMethod(job, setid, i, jst); - break; - case TYPE_INT: - if (gmID(g, setid, "SetIntParm", "(II)V")) - return true; - - n = (jint)val->GetIntValue(); - env->CallVoidMethod(job, setid, i, n); - break; - case TYPE_TINY: - case TYPE_SHORT: - if (gmID(g, setid, "SetShortParm", "(IS)V")) - return true; + jrc = env->CallIntMethod(job, setid, i, (jint)GetJDBCType(val->GetType())); + } else switch (val->GetType()) { + case TYPE_STRING: + if (gmID(g, setid, "SetStringParm", "(ILjava/lang/String;)V")) + return true; - s = (jshort)val->GetShortValue(); - env->CallVoidMethod(job, setid, i, s); - break; - case TYPE_BIGINT: - if (gmID(g, setid, "SetBigintParm", "(IJ)V")) - return true; + jst = env->NewStringUTF(val->GetCharValue()); + env->CallVoidMethod(job, setid, i, jst); + break; + case TYPE_INT: + if (gmID(g, setid, "SetIntParm", "(II)V")) + return true; - lg = (jlong)val->GetBigintValue(); - env->CallVoidMethod(job, setid, i, lg); - break; - case TYPE_DOUBLE: - case TYPE_DECIM: - if (gmID(g, setid, "SetDoubleParm", "(ID)V")) - return true; + n = (jint)val->GetIntValue(); + env->CallVoidMethod(job, setid, i, n); + break; + case TYPE_TINY: + case TYPE_SHORT: + if (gmID(g, setid, "SetShortParm", "(IS)V")) + return true; - d = (jdouble)val->GetFloatValue(); - env->CallVoidMethod(job, setid, i, d); - break; - case TYPE_DATE: - if ((dat = env->FindClass("java/sql/Timestamp")) == nullptr) { - strcpy(g->Message, "Cannot find Timestamp class"); - return true; - } else if (!(dtc = env->GetMethodID(dat, "", "(J)V"))) { - strcpy(g->Message, "Cannot find Timestamp class constructor"); - return true; - } // endif's + s = (jshort)val->GetShortValue(); + env->CallVoidMethod(job, setid, i, s); + break; + case TYPE_BIGINT: + if (gmID(g, setid, "SetBigintParm", "(IJ)V")) + return true; - lg = (jlong)val->GetBigintValue() * 1000; + lg = (jlong)val->GetBigintValue(); + env->CallVoidMethod(job, setid, i, lg); + break; + case TYPE_DOUBLE: + case TYPE_DECIM: + if (gmID(g, setid, "SetDoubleParm", "(ID)V")) + return true; - if ((datobj = env->NewObject(dat, dtc, lg)) == nullptr) { - strcpy(g->Message, "Cannot make Timestamp object"); - return true; - } else if (gmID(g, setid, "SetTimestampParm", "(ILjava/sql/Timestamp;)V")) + d = (jdouble)val->GetFloatValue(); + env->CallVoidMethod(job, setid, i, d); + break; + case TYPE_DATE: + if ((dat = env->FindClass("java/sql/Timestamp")) == nullptr) { + strcpy(g->Message, "Cannot find Timestamp class"); + return true; + } else if (!(dtc = env->GetMethodID(dat, "", "(J)V"))) { + strcpy(g->Message, "Cannot find Timestamp class constructor"); + return true; + } // endif's + + lg = (jlong)val->GetBigintValue() * 1000; + + if ((datobj = env->NewObject(dat, dtc, lg)) == nullptr) { + strcpy(g->Message, "Cannot make Timestamp object"); + return true; + } else if (gmID(g, setid, "SetTimestampParm", "(ILjava/sql/Timestamp;)V")) + return true; + + env->CallVoidMethod(job, setid, i, datobj); + break; + default: + sprintf(g->Message, "Parm type %d not supported", val->GetType()); return true; + } // endswitch Type - env->CallVoidMethod(job, setid, i, datobj); - break; - default: - sprintf(g->Message, "Parm type %d not supported", val->GetType()); - return true; - } // endswitch Type - - if (Check()) { + if (Check(jrc)) { sprintf(g->Message, "SetParam: col=%s msg=%s", colp->GetName(), Msg); rc = true; } // endif msg diff --git a/storage/connect/mysql-test/connect/disabled.def b/storage/connect/mysql-test/connect/disabled.def index 9b4570915c7ec..5e15e0806ba84 100644 --- a/storage/connect/mysql-test/connect/disabled.def +++ b/storage/connect/mysql-test/connect/disabled.def @@ -9,8 +9,7 @@ # Do not use any TAB characters for whitespace. # ############################################################################## -#json_udf_bin : broken upstream in --ps (fixed) -jdbc : Variable settings depend on machine configuration -jdbc_new : Variable settings depend on machine configuration +#jdbc : Variable settings depend on machine configuration +#jdbc_new : Variable settings depend on machine configuration jdbc_oracle : Variable settings depend on machine configuration jdbc_postgresql : Variable settings depend on machine configuration diff --git a/storage/connect/mysql-test/connect/r/jdbc_new.result b/storage/connect/mysql-test/connect/r/jdbc_new.result index 14381b0b11fef..5cc4826213d1a 100644 --- a/storage/connect/mysql-test/connect/r/jdbc_new.result +++ b/storage/connect/mysql-test/connect/r/jdbc_new.result @@ -56,7 +56,7 @@ t1 CREATE TABLE `t1` ( ) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`='JDBC' SELECT * FROM t1; a b -0 NULL +NULL NULL 0 test00 1 test01 2 test02 @@ -72,7 +72,7 @@ t1 CREATE TABLE `t1` ( ) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`=JDBC `TABNAME`='t1' SELECT * FROM t1; a b -0 NULL +NULL NULL 0 test00 1 test01 2 test02 @@ -104,7 +104,7 @@ t1 CREATE TABLE `t1` ( ) ENGINE=CONNECT DEFAULT CHARSET=latin1 CONNECTION='jdbc:mysql://127.0.0.1:SLAVE_PORT/test?user=root' `TABLE_TYPE`=JDBC SELECT * FROM t1; a b -0 NULL +NULL NULL 0 0 1 0 2 0 diff --git a/storage/connect/mysql-test/connect/std_data/JdbcMariaDB.jar b/storage/connect/mysql-test/connect/std_data/JdbcMariaDB.jar index 81f91e4465a4a11c4a1593dd6b44232421ee47d5..9d461ad6223b41e2a18d97773a064aa8f96405cc 100644 GIT binary patch delta 190883 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 z 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�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 QA~Y*M>(|E+WyD|q0pOP5cK`qY delta 163865 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

n}_f>6EWC?SHbxzirbYUn8xCFh)z&zJ~C$dD{zm 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

  • ^A^bh`soFTN->Ho1a=Ko-(Ax}$AHV_P>o+BhX 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 vn@&e9%CbwovoLDNe%s Date: Wed, 16 Nov 2016 13:41:27 +0100 Subject: [PATCH 25/73] Bug#25088048 ADDITIONAL ISSUES IN MYSQLD_SAFE Don't read --ledir option from config file. Ignore current working for finding location of mysqld Remove use of chown/chmod in scripts. Be helpful only when basedir is /var/log or /var/lib. Removed unused systemd files for SLES. Set explicit basedir in scripts. --- packaging/rpm-oel/mysql-systemd-start | 4 +- packaging/rpm-oel/mysql.init | 24 +++++----- packaging/rpm-oel/mysqld.service | 2 +- packaging/rpm-sles/CMakeLists.txt | 3 +- packaging/rpm-sles/mysql-systemd-start | 66 -------------------------- packaging/rpm-sles/mysql.conf | 1 - packaging/rpm-sles/mysql.init | 13 ++--- packaging/rpm-sles/mysqld.service | 48 ------------------- scripts/mysqld_safe.sh | 18 ++++++- 9 files changed, 41 insertions(+), 138 deletions(-) delete mode 100644 packaging/rpm-sles/mysql-systemd-start delete mode 100644 packaging/rpm-sles/mysql.conf delete mode 100644 packaging/rpm-sles/mysqld.service diff --git a/packaging/rpm-oel/mysql-systemd-start b/packaging/rpm-oel/mysql-systemd-start index 231a76087ac3e..af6e906efe135 100644 --- a/packaging/rpm-oel/mysql-systemd-start +++ b/packaging/rpm-oel/mysql-systemd-start @@ -22,7 +22,9 @@ install_db () { datadir=$(get_option mysqld datadir "/var/lib/mysql") # Restore log, dir, perms and SELinux contexts - [ -d "$datadir" ] || install -d -m 0755 -omysql -gmysql "$datadir" || exit 1 + if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + install -d -m 0755 -omysql -gmysql "$datadir" || exit 1 + fi log=/var/log/mysqld.log [ -e $log ] || touch $log chmod 0640 $log diff --git a/packaging/rpm-oel/mysql.init b/packaging/rpm-oel/mysql.init index 75ae672801b77..50d1bba017dea 100644 --- a/packaging/rpm-oel/mysql.init +++ b/packaging/rpm-oel/mysql.init @@ -70,18 +70,19 @@ start(){ ret=0 else # prepare for start - touch "$errlogfile" - chown mysql:mysql "$errlogfile" - chmod 0640 "$errlogfile" + if [ ! -e "$errlogfile" -a ! -h "$errlogfile" -a "x$(dirname "$errlogfile")" = "x/var/log" ]; then + install /dev/null -m0640 -omysql -gmysql "$errlogfile" + fi [ -x /sbin/restorecon ] && /sbin/restorecon "$errlogfile" if [ ! -d "$datadir/mysql" ] ; then # First, make sure $datadir is there with correct permissions - if [ ! -e "$datadir" -a ! -h "$datadir" ] - then - mkdir -p "$datadir" || exit 1 + if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + install -d -m0755 -omysql -gmysql "$datadir" || exit 1 + fi + if [ ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + chown mysql:mysql "$datadir" + chmod 0755 "$datadir" fi - chown mysql:mysql "$datadir" - chmod 0755 "$datadir" if [ -x /sbin/restorecon ]; then /sbin/restorecon "$datadir" for dir in /var/lib/mysql-files ; do @@ -94,13 +95,14 @@ start(){ # Now create the database action $"Initializing MySQL database: " /usr/bin/mysql_install_db --rpm --datadir="$datadir" --user=mysql ret=$? - chown -R mysql:mysql "$datadir" if [ $ret -ne 0 ] ; then return $ret fi fi - chown mysql:mysql "$datadir" - chmod 0755 "$datadir" + if [ ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + chown mysql:mysql "$datadir" + chmod 0755 "$datadir" + fi # Pass all the options determined above, to ensure consistent behavior. # In many cases mysqld_safe would arrive at the same conclusions anyway # but we need to be sure. (An exception is that we don't force the diff --git a/packaging/rpm-oel/mysqld.service b/packaging/rpm-oel/mysqld.service index 78ef3bffe601c..871f191c0171a 100644 --- a/packaging/rpm-oel/mysqld.service +++ b/packaging/rpm-oel/mysqld.service @@ -34,7 +34,7 @@ PermissionsStartOnly=true ExecStartPre=/usr/bin/mysql-systemd-start pre # Start main service -ExecStart=/usr/bin/mysqld_safe +ExecStart=/usr/bin/mysqld_safe --basedir=/usr # Don't signal startup success before a ping works ExecStartPost=/usr/bin/mysql-systemd-start post diff --git a/packaging/rpm-sles/CMakeLists.txt b/packaging/rpm-sles/CMakeLists.txt index b5ffaa1556574..98aa4f4d222b4 100644 --- a/packaging/rpm-sles/CMakeLists.txt +++ b/packaging/rpm-sles/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 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 @@ -20,7 +20,6 @@ IF(UNIX) # Left in current directory, to be taken during build CONFIGURE_FILE(mysql.spec.in ${CMAKE_CURRENT_BINARY_DIR}/${SPECFILENAME} @ONLY) FOREACH(fedfile my.cnf my_config.h mysql.init - mysqld.service mysql-systemd-start mysql.conf filter-requires.sh filter-provides.sh) CONFIGURE_FILE(${fedfile} ${CMAKE_CURRENT_BINARY_DIR}/${fedfile} COPYONLY) ENDFOREACH() diff --git a/packaging/rpm-sles/mysql-systemd-start b/packaging/rpm-sles/mysql-systemd-start deleted file mode 100644 index 28472249eda16..0000000000000 --- a/packaging/rpm-sles/mysql-systemd-start +++ /dev/null @@ -1,66 +0,0 @@ -#! /bin/bash -# -# Scripts to run by MySQL systemd service -# -# Needed argument: pre | post -# -# pre mode : try to run mysql_install_db and fix perms and SELinux contexts -# post mode : ping server until answer is received -# - -install_db () { - # Note: something different than datadir=/var/lib/mysql requires SELinux policy changes (in enforcing mode) - datadir=$(/usr/bin/my_print_defaults server mysqld | grep '^--datadir=' | sed -n 's/--datadir=//p' | tail -n 1) - - # Restore log, dir, perms and SELinux contexts - [ -d "$datadir" ] || install -d -m 0755 -omysql -gmysql "$datadir" || exit 1 - log=/var/log/mysqld.log - [ -e $log ] || touch $log - chmod 0640 $log - chown mysql:mysql $log || exit 1 - if [ -x /usr/sbin/restorecon ]; then - /usr/sbin/restorecon "$datadir" - /usr/sbin/restorecon $log - fi - - # If special mysql dir is in place, skip db install - [ -d "$datadir/mysql" ] && exit 0 - - # Create initial db - /usr/bin/mysql_install_db --rpm --datadir="$datadir" --user=mysql - - # Create a file to trigger execution of mysql_secure_installation - # after server has started - touch "$datadir"/.phase_two_required - - exit 0 -} - -pinger () { - # Wait for ping to answer to signal startup completed, - # might take a while in case of e.g. crash recovery - # MySQL systemd service will timeout script if no answer - ret=1 - while /bin/true ; do - sleep 1 - mysqladmin ping >/dev/null 2>&1 && ret=0 && break - done - - # If server has been started successfully and file created in - # install_db step is present we run mysql_secure_installation - if [ $ret -eq 0 -a -e "$datadir"/.phase_two_required -a -x /usr/bin/mysql_secure_installation ] ; then - /usr/bin/mysql_secure_installation --use-default --defaults-file=/etc/my.cnf - rm -f "$datadir"/.phase_two_required - fi - - exit 0 -} - -# main -case $1 in - "pre") install_db ;; - "post") pinger ;; -esac - -exit 0 - diff --git a/packaging/rpm-sles/mysql.conf b/packaging/rpm-sles/mysql.conf deleted file mode 100644 index 74cd5f836e76f..0000000000000 --- a/packaging/rpm-sles/mysql.conf +++ /dev/null @@ -1 +0,0 @@ -d /var/run/mysqld 0755 mysql mysql - diff --git a/packaging/rpm-sles/mysql.init b/packaging/rpm-sles/mysql.init index dda0bebba5658..25762d9bee263 100644 --- a/packaging/rpm-sles/mysql.init +++ b/packaging/rpm-sles/mysql.init @@ -49,7 +49,6 @@ get_option () { datadir=$(get_option mysqld datadir "/var/lib/mysql") socket=$(get_option mysqld socket "$datadir/mysql.sock") pidfile=$(get_option mysqld_safe pid-file "/var/run/mysql/mysqld.pid") -logfile=$(get_option mysqld_safe log-error "/var/log/mysql/mysqld.log") install_db () { # Note: something different than datadir=/var/lib/mysql requires @@ -58,14 +57,16 @@ install_db () { logfile=$(get_option mysqld_safe log-error "/var/log/mysql/mysqld.log") # Restore log, dir, perms and SELinux contexts - [ -d "$datadir" ] || install -d -m 0755 -omysql -gmysql "$datadir" || return 1 + if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + install -d -m 0755 -omysql -gmysql "$datadir" || return 1 + fi - [ -e $logfile ] || touch $logfile || return 1 - chmod 0640 $logfile - chown mysql:mysql $logfile || return 1 + if [ ! -e "$logfile" -a ! -h "$logfile" -a "x$(dirname "$logfile")" = "x/var/log/mysql" ]; then + install /dev/null -omysql -gmysql "$logfile" || return 1 + fi if [ -x /usr/sbin/restorecon ]; then /usr/sbin/restorecon "$datadir" - /usr/sbin/restorecon $logfile + /usr/sbin/restorecon "$logfile" fi # If special mysql dir is in place, skip db install diff --git a/packaging/rpm-sles/mysqld.service b/packaging/rpm-sles/mysqld.service deleted file mode 100644 index 78ef3bffe601c..0000000000000 --- a/packaging/rpm-sles/mysqld.service +++ /dev/null @@ -1,48 +0,0 @@ -# -# Simple MySQL systemd service file -# -# systemd supports lots of fancy features, look here (and linked docs) for a full list: -# http://www.freedesktop.org/software/systemd/man/systemd.exec.html -# -# Note: this file ( /usr/lib/systemd/system/mysql.service ) -# will be overwritten on package upgrade, please copy the file to -# -# /etc/systemd/system/mysql.service -# -# to make needed changes. -# -# systemd-delta can be used to check differences between the two mysql.service files. -# - -[Unit] -Description=MySQL Community Server -After=network.target -After=syslog.target - -[Install] -WantedBy=multi-user.target -Alias=mysql.service - -[Service] -User=mysql -Group=mysql - -# Execute pre and post scripts as root -PermissionsStartOnly=true - -# Needed to create system tables etc. -ExecStartPre=/usr/bin/mysql-systemd-start pre - -# Start main service -ExecStart=/usr/bin/mysqld_safe - -# Don't signal startup success before a ping works -ExecStartPost=/usr/bin/mysql-systemd-start post - -# Give up if ping don't get an answer -TimeoutSec=600 - -Restart=always -PrivateTmp=false - - diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 4b103817ab67d..a5c87a44e65c8 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -218,7 +218,13 @@ parse_arguments() { # mysqld_safe-specific options - must be set in my.cnf ([mysqld_safe])! --core-file-size=*) core_file_size="$val" ;; - --ledir=*) ledir="$val" ;; + --ledir=*) + if [ -z "$pick_args" ]; then + log_error "--ledir option can only be used as command line option, found in config file" + exit 1 + fi + ledir="$val" + ;; --malloc-lib=*) set_malloc_lib "$val" ;; --mysqld=*) if [ -z "$pick_args" ]; then @@ -394,7 +400,15 @@ else relpkgdata='@pkgdatadir@' fi -MY_PWD=`pwd` +case "$0" in + /*) + MY_PWD='@prefix@' + ;; + *) + MY_PWD=`dirname $0` + MY_PWD=`dirname $MY_PWD` + ;; +esac # Check for the directories we would expect from a binary release install if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION" then From 42732cc195fd237c9b17490e96f1bb29db433b0b Mon Sep 17 00:00:00 2001 From: Dyre Tjeldvoll Date: Thu, 24 Nov 2016 09:57:54 +0100 Subject: [PATCH 26/73] Bug#25092566: CREATE TABLE WITH DATA DIRECTORY CLAUSE DOES NOT REQUIRE SPECIAL PRIVILEGES Require FILE privilege when creating tables using external data directory or index directory. --- mysql-test/r/partition_symlink.result | 2 ++ mysql-test/t/partition_symlink.test | 2 ++ sql/partition_info.cc | 26 +++++++++++++++++++++++++- sql/partition_info.h | 13 ++++++++++++- sql/sql_parse.cc | 19 ++++++++++++++++++- 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/partition_symlink.result b/mysql-test/r/partition_symlink.result index f26a1642a529e..91cc78f136127 100644 --- a/mysql-test/r/partition_symlink.result +++ b/mysql-test/r/partition_symlink.result @@ -4,6 +4,8 @@ DROP DATABASE IF EXISTS mysqltest2; # test.t1 have partitions in mysqltest2-directory! # user root: CREATE USER mysqltest_1@localhost; +# Need FILE permission to use external datadir or indexdir. +GRANT FILE ON *.* TO mysqltest_1@localhost; CREATE DATABASE mysqltest2; USE mysqltest2; CREATE TABLE t1 (a INT) ENGINE = MyISAM; diff --git a/mysql-test/t/partition_symlink.test b/mysql-test/t/partition_symlink.test index 5fdde8e0abc28..9e57d6162130f 100644 --- a/mysql-test/t/partition_symlink.test +++ b/mysql-test/t/partition_symlink.test @@ -32,6 +32,8 @@ DROP DATABASE IF EXISTS mysqltest2; -- echo # test.t1 have partitions in mysqltest2-directory! -- echo # user root: CREATE USER mysqltest_1@localhost; +-- echo # Need FILE permission to use external datadir or indexdir. + GRANT FILE ON *.* TO mysqltest_1@localhost; CREATE DATABASE mysqltest2; USE mysqltest2; CREATE TABLE t1 (a INT) ENGINE = MyISAM; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index a0d09557b81cb..cd17e3366cfb7 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 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 @@ -2550,6 +2550,30 @@ void partition_info::print_debug(const char *str, uint *value) DBUG_PRINT("info", ("parser: %s", str)); DBUG_VOID_RETURN; } + +bool has_external_data_or_index_dir(partition_info &pi) +{ + List_iterator part_it(pi.partitions); + for (partition_element *part= part_it++; part; part= part_it++) + { + if (part->data_file_name != NULL || part->index_file_name != NULL) + { + return true; + } + List_iterator subpart_it(part->subpartitions); + for (const partition_element *subpart= subpart_it++; + subpart; + subpart= subpart_it++) + { + if (subpart->data_file_name != NULL || subpart->index_file_name != NULL) + { + return true; + } + } + } + return false; +} + #else /* WITH_PARTITION_STORAGE_ENGINE */ /* For builds without partitioning we need to define these functions diff --git a/sql/partition_info.h b/sql/partition_info.h index 7bfbf8a1b1a1b..7ff6abeebd24c 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -1,7 +1,7 @@ #ifndef PARTITION_INFO_INCLUDED #define PARTITION_INFO_INCLUDED -/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 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 @@ -349,4 +349,15 @@ void init_all_partitions_iterator(partition_info *part_info, part_iter->get_next= get_next_partition_id_range; } +/** + Predicate which returns true if any partition or subpartition uses + an external data directory or external index directory. + + @param pi partitioning information + @retval true if any partition or subpartition has an external + data directory or external index directory. + @retval false otherwise + */ +bool has_external_data_or_index_dir(partition_info &pi); + #endif /* PARTITION_INFO_INCLUDED */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ac3901997f314..18cb758c9b559 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -35,6 +35,9 @@ #include "sql_insert.h" // mysql_insert #include "sql_update.h" // mysql_update, mysql_multi_update #include "sql_partition.h" // struct partition_info +#ifdef WITH_PARTITION_STORAGE_ENGINE +#include "partition_info.h" // has_external_data_or_index_dir +#endif /* WITH_PARTITION_STORAGE_ENGINE */ #include "sql_db.h" // mysql_change_db, mysql_create_db, // mysql_rm_db, mysql_upgrade_db, // mysql_alter_db, @@ -2413,7 +2416,6 @@ case SQLCOM_PREPARE: copy. */ Alter_info alter_info(lex->alter_info, thd->mem_root); - if (thd->is_fatal_error) { /* If out of memory when creating a copy of alter_info. */ @@ -2421,6 +2423,15 @@ case SQLCOM_PREPARE: goto end_with_restore_list; } + if (((lex->create_info.used_fields & HA_CREATE_USED_DATADIR) != 0 || + (lex->create_info.used_fields & HA_CREATE_USED_INDEXDIR) != 0) && + check_access(thd, FILE_ACL, NULL, NULL, NULL, FALSE, FALSE)) + { + res= 1; + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "FILE"); + goto end_with_restore_list; + } + if ((res= create_table_precheck(thd, select_tables, create_table))) goto end_with_restore_list; @@ -2458,6 +2469,12 @@ case SQLCOM_PREPARE: #ifdef WITH_PARTITION_STORAGE_ENGINE { partition_info *part_info= thd->lex->part_info; + if (part_info != NULL && has_external_data_or_index_dir(*part_info) && + check_access(thd, FILE_ACL, NULL, NULL, NULL, FALSE, FALSE)) + { + res= -1; + goto end_with_restore_list; + } if (part_info && !(part_info= thd->lex->part_info->get_clone(true))) { res= -1; From 202355104f3a4b2524a6b623886c8bf4db49c02e Mon Sep 17 00:00:00 2001 From: "mysql-builder@oracle.com" <> Date: Thu, 24 Nov 2016 21:53:55 +0100 Subject: [PATCH 27/73] From 64cc76bbf8e4d21cffdf2b2b3644ff8a71d4d97a Mon Sep 17 00:00:00 2001 From: Balasubramanian Kandasamy Date: Sat, 26 Nov 2016 20:41:48 +0530 Subject: [PATCH 28/73] Followup fix for Bug#25088048 - ADDITIONAL ISSUES IN MYSQLD_SAFE - Removed mysql.conf, mysqld.service and mysql-systemd-start from sles spec file (cherry picked from commit 35c1adc17c1a99b2c256d374500437a6ce21339e) --- packaging/rpm-sles/mysql.spec.in | 43 -------------------------------- 1 file changed, 43 deletions(-) diff --git a/packaging/rpm-sles/mysql.spec.in b/packaging/rpm-sles/mysql.spec.in index 6652cdcccb614..1b5f1806321e0 100644 --- a/packaging/rpm-sles/mysql.spec.in +++ b/packaging/rpm-sles/mysql.spec.in @@ -24,7 +24,6 @@ # Regression tests may take a long time, override the default to skip them %{!?runselftest:%global runselftest 0} -%{!?with_systemd: %global systemd 0} %{!?with_debuginfo: %global nodebuginfo 1} %{!?product_suffix: %global product_suffix community} %{!?feature_set: %global feature_set community} @@ -69,9 +68,6 @@ Source0: https://cdn.mysql.com/Downloads/MySQL-@MYSQL_BASE_VERSION@/%{src URL: http://www.mysql.com/ Packager: MySQL Release Engineering Vendor: %{mysql_vendor} -Source1: mysql-systemd-start -Source2: mysqld.service -Source3: mysql.conf Source4: my_config.h Source90: filter-provides.sh Source91: filter-requires.sh @@ -81,9 +77,6 @@ BuildRequires: libaio-devel BuildRequires: ncurses-devel BuildRequires: openssl-devel BuildRequires: zlib-devel -%if 0%{?systemd} -BuildRequires: systemd -%endif BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %if 0%{?rhel} > 6 @@ -141,15 +134,9 @@ Obsoletes: mariadb-server Obsoletes: mariadb-galera-server Provides: mysql = %{version}-%{release} Provides: mysql-tools = %{version}-%{release} -%if 0%{?systemd} -Requires(post): systemd -Requires(preun): systemd -Requires(postun): systemd -%else PreReq: insserv PreReq: sed PreReq: pwdutils -%endif Conflicts: otherproviders(mysql) Conflicts: otherproviders(mysql-debug) Conflicts: otherproviders(mysql-tools) @@ -435,13 +422,7 @@ make DESTDIR=%{buildroot} install install -D -m 0644 $MBD/release/support-files/mysql-log-rotate %{buildroot}%{_sysconfdir}/logrotate.d/mysql install -D -m 0644 $MBD/release/packaging/rpm-sles/my.cnf %{buildroot}%{_sysconfdir}/my.cnf install -d %{buildroot}%{_sysconfdir}/my.cnf.d -%if 0%{?systemd} -install -D -m 0755 %{SOURCE1} %{buildroot}%{_bindir}/mysql-systemd-start -install -D -m 0644 %{SOURCE2} %{buildroot}%{_unitdir}/mysqld.service -%else install -D -m 0755 $MBD/release/packaging/rpm-sles/mysql.init %{buildroot}%{_sysconfdir}/init.d/mysql -%endif -install -D -m 0644 %{SOURCE3} %{buildroot}%{_prefix}/lib/tmpfiles.d/mysql.conf # Make library links install -d -m 0755 %{buildroot}%{_sysconfdir}/ld.so.conf.d @@ -461,9 +442,6 @@ rm -rf %{buildroot}%{_datadir}/mysql/mysql.server rm -rf %{buildroot}%{_datadir}/mysql/mysqld_multi.server rm -f %{buildroot}%{_datadir}/mysql/{ndb-config-2-node,config*}.ini rm -f %{buildroot}%{_datadir}/mysql/my-*.cnf -%if 0%{?systemd} -rm -rf %{buildroot}%{_sysconfdir}/init.d/mysql -%endif rm -rf %{buildroot}%{_bindir}/mysql_embedded rm -rf %{buildroot}%{_bindir}/mysql_setpermission rm -rf %{buildroot}%{_mandir}/man1/mysql_setpermission.1* @@ -496,31 +474,18 @@ datadir=$(/usr/bin/my_print_defaults server mysqld | grep '^--datadir=' | sed -n /bin/chmod 0755 "$datadir" /bin/touch /var/log/mysql/mysqld.log /bin/chown mysql:mysql /var/log/mysql/mysqld.log >/dev/null 2>&1 || : -%if 0%{?systemd} -%systemd_post mysqld.service -/sbin/service mysqld enable >/dev/null 2>&1 || : -%else /sbin/insserv /etc/init.d/mysql -%endif %preun server -%if 0%{?systemd} -%systemd_preun mysqld.service -%else if [ "$1" -eq 0 ]; then /usr/sbin/rcmysql stop >/dev/null 2>&1 || : /sbin/insserv /etc/init.d fi -%endif %postun server -%if 0%{?systemd} -%systemd_postun_with_restart mysqld.service -%else if [ $1 -ge 1 ]; then /usr/sbin/rcmysql condrestart >/dev/null 2>&1 || : fi -%endif %post libs -p /sbin/ldconfig @@ -596,9 +561,6 @@ fi %attr(755, root, root) %{_bindir}/replace %attr(755, root, root) %{_bindir}/resolve_stack_dump %attr(755, root, root) %{_bindir}/resolveip -%if 0%{?systemd} -%attr(755, root, root) %{_bindir}/mysql-systemd-start -%endif %attr(755, root, root) %{_sbindir}/mysqld %attr(755, root, root) %{_sbindir}/mysqld-debug %attr(755, root, root) %{_sbindir}/rcmysql @@ -629,12 +591,7 @@ fi %attr(644, root, root) %{_datadir}/mysql/mysql_test_data_timezone.sql %attr(644, root, root) %{_datadir}/mysql/mysql-log-rotate %attr(644, root, root) %{_datadir}/mysql/magic -%attr(644, root, root) %{_prefix}/lib/tmpfiles.d/mysql.conf -%if 0%{?systemd} -%attr(644, root, root) %{_unitdir}/mysqld.service -%else %attr(755, root, root) %{_sysconfdir}/init.d/mysql -%endif %attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/logrotate.d/mysql %dir %attr(755, mysql, mysql) /var/lib/mysql %dir %attr(755, mysql, mysql) /var/run/mysql From 2d78b25c49407fb3f17fd4e44b551e8b43ffa4b5 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sun, 27 Nov 2016 14:42:37 +0100 Subject: [PATCH 29/73] - Fix null pointer java error when connecting to jdbc:drill driver. By setting the context class loader. modified: storage/connect/JavaWrappers.jar modified: storage/connect/JdbcInterface.java modified: storage/connect/mysql-test/connect/std_data/JdbcMariaDB.jar --- storage/connect/JavaWrappers.jar | Bin 19615 -> 19696 bytes storage/connect/JdbcInterface.java | 3 +++ .../connect/std_data/JdbcMariaDB.jar | Bin 6021866 -> 6021947 bytes 3 files changed, 3 insertions(+) diff --git a/storage/connect/JavaWrappers.jar b/storage/connect/JavaWrappers.jar index d5353d2cbfd08c449d2c4ddc7a81b1f8c32c1ad1..8c01c364a3f4d8c05872438b5f3cd766cb3929e3 100644 GIT binary patch delta 7907 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 diff --git a/storage/connect/JdbcInterface.java b/storage/connect/JdbcInterface.java index 07dcaf985b48e..a1b1360e6ea79 100644 --- a/storage/connect/JdbcInterface.java +++ b/storage/connect/JdbcInterface.java @@ -82,6 +82,9 @@ public int JdbcConnect(String[] parms, int fsize, boolean scrollable) { System.out.println("URL=" + parms[1]); CheckURL(parms[1], null); + + // This is required for drivers using context class loaders + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); if (parms[2] != null && !parms[2].isEmpty()) { if (DEBUG) diff --git a/storage/connect/mysql-test/connect/std_data/JdbcMariaDB.jar b/storage/connect/mysql-test/connect/std_data/JdbcMariaDB.jar index 9d461ad6223b41e2a18d97773a064aa8f96405cc..8525da74018a98ffbd5e6bc434940e885ff9b9a5 100644 GIT binary patch delta 147634 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

    yfm(S=wE79z0UwxEgddZ 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 delta 146234 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∨WfcC_SI$twQ3$yno{2Iik6;;%T>z^1 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 PT>Yz9mW*?c2k!p>K;}@s From c8f0eeb9c8596be83fefb7fef9f9871e53edb020 Mon Sep 17 00:00:00 2001 From: Balasubramanian Kandasamy Date: Mon, 28 Nov 2016 16:38:03 +0530 Subject: [PATCH 30/73] Bug#25159791 BASEDIR: COMMAND NOT FOUND ERROR WHILE STARTING SERVER WITH INIT SCRIPTS (cherry picked from commit 7a39efab8a59ebdcd562fb788bc004ff338796ea) --- packaging/rpm-oel/mysql-systemd-start | 2 +- packaging/rpm-oel/mysql.init | 6 +++--- packaging/rpm-sles/mysql.init | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packaging/rpm-oel/mysql-systemd-start b/packaging/rpm-oel/mysql-systemd-start index af6e906efe135..c744bdceeb4d9 100644 --- a/packaging/rpm-oel/mysql-systemd-start +++ b/packaging/rpm-oel/mysql-systemd-start @@ -22,7 +22,7 @@ install_db () { datadir=$(get_option mysqld datadir "/var/lib/mysql") # Restore log, dir, perms and SELinux contexts - if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then install -d -m 0755 -omysql -gmysql "$datadir" || exit 1 fi log=/var/log/mysqld.log diff --git a/packaging/rpm-oel/mysql.init b/packaging/rpm-oel/mysql.init index 50d1bba017dea..c3bbb277b91e7 100644 --- a/packaging/rpm-oel/mysql.init +++ b/packaging/rpm-oel/mysql.init @@ -76,10 +76,10 @@ start(){ [ -x /sbin/restorecon ] && /sbin/restorecon "$errlogfile" if [ ! -d "$datadir/mysql" ] ; then # First, make sure $datadir is there with correct permissions - if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then install -d -m0755 -omysql -gmysql "$datadir" || exit 1 fi - if [ ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + if [ ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then chown mysql:mysql "$datadir" chmod 0755 "$datadir" fi @@ -99,7 +99,7 @@ start(){ return $ret fi fi - if [ ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + if [ ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then chown mysql:mysql "$datadir" chmod 0755 "$datadir" fi diff --git a/packaging/rpm-sles/mysql.init b/packaging/rpm-sles/mysql.init index 25762d9bee263..62f2a62e1b970 100644 --- a/packaging/rpm-sles/mysql.init +++ b/packaging/rpm-sles/mysql.init @@ -57,7 +57,7 @@ install_db () { logfile=$(get_option mysqld_safe log-error "/var/log/mysql/mysqld.log") # Restore log, dir, perms and SELinux contexts - if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(basedir "$datadir")" = "x/var/lib" ]; then + if [ ! -d "$datadir" -a ! -h "$datadir" -a "x$(dirname "$datadir")" = "x/var/lib" ]; then install -d -m 0755 -omysql -gmysql "$datadir" || return 1 fi From 599d8cc2deee615526838ddc962778e51cd3a15a Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Fri, 2 Dec 2016 23:03:43 +0100 Subject: [PATCH 31/73] - MDEV-11366 SIGBUS errors in Connect Storage Engine for ArmHF and MIPS. Fix includes launchpad fix plus more to cover writing BIN tables. modified: storage/connect/tabfix.cpp modified: storage/connect/value.cpp modified: storage/connect/value.h - Typo: Change the name of filamzip to filamgz to prepare future ZIP tables. modified: storage/connect/CMakeLists.txt added: storage/connect/filamgz.cpp added: storage/connect/filamgz.h deleted: storage/connect/filamzip.cpp deleted: storage/connect/filamzip.h modified: storage/connect/plgdbsem.h modified: storage/connect/reldef.cpp modified: storage/connect/tabdos.cpp modified: storage/connect/tabdos.h modified: storage/connect/tabfix.cpp modified: storage/connect/tabfmt.cpp modified: storage/connect/tabjson.cpp --- storage/connect/CMakeLists.txt | 12 +-- storage/connect/{filamzip.cpp => filamgz.cpp} | 78 +++++++++---------- storage/connect/{filamzip.h => filamgz.h} | 28 +++---- storage/connect/plgdbsem.h | 2 +- storage/connect/reldef.cpp | 16 ++-- storage/connect/tabdos.cpp | 48 ++++++------ storage/connect/tabdos.h | 2 +- storage/connect/tabfix.cpp | 19 ++--- storage/connect/tabfmt.cpp | 22 +++--- storage/connect/tabjson.cpp | 20 ++--- storage/connect/value.cpp | 35 +++++++-- storage/connect/value.h | 66 +++++++++++----- 12 files changed, 197 insertions(+), 151 deletions(-) rename storage/connect/{filamzip.cpp => filamgz.cpp} (96%) rename storage/connect/{filamzip.h => filamgz.h} (91%) diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt index 46c4841ff9702..f1567730b26ae 100644 --- a/storage/connect/CMakeLists.txt +++ b/storage/connect/CMakeLists.txt @@ -20,14 +20,14 @@ SET(CONNECT_SOURCES ha_connect.cc connect.cc user_connect.cc mycat.cc fmdlex.c osutil.c plugutil.c rcmsg.c rcmsg.h array.cpp blkfil.cpp colblk.cpp csort.cpp -filamap.cpp filamdbf.cpp filamfix.cpp filamtxt.cpp filamzip.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 array.h blkfil.h block.h catalog.h checklvl.h colblk.h connect.h csort.h -engmsg.h filamap.h filamdbf.h filamfix.h filamtxt.h filamzip.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 @@ -38,7 +38,7 @@ user_connect.h valblk.h value.h xindex.h xobject.h xtable.h) # Definitions that are shared for all OSes # add_definitions( -DMARIADB -DFORCE_INIT_OF_VARS -Dconnect_EXPORTS) -add_definitions( -DHUGE_SUPPORT -DZIP_SUPPORT -DPIVOT_SUPPORT ) +add_definitions( -DHUGE_SUPPORT -DGZ_SUPPORT -DPIVOT_SUPPORT ) # @@ -270,9 +270,9 @@ IF(CONNECT_WITH_JDBC) # Find required libraries and include directories SET (JAVA_SOURCES JdbcInterface.java) add_jar(JdbcInterface ${JAVA_SOURCES}) - install_jar(JdbcInterface DESTINATION ${INSTALL_PLUGINDIR} COMPONENT connect-engine) - INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/JavaWrappers.jar - DESTINATION ${INSTALL_PLUGINDIR} COMPONENT connect-engine) + install_jar(JdbcInterface DESTINATION ${INSTALL_PLUGINDIR} COMPONENT connect-engine) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/JavaWrappers.jar + DESTINATION ${INSTALL_PLUGINDIR} COMPONENT connect-engine) add_definitions(-DJDBC_SUPPORT) ELSE() SET(JDBC_LIBRARY "") diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamgz.cpp similarity index 96% rename from storage/connect/filamzip.cpp rename to storage/connect/filamgz.cpp index d9834e56dcd16..07242ea633c92 100644 --- a/storage/connect/filamzip.cpp +++ b/storage/connect/filamgz.cpp @@ -1,11 +1,11 @@ -/*********** File AM Zip C++ Program Source Code File (.CPP) ***********/ -/* PROGRAM NAME: FILAMZIP */ +/************ File AM GZ C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: FILAMGZ */ /* ------------- */ /* Version 1.5 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2016 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -56,7 +56,7 @@ /***********************************************************************/ //#define ZLIB_DLL -#include "filamzip.h" +#include "filamgz.h" /***********************************************************************/ /* DB static variables. */ @@ -66,13 +66,13 @@ extern int num_read, num_there, num_eq[]; // Statistics /* ------------------------------------------------------------------- */ /***********************************************************************/ -/* Implementation of the ZIPFAM class. */ +/* Implementation of the GZFAM class. */ /***********************************************************************/ -ZIPFAM::ZIPFAM(PZIPFAM txfp) : TXTFAM(txfp) +GZFAM::GZFAM(PGZFAM txfp) : TXTFAM(txfp) { Zfile = txfp->Zfile; Zpos = txfp->Zpos; - } // end of ZIPFAM copy constructor + } // end of GZFAM copy constructor /***********************************************************************/ /* Zerror: Error function for gz calls. */ @@ -82,7 +82,7 @@ ZIPFAM::ZIPFAM(PZIPFAM txfp) : TXTFAM(txfp) /* library, errnum is set to Z_ERRNO and the application may consult */ /* errno to get the exact error code. */ /***********************************************************************/ -int ZIPFAM::Zerror(PGLOBAL g) +int GZFAM::Zerror(PGLOBAL g) { int errnum; @@ -101,7 +101,7 @@ int ZIPFAM::Zerror(PGLOBAL g) /***********************************************************************/ /* Reset: reset position values at the beginning of file. */ /***********************************************************************/ -void ZIPFAM::Reset(void) +void GZFAM::Reset(void) { TXTFAM::Reset(); //gzrewind(Zfile); // Useful ????? @@ -109,10 +109,10 @@ void ZIPFAM::Reset(void) } // end of Reset /***********************************************************************/ -/* ZIP GetFileLength: returns an estimate of what would be the */ +/* GZ GetFileLength: returns an estimate of what would be the */ /* uncompressed file size in number of bytes. */ /***********************************************************************/ -int ZIPFAM::GetFileLength(PGLOBAL g) +int GZFAM::GetFileLength(PGLOBAL g) { int len = TXTFAM::GetFileLength(g); @@ -124,9 +124,9 @@ int ZIPFAM::GetFileLength(PGLOBAL g) } // end of GetFileLength /***********************************************************************/ -/* ZIP Access Method opening routine. */ +/* GZ Access Method opening routine. */ /***********************************************************************/ -bool ZIPFAM::OpenTableFile(PGLOBAL g) +bool GZFAM::OpenTableFile(PGLOBAL g) { char opmode[4], filename[_MAX_PATH]; MODE mode = Tdbp->GetMode(); @@ -137,7 +137,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) break; case MODE_UPDATE: /*****************************************************************/ - /* Updating ZIP files not implemented yet. */ + /* Updating GZ files not implemented yet. */ /*****************************************************************/ strcpy(g->Message, MSG(UPD_ZIP_NOT_IMP)); return true; @@ -152,7 +152,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) // Last = Nrec; // For ZBKFAM Tdbp->ResetSize(); } else { - sprintf(g->Message, MSG(NO_PART_DEL), "ZIP"); + sprintf(g->Message, MSG(NO_PART_DEL), "GZ"); return true; } // endif filter @@ -196,7 +196,7 @@ bool ZIPFAM::OpenTableFile(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 ZIPFAM::AllocateBuffer(PGLOBAL g) +bool GZFAM::AllocateBuffer(PGLOBAL g) { MODE mode = Tdbp->GetMode(); @@ -223,7 +223,7 @@ bool ZIPFAM::AllocateBuffer(PGLOBAL g) /***********************************************************************/ /* GetRowID: return the RowID of last read record. */ /***********************************************************************/ -int ZIPFAM::GetRowID(void) +int GZFAM::GetRowID(void) { return Rows; } // end of GetRowID @@ -231,7 +231,7 @@ int ZIPFAM::GetRowID(void) /***********************************************************************/ /* GetPos: return the position of last read record. */ /***********************************************************************/ -int ZIPFAM::GetPos(void) +int GZFAM::GetPos(void) { return (int)Zpos; } // end of GetPos @@ -239,7 +239,7 @@ int ZIPFAM::GetPos(void) /***********************************************************************/ /* GetNextPos: return the position of next record. */ /***********************************************************************/ -int ZIPFAM::GetNextPos(void) +int GZFAM::GetNextPos(void) { return gztell(Zfile); } // end of GetNextPos @@ -247,9 +247,9 @@ int ZIPFAM::GetNextPos(void) /***********************************************************************/ /* SetPos: Replace the table at the specified position. */ /***********************************************************************/ -bool ZIPFAM::SetPos(PGLOBAL g, int pos __attribute__((unused))) +bool GZFAM::SetPos(PGLOBAL g, int pos __attribute__((unused))) { - sprintf(g->Message, MSG(NO_SETPOS_YET), "ZIP"); + sprintf(g->Message, MSG(NO_SETPOS_YET), "GZ"); return true; #if 0 Fpos = pos; @@ -267,7 +267,7 @@ bool ZIPFAM::SetPos(PGLOBAL g, int pos __attribute__((unused))) /***********************************************************************/ /* Record file position in case of UPDATE or DELETE. */ /***********************************************************************/ -bool ZIPFAM::RecordPos(PGLOBAL) +bool GZFAM::RecordPos(PGLOBAL) { Zpos = gztell(Zfile); return false; @@ -276,7 +276,7 @@ bool ZIPFAM::RecordPos(PGLOBAL) /***********************************************************************/ /* Skip one record in file. */ /***********************************************************************/ -int ZIPFAM::SkipRecord(PGLOBAL g, bool header) +int GZFAM::SkipRecord(PGLOBAL g, bool header) { // Skip this record if (gzeof(Zfile)) @@ -293,7 +293,7 @@ int ZIPFAM::SkipRecord(PGLOBAL g, bool header) /***********************************************************************/ /* ReadBuffer: Read one line from a compressed text file. */ /***********************************************************************/ -int ZIPFAM::ReadBuffer(PGLOBAL g) +int GZFAM::ReadBuffer(PGLOBAL g) { char *p; int rc; @@ -357,7 +357,7 @@ int ZIPFAM::ReadBuffer(PGLOBAL g) /* WriteDB: Data Base write routine for ZDOS access method. */ /* Update is not possible without using a temporary file (NIY). */ /***********************************************************************/ -int ZIPFAM::WriteBuffer(PGLOBAL g) +int GZFAM::WriteBuffer(PGLOBAL g) { /*********************************************************************/ /* Prepare the write buffer. */ @@ -376,7 +376,7 @@ int ZIPFAM::WriteBuffer(PGLOBAL g) /***********************************************************************/ /* Data Base delete line routine for ZDOS access method. (NIY) */ /***********************************************************************/ -int ZIPFAM::DeleteRecords(PGLOBAL g, int) +int GZFAM::DeleteRecords(PGLOBAL g, int) { strcpy(g->Message, MSG(NO_ZIP_DELETE)); return RC_FX; @@ -385,21 +385,21 @@ int ZIPFAM::DeleteRecords(PGLOBAL g, int) /***********************************************************************/ /* Data Base close routine for DOS access method. */ /***********************************************************************/ -void ZIPFAM::CloseTableFile(PGLOBAL, bool) +void GZFAM::CloseTableFile(PGLOBAL, bool) { int rc = gzclose(Zfile); if (trace) - htrc("ZIP CloseDB: closing %s rc=%d\n", To_File, rc); + htrc("GZ CloseDB: closing %s rc=%d\n", To_File, rc); Zfile = NULL; // So we can know whether table is open //To_Fb->Count = 0; // Avoid double closing by PlugCloseAll } // end of CloseTableFile /***********************************************************************/ -/* Rewind routine for ZIP access method. */ +/* Rewind routine for GZ access method. */ /***********************************************************************/ -void ZIPFAM::Rewind(void) +void GZFAM::Rewind(void) { gzrewind(Zfile); } // end of Rewind @@ -409,7 +409,7 @@ void ZIPFAM::Rewind(void) /***********************************************************************/ /* Constructors. */ /***********************************************************************/ -ZBKFAM::ZBKFAM(PDOSDEF tdp) : ZIPFAM(tdp) +ZBKFAM::ZBKFAM(PDOSDEF tdp) : GZFAM(tdp) { Blocked = true; Block = tdp->GetBlock(); @@ -421,7 +421,7 @@ ZBKFAM::ZBKFAM(PDOSDEF tdp) : ZIPFAM(tdp) BlkPos = tdp->GetTo_Pos(); } // end of ZBKFAM standard constructor -ZBKFAM::ZBKFAM(PZBKFAM txfp) : ZIPFAM(txfp) +ZBKFAM::ZBKFAM(PZBKFAM txfp) : GZFAM(txfp) { CurLine = txfp->CurLine; NxtLine = txfp->NxtLine; @@ -505,7 +505,7 @@ int ZBKFAM::GetPos(void) /***********************************************************************/ bool ZBKFAM::RecordPos(PGLOBAL /*g*/) { -//strcpy(g->Message, "RecordPos not implemented for zip blocked tables"); +//strcpy(g->Message, "RecordPos not implemented for gz blocked tables"); //return true; return RC_OK; } // end of RecordPos @@ -515,7 +515,7 @@ bool ZBKFAM::RecordPos(PGLOBAL /*g*/) /***********************************************************************/ int ZBKFAM::SkipRecord(PGLOBAL /*g*/, bool) { -//strcpy(g->Message, "SkipRecord not implemented for zip blocked tables"); +//strcpy(g->Message, "SkipRecord not implemented for gz blocked tables"); //return RC_FX; return RC_OK; } // end of SkipRecord @@ -615,7 +615,7 @@ int ZBKFAM::WriteBuffer(PGLOBAL g) /*********************************************************************/ /* In Insert mode, blocs are added sequentialy to the file end. */ - /* Note: Update mode is not handled for zip files. */ + /* Note: Update mode is not handled for gz files. */ /*********************************************************************/ if (++CurNum == Rbuf) { /*******************************************************************/ @@ -703,7 +703,7 @@ void ZBKFAM::CloseTableFile(PGLOBAL g, bool) rc = gzclose(Zfile); if (trace) - htrc("ZIP CloseDB: closing %s rc=%d\n", To_File, rc); + htrc("GZ CloseDB: closing %s rc=%d\n", To_File, rc); Zfile = NULL; // So we can know whether table is open //To_Fb->Count = 0; // Avoid double closing by PlugCloseAll @@ -854,7 +854,7 @@ int ZIXFAM::WriteBuffer(PGLOBAL g) { /*********************************************************************/ /* In Insert mode, blocs are added sequentialy to the file end. */ - /* Note: Update mode is not handled for zip files. */ + /* Note: Update mode is not handled for gz files. */ /*********************************************************************/ if (++CurNum == Rbuf) { /*******************************************************************/ @@ -1062,7 +1062,7 @@ int ZLBFAM::GetNextPos(void) /***********************************************************************/ bool ZLBFAM::SetPos(PGLOBAL g, int pos __attribute__((unused))) { - sprintf(g->Message, MSG(NO_SETPOS_YET), "ZIP"); + sprintf(g->Message, MSG(NO_SETPOS_YET), "GZ"); return true; #if 0 // All this must be checked if (pos < 0) { @@ -1423,4 +1423,4 @@ void ZLBFAM::Rewind(void) //Rbuf = 0; commented out in case we reuse last read block } // end of Rewind -/* ------------------------ End of ZipFam ---------------------------- */ +/* ------------------------ End of GzFam ---------------------------- */ diff --git a/storage/connect/filamzip.h b/storage/connect/filamgz.h similarity index 91% rename from storage/connect/filamzip.h rename to storage/connect/filamgz.h index edb8b5db3239c..d667fdddcc2b8 100644 --- a/storage/connect/filamzip.h +++ b/storage/connect/filamgz.h @@ -1,16 +1,16 @@ -/************** FilAmZip H Declares Source Code File (.H) **************/ -/* Name: FILAMZIP.H Version 1.2 */ +/*************** FilAmGz H Declares Source Code File (.H) **************/ +/* Name: FILAMGZ.H Version 1.3 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2016 */ /* */ /* This file contains the GZIP access method classes declares. */ /***********************************************************************/ -#ifndef __FILAMZIP_H -#define __FILAMZIP_H +#ifndef __FILAMGZ_H +#define __FILAMGZ_H #include "zlib.h" -typedef class ZIPFAM *PZIPFAM; +typedef class GZFAM *PGZFAM; typedef class ZBKFAM *PZBKFAM; typedef class ZIXFAM *PZIXFAM; typedef class ZLBFAM *PZLBFAM; @@ -20,19 +20,19 @@ typedef class ZLBFAM *PZLBFAM; /* variable record length files compressed using the gzip library */ /* functions. File is accessed record by record (row). */ /***********************************************************************/ -class DllExport ZIPFAM : public TXTFAM { +class DllExport GZFAM : public TXTFAM { // friend class DOSCOL; public: // Constructor - ZIPFAM(PDOSDEF tdp) : TXTFAM(tdp) {Zfile = NULL; Zpos = 0;} - ZIPFAM(PZIPFAM txfp); + GZFAM(PDOSDEF tdp) : TXTFAM(tdp) {Zfile = NULL; Zpos = 0;} + GZFAM(PGZFAM txfp); // Implementation - virtual AMT GetAmType(void) {return TYPE_AM_ZIP;} + virtual AMT GetAmType(void) {return TYPE_AM_GZ;} virtual int GetPos(void); virtual int GetNextPos(void); virtual PTXF Duplicate(PGLOBAL g) - {return (PTXF)new(g) ZIPFAM(this);} + {return (PTXF)new(g) GZFAM(this);} // Methods virtual void Reset(void); @@ -57,14 +57,14 @@ class DllExport ZIPFAM : public TXTFAM { // Members gzFile Zfile; // Points to GZ file structure z_off_t Zpos; // Uncompressed file position - }; // end of class ZIPFAM + }; // end of class GZFAM /***********************************************************************/ /* This is the access method class declaration for optimized variable */ /* record length files compressed using the gzip library functions. */ /* The File is accessed by block (requires an opt file). */ /***********************************************************************/ -class DllExport ZBKFAM : public ZIPFAM { +class DllExport ZBKFAM : public GZFAM { public: // Constructor ZBKFAM(PDOSDEF tdp); @@ -167,4 +167,4 @@ class DllExport ZLBFAM : public BLKFAM { bool Optimized; // true when opt file is available }; // end of class ZLBFAM -#endif // __FILAMZIP_H +#endif // __FILAMGZ_H diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h index 910ce97f48ae2..e99f5f364440f 100644 --- a/storage/connect/plgdbsem.h +++ b/storage/connect/plgdbsem.h @@ -123,7 +123,7 @@ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */ TYPE_AM_PRX = 129, /* PROXY access method type no */ TYPE_AM_XTB = 130, /* SYS table access method type */ TYPE_AM_BLK = 131, /* BLK access method type no */ - TYPE_AM_ZIP = 132, /* ZIP access method type no */ + TYPE_AM_GZ = 132, /* GZ access method type no */ TYPE_AM_ZLIB = 133, /* ZLIB access method type no */ TYPE_AM_JSON = 134, /* JSON access method type no */ TYPE_AM_JSN = 135, /* JSN access method type no */ diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp index a62fcbf941623..30e4d49d24988 100644 --- a/storage/connect/reldef.cpp +++ b/storage/connect/reldef.cpp @@ -1,7 +1,7 @@ /************* RelDef CPP Program Source Code File (.CPP) **************/ /* PROGRAM NAME: RELDEF */ /* ------------- */ -/* Version 1.5 */ +/* Version 1.6 */ /* */ /* COPYRIGHT: */ /* ---------- */ @@ -43,9 +43,9 @@ #if defined(VCT_SUPPORT) #include "filamvct.h" #endif // VCT_SUPPORT -#if defined(ZIP_SUPPORT) -#include "filamzip.h" -#endif // ZIP_SUPPORT +#if defined(GZ_SUPPORT) +#include "filamgz.h" +#endif // GZ_SUPPORT #include "tabdos.h" #include "valblk.h" #include "tabmul.h" @@ -665,15 +665,15 @@ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode) /*********************************************************************/ if (!((PTDBDOS)tdbp)->GetTxfp()) { if (cmpr) { -#if defined(ZIP_SUPPORT) +#if defined(GZ_SUPPORT) if (cmpr == 1) - txfp = new(g) ZIPFAM(defp); + txfp = new(g) GZFAM(defp); else txfp = new(g) ZLBFAM(defp); -#else // !ZIP_SUPPORT +#else // !GZ_SUPPORT strcpy(g->Message, "Compress not supported"); return NULL; -#endif // !ZIP_SUPPORT +#endif // !GZ_SUPPORT } else if (rfm == RECFM_VAR) { if (map) txfp = new(g) MAPFAM(defp); diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp index 98633f49d232e..06dde34a27f98 100644 --- a/storage/connect/tabdos.cpp +++ b/storage/connect/tabdos.cpp @@ -1,11 +1,11 @@ /************* TabDos C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABDOS */ /* ------------- */ -/* Version 4.9 */ +/* Version 4.9.1 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2016 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -51,9 +51,9 @@ #include "filamap.h" #include "filamfix.h" #include "filamdbf.h" -#if defined(ZIP_SUPPORT) -#include "filamzip.h" -#endif // ZIP_SUPPORT +#if defined(GZ_SUPPORT) +#include "filamgz.h" +#endif // GZ_SUPPORT #include "tabdos.h" #include "tabfix.h" #include "tabmul.h" @@ -350,28 +350,28 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) else if (map) txfp = new(g) MPXFAM(this); else if (Compressed) { -#if defined(ZIP_SUPPORT) +#if defined(GZ_SUPPORT) txfp = new(g) ZIXFAM(this); -#else // !ZIP_SUPPORT - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); +#else // !GZ_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ"); return NULL; -#endif // !ZIP_SUPPORT +#endif // !GZ_SUPPORT } else txfp = new(g) FIXFAM(this); tdbp = new(g) TDBFIX(this, txfp); } else { if (Compressed) { -#if defined(ZIP_SUPPORT) +#if defined(GZ_SUPPORT) if (Compressed == 1) - txfp = new(g) ZIPFAM(this); + txfp = new(g) GZFAM(this); else txfp = new(g) ZLBFAM(this); -#else // !ZIP_SUPPORT - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); +#else // !GZ_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ"); return NULL; -#endif // !ZIP_SUPPORT +#endif // !GZ_SUPPORT } else if (map) txfp = new(g) MAPFAM(this); else @@ -396,7 +396,7 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) if (map) { txfp = new(g) MBKFAM(this); } else if (Compressed) { -#if defined(ZIP_SUPPORT) +#if defined(GZ_SUPPORT) if (Compressed == 1) txfp = new(g) ZBKFAM(this); else { @@ -404,7 +404,7 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL); } // endelse #else - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ"); return NULL; #endif } else @@ -531,13 +531,13 @@ int TDBDOS::ResetTableOpt(PGLOBAL g, bool dop, bool dox) // except for ZLIB access method. if (Txfp->GetAmType() == TYPE_AM_MAP) { Txfp = new(g) MAPFAM((PDOSDEF)To_Def); -#if defined(ZIP_SUPPORT) - } else if (Txfp->GetAmType() == TYPE_AM_ZIP) { - Txfp = new(g) ZIPFAM((PDOSDEF)To_Def); +#if defined(GZ_SUPPORT) + } else if (Txfp->GetAmType() == TYPE_AM_GZ) { + Txfp = new(g) GZFAM((PDOSDEF)To_Def); } else if (Txfp->GetAmType() == TYPE_AM_ZLIB) { Txfp->Reset(); ((PZLBFAM)Txfp)->SetOptimized(false); -#endif // ZIP_SUPPORT +#endif // GZ_SUPPORT } else if (Txfp->GetAmType() == TYPE_AM_BLK) Txfp = new(g) DOSFAM((PDOSDEF)To_Def); @@ -2079,10 +2079,10 @@ bool TDBDOS::OpenDB(PGLOBAL g) /*******************************************************************/ if (Txfp->GetAmType() == TYPE_AM_MAP && Mode == MODE_DELETE) Txfp = new(g) MAPFAM((PDOSDEF)To_Def); -#if defined(ZIP_SUPPORT) - else if (Txfp->GetAmType() == TYPE_AM_ZIP) - Txfp = new(g) ZIPFAM((PDOSDEF)To_Def); -#endif // ZIP_SUPPORT +#if defined(GZ_SUPPORT) + else if (Txfp->GetAmType() == TYPE_AM_GZ) + Txfp = new(g) GZFAM((PDOSDEF)To_Def); +#endif // GZ_SUPPORT else // if (Txfp->GetAmType() != TYPE_AM_DOS) ??? Txfp = new(g) DOSFAM((PDOSDEF)To_Def); diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h index c098886f14bd0..c70e0032f47cd 100644 --- a/storage/connect/tabdos.h +++ b/storage/connect/tabdos.h @@ -91,7 +91,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ int Maxerr; /* Maximum number of bad records (DBF) */ int ReadMode; /* Specific to DBF */ int Ending; /* Length of end of lines */ - int Teds; /* Binary table default endian setting */ + char Teds; /* Binary table default endian setting */ }; // end of DOSDEF /***********************************************************************/ diff --git a/storage/connect/tabfix.cpp b/storage/connect/tabfix.cpp index 55c254f41ea19..d99f7800f2676 100644 --- a/storage/connect/tabfix.cpp +++ b/storage/connect/tabfix.cpp @@ -1,11 +1,11 @@ /************* TabFix C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABFIX */ /* ------------- */ -/* Version 4.9 */ +/* Version 4.9.1 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2016 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -589,9 +589,10 @@ void BINCOL::WriteColumn(PGLOBAL g) switch (Fmt) { case 'X': // Standard not converted values - if (Eds && IsTypeChar(Buf_Type)) - *(longlong *)p = Value->GetBigintValue(); - else if (Value->GetBinValue(p, Long, Status)) { + if (Eds && IsTypeChar(Buf_Type)) { + if (Status) + Value->GetValueNonAligned(p, Value->GetBigintValue()); + } else if (Value->GetBinValue(p, Long, Status)) { sprintf(g->Message, MSG(BIN_F_TOO_LONG), Name, Value->GetSize(), Long); longjmp(g->jumper[g->jump_level], 31); @@ -605,7 +606,7 @@ void BINCOL::WriteColumn(PGLOBAL g) sprintf(g->Message, MSG(VALUE_TOO_BIG), n, Name); longjmp(g->jumper[g->jump_level], 31); } else if (Status) - *(short *)p = (short)n; + Value->GetValueNonAligned(p, (short)n); break; case 'T': // Tiny integer @@ -625,7 +626,7 @@ void BINCOL::WriteColumn(PGLOBAL g) sprintf(g->Message, MSG(VALUE_TOO_BIG), n, Name); longjmp(g->jumper[g->jump_level], 31); } else if (Status) - *(int *)p = Value->GetIntValue(); + Value->GetValueNonAligned(p, (int)n); break; case 'G': // Large (great) integer @@ -636,12 +637,12 @@ void BINCOL::WriteColumn(PGLOBAL g) case 'F': // Float case 'R': // Real if (Status) - *(float *)p = (float)Value->GetFloatValue(); + Value->GetValueNonAligned(p, (float)Value->GetFloatValue()); break; case 'D': // Double if (Status) - *(double *)p = Value->GetFloatValue(); + Value->GetValueNonAligned(p, Value->GetFloatValue()); break; case 'C': // Characters diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index d21a8b977dabb..4a39ecd6e0fcd 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -1,11 +1,11 @@ /************* TabFmt C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABFMT */ /* ------------- */ -/* Version 3.9 */ +/* Version 3.9.1 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2001 - 2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2001 - 2016 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -51,9 +51,9 @@ #include "plgdbsem.h" #include "mycat.h" #include "filamap.h" -#if defined(ZIP_SUPPORT) -#include "filamzip.h" -#endif // ZIP_SUPPORT +#if defined(GZ_SUPPORT) +#include "filamgz.h" +#endif // GZ_SUPPORT #include "tabfmt.h" #include "tabmul.h" #define NO_FUNC @@ -462,16 +462,16 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode) // Should be now compatible with UNIX txfp = new(g) MAPFAM(this); } else if (Compressed) { -#if defined(ZIP_SUPPORT) +#if defined(GZ_SUPPORT) if (Compressed == 1) - txfp = new(g) ZIPFAM(this); + txfp = new(g) GZFAM(this); else txfp = new(g) ZLBFAM(this); -#else // !ZIP_SUPPORT +#else // !GZ_SUPPORT strcpy(g->Message, "Compress not supported"); return NULL; -#endif // !ZIP_SUPPORT +#endif // !GZ_SUPPORT } else txfp = new(g) DOSFAM(this); @@ -498,7 +498,7 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode) if (map) { txfp = new(g) MBKFAM(this); } else if (Compressed) { -#if defined(ZIP_SUPPORT) +#if defined(GZ_SUPPORT) if (Compressed == 1) txfp = new(g) ZBKFAM(this); else { @@ -506,7 +506,7 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode) ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL); } // endelse #else - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ"); return NULL; #endif } else diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp index 1726083637102..5f864f0bd4813 100644 --- a/storage/connect/tabjson.cpp +++ b/storage/connect/tabjson.cpp @@ -1,6 +1,6 @@ /************* tabjson C++ Program Source Code File (.CPP) *************/ -/* PROGRAM NAME: tabjson Version 1.1 */ -/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */ +/* PROGRAM NAME: tabjson Version 1.2 */ +/* (C) Copyright to the author Olivier BERTRAND 2014 - 2016 */ /* This program are the JSON class DB execution routines. */ /***********************************************************************/ @@ -25,9 +25,9 @@ //#include "resource.h" // for IDS_COLUMNS #include "tabjson.h" #include "filamap.h" -#if defined(ZIP_SUPPORT) -#include "filamzip.h" -#endif // ZIP_SUPPORT +#if defined(GZ_SUPPORT) +#include "filamgz.h" +#endif // GZ_SUPPORT #include "tabmul.h" #include "checklvl.h" #include "resource.h" @@ -396,15 +396,15 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) (m == MODE_UPDATE || m == MODE_DELETE)); if (Compressed) { -#if defined(ZIP_SUPPORT) +#if defined(GZ_SUPPORT) if (Compressed == 1) - txfp = new(g) ZIPFAM(this); + txfp = new(g) GZFAM(this); else txfp = new(g) ZLBFAM(this); -#else // !ZIP_SUPPORT - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); +#else // !GZ_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ"); return NULL; -#endif // !ZIP_SUPPORT +#endif // !GZ_SUPPORT } else if (map) txfp = new(g) MAPFAM(this); else diff --git a/storage/connect/value.cpp b/storage/connect/value.cpp index 64d0e13e8c42f..ced690e77c05c 100644 --- a/storage/connect/value.cpp +++ b/storage/connect/value.cpp @@ -1,7 +1,7 @@ /************* Value C++ Functions Source Code File (.CPP) *************/ -/* Name: VALUE.CPP Version 2.5 */ +/* Name: VALUE.CPP Version 2.6 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2001-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2001-2016 */ /* */ /* This file contains the VALUE and derived classes family functions. */ /* These classes contain values of different types. They are used so */ @@ -792,19 +792,29 @@ uchar TYPVAL::GetTypedValue(PVBLK blk, int n) /***********************************************************************/ /* TYPVAL SetBinValue: with bytes extracted from a line. */ +/* Currently only used reading column of binary files. */ /***********************************************************************/ template void TYPVAL::SetBinValue(void *p) - { - Tval = *(TYPE *)p; - Null = false; - } // end of SetBinValue +{ +#if defined(UNALIGNED_OK) + // x86 can cast non-aligned memory directly + Tval = *(TYPE *)p; +#else + // Prevent unaligned memory access on MIPS and ArmHF platforms. + // Make use of memcpy instead of straight pointer dereferencing. + // Currently only used by WriteColumn of binary files. + // From original author: Vicentiu Ciorbaru + memcpy(&Tval, p, sizeof(TYPE)); +#endif + Null = false; +} // end of SetBinValue /***********************************************************************/ /* GetBinValue: fill a buffer with the internal binary value. */ /* This function checks whether the buffer length is enough and */ /* returns true if not. Actual filling occurs only if go is true. */ -/* Currently used by WriteColumn of binary files. */ +/* Currently only used writing column of binary files. */ /***********************************************************************/ template bool TYPVAL::GetBinValue(void *buf, int buflen, bool go) @@ -819,7 +829,16 @@ bool TYPVAL::GetBinValue(void *buf, int buflen, bool go) //#endif if (go) - *(TYPE *)buf = Tval; +#if defined(UNALIGNED_OK) + // x86 can cast non-aligned memory directly + *(TYPE *)buf = Tval; +#else + // Prevent unaligned memory access on MIPS and ArmHF platforms. + // Make use of memcpy instead of straight pointer dereferencing. + // Currently only used by WriteColumn of binary files. + // From original author: Vicentiu Ciorbaru + memcpy(buf, &Tval, sizeof(TYPE)); +#endif Null = false; return false; diff --git a/storage/connect/value.h b/storage/connect/value.h index c5a381e89daee..a670ade4c2888 100644 --- a/storage/connect/value.h +++ b/storage/connect/value.h @@ -1,7 +1,7 @@ /**************** Value H Declares Source Code File (.H) ***************/ -/* Name: VALUE.H Version 2.1 */ +/* Name: VALUE.H Version 2.2 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2001-2014 */ +/* (C) Copyright to the author Olivier BERTRAND 2001-2016 */ /* */ /* This file contains the VALUE and derived classes declares. */ /***********************************************************************/ @@ -16,6 +16,13 @@ #include "assert.h" #include "block.h" +/***********************************************************************/ +/* This should list the processors accepting unaligned numeral values.*/ +/***********************************************************************/ +#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(_M_IA64) +#define UNALIGNED_OK +#endif + /***********************************************************************/ /* Types used in some class definitions. */ /***********************************************************************/ @@ -116,27 +123,46 @@ class DllExport VALUE : public BLOCK { virtual bool Compute(PGLOBAL g, PVAL *vp, int np, OPVAL op); virtual bool FormatValue(PVAL vp, char *fmt) = 0; - /** - Set value from a non-aligned in-memory value in the machine byte order. - TYPE can be either of: - - int, short, longlong - - uint, ushort, ulonglong - - float, double - @param - a pointer to a non-aligned value of type TYPE. - */ - template - void SetValueNonAligned(const char *p) - { -#if defined(__i386__) || defined(__x86_64__) - SetValue(*((TYPE*) p)); // x86 can cast non-aligned memory directly + /** + Set value from a non-aligned in-memory value in the machine byte order. + TYPE can be either of: + - int, short, longlong + - uint, ushort, ulonglong + - float, double + @param - a pointer to a non-aligned value of type TYPE. + */ + template + void SetValueNonAligned(const char *p) + { +#if defined(UNALIGNED_OK) + SetValue(*((TYPE*)p)); // x86 can cast non-aligned memory directly #else - TYPE tmp; // a slower version for non-x86 platforms - memcpy(&tmp, p, sizeof(tmp)); - SetValue(tmp); + TYPE tmp; // a slower version for non-x86 platforms + memcpy(&tmp, p, sizeof(tmp)); + SetValue(tmp); #endif - } + } // end of SetValueNonAligned + + /** + Get value from a non-aligned in-memory value in the machine byte order. + TYPE can be either of: + - int, short, longlong + - uint, ushort, ulonglong + - float, double + @params - a pointer to a non-aligned value of type TYPE, the TYPE value. + */ + template + void GetValueNonAligned(char *p, TYPE n) + { +#if defined(UNALIGNED_OK) + *(TYPE *)p = n; // x86 can cast non-aligned memory directly +#else + TYPE tmp = n; // a slower version for non-x86 platforms + memcpy(p, &tmp, sizeof(tmp)); +#endif + } // end of SetValueNonAligned - protected: +protected: virtual bool SetConstFormat(PGLOBAL, FORMAT&) = 0; const char *GetXfmt(void); From 14e1f32894cdbe63a614738cfd93e9d0818dedee Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Sun, 11 Dec 2016 00:50:00 +0200 Subject: [PATCH 32/73] Follow-up for 02d153c7b9 (str2decimal: don't return a negative zero) --- mysql-test/suite/engines/iuds/r/insert_decimal.result | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/suite/engines/iuds/r/insert_decimal.result b/mysql-test/suite/engines/iuds/r/insert_decimal.result index 50fde80d81d82..eab8592c4ee3b 100644 --- a/mysql-test/suite/engines/iuds/r/insert_decimal.result +++ b/mysql-test/suite/engines/iuds/r/insert_decimal.result @@ -48,7 +48,6 @@ Warning 1264 Out of range value for column 'c2' at row 1 Note 1265 Data truncated for column 'c3' at row 1 insert into t2 values ("0.0","0.0","0.0",7),("-0.0","-0.0","-0.0",8),("+0.0","+0.0","+0.0",9),("01.0","01.0","01.0",10),("+01.0","+01.0","+01.0",11),("-01.0","-01.0","-01.0",12); Warnings: -Warning 1264 Out of range value for column 'c2' at row 2 Warning 1264 Out of range value for column 'c2' at row 6 insert into t2 values ("-.1","-.1","-.1",13),("+.1","+.1","+.1",14),(".1",".1",".1",15); Warnings: From d44723e62153d9fb4165d038e9448c20a3ad890b Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Mon, 12 Dec 2016 10:57:19 +0100 Subject: [PATCH 33/73] - MDEV-11295: developing handling files contained in ZIP file. A first experimental and limited implementation. modified: storage/connect/CMakeLists.txt modified: storage/connect/filamap.cpp new file: storage/connect/filamzip.cpp new file: storage/connect/filamzip.h modified: storage/connect/ha_connect.cc new file: storage/connect/ioapi.c new file: storage/connect/ioapi.h modified: storage/connect/mycat.cc modified: storage/connect/plgdbsem.h modified: storage/connect/plgdbutl.cpp modified: storage/connect/tabdos.cpp modified: storage/connect/tabdos.h modified: storage/connect/tabfmt.cpp modified: storage/connect/tabfmt.h modified: storage/connect/tabjson.cpp modified: storage/connect/tabjson.h new file: storage/connect/tabzip.cpp new file: storage/connect/tabzip.h new file: storage/connect/unzip.c new file: storage/connect/unzip.h new file: storage/connect/zip.c --- storage/connect/CMakeLists.txt | 12 + storage/connect/filamap.cpp | 6 +- storage/connect/filamzip.cpp | 501 ++++++++ storage/connect/filamzip.h | 83 ++ storage/connect/ha_connect.cc | 27 +- storage/connect/ioapi.c | 247 ++++ storage/connect/ioapi.h | 208 ++++ storage/connect/mycat.cc | 16 +- storage/connect/plgdbsem.h | 15 +- storage/connect/plgdbutl.cpp | 14 +- storage/connect/tabdos.cpp | 36 +- storage/connect/tabdos.h | 6 +- storage/connect/tabfmt.cpp | 168 ++- storage/connect/tabfmt.h | 29 +- storage/connect/tabjson.cpp | 72 +- storage/connect/tabjson.h | 3 +- storage/connect/tabzip.cpp | 230 ++++ storage/connect/tabzip.h | 100 ++ storage/connect/unzip.c | 2125 ++++++++++++++++++++++++++++++++ storage/connect/unzip.h | 437 +++++++ storage/connect/zip.c | 2007 ++++++++++++++++++++++++++++++ storage/connect/zip.h | 362 ++++++ 22 files changed, 6579 insertions(+), 125 deletions(-) create mode 100644 storage/connect/filamzip.cpp create mode 100644 storage/connect/filamzip.h create mode 100644 storage/connect/ioapi.c create mode 100644 storage/connect/ioapi.h create mode 100644 storage/connect/tabzip.cpp create mode 100644 storage/connect/tabzip.h create mode 100644 storage/connect/unzip.c create mode 100644 storage/connect/unzip.h create mode 100644 storage/connect/zip.c create mode 100644 storage/connect/zip.h diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt index f1567730b26ae..ce6de42442151 100644 --- a/storage/connect/CMakeLists.txt +++ b/storage/connect/CMakeLists.txt @@ -279,6 +279,18 @@ IF(CONNECT_WITH_JDBC) ENDIF() ENDIF(CONNECT_WITH_JDBC) +# +# ZIP +# + +OPTION(CONNECT_WITH_ZIP "Compile CONNECT storage engine with ZIP support" ON) + +IF(CONNECT_WITH_ZIP) + SET(CONNECT_SOURCES ${CONNECT_SOURCES} filamzip.cpp tabzip.cpp unzip.c ioapi.c zip.c + filamzip.h tabzip.h ioapi.h unzip.h zip.h) + add_definitions(-DZIP_SUPPORT -DNOCRYPT) +ENDIF(CONNECT_WITH_ZIP) + # # XMAP diff --git a/storage/connect/filamap.cpp b/storage/connect/filamap.cpp index 5cf9a4d945c27..3c5b3ae7592fe 100644 --- a/storage/connect/filamap.cpp +++ b/storage/connect/filamap.cpp @@ -87,7 +87,7 @@ int MAPFAM::GetFileLength(PGLOBAL g) { int len; - len = (To_Fb) ? To_Fb->Length : TXTFAM::GetFileLength(g); + len = (To_Fb && To_Fb->Count) ? To_Fb->Length : TXTFAM::GetFileLength(g); if (trace) htrc("Mapped file length=%d\n", len); @@ -413,7 +413,7 @@ int MAPFAM::DeleteRecords(PGLOBAL g, int irc) if (Tpos == Spos) { /*******************************************************************/ - /* First line to delete. Move of eventual preceeding lines is */ + /* First line to delete. Move of eventual preceding lines is */ /* not required here, just setting of future Spos and Tpos. */ /*******************************************************************/ Tpos = Spos = Fpos; @@ -498,7 +498,7 @@ int MAPFAM::DeleteRecords(PGLOBAL g, int irc) void MAPFAM::CloseTableFile(PGLOBAL g, bool) { PlugCloseFile(g, To_Fb); - To_Fb = NULL; // To get correct file size in Cardinality +//To_Fb = NULL; // To get correct file size in Cardinality if (trace) htrc("MAP Close: closing %s count=%d\n", diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp new file mode 100644 index 0000000000000..ea8b827974b37 --- /dev/null +++ b/storage/connect/filamzip.cpp @@ -0,0 +1,501 @@ +/*********** File AM Zip C++ Program Source Code File (.CPP) ***********/ +/* PROGRAM NAME: FILAMZIP */ +/* ------------- */ +/* Version 1.0 */ +/* */ +/* COPYRIGHT: */ +/* ---------- */ +/* (C) Copyright to the author Olivier BERTRAND 2016 */ +/* */ +/* WHAT THIS PROGRAM DOES: */ +/* ----------------------- */ +/* This program are the ZIP file access method classes. */ +/* */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the System header files. */ +/***********************************************************************/ +#include "my_global.h" +#if !defined(__WIN__) +#if defined(UNIX) +#include +#include +#else // !UNIX +#include +#endif // !UNIX +#include +#endif // !__WIN__ + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "osutil.h" +#include "filamtxt.h" +#include "tabfmt.h" +//#include "tabzip.h" +#include "filamzip.h" + +/* -------------------------- class ZIPFAM --------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +ZIPFAM::ZIPFAM(PDOSDEF tdp) : MAPFAM(tdp) +{ + zipfile = NULL; + zfn = tdp->Zipfn; + target = tdp->Fn; +//*fn = 0; + entryopen = false; + multiple = tdp->Multiple; + + // Init the case mapping table. +#if defined(__WIN__) + for (int i = 0; i < 256; ++i) mapCaseTable[i] = toupper(i); +#else + for (int i = 0; i < 256; ++i) mapCaseTable[i] = i; +#endif +} // end of ZIPFAM standard constructor + +ZIPFAM::ZIPFAM(PZIPFAM txfp) : MAPFAM(txfp) +{ + zipfile = txfp->zipfile; + zfn = txfp->zfn; + target = txfp->target; +//strcpy(fn, txfp->fn); + finfo = txfp->finfo; + entryopen = txfp->entryopen; + multiple = txfp->multiple; + for (int i = 0; i < 256; ++i) mapCaseTable[i] = txfp->mapCaseTable[i]; +} // end of ZIPFAM copy constructor + +/***********************************************************************/ +/* This code is the copyright property of Alessandro Felice Cantatore. */ +/* http://xoomer.virgilio.it/acantato/dev/wildcard/wildmatch.html */ +/***********************************************************************/ +bool ZIPFAM::WildMatch(PSZ pat, PSZ str) { + PSZ s, p; + bool star = FALSE; + + loopStart: + for (s = str, p = pat; *s; ++s, ++p) { + switch (*p) { + case '?': + if (*s == '.') goto starCheck; + break; + case '*': + star = TRUE; + str = s, pat = p; + if (!*++pat) return TRUE; + goto loopStart; + default: + if (mapCaseTable[*s] != mapCaseTable[*p]) + goto starCheck; + break; + } /* endswitch */ + } /* endfor */ + if (*p == '*') ++p; + return (!*p); + + starCheck: + if (!star) return FALSE; + str++; + goto loopStart; +} // end of WildMatch + +/***********************************************************************/ +/* ZIP GetFileLength: returns file size in number of bytes. */ +/***********************************************************************/ +int ZIPFAM::GetFileLength(PGLOBAL g) +{ + int len = (entryopen) ? Top - Memory : 100; // not 0 to avoid ASSERT + + if (trace) + htrc("Zipped file length=%d\n", len); + + return len; +} // end of GetFileLength + +/***********************************************************************/ +/* open a zip file. */ +/* param: filename path and the filename of the zip file to open. */ +/* return: true if open, false otherwise. */ +/***********************************************************************/ +bool ZIPFAM::open(PGLOBAL g, const char *filename) +{ + if (!zipfile && !(zipfile = unzOpen64(filename))) + sprintf(g->Message, "Zipfile open error"); + + return (zipfile == NULL); +} // end of open + +/***********************************************************************/ +/* Close the zip file. */ +/***********************************************************************/ +void ZIPFAM::close() +{ + if (zipfile) { + closeEntry(); + unzClose(zipfile); + zipfile = NULL; + } // endif zipfile + +} // end of close + +/***********************************************************************/ +/* Find next entry matching target pattern. */ +/***********************************************************************/ +int ZIPFAM::findEntry(PGLOBAL g, bool next) +{ + int rc; + char fn[FILENAME_MAX]; // The current entry file name + + do { + if (next) { + rc = unzGoToNextFile(zipfile); + + if (rc == UNZ_END_OF_LIST_OF_FILE) + return RC_EF; + else if (rc != UNZ_OK) { + sprintf(g->Message, "unzGoToNextFile rc = ", rc); + return RC_FX; + } // endif rc + + } // endif next + + if (target && *target) { + rc = unzGetCurrentFileInfo(zipfile, NULL, fn, sizeof(fn), + NULL, 0, NULL, 0); + if (rc == UNZ_OK) { + if (WildMatch(target, fn)) + return RC_OK; + + } else { + sprintf(g->Message, "GetCurrentFileInfo rc = %d", rc); + return RC_FX; + } // endif rc + + } else + return RC_OK; + + next = true; + } while (true); + + strcpy(g->Message, "FindNext logical error"); + return RC_FX; +} // end of FindNext + +/***********************************************************************/ +/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ +/***********************************************************************/ +bool ZIPFAM::OpenTableFile(PGLOBAL g) +{ + char filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); + PFBLOCK fp; + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + + /*********************************************************************/ + /* The file will be decompressed into virtual memory. */ + /*********************************************************************/ + if (mode == MODE_READ) { + // We used the file name relative to recorded datapath + PlugSetPath(filename, zfn, Tdbp->GetPath()); + + bool b = open(g, filename); + + if (!b) { + int rc; + + if (target && *target) { + if (!multiple) { + rc = unzLocateFile(zipfile, target, 0); + + if (rc == UNZ_END_OF_LIST_OF_FILE) { + sprintf(g->Message, "Target file %s not in %s", target, filename); + return true; + } else if (rc != UNZ_OK) { + sprintf(g->Message, "unzLocateFile rc=%d", rc); + return true; + } // endif's rc + + } else { + if ((rc = findEntry(g, false)) == RC_FX) + return true; + else if (rc == RC_NF) { + sprintf(g->Message, "No match of %s in %s", target, filename); + return true; + } // endif rc + + } // endif multiple + + } // endif target + + if (openEntry(g)) + return true; + + if (Top > Memory) { + /*******************************************************************/ + /* Link a Fblock. This make possible to automatically close it */ + /* in case of error g->jump. */ + /*******************************************************************/ + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_ZIP; + fp->Fname = PlugDup(g, filename); + fp->Next = dbuserp->Openlist; + dbuserp->Openlist = fp; + fp->Count = 1; + fp->Length = Top - Memory; + fp->Memory = Memory; + fp->Mode = mode; + fp->File = this; + fp->Handle = NULL; + } // endif fp + + To_Fb = fp; // Useful when closing + } // endif b + + } else { + strcpy(g->Message, "Only READ mode supported for ZIP files"); + return true; + } // endif mode + + return false; + } // end of OpenTableFile + +/***********************************************************************/ +/* Open target in zip file. */ +/***********************************************************************/ +bool ZIPFAM::openEntry(PGLOBAL g) +{ + int rc; + uint size; + + rc = unzGetCurrentFileInfo(zipfile, &finfo, 0, 0, 0, 0, 0, 0); + + if (rc != UNZ_OK) { + sprintf(g->Message, "unzGetCurrentFileInfo64 rc=%d", rc); + return true; + } else if ((rc = unzOpenCurrentFile(zipfile)) != UNZ_OK) { + sprintf(g->Message, "unzOpenCurrentFile rc=%d", rc); + return true; + } // endif rc + + size = finfo.uncompressed_size; + Memory = new char[size]; + + if ((rc = unzReadCurrentFile(zipfile, Memory, size)) < 0) { + sprintf(g->Message, "unzReadCurrentFile rc = ", rc); + unzCloseCurrentFile(zipfile); + free(Memory); + entryopen = false; + } else { + // The pseudo "buffer" is here the entire real buffer + Fpos = Mempos = Memory; + Top = Memory + size; + + if (trace) + htrc("Memory=%p size=%ud Top=%p\n", Memory, size, Top); + + entryopen = true; + } // endif rc + + return !entryopen; +} // end of openEntry + +/***********************************************************************/ +/* Close the zip file. */ +/***********************************************************************/ +void ZIPFAM::closeEntry() +{ + if (entryopen) { + unzCloseCurrentFile(zipfile); + entryopen = false; + } // endif entryopen + + if (Memory) { + free(Memory); + Memory = NULL; + } // endif Memory + +} // end of closeEntry + +/***********************************************************************/ +/* ReadBuffer: Read one line for a ZIP file. */ +/***********************************************************************/ +int ZIPFAM::ReadBuffer(PGLOBAL g) +{ + int rc, len; + + // Are we at the end of the memory + if (Mempos >= Top) { + if (multiple) { + closeEntry(); + + if ((rc = findEntry(g, true)) != RC_OK) + return rc; + + if (openEntry(g)) + return RC_FX; + + } else + return RC_EF; + + } // endif Mempos + +#if 0 + if (!Placed) { + /*******************************************************************/ + /* Record file position in case of UPDATE or DELETE. */ + /*******************************************************************/ + int rc; + + next: + Fpos = Mempos; + CurBlk = (int)Rows++; + + /*******************************************************************/ + /* Check whether optimization on ROWID */ + /* can be done, as well as for join as for local filtering. */ + /*******************************************************************/ + switch (Tdbp->TestBlock(g)) { + case RC_EF: + return RC_EF; + case RC_NF: + // Skip this record + if ((rc = SkipRecord(g, false)) != RC_OK) + return rc; + + goto next; + } // endswitch rc + + } else + Placed = false; +#else + // Perhaps unuseful + Fpos = Mempos; + CurBlk = (int)Rows++; + Placed = false; +#endif + + // Immediately calculate next position (Used by DeleteDB) + while (*Mempos++ != '\n'); // What about Unix ??? + + // Set caller line buffer + len = (Mempos - Fpos) - 1; + + // Don't rely on ENDING setting + if (len > 0 && *(Mempos - 2) == '\r') + len--; // Line ends by CRLF + + memcpy(Tdbp->GetLine(), Fpos, len); + Tdbp->GetLine()[len] = '\0'; + return RC_OK; +} // end of ReadBuffer + +#if 0 +/***********************************************************************/ +/* Table file close routine for MAP access method. */ +/***********************************************************************/ +void ZIPFAM::CloseTableFile(PGLOBAL g, bool) +{ + close(); +} // end of CloseTableFile +#endif // 0 + +/* -------------------------- class ZPXFAM --------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +ZPXFAM::ZPXFAM(PDOSDEF tdp) : ZIPFAM(tdp) +{ + Lrecl = tdp->GetLrecl(); +} // end of ZPXFAM standard constructor + +ZPXFAM::ZPXFAM(PZPXFAM txfp) : ZIPFAM(txfp) +{ + Lrecl = txfp->Lrecl; +} // end of ZPXFAM copy constructor + +/***********************************************************************/ +/* ReadBuffer: Read one line for a fixed ZIP file. */ +/***********************************************************************/ +int ZPXFAM::ReadBuffer(PGLOBAL g) +{ + int rc, len; + + // Are we at the end of the memory + if (Mempos >= Top) { + if (multiple) { + closeEntry(); + + if ((rc = findEntry(g, true)) != RC_OK) + return rc; + + if (openEntry(g)) + return RC_FX; + + } else + return RC_EF; + + } // endif Mempos + +#if 0 + if (!Placed) { + /*******************************************************************/ + /* Record file position in case of UPDATE or DELETE. */ + /*******************************************************************/ + int rc; + + next: + Fpos = Mempos; + CurBlk = (int)Rows++; + + /*******************************************************************/ + /* Check whether optimization on ROWID */ + /* can be done, as well as for join as for local filtering. */ + /*******************************************************************/ + switch (Tdbp->TestBlock(g)) { + case RC_EF: + return RC_EF; + case RC_NF: + // Skip this record + if ((rc = SkipRecord(g, false)) != RC_OK) + return rc; + + goto next; + } // endswitch rc + + } else + Placed = false; +#else + // Perhaps unuseful + Fpos = Mempos; + CurBlk = (int)Rows++; + Placed = false; +#endif + + // Immediately calculate next position (Used by DeleteDB) + Mempos += Lrecl; + + // Set caller line buffer + len = Lrecl; + + // Don't rely on ENDING setting + if (len > 0 && *(Mempos - 1) == '\n') + len--; // Line ends by LF + + if (len > 0 && *(Mempos - 2) == '\r') + len--; // Line ends by CRLF + + memcpy(Tdbp->GetLine(), Fpos, len); + Tdbp->GetLine()[len] = '\0'; + return RC_OK; +} // end of ReadBuffer + diff --git a/storage/connect/filamzip.h b/storage/connect/filamzip.h new file mode 100644 index 0000000000000..85c1f907d20f7 --- /dev/null +++ b/storage/connect/filamzip.h @@ -0,0 +1,83 @@ +/************** filamzip H Declares Source Code File (.H) **************/ +/* Name: filamzip.h Version 1.0 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2016 */ +/* */ +/* This file contains the ZIP file access method classes declares. */ +/***********************************************************************/ +#ifndef __FILAMZIP_H +#define __FILAMZIP_H + +#include "block.h" +#include "filamap.h" +#include "unzip.h" + +#define DLLEXPORT extern "C" + +typedef class ZIPFAM *PZIPFAM; +typedef class ZPXFAM *PZPXFAM; + +/***********************************************************************/ +/* This is the ZIP file access method. */ +/***********************************************************************/ +class DllExport ZIPFAM : public MAPFAM { +public: + // Constructor + ZIPFAM(PDOSDEF tdp); + ZIPFAM(PZIPFAM txfp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_ZIP;} + virtual PTXF Duplicate(PGLOBAL g) {return (PTXF) new(g) ZIPFAM(this);} + + // Methods + virtual int GetFileLength(PGLOBAL g); + virtual int Cardinality(PGLOBAL g) {return (g) ? 10 : 1;} +//virtual int MaxBlkSize(PGLOBAL g, int s) {return s;} + virtual bool OpenTableFile(PGLOBAL g); + virtual bool DeferReading(void) {return false;} + virtual int ReadBuffer(PGLOBAL g); +//virtual int WriteBuffer(PGLOBAL g); +//virtual int DeleteRecords(PGLOBAL g, int irc); +//virtual void CloseTableFile(PGLOBAL g, bool abort); + void close(void); + +protected: + bool open(PGLOBAL g, const char *filename); + bool openEntry(PGLOBAL g); + void closeEntry(void); + bool WildMatch(PSZ pat, PSZ str); + int findEntry(PGLOBAL g, bool next); + + // Members + unzFile zipfile; // The ZIP container file + PSZ zfn; // The ZIP file name + PSZ target; // The target file name + unz_file_info finfo; // The current file info +//char fn[FILENAME_MAX]; // The current file name + bool entryopen; // True when open current entry + int multiple; // Multiple targets + char mapCaseTable[256]; +}; // end of ZIPFAM + +/***********************************************************************/ +/* This is the fixed ZIP file access method. */ +/***********************************************************************/ +class DllExport ZPXFAM : public ZIPFAM { +public: + // Constructor + ZPXFAM(PDOSDEF tdp); + ZPXFAM(PZPXFAM txfp); + + // Implementation + virtual PTXF Duplicate(PGLOBAL g) {return (PTXF) new(g) ZPXFAM(this);} + + // Methods + virtual int ReadBuffer(PGLOBAL g); + +protected: + // Members + int Lrecl; +}; // end of ZPXFAM + +#endif // __FILAMZIP_H diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 2222e51b083d3..b690dff24f42c 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -171,9 +171,9 @@ #define JSONMAX 10 // JSON Default max grp size extern "C" { - char version[]= "Version 1.04.0008 October 20, 2016"; + char version[]= "Version 1.04.0009 December 09, 2016"; #if defined(__WIN__) - char compver[]= "Version 1.04.0008 " __DATE__ " " __TIME__; + char compver[]= "Version 1.04.0009 " __DATE__ " " __TIME__; char slash= '\\'; #else // !__WIN__ char slash= '/'; @@ -4165,7 +4165,8 @@ bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn, bool quick) case TAB_DIR: case TAB_MAC: case TAB_WMI: - case TAB_OEM: + case TAB_ZIP: + case TAB_OEM: #ifdef NO_EMBEDDED_ACCESS_CHECKS return false; #endif @@ -5173,13 +5174,13 @@ static int connect_assisted_discovery(handlerton *, THD* thd, char v=0, spc= ',', qch= 0; const char *fncn= "?"; const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src; - const char *col, *ocl, *rnk, *pic, *fcl, *skc; + const char *col, *ocl, *rnk, *pic, *fcl, *skc, *zfn; char *tab, *dsn, *shm, *dpath; #if defined(__WIN__) char *nsp= NULL, *cls= NULL; #endif // __WIN__ - int port= 0, hdr= 0, mxr= 0, mxe= 0, rc= 0; - int cop __attribute__((unused))= 0, lrecl= 0; +//int hdr, mxe; + int port = 0, mxr = 0, rc = 0, mul = 0, lrecl = 0; #if defined(ODBC_SUPPORT) POPARM sop= NULL; char *ucnc= NULL; @@ -5211,7 +5212,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd, if (!g) return HA_ERR_INTERNAL_ERROR; - user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= dsn= NULL; + user= host= pwd= tbl= src= col= ocl= pic= fcl= skc= rnk= zfn= dsn= NULL; // Get the useful create options ttp= GetTypeID(topt->type); @@ -5224,7 +5225,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd, sep= topt->separator; spc= (!sep) ? ',' : *sep; qch= topt->qchar ? *topt->qchar : (signed)topt->quoted >= 0 ? '"' : 0; - hdr= (int)topt->header; + mul = (int)topt->multiple; tbl= topt->tablist; col= topt->colist; @@ -5260,11 +5261,13 @@ static int connect_assisted_discovery(handlerton *, THD* thd, // prop = GetListOption(g, "Properties", topt->oplist, NULL); tabtyp = GetListOption(g, "Tabtype", topt->oplist, NULL); #endif // JDBC_SUPPORT - mxe= atoi(GetListOption(g,"maxerr", topt->oplist, "0")); #if defined(PROMPT_OK) cop= atoi(GetListOption(g, "checkdsn", topt->oplist, "0")); #endif // PROMPT_OK - } else { +#if defined(ZIP_SUPPORT) + zfn = GetListOption(g, "Zipfile", topt->oplist, NULL); +#endif // ZIP_SUPPORT + } else { host= "localhost"; user= (ttp == TAB_ODBC ? NULL : "root"); } // endif option_list @@ -5471,7 +5474,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd, case TAB_XML: #endif // LIBXML2_SUPPORT || DOMDOC_SUPPORT case TAB_JSON: - if (!fn) + if (!fn && !zfn && !mul) sprintf(g->Message, "Missing %s file name", topt->type); else ok= true; @@ -5585,7 +5588,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd, NULL, port, fnc == FNC_COL); break; case TAB_CSV: - qrp= CSVColumns(g, dpath, fn, spc, qch, hdr, mxe, fnc == FNC_COL); + qrp = CSVColumns(g, dpath, topt, fnc == FNC_COL); break; #if defined(__WIN__) case TAB_WMI: diff --git a/storage/connect/ioapi.c b/storage/connect/ioapi.c new file mode 100644 index 0000000000000..7f5c191b2afd1 --- /dev/null +++ b/storage/connect/ioapi.c @@ -0,0 +1,247 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS))) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#if defined(__APPLE__) || defined(IOAPI_NO_64) +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == MAXU32) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = FOPEN_FUNC((const char*)filename, mode_fopen); + return file; +} + + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = FTELLO_FUNC((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/storage/connect/ioapi.h b/storage/connect/ioapi.h new file mode 100644 index 0000000000000..8dcbdb06e35ad --- /dev/null +++ b/storage/connect/ioapi.h @@ -0,0 +1,208 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif + +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef __FreeBSD__ +#define fopen64 fopen +#define ftello64 ftello +#define fseeko64 fseeko +#endif +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#define MAXU32 0xffffffff + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc index 19c9f62b5bf6d..497fe5e1aa8d6 100644 --- a/storage/connect/mycat.cc +++ b/storage/connect/mycat.cc @@ -16,7 +16,7 @@ /*************** Mycat CC Program Source Code File (.CC) ***************/ /* PROGRAM NAME: MYCAT */ /* ------------- */ -/* Version 1.4 */ +/* Version 1.5 */ /* */ /* Author: Olivier Bertrand 2012 - 2016 */ /* */ @@ -95,6 +95,9 @@ #if defined(XML_SUPPORT) #include "tabxml.h" #endif // XML_SUPPORT +#if defined(ZIP_SUPPORT) +#include "tabzip.h" +#endif // ZIP_SUPPORT #include "mycat.h" /***********************************************************************/ @@ -154,7 +157,10 @@ TABTYPE GetTypeID(const char *type) #endif : (!stricmp(type, "VIR")) ? TAB_VIR : (!stricmp(type, "JSON")) ? TAB_JSON - : (!stricmp(type, "OEM")) ? TAB_OEM : TAB_NIY; +#ifdef ZIP_SUPPORT + : (!stricmp(type, "ZIP")) ? TAB_ZIP +#endif + : (!stricmp(type, "OEM")) ? TAB_OEM : TAB_NIY; } // end of GetTypeID /***********************************************************************/ @@ -175,6 +181,7 @@ bool IsFileType(TABTYPE type) case TAB_INI: case TAB_VEC: case TAB_JSON: +// case TAB_ZIP: isfile= true; break; default: @@ -575,7 +582,10 @@ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, PTABLE tablep, LPCSTR am) #endif // PIVOT_SUPPORT case TAB_VIR: tdp= new(g) VIRDEF; break; case TAB_JSON: tdp= new(g) JSONDEF; break; - default: +#if defined(ZIP_SUPPORT) + case TAB_ZIP: tdp= new(g) ZIPDEF; break; +#endif // ZIP_SUPPORT + default: sprintf(g->Message, MSG(BAD_TABLE_TYPE), am, name); } // endswitch diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h index e99f5f364440f..cb408494319f4 100644 --- a/storage/connect/plgdbsem.h +++ b/storage/connect/plgdbsem.h @@ -1,9 +1,9 @@ /************** PlgDBSem H Declares Source Code File (.H) **************/ -/* Name: PLGDBSEM.H Version 3.6 */ +/* Name: PLGDBSEM.H Version 3.7 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2016 */ /* */ -/* This file contains the PlugDB++ application type definitions. */ +/* This file contains the CONNECT storage engine definitions. */ /***********************************************************************/ /***********************************************************************/ @@ -49,7 +49,8 @@ enum BLKTYP {TYPE_TABLE = 50, /* Table Name/Srcdef/... Block */ TYPE_FB_MAP = 23, /* Mapped file block (storage) */ TYPE_FB_HANDLE = 24, /* File block (handle) */ TYPE_FB_XML = 21, /* DOM XML file block */ - TYPE_FB_XML2 = 27}; /* libxml2 XML file block */ + TYPE_FB_XML2 = 27, /* libxml2 XML file block */ + TYPE_FB_ZIP = 28}; /* ZIP file block */ enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */ TAB_DOS = 1, /* Fixed column offset, variable LRECL */ @@ -78,7 +79,8 @@ enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */ TAB_JCT = 24, /* Junction tables NIY */ TAB_DMY = 25, /* DMY Dummy tables NIY */ TAB_JDBC = 26, /* Table accessed via JDBC */ - TAB_NIY = 27}; /* Table not implemented yet */ + TAB_ZIP = 27, /* ZIP file info table */ + TAB_NIY = 28}; /* Table not implemented yet */ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */ TYPE_AM_ROWID = 1, /* ROWID type (special column) */ @@ -140,7 +142,8 @@ enum AMT {TYPE_AM_ERROR = 0, /* Type not defined */ TYPE_AM_MYSQL = 192, /* MYSQL access method type no */ TYPE_AM_MYX = 193, /* MYSQL EXEC access method type */ TYPE_AM_CAT = 195, /* Catalog access method type no */ - TYPE_AM_OUT = 200}; /* Output relations (storage) */ + TYPE_AM_ZIP = 198, /* ZIP access method type no */ + TYPE_AM_OUT = 200}; /* Output relations (storage) */ enum RECFM {RECFM_NAF = -2, /* Not a file */ RECFM_OEM = -1, /* OEM file access method */ diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp index 13c0dfd1e1892..31c040c6957ea 100644 --- a/storage/connect/plgdbutl.cpp +++ b/storage/connect/plgdbutl.cpp @@ -68,6 +68,9 @@ #include "tabcol.h" // header of XTAB and COLUMN classes #include "valblk.h" #include "rcmsg.h" +#ifdef ZIP_SUPPORT +#include "filamzip.h" +#endif // ZIP_SUPPORT /***********************************************************************/ /* DB static variables. */ @@ -934,7 +937,16 @@ int PlugCloseFile(PGLOBAL g __attribute__((unused)), PFBLOCK fp, bool all) CloseXML2File(g, fp, all); break; #endif // LIBXML2_SUPPORT - default: +#ifdef ZIP_SUPPORT + case TYPE_FB_ZIP: + ((PZIPFAM)fp->File)->close(); + fp->Memory = NULL; + fp->Mode = MODE_ANY; + fp->Count = 0; + fp->File = NULL; + break; +#endif // ZIP_SUPPORT + default: rc = RC_FX; } // endswitch Type diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp index 06dde34a27f98..9bcac0b5f1a52 100644 --- a/storage/connect/tabdos.cpp +++ b/storage/connect/tabdos.cpp @@ -1,7 +1,7 @@ /************* TabDos C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABDOS */ /* ------------- */ -/* Version 4.9.1 */ +/* Version 4.9.2 */ /* */ /* COPYRIGHT: */ /* ---------- */ @@ -54,6 +54,9 @@ #if defined(GZ_SUPPORT) #include "filamgz.h" #endif // GZ_SUPPORT +#if defined(ZIP_SUPPORT) +#include "filamzip.h" +#endif // ZIP_SUPPORT #include "tabdos.h" #include "tabfix.h" #include "tabmul.h" @@ -93,6 +96,7 @@ DOSDEF::DOSDEF(void) Pseudo = 3; Fn = NULL; Ofn = NULL; + Zipfn = NULL; To_Indx = NULL; Recfm = RECFM_VAR; Mapped = false; @@ -126,7 +130,20 @@ bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int) : (am && (*am == 'B' || *am == 'b')) ? "B" : (am && !stricmp(am, "DBF")) ? "D" : "V"; - Desc = Fn = GetStringCatInfo(g, "Filename", NULL); + if (*dfm != 'D') + Zipfn = GetStringCatInfo(g, "Zipfile", NULL); + + if (Zipfn && Multiple) { + // Prevent Fn to default to table name + Desc = GetStringCatInfo(g, "Filename", NULL); + Fn = GetStringCatInfo(g, "Filename", "<%>"); + + if (!strcmp(Fn, "<%>")) + Fn = NULL; + + } else + Desc = Fn = GetStringCatInfo(g, "Filename", NULL); + Ofn = GetStringCatInfo(g, "Optname", Fn); GetCharCatInfo("Recfm", (PSZ)dfm, buf, sizeof(buf)); Recfm = (toupper(*buf) == 'F') ? RECFM_FIX : @@ -333,7 +350,20 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) /* Allocate table and file processing class of the proper type. */ /* Column blocks will be allocated only when needed. */ /*********************************************************************/ - if (Recfm == RECFM_DBF) { + if (Zipfn) { +#if defined(ZIP_SUPPORT) + if (Recfm == RECFM_VAR) + txfp = new(g) ZIPFAM(this); + else + txfp = new(g) ZPXFAM(this); + + tdbp = new(g) TDBDOS(this, txfp); + return tdbp; +#else // !ZIP_SUPPORT + strcpy(g->Message, "ZIP not supported"); + return NULL; +#endif // !ZIP_SUPPORT + } else if (Recfm == RECFM_DBF) { if (Catfunc == FNC_NO) { if (map) txfp = new(g) DBMFAM(this); diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h index c70e0032f47cd..501ddbc2e0bc8 100644 --- a/storage/connect/tabdos.h +++ b/storage/connect/tabdos.h @@ -28,6 +28,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ friend class TDBFIX; friend class TXTFAM; friend class DBFBASE; + friend class ZIPFAM; public: // Constructor DOSDEF(void); @@ -58,7 +59,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ // Methods virtual int Indexable(void) - {return (!Multiple && Compressed != 1) ? 1 : 0;} + {return (!Multiple && !Zipfn && Compressed != 1) ? 1 : 0;} virtual bool DeleteIndexFile(PGLOBAL g, PIXDEF pxdf); virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); virtual PTDB GetTable(PGLOBAL g, MODE mode); @@ -72,7 +73,8 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ // Members PSZ Fn; /* Path/Name of corresponding file */ PSZ Ofn; /* Base Path/Name of matching index files*/ - PIXDEF To_Indx; /* To index definitions blocks */ + PSZ Zipfn; /* Zip container name */ + PIXDEF To_Indx; /* To index definitions blocks */ RECFM Recfm; /* 0:VAR, 1:FIX, 2:BIN, 3:VCT, 6:DBF */ bool Mapped; /* 0: disk file, 1: memory mapped file */ bool Padded; /* true for padded table file */ diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index 4a39ecd6e0fcd..d6649a0093bf4 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -1,7 +1,7 @@ /************* TabFmt C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABFMT */ /* ------------- */ -/* Version 3.9.1 */ +/* Version 3.9.2 */ /* */ /* COPYRIGHT: */ /* ---------- */ @@ -54,6 +54,9 @@ #if defined(GZ_SUPPORT) #include "filamgz.h" #endif // GZ_SUPPORT +#if defined(ZIP_SUPPORT) +#include "filamzip.h" +#endif // ZIP_SUPPORT #include "tabfmt.h" #include "tabmul.h" #define NO_FUNC @@ -78,20 +81,24 @@ USETEMP UseTemp(void); /* of types (TYPE_STRING < TYPE_DOUBLE < TYPE_INT) (1 < 2 < 7). */ /* If these values are changed, this will have to be revisited. */ /***********************************************************************/ -PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, - char q, int hdr, int mxr, bool info) +PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) { static int buftyp[] = {TYPE_STRING, TYPE_SHORT, TYPE_STRING, TYPE_INT, TYPE_INT, TYPE_SHORT}; static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH, FLD_SCALE}; static unsigned int length[] = {6, 6, 8, 10, 10, 6}; - char *p, *colname[MAXCOL], dechar, filename[_MAX_PATH], buf[4096]; + const char *fn; + char sep, q; + int rc, mxr; + bool hdr; + char *p, *colname[MAXCOL], dechar, buf[8]; int i, imax, hmax, n, nerr, phase, blank, digit, dec, type; int ncol = sizeof(buftyp) / sizeof(int); int num_read = 0, num_max = 10000000; // Statistics int len[MAXCOL], typ[MAXCOL], prc[MAXCOL]; - FILE *infile; + PCSVDEF tdp; + PTDBCSV tdbp; PQRYRES qrp; PCOLRES crp; @@ -102,26 +109,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, } // endif info // num_max = atoi(p+1); // Max num of record to test -#if defined(__WIN__) - if (sep == ',' || strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6)) - dechar = '.'; - else - dechar = ','; -#else // !__WIN__ - dechar = '.'; -#endif // !__WIN__ - - if (trace) - htrc("File %s sep=%c q=%c hdr=%d mxr=%d\n", - SVP(fn), sep, q, hdr, mxr); - - if (!fn) { - strcpy(g->Message, MSG(MISSING_FNAME)); - return NULL; - } // endif fn - imax = hmax = nerr = 0; - mxr = MY_MAX(0, mxr); for (i = 0; i < MAXCOL; i++) { colname[i] = NULL; @@ -131,12 +119,73 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, } // endfor i /*********************************************************************/ - /* Open the input file. */ + /* Get the CSV table description block. */ /*********************************************************************/ - PlugSetPath(filename, fn, dp); + tdp = new(g) CSVDEF; +#if defined(ZIP_SUPPORT) + tdp->Zipfn = GetStringTableOption(g, topt, "Zipfile", NULL); + tdp->Multiple = GetIntegerTableOption(g, topt, "Multiple", 0); +#endif // ZIP_SUPPORT + tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL); + + if (!tdp->Fn && !tdp->Zipfn && !tdp->Multiple) { + strcpy(g->Message, MSG(MISSING_FNAME)); + return NULL; + } // endif Fn + + fn = (tdp->Fn) ? tdp->Fn : "unnamed"; + + if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0))) + tdp->Lrecl = 4096; - if (!(infile= global_fopen(g, MSGID_CANNOT_OPEN, filename, "r"))) - return NULL; + p = GetStringTableOption(g, topt, "Separator", ","); + tdp->Sep = (strlen(p) == 2 && p[0] == '\\' && p[1] == 't') ? '\t' : *p; + +#if defined(__WIN__) + if (tdp->Sep == ',' || strnicmp(setlocale(LC_NUMERIC, NULL), "French", 6)) + dechar = '.'; + else + dechar = ','; +#else // !__WIN__ + dechar = '.'; +#endif // !__WIN__ + + sep = tdp->Sep; + tdp->Quoted = GetIntegerTableOption(g, topt, "Quoted", -1); + p = GetStringTableOption(g, topt, "Qchar", ""); + tdp->Qot = *p; + + if (tdp->Qot && tdp->Quoted < 0) + tdp->Quoted = 0; + else if (!tdp->Qot && tdp->Quoted >= 0) + tdp->Qot = '"'; + + q = tdp->Qot; + hdr = GetBooleanTableOption(g, topt, "Header", false); + tdp->Maxerr = GetIntegerTableOption(g, topt, "Maxerr", 0); + tdp->Accept = GetBooleanTableOption(g, topt, "Accept", false); + + if (tdp->Accept && tdp->Maxerr == 0) + tdp->Maxerr = INT_MAX32; // Accept all bad lines + + mxr = MY_MAX(0, tdp->Maxerr); + + if (trace) + 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->Zipfn) + tdbp = new(g) TDBCSV(tdp, new(g) ZIPFAM(tdp)); + else + tdbp = new(g) TDBCSV(tdp, new(g) DOSFAM(tdp)); + + tdbp->SetMode(MODE_READ); + + /*********************************************************************/ + /* Open the CSV file. */ + /*********************************************************************/ + if (tdbp->OpenDB(g)) + return NULL; if (hdr) { /*******************************************************************/ @@ -144,16 +193,8 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, /*******************************************************************/ phase = 0; - if (fgets(buf, sizeof(buf), infile)) { - n = strlen(buf) + 1; - buf[n - 2] = '\0'; -#if !defined(__WIN__) - // The file can be imported from Windows - if (buf[n - 3] == '\r') - buf[n - 3] = 0; -#endif // UNIX - p = (char*)PlugSubAlloc(g, NULL, n); - memcpy(p, buf, n); + if ((rc = tdbp->ReadDB(g)) == RC_OK) { + p = PlgDBDup(g, tdbp->To_Line); //skip leading blanks for (; *p == ' '; p++) ; @@ -165,10 +206,11 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, } // endif q colname[0] = p; - } else { + } else if (rc == RC_EF) { sprintf(g->Message, MSG(FILE_IS_EMPTY), fn); goto err; - } // endif's + } else + goto err; for (i = 1; *p; p++) if (phase == 1 && *p == q) { @@ -201,15 +243,8 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, /*******************************************************************/ /* Now start the reading process. Read one line. */ /*******************************************************************/ - if (fgets(buf, sizeof(buf), infile)) { - n = strlen(buf); - buf[n - 1] = '\0'; -#if !defined(__WIN__) - // The file can be imported from Windows - if (buf[n - 2] == '\r') - buf[n - 2] = 0; -#endif // UNIX - } else if (feof(infile)) { + if ((rc = tdbp->ReadDB(g)) == RC_OK) { + } else if (rc == RC_EF) { sprintf(g->Message, MSG(EOF_AFTER_LINE), num_read -1); break; } else { @@ -222,7 +257,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, /*******************************************************************/ i = n = phase = blank = digit = dec = 0; - for (p = buf; *p; p++) + for (p = tdbp->To_Line; *p; p++) if (*p == sep) { if (phase != 1) { if (i == MAXCOL - 1) { @@ -331,7 +366,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, htrc("\n"); } // endif trace - fclose(infile); + tdbp->CloseDB(g); skipit: if (trace) @@ -381,7 +416,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, return qrp; err: - fclose(infile); + tdbp->CloseDB(g); return NULL; } // end of CSVCColumns @@ -458,7 +493,21 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode) /*******************************************************************/ /* Allocate a file processing class of the proper type. */ /*******************************************************************/ - if (map) { + if (Zipfn) { +#if defined(ZIP_SUPPORT) + txfp = new(g) ZIPFAM(this); + + if (!Fmtd) + tdbp = new(g) TDBCSV(this, txfp); + else + tdbp = new(g) TDBFMT(this, txfp); + + return tdbp; +#else // !ZIP_SUPPORT + strcpy(g->Message, "ZIP not supported"); + return NULL; +#endif // !ZIP_SUPPORT + } else if (map) { // Should be now compatible with UNIX txfp = new(g) MAPFAM(this); } else if (Compressed) { @@ -1476,21 +1525,16 @@ void CSVCOL::WriteColumn(PGLOBAL g) /* TDBCCL class constructor. */ /***********************************************************************/ TDBCCL::TDBCCL(PCSVDEF tdp) : TDBCAT(tdp) - { - Fn = tdp->GetFn(); - Hdr = tdp->Header; - Mxr = tdp->Maxerr; - Qtd = tdp->Quoted; - Sep = tdp->Sep; - } // end of TDBCCL constructor +{ + Topt = tdp->GetTopt(); +} // end of TDBCCL constructor /***********************************************************************/ /* GetResult: Get the list the CSV file columns. */ /***********************************************************************/ PQRYRES TDBCCL::GetResult(PGLOBAL g) { - return CSVColumns(g, ((PTABDEF)To_Def)->GetPath(), - Fn, Sep, Qtd, Hdr, Mxr, false); + return CSVColumns(g, ((PTABDEF)To_Def)->GetPath(), Topt, false); } // end of GetResult /* ------------------------ End of TabFmt ---------------------------- */ diff --git a/storage/connect/tabfmt.h b/storage/connect/tabfmt.h index ce80a276cdc9c..5ce8d399a6455 100644 --- a/storage/connect/tabfmt.h +++ b/storage/connect/tabfmt.h @@ -1,7 +1,7 @@ /*************** TabFmt H Declares Source Code File (.H) ***************/ -/* Name: TABFMT.H Version 2.4 */ +/* Name: TABFMT.H Version 2.5 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2001-2014 */ +/* (C) Copyright to the author Olivier BERTRAND 2001-2016 */ /* */ /* This file contains the CSV and FMT classes declares. */ /***********************************************************************/ @@ -13,8 +13,7 @@ typedef class TDBFMT *PTDBFMT; /***********************************************************************/ /* Functions used externally. */ /***********************************************************************/ -PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, - char q, int hdr, int mxr, bool info); +PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info); /***********************************************************************/ /* CSV table. */ @@ -22,7 +21,8 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, const char *fn, char sep, class DllExport CSVDEF : public DOSDEF { /* Logical table description */ friend class TDBCSV; friend class TDBCCL; - public: + friend PQRYRES CSVColumns(PGLOBAL, char *, PTOS, bool); +public: // Constructor CSVDEF(void); @@ -50,9 +50,10 @@ class DllExport CSVDEF : public DOSDEF { /* Logical table description */ /* This is the DOS/UNIX Access Method class declaration for files */ /* that are CSV files with columns separated by the Sep character. */ /***********************************************************************/ -class TDBCSV : public TDBDOS { +class DllExport TDBCSV : public TDBDOS { friend class CSVCOL; - public: + friend PQRYRES CSVColumns(PGLOBAL, char *, PTOS, bool); +public: // Constructor TDBCSV(PCSVDEF tdp, PTXF txfp); TDBCSV(PGLOBAL g, PTDBCSV tdbp); @@ -101,7 +102,7 @@ class TDBCSV : public TDBDOS { /* Class CSVCOL: CSV access method column descriptor. */ /* This A.M. is used for Comma Separated V(?) files. */ /***********************************************************************/ -class CSVCOL : public DOSCOL { +class DllExport CSVCOL : public DOSCOL { friend class TDBCSV; friend class TDBFMT; public: @@ -129,7 +130,7 @@ class CSVCOL : public DOSCOL { /* This is the DOS/UNIX Access Method class declaration for files */ /* whose record format is described by a Format keyword. */ /***********************************************************************/ -class TDBFMT : public TDBCSV { +class DllExport TDBFMT : public TDBCSV { friend class CSVCOL; //friend class FMTCOL; public: @@ -173,7 +174,7 @@ class TDBFMT : public TDBCSV { /***********************************************************************/ /* This is the class declaration for the CSV catalog table. */ /***********************************************************************/ -class TDBCCL : public TDBCAT { +class DllExport TDBCCL : public TDBCAT { public: // Constructor TDBCCL(PCSVDEF tdp); @@ -183,11 +184,7 @@ class TDBCCL : public TDBCAT { virtual PQRYRES GetResult(PGLOBAL g); // Members - char *Fn; // The CSV file (path) name - bool Hdr; // true if first line contains headers - int Mxr; // Maximum number of bad records - int Qtd; // Quoting level for quoted fields - char Sep; // Separator for standard CSV files - }; // end of class TDBCCL + PTOS Topt; +}; // end of class TDBCCL /* ------------------------- End of TabFmt.H ------------------------- */ diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp index 5f864f0bd4813..73c6a6d85a417 100644 --- a/storage/connect/tabjson.cpp +++ b/storage/connect/tabjson.cpp @@ -1,5 +1,5 @@ /************* tabjson C++ Program Source Code File (.CPP) *************/ -/* PROGRAM NAME: tabjson Version 1.2 */ +/* PROGRAM NAME: tabjson Version 1.3 */ /* (C) Copyright to the author Olivier BERTRAND 2014 - 2016 */ /* This program are the JSON class DB execution routines. */ /***********************************************************************/ @@ -28,6 +28,9 @@ #if defined(GZ_SUPPORT) #include "filamgz.h" #endif // GZ_SUPPORT +#if defined(ZIP_SUPPORT) +#include "filamzip.h" +#endif // ZIP_SUPPORT #include "tabmul.h" #include "checklvl.h" #include "resource.h" @@ -67,7 +70,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) static XFLD fldtyp[] = {FLD_NAME, FLD_TYPE, FLD_TYPENAME, FLD_PREC, FLD_LENGTH, FLD_SCALE, FLD_NULL, FLD_FORMAT}; static unsigned int length[] = {0, 6, 8, 10, 10, 6, 6, 0}; - char *fn, colname[65], fmt[129]; + char colname[65], fmt[129]; int i, j, lvl, n = 0; int ncol = sizeof(buftyp) / sizeof(int); PVAL valp; @@ -94,16 +97,21 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) /*********************************************************************/ /* Open the input file. */ /*********************************************************************/ - if (!(fn = GetStringTableOption(g, topt, "Filename", NULL))) { - strcpy(g->Message, MSG(MISSING_FNAME)); - return NULL; - } else { - lvl = GetIntegerTableOption(g, topt, "Level", 0); - lvl = (lvl < 0) ? 0 : (lvl > 16) ? 16 : lvl; - } // endif fn + lvl = GetIntegerTableOption(g, topt, "Level", 0); + lvl = (lvl < 0) ? 0 : (lvl > 16) ? 16 : lvl; tdp = new(g) JSONDEF; - tdp->Fn = fn; +#if defined(ZIP_SUPPORT) + tdp->Zipfn = GetStringTableOption(g, topt, "Zipfile", NULL); + tdp->Multiple = GetIntegerTableOption(g, topt, "Multiple", 0); +#endif // ZIP_SUPPORT + tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL); + + if (!tdp->Fn && !tdp->Zipfn && !tdp->Multiple) { + strcpy(g->Message, MSG(MISSING_FNAME)); + return NULL; + } // endif Fn + tdp->Database = SetPath(g, db); tdp->Objname = GetStringTableOption(g, topt, "Object", NULL); tdp->Base = GetIntegerTableOption(g, topt, "Base", 0) ? 1 : 0; @@ -114,7 +122,10 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) tdp->Fn, tdp->Objname, tdp->Pretty, lvl); if (tdp->Pretty == 2) { - tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp)); + if (tdp->Zipfn) + tjsp = new(g) TDBJSON(tdp, new(g) ZIPFAM(tdp)); + else + tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp)); if (tjsp->MakeDocument(g)) return NULL; @@ -127,10 +138,28 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) } // endif lrecl tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF); - tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp)); + + if (tdp->Zipfn) + tjnp = new(g) TDBJSN(tdp, new(g) ZIPFAM(tdp)); + else + tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp)); + tjnp->SetMode(MODE_READ); - if (tjnp->OpenDB(g)) +#if USE_G + // Allocate the parse work memory + PGLOBAL G = (PGLOBAL)PlugSubAlloc(g, NULL, sizeof(GLOBAL)); + memset(G, 0, sizeof(GLOBAL)); + G->Sarea_Size = tdp->Lrecl * 10; + G->Sarea = PlugSubAlloc(g, NULL, G->Sarea_Size); + PlugSubSet(G, G->Sarea, G->Sarea_Size); + G->jump_level = -1; + tjnp->SetG(G); +#else + tjnp->SetG(g); +#endif + + if (tjnp->OpenDB(g)) return NULL; switch (tjnp->ReadDB(g)) { @@ -395,7 +424,14 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) !(tmp == TMP_FORCE && (m == MODE_UPDATE || m == MODE_DELETE)); - if (Compressed) { + if (Zipfn) { +#if defined(ZIP_SUPPORT) + txfp = new(g) ZIPFAM(this); +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif // !ZIP_SUPPORT + } else if (Compressed) { #if defined(GZ_SUPPORT) if (Compressed == 1) txfp = new(g) GZFAM(this); @@ -426,12 +462,16 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) ((TDBJSN*)tdbp)->G = g; #endif } else { - txfp = new(g) MAPFAM(this); + if (Zipfn) + txfp = new(g) ZIPFAM(this); + else + txfp = new(g) MAPFAM(this); + tdbp = new(g) TDBJSON(this, txfp); ((TDBJSON*)tdbp)->G = g; } // endif Pretty - if (Multiple) + if (Multiple && !Zipfn) tdbp = new(g) TDBMUL(tdbp); return tdbp; diff --git a/storage/connect/tabjson.h b/storage/connect/tabjson.h index f7cb74c3c4d2e..c9d30d48f2a5e 100644 --- a/storage/connect/tabjson.h +++ b/storage/connect/tabjson.h @@ -78,7 +78,8 @@ class TDBJSN : public TDBDOS { virtual AMT GetAmType(void) {return TYPE_AM_JSN;} virtual bool SkipHeader(PGLOBAL g); virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBJSN(this);} - PJSON GetRow(void) {return Row;} + PJSON GetRow(void) {return Row;} + void SetG(PGLOBAL g) {G = g;} // Methods virtual PTDB CopyOne(PTABS t); diff --git a/storage/connect/tabzip.cpp b/storage/connect/tabzip.cpp new file mode 100644 index 0000000000000..11f414ee154d7 --- /dev/null +++ b/storage/connect/tabzip.cpp @@ -0,0 +1,230 @@ +/************* TabZip C++ Program Source Code File (.CPP) **************/ +/* PROGRAM NAME: TABZIP Version 1.0 */ +/* (C) Copyright to the author Olivier BERTRAND 2016 */ +/* This program are the TABZIP class DB execution routines. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the MariaDB header file. */ +/***********************************************************************/ +#include + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* (x)table.h is header containing the TDBASE declarations. */ +/* tabzip.h is header containing the TABZIP classes declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "xtable.h" +#include "filamtxt.h" +#include "filamzip.h" +#include "resource.h" // for IDS_COLUMNS +#include "tabdos.h" +#include "tabzip.h" + +/* -------------------------- Class ZIPDEF --------------------------- */ + +/************************************************************************/ +/* DefineAM: define specific AM block values. */ +/************************************************************************/ +bool ZIPDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) +{ +//target = GetStringCatInfo(g, "Target", NULL); + return DOSDEF::DefineAM(g, "ZIP", poff); +} // end of DefineAM + +/***********************************************************************/ +/* GetTable: makes a new Table Description Block. */ +/***********************************************************************/ +PTDB ZIPDEF::GetTable(PGLOBAL g, MODE m) +{ + return new(g) TDBZIP(this); +} // end of GetTable + +/* ------------------------------------------------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBZIP class. */ +/***********************************************************************/ +TDBZIP::TDBZIP(PZIPDEF tdp) : TDBASE(tdp) +{ + zipfile = NULL; + zfn = tdp->Fn; +//target = tdp->target; + nexterr = UNZ_OK; +} // end of TDBZIP standard constructor + +/***********************************************************************/ +/* Allocate ZIP column description block. */ +/***********************************************************************/ +PCOL TDBZIP::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) +{ + return new(g) ZIPCOL(cdp, this, cprec, n); +} // end of MakeCol + +/***********************************************************************/ +/* open a zip file. */ +/* 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) +{ + if (!zipfile && !(zipfile = unzOpen64(filename))) + sprintf(g->Message, "Zipfile open error"); + + return (zipfile == NULL); +} // end of open + +/***********************************************************************/ +/* Close the zip file. */ +/***********************************************************************/ +void TDBZIP::close() +{ + if (zipfile) { + unzClose(zipfile); + zipfile = NULL; + } // endif zipfile + +} // end of close + +/***********************************************************************/ +/* ZIP Cardinality: returns table size in number of rows. */ +/***********************************************************************/ +int TDBZIP::Cardinality(PGLOBAL g) +{ + if (!g) + return 1; + else if (Cardinal < 0) { + if (!open(g, zfn)) { + unz_global_info64 ginfo; + int err = unzGetGlobalInfo64(zipfile, &ginfo); + + Cardinal = (err == UNZ_OK) ? ginfo.number_entry : 0; + } else + Cardinal = 0; + + } // endif Cardinal + + return Cardinal; +} // end of Cardinality + +/***********************************************************************/ +/* ZIP GetMaxSize: returns file size estimate in number of lines. */ +/***********************************************************************/ +int TDBZIP::GetMaxSize(PGLOBAL g) +{ + if (MaxSize < 0) + MaxSize = Cardinality(g); + + return MaxSize; +} // end of GetMaxSize + +/***********************************************************************/ +/* ZIP Access Method opening routine. */ +/***********************************************************************/ +bool TDBZIP::OpenDB(PGLOBAL g) +{ + if (Use == USE_OPEN) + // Table already open + return false; + + Use = USE_OPEN; // To be clean + return open(g, zfn); +} // end of OpenDB + +/***********************************************************************/ +/* ReadDB: Data Base read routine for ZIP access method. */ +/***********************************************************************/ +int TDBZIP::ReadDB(PGLOBAL g) +{ + if (nexterr == UNZ_END_OF_LIST_OF_FILE) + return RC_EF; + else if (nexterr != UNZ_OK) { + sprintf(g->Message, "unzGoToNextFile error %d", nexterr); + return RC_FX; + } // endif nexterr + + int err = unzGetCurrentFileInfo64(zipfile, &finfo, fn, + sizeof(fn), NULL, 0, NULL, 0); + + if (err != UNZ_OK) { + sprintf(g->Message, "unzGetCurrentFileInfo64 error %d", err); + return RC_FX; + } // endif err + + nexterr = unzGoToNextFile(zipfile); + return RC_OK; +} // end of ReadDB + +/***********************************************************************/ +/* WriteDB: Data Base write routine for ZIP access method. */ +/***********************************************************************/ +int TDBZIP::WriteDB(PGLOBAL g) +{ + strcpy(g->Message, "ZIP tables are read only"); + return RC_FX; +} // end of WriteDB + +/***********************************************************************/ +/* Data Base delete line routine for ZIP access method. */ +/***********************************************************************/ +int TDBZIP::DeleteDB(PGLOBAL g, int irc) +{ + strcpy(g->Message, "Delete not enabled for ZIP tables"); + return RC_FX; +} // end of DeleteDB + +/***********************************************************************/ +/* Data Base close routine for ZIP access method. */ +/***********************************************************************/ +void TDBZIP::CloseDB(PGLOBAL g) +{ + close(); + Use = USE_READY; // Just to be clean +} // end of CloseDB + +/* ---------------------------- ZIPCOL ------------------------------- */ + +/***********************************************************************/ +/* ZIPCOL public constructor. */ +/***********************************************************************/ +ZIPCOL::ZIPCOL(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 + + Tdbz = (TDBZIP*)tdbp; + flag = cdp->GetOffset(); +} // end of ZIPCOL constructor + +/***********************************************************************/ +/* ReadColumn: */ +/***********************************************************************/ +void ZIPCOL::ReadColumn(PGLOBAL g) +{ + switch (flag) { + case 1: + Value->SetValue(Tdbz->finfo.compressed_size); + break; + case 2: + Value->SetValue(Tdbz->finfo.uncompressed_size); + break; + case 3: + Value->SetValue((int)Tdbz->finfo.compression_method); + break; + default: + Value->SetValue_psz((PSZ)Tdbz->fn); + } // endswitch flag + +} // end of ReadColumn + +/* -------------------------- End of tabzip -------------------------- */ diff --git a/storage/connect/tabzip.h b/storage/connect/tabzip.h new file mode 100644 index 0000000000000..6f1735258e782 --- /dev/null +++ b/storage/connect/tabzip.h @@ -0,0 +1,100 @@ +/*************** tabzip H Declares Source Code File (.H) ***************/ +/* Name: tabzip.h Version 1.0 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2016 */ +/* */ +/* This file contains the ZIP classe declares. */ +/***********************************************************************/ +#include "osutil.h" +#include "block.h" +#include "colblk.h" +#include "xtable.h" +#include "unzip.h" + +typedef class ZIPDEF *PZIPDEF; +typedef class TDBZIP *PTDBZIP; +typedef class ZIPCOL *PZIPCOL; + +/***********************************************************************/ +/* ZIP table: display info about a ZIP file. */ +/***********************************************************************/ +class DllExport ZIPDEF : public DOSDEF { /* Table description */ + friend class TDBZIP; + friend class ZIPFAM; +public: + // Constructor + ZIPDEF(void) {} + + // Implementation + virtual const char *GetType(void) {return "ZIP";} + + // Methods + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + virtual PTDB GetTable(PGLOBAL g, MODE m); + +protected: + // Members + PSZ target; // The inside file to query +}; // end of ZIPDEF + +/***********************************************************************/ +/* This is the ZIP Access Method class declaration. */ +/***********************************************************************/ +class DllExport TDBZIP : public TDBASE { + friend class ZIPCOL; +public: + // Constructor + TDBZIP(PZIPDEF tdp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_ZIP;} + + // Methods + virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); + virtual int Cardinality(PGLOBAL g); + virtual int GetMaxSize(PGLOBAL g); + virtual int GetRecpos(void) {return 0;} + + // Database routines + 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: + bool open(PGLOBAL g, const char *filename); + void close(void); + + // Members + unzFile zipfile; // The ZIP container file + PSZ zfn; // The ZIP file name +//PSZ target; + unz_file_info64 finfo; // The current file info + char fn[FILENAME_MAX]; // The current file name + int nexterr; // Next file error +}; // end of class TDBZIP + +/***********************************************************************/ +/* Class ZIPCOL: ZIP access method column descriptor. */ +/***********************************************************************/ +class DllExport ZIPCOL : public COLBLK { + friend class TDBZIP; +public: + // Constructors + ZIPCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "ZIP"); + + // Implementation + virtual int GetAmType(void) { return TYPE_AM_ZIP; } + + // Methods + virtual void ReadColumn(PGLOBAL g); + +protected: + // Default constructor not to be used + ZIPCOL(void) {} + + // Members + TDBZIP *Tdbz; + int flag; +}; // end of class ZIPCOL diff --git a/storage/connect/unzip.c b/storage/connect/unzip.c new file mode 100644 index 0000000000000..909350435a51f --- /dev/null +++ b/storage/connect/unzip.c @@ -0,0 +1,2125 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == MAXU32) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == MAXU32) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if ((err==UNZ_OK) && (pfile_info != NULL)) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/storage/connect/unzip.h b/storage/connect/unzip.h new file mode 100644 index 0000000000000..2104e39150749 --- /dev/null +++ b/storage/connect/unzip.h @@ -0,0 +1,437 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzCloseCurrentFile before call unzClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/storage/connect/zip.c b/storage/connect/zip.c new file mode 100644 index 0000000000000..ea54853e858a7 --- /dev/null +++ b/storage/connect/zip.c @@ -0,0 +1,2007 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const z_crc_t* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writting_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + (crcForCrypting); + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if (level==2) + zi->ci.flag |= 4; + if (level==1) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff ) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + else + err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/storage/connect/zip.h b/storage/connect/zip.h new file mode 100644 index 0000000000000..8aaebb623430f --- /dev/null +++ b/storage/connect/zip.h @@ -0,0 +1,362 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ From 952306502ebf1b26c627c5dc8b141581eeb30671 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Wed, 14 Dec 2016 14:20:23 +0100 Subject: [PATCH 34/73] - MDEV-11295: developing handling files contained in ZIP file. Enable using multiple zip files modified: storage/connect/filamzip.cpp modified: storage/connect/ha_connect.cc modified: storage/connect/ha_connect.h modified: storage/connect/mycat.h modified: storage/connect/tabdos.cpp modified: storage/connect/tabdos.h modified: storage/connect/tabfmt.cpp modified: storage/connect/tabjson.cpp --- storage/connect/filamzip.cpp | 19 +++++++++-------- storage/connect/filamzip.h | 2 +- storage/connect/ha_connect.cc | 14 ++++++------- storage/connect/ha_connect.h | 39 +++-------------------------------- storage/connect/mycat.h | 1 + storage/connect/tabdos.cpp | 23 +++++++-------------- storage/connect/tabdos.h | 7 ++++--- storage/connect/tabfmt.cpp | 26 ++++++++++------------- storage/connect/tabjson.cpp | 23 +++++++++++++-------- storage/connect/tabxml.cpp | 7 ++++++- 10 files changed, 64 insertions(+), 97 deletions(-) diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp index ea8b827974b37..8386e5be48120 100644 --- a/storage/connect/filamzip.cpp +++ b/storage/connect/filamzip.cpp @@ -48,11 +48,11 @@ ZIPFAM::ZIPFAM(PDOSDEF tdp) : MAPFAM(tdp) { zipfile = NULL; - zfn = tdp->Zipfn; - target = tdp->Fn; +//zfn = tdp->Fn; + target = tdp->Entry; //*fn = 0; entryopen = false; - multiple = tdp->Multiple; + multiple = (target && !(strchr(target, '*') || strchr(target, '?'))) ? 0 : 1; // Init the case mapping table. #if defined(__WIN__) @@ -65,7 +65,7 @@ ZIPFAM::ZIPFAM(PDOSDEF tdp) : MAPFAM(tdp) ZIPFAM::ZIPFAM(PZIPFAM txfp) : MAPFAM(txfp) { zipfile = txfp->zipfile; - zfn = txfp->zfn; +//zfn = txfp->zfn; target = txfp->target; //strcpy(fn, txfp->fn); finfo = txfp->finfo; @@ -129,7 +129,7 @@ int ZIPFAM::GetFileLength(PGLOBAL g) bool ZIPFAM::open(PGLOBAL g, const char *filename) { if (!zipfile && !(zipfile = unzOpen64(filename))) - sprintf(g->Message, "Zipfile open error"); + sprintf(g->Message, "Zipfile open error on %s", filename); return (zipfile == NULL); } // end of open @@ -205,7 +205,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) /*********************************************************************/ if (mode == MODE_READ) { // We used the file name relative to recorded datapath - PlugSetPath(filename, zfn, Tdbp->GetPath()); + PlugSetPath(filename, To_File, Tdbp->GetPath()); bool b = open(g, filename); @@ -218,7 +218,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) if (rc == UNZ_END_OF_LIST_OF_FILE) { sprintf(g->Message, "Target file %s not in %s", target, filename); - return true; + return false; } else if (rc != UNZ_OK) { sprintf(g->Message, "unzLocateFile rc=%d", rc); return true; @@ -229,7 +229,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) return true; else if (rc == RC_NF) { sprintf(g->Message, "No match of %s in %s", target, filename); - return true; + return false; } // endif rc } // endif multiple @@ -258,7 +258,8 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) } // endif fp To_Fb = fp; // Useful when closing - } // endif b + } else + return true; } else { strcpy(g->Message, "Only READ mode supported for ZIP files"); diff --git a/storage/connect/filamzip.h b/storage/connect/filamzip.h index 85c1f907d20f7..c3c04b2b3bb16 100644 --- a/storage/connect/filamzip.h +++ b/storage/connect/filamzip.h @@ -51,7 +51,7 @@ class DllExport ZIPFAM : public MAPFAM { // Members unzFile zipfile; // The ZIP container file - PSZ zfn; // The ZIP file name +//PSZ zfn; // The ZIP file name PSZ target; // The target file name unz_file_info finfo; // The current file info //char fn[FILENAME_MAX]; // The current file name diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index b690dff24f42c..45ca546ad4e84 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -171,9 +171,9 @@ #define JSONMAX 10 // JSON Default max grp size extern "C" { - char version[]= "Version 1.04.0009 December 09, 2016"; + char version[]= "Version 1.05.0001 December 13, 2016"; #if defined(__WIN__) - char compver[]= "Version 1.04.0009 " __DATE__ " " __TIME__; + char compver[]= "Version 1.05.0001 " __DATE__ " " __TIME__; char slash= '\\'; #else // !__WIN__ char slash= '/'; @@ -512,13 +512,13 @@ ha_create_table_option connect_table_option_list[]= HA_TOPTION_NUMBER("QUOTED", quoted, (ulonglong) -1, 0, 3, 1), HA_TOPTION_NUMBER("ENDING", ending, (ulonglong) -1, 0, INT_MAX32, 1), HA_TOPTION_NUMBER("COMPRESS", compressed, 0, 0, 2, 1), -//HA_TOPTION_BOOL("COMPRESS", compressed, 0), HA_TOPTION_BOOL("MAPPED", mapped, 0), HA_TOPTION_BOOL("HUGE", huge, 0), HA_TOPTION_BOOL("SPLIT", split, 0), HA_TOPTION_BOOL("READONLY", readonly, 0), HA_TOPTION_BOOL("SEPINDEX", sepindex, 0), - HA_TOPTION_END + HA_TOPTION_BOOL("ZIPPED", zipped, 0), + HA_TOPTION_END }; @@ -532,7 +532,6 @@ ha_create_table_option connect_field_option_list[]= { HA_FOPTION_NUMBER("FLAG", offset, (ulonglong) -1, 0, INT_MAX32, 1), HA_FOPTION_NUMBER("MAX_DIST", freq, 0, 0, INT_MAX32, 1), // BLK_INDX -//HA_FOPTION_NUMBER("DISTRIB", opt, 0, 0, 2, 1), // used for BLK_INDX HA_FOPTION_NUMBER("FIELD_LENGTH", fldlen, 0, 0, INT_MAX32, 1), HA_FOPTION_STRING("DATE_FORMAT", dateformat), HA_FOPTION_STRING("FIELD_FORMAT", fieldformat), @@ -678,7 +677,6 @@ static int connect_init_func(void *p) connect_hton= (handlerton *)p; connect_hton->state= SHOW_OPTION_YES; connect_hton->create= connect_create_handler; -//connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED | HTON_NO_PARTITION; connect_hton->flags= HTON_TEMPORARY_NOT_SUPPORTED; connect_hton->table_options= connect_table_option_list; connect_hton->field_options= connect_field_option_list; @@ -1135,7 +1133,9 @@ bool GetBooleanTableOption(PGLOBAL g, PTOS options, char *opname, bool bdef) opval= options->sepindex; else if (!stricmp(opname, "Header")) opval= (options->header != 0); // Is Boolean for some table types - else if (options->oplist) + else if (!stricmp(opname, "Zipped")) + opval = options->zipped; + else if (options->oplist) if ((pv= GetListOption(g, opname, options->oplist))) opval= (!*pv || *pv == 'y' || *pv == 'Y' || atoi(pv) != 0); diff --git a/storage/connect/ha_connect.h b/storage/connect/ha_connect.h index 60194ac0e3ca8..3d9ff96761811 100644 --- a/storage/connect/ha_connect.h +++ b/storage/connect/ha_connect.h @@ -83,42 +83,9 @@ extern handlerton *connect_hton; These can be specified in the CREATE TABLE: CREATE TABLE ( ... ) {...here...} -*/ -#if 0 // moved to mycat.h -typedef struct ha_table_option_struct TOS, *PTOS; - -struct ha_table_option_struct { - const char *type; - const char *filename; - const char *optname; - const char *tabname; - const char *tablist; - const char *dbname; - const char *separator; -//const char *connect; - const char *qchar; - const char *module; - const char *subtype; - const char *catfunc; - const char *srcdef; - const char *colist; - const char *oplist; - const char *data_charset; - ulonglong lrecl; - ulonglong elements; -//ulonglong estimate; - ulonglong multiple; - ulonglong header; - ulonglong quoted; - ulonglong ending; - ulonglong compressed; - bool mapped; - bool huge; - bool split; - bool readonly; - bool sepindex; - }; -#endif // 0 + + ------ Was moved to mycat.h ------ + */ /** structure for CREATE TABLE options (field options) diff --git a/storage/connect/mycat.h b/storage/connect/mycat.h index 05163f08f1ba3..663b68fd4b9b5 100644 --- a/storage/connect/mycat.h +++ b/storage/connect/mycat.h @@ -62,6 +62,7 @@ struct ha_table_option_struct { bool split; bool readonly; bool sepindex; + bool zipped; }; // Possible value for catalog functions diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp index 9bcac0b5f1a52..f47e66b014b79 100644 --- a/storage/connect/tabdos.cpp +++ b/storage/connect/tabdos.cpp @@ -96,11 +96,12 @@ DOSDEF::DOSDEF(void) Pseudo = 3; Fn = NULL; Ofn = NULL; - Zipfn = NULL; + Entry = NULL; To_Indx = NULL; Recfm = RECFM_VAR; Mapped = false; - Padded = false; + Zipped = false; + Padded = false; Huge = false; Accept = false; Eof = false; @@ -131,20 +132,11 @@ bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int) : (am && !stricmp(am, "DBF")) ? "D" : "V"; if (*dfm != 'D') - Zipfn = GetStringCatInfo(g, "Zipfile", NULL); - - if (Zipfn && Multiple) { - // Prevent Fn to default to table name - Desc = GetStringCatInfo(g, "Filename", NULL); - Fn = GetStringCatInfo(g, "Filename", "<%>"); - - if (!strcmp(Fn, "<%>")) - Fn = NULL; - - } else - Desc = Fn = GetStringCatInfo(g, "Filename", NULL); + Zipped = GetBoolCatInfo("Zipped", false); + Desc = Fn = GetStringCatInfo(g, "Filename", NULL); Ofn = GetStringCatInfo(g, "Optname", Fn); + Entry = GetStringCatInfo(g, "Entry", NULL); GetCharCatInfo("Recfm", (PSZ)dfm, buf, sizeof(buf)); Recfm = (toupper(*buf) == 'F') ? RECFM_FIX : (toupper(*buf) == 'B') ? RECFM_BIN : @@ -350,7 +342,7 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) /* Allocate table and file processing class of the proper type. */ /* Column blocks will be allocated only when needed. */ /*********************************************************************/ - if (Zipfn) { + if (Zipped) { #if defined(ZIP_SUPPORT) if (Recfm == RECFM_VAR) txfp = new(g) ZIPFAM(this); @@ -358,7 +350,6 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) txfp = new(g) ZPXFAM(this); tdbp = new(g) TDBDOS(this, txfp); - return tdbp; #else // !ZIP_SUPPORT strcpy(g->Message, "ZIP not supported"); return NULL; diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h index 501ddbc2e0bc8..623adcfed0d6d 100644 --- a/storage/connect/tabdos.h +++ b/storage/connect/tabdos.h @@ -59,7 +59,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ // Methods virtual int Indexable(void) - {return (!Multiple && !Zipfn && Compressed != 1) ? 1 : 0;} + {return (!Multiple && !Zipped && Compressed != 1) ? 1 : 0;} virtual bool DeleteIndexFile(PGLOBAL g, PIXDEF pxdf); virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); virtual PTDB GetTable(PGLOBAL g, MODE mode); @@ -73,11 +73,12 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ // Members PSZ Fn; /* Path/Name of corresponding file */ PSZ Ofn; /* Base Path/Name of matching index files*/ - PSZ Zipfn; /* Zip container name */ + PSZ Entry; /* Zip entry name or pattern */ PIXDEF To_Indx; /* To index definitions blocks */ RECFM Recfm; /* 0:VAR, 1:FIX, 2:BIN, 3:VCT, 6:DBF */ bool Mapped; /* 0: disk file, 1: memory mapped file */ - bool Padded; /* true for padded table file */ + bool Zipped; /* true for zipped table file */ + bool Padded; /* true for padded table file */ bool Huge; /* true for files larger than 2GB */ bool Accept; /* true if wrong lines are accepted */ bool Eof; /* true if an EOF (0xA) character exists */ diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index d6649a0093bf4..2c4d605e66c79 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -108,6 +108,11 @@ 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 + // num_max = atoi(p+1); // Max num of record to test imax = hmax = nerr = 0; @@ -123,18 +128,16 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) /*********************************************************************/ tdp = new(g) CSVDEF; #if defined(ZIP_SUPPORT) - tdp->Zipfn = GetStringTableOption(g, topt, "Zipfile", NULL); - tdp->Multiple = GetIntegerTableOption(g, topt, "Multiple", 0); + tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL); + tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false); #endif // ZIP_SUPPORT - tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL); + fn = tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL); - if (!tdp->Fn && !tdp->Zipfn && !tdp->Multiple) { + if (!tdp->Fn) { strcpy(g->Message, MSG(MISSING_FNAME)); return NULL; } // endif Fn - fn = (tdp->Fn) ? tdp->Fn : "unnamed"; - if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0))) tdp->Lrecl = 4096; @@ -174,7 +177,7 @@ 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->Zipfn) + if (tdp->Zipped) tdbp = new(g) TDBCSV(tdp, new(g) ZIPFAM(tdp)); else tdbp = new(g) TDBCSV(tdp, new(g) DOSFAM(tdp)); @@ -493,16 +496,9 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode) /*******************************************************************/ /* Allocate a file processing class of the proper type. */ /*******************************************************************/ - if (Zipfn) { + if (Zipped) { #if defined(ZIP_SUPPORT) txfp = new(g) ZIPFAM(this); - - if (!Fmtd) - tdbp = new(g) TDBCSV(this, txfp); - else - tdbp = new(g) TDBFMT(this, txfp); - - return tdbp; #else // !ZIP_SUPPORT strcpy(g->Message, "ZIP not supported"); return NULL; diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp index 73c6a6d85a417..eff95445a3a22 100644 --- a/storage/connect/tabjson.cpp +++ b/storage/connect/tabjson.cpp @@ -94,7 +94,12 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, 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 + + /*********************************************************************/ /* Open the input file. */ /*********************************************************************/ lvl = GetIntegerTableOption(g, topt, "Level", 0); @@ -102,12 +107,12 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) tdp = new(g) JSONDEF; #if defined(ZIP_SUPPORT) - tdp->Zipfn = GetStringTableOption(g, topt, "Zipfile", NULL); - tdp->Multiple = GetIntegerTableOption(g, topt, "Multiple", 0); + tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL); + tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false); #endif // ZIP_SUPPORT tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL); - if (!tdp->Fn && !tdp->Zipfn && !tdp->Multiple) { + if (!tdp->Fn) { strcpy(g->Message, MSG(MISSING_FNAME)); return NULL; } // endif Fn @@ -122,7 +127,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) tdp->Fn, tdp->Objname, tdp->Pretty, lvl); if (tdp->Pretty == 2) { - if (tdp->Zipfn) + if (tdp->Zipped) tjsp = new(g) TDBJSON(tdp, new(g) ZIPFAM(tdp)); else tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp)); @@ -139,7 +144,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF); - if (tdp->Zipfn) + if (tdp->Zipped) tjnp = new(g) TDBJSN(tdp, new(g) ZIPFAM(tdp)); else tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp)); @@ -424,7 +429,7 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) !(tmp == TMP_FORCE && (m == MODE_UPDATE || m == MODE_DELETE)); - if (Zipfn) { + if (Zipped) { #if defined(ZIP_SUPPORT) txfp = new(g) ZIPFAM(this); #else // !ZIP_SUPPORT @@ -462,7 +467,7 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) ((TDBJSN*)tdbp)->G = g; #endif } else { - if (Zipfn) + if (Zipped) txfp = new(g) ZIPFAM(this); else txfp = new(g) MAPFAM(this); @@ -471,7 +476,7 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) ((TDBJSON*)tdbp)->G = g; } // endif Pretty - if (Multiple && !Zipfn) + if (Multiple) tdbp = new(g) TDBMUL(tdbp); return tdbp; diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp index 57d204a428617..1993b07eb7a0a 100644 --- a/storage/connect/tabxml.cpp +++ b/storage/connect/tabxml.cpp @@ -136,7 +136,12 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, 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 + + /*********************************************************************/ /* Open the input file. */ /*********************************************************************/ if (!(fn = GetStringTableOption(g, topt, "Filename", NULL))) { From 211cf9321a91b12eaef3d7968adc699085dbb54e Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Fri, 16 Dec 2016 18:37:11 +0400 Subject: [PATCH 35/73] MDEV-11510 Audit plugin sometimes causes server to crash when using with MySQL. MySQL has a bug failing to handle MYSQL_THDVAR_STR(... PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_MEMALLOC) so fall back to just PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC whem MySQL started. --- plugin/server_audit/server_audit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 95150c82f25ef..87ba00b2d3536 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -2845,6 +2845,7 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void) _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; } memset(locinfo_ini_value, 'O', sizeof(locinfo_ini_value)-1); From e86580c3dda707788fb0ca35244cf602d7e8d50d Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 15 Dec 2016 18:20:58 +0100 Subject: [PATCH 36/73] MDEV-11552 Queries executed by event scheduler are written to slow log incorrectly or not written at all because thd->update_server_status() is used to measure the query time for the slow log (not only to set protocol level flags), it needs to be called also when the server isn't going to send anything to the client. --- mysql-test/r/events_slowlog.result | 12 ++++++++++++ mysql-test/t/events_slowlog.test | 28 ++++++++++++++++++++++++++++ sql/sp_head.cc | 10 +++++----- 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 mysql-test/r/events_slowlog.result create mode 100644 mysql-test/t/events_slowlog.test diff --git a/mysql-test/r/events_slowlog.result b/mysql-test/r/events_slowlog.result new file mode 100644 index 0000000000000..c97fe8a2ab92d --- /dev/null +++ b/mysql-test/r/events_slowlog.result @@ -0,0 +1,12 @@ +set @event_scheduler_save= @@global.event_scheduler; +set @slow_query_log_save= @@global.slow_query_log; +set global event_scheduler= on; +set global slow_query_log= on; +set global long_query_time=0.2; +create table t1 (i int); +insert into t1 values (0); +create event ev on schedule at CURRENT_TIMESTAMP + INTERVAL 1 second do update t1 set i=1+sleep(0.5); +drop table t1; +set global event_scheduler= @event_scheduler_save; +set global slow_query_log= @slow_query_log_save; +set global long_query_time= @@session.long_query_time; diff --git a/mysql-test/t/events_slowlog.test b/mysql-test/t/events_slowlog.test new file mode 100644 index 0000000000000..9679714dba353 --- /dev/null +++ b/mysql-test/t/events_slowlog.test @@ -0,0 +1,28 @@ +--source include/not_embedded.inc +# +# MDEV-11552 Queries executed by event scheduler are written to slow log incorrectly or not written at all +# +set @event_scheduler_save= @@global.event_scheduler; +set @slow_query_log_save= @@global.slow_query_log; + +set global event_scheduler= on; +set global slow_query_log= on; +set global long_query_time=0.2; + +create table t1 (i int); +insert into t1 values (0); +create event ev on schedule at CURRENT_TIMESTAMP + INTERVAL 1 second do update t1 set i=1+sleep(0.5); + +--let wait_condition= select i from t1 where i > 0 +--source include/wait_condition.inc + +--let SEARCH_FILE = `SELECT @@slow_query_log_file` +--let SEARCH_PATTERN= update t1 set i=1 +--let SEARCH_RANGE= -1000 +--source include/search_pattern_in_file.inc + +drop table t1; + +set global event_scheduler= @event_scheduler_save; +set global slow_query_log= @slow_query_log_save; +set global long_query_time= @@session.long_query_time; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 019e9d9a47818..9bfa60a07d38a 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3146,18 +3146,18 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) thd->query_length()) <= 0) { res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this); + bool log_slow= !res && thd->enable_slow_log; - if (thd->stmt_da->is_eof()) - { - /* Finalize server status flags after executing a statement. */ + /* Finalize server status flags after executing a statement. */ + if (log_slow || thd->stmt_da->is_eof()) thd->update_server_status(); + if (thd->stmt_da->is_eof()) thd->protocol->end_statement(); - } query_cache_end_of_result(thd); - if (!res && unlikely(thd->enable_slow_log)) + if (log_slow) log_slow_statement(thd); } else From b03b38dd6515e60689adb6c9ca57d9612618e2bf Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 16 Dec 2016 10:10:08 +0100 Subject: [PATCH 37/73] cleanup: rpl.rpl_row_mysqlbinlog some trivial simplifications. drinking the ocean, one drop at a time --- .../suite/rpl/r/rpl_row_mysqlbinlog.result | 37 ++---- .../suite/rpl/t/rpl_row_mysqlbinlog.test | 120 ++++-------------- 2 files changed, 38 insertions(+), 119 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_row_mysqlbinlog.result b/mysql-test/suite/rpl/r/rpl_row_mysqlbinlog.result index 1489af830cc2e..19611ac52b596 100644 --- a/mysql-test/suite/rpl/r/rpl_row_mysqlbinlog.result +++ b/mysql-test/suite/rpl/r/rpl_row_mysqlbinlog.result @@ -1,14 +1,21 @@ include/master-slave.inc [connection master] - ---Setup Section -- set timestamp=1000000000; -DROP TABLE IF EXISTS t1,t2,t3; CREATE TABLE t1(word VARCHAR(20)); CREATE TABLE t2(id INT AUTO_INCREMENT NOT NULL PRIMARY KEY); CREATE TABLE t3(c1 INT NOT NULL PRIMARY KEY, c2 LONGBLOB, c3 TIMESTAMP, c4 TEXT, c5 FLOAT); - ----Test1 check table load -- +INSERT INTO t1 VALUES ("abirvalg"); +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1; +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1; +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1; +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1; +LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1; +set @d1 = 'dd1'; +set @d1 = concat(@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1); +set @d1 = concat(@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1); +set @d1 = concat(@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1); +---Test 1 check table load -- SELECT COUNT(*) from t1; COUNT(*) 351 @@ -71,9 +78,7 @@ c1 c3 c4 c5 5 2006-02-22 00:00:00 Tested in Texas 11 insert into t1 values ("Alas"); flush logs; - --- Test 1 Dump binlog to file -- - --- Test 1 delete tables, clean master and slave -- DROP TABLE t1; DROP TABLE t2; @@ -84,9 +89,7 @@ reset master; reset slave; start slave; include/wait_for_slave_to_start.inc - --- Test 1 Load from Dump binlog file -- - --- Test 1 Check Load Results -- SELECT COUNT(*) from t1; COUNT(*) @@ -148,7 +151,6 @@ c1 c3 c4 c5 3 2006-02-22 00:00:00 Tested in Texas 6.6 4 2006-02-22 00:00:00 Tested in Texas 8.8 5 2006-02-22 00:00:00 Tested in Texas 11 - --- Test 2 position test -- /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!40019 SET @@session.max_insert_delayed_threads=0*/; @@ -172,7 +174,6 @@ DELIMITER ; ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; - --- Test 3 First Remote test -- /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!40019 SET @@session.max_insert_delayed_threads=0*/; @@ -189,9 +190,6 @@ SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/ SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; -DROP TABLE IF EXISTS `t1`,`t2`,`t3` /* generated by server */ -/*!*/; -SET TIMESTAMP=1000000000/*!*/; CREATE TABLE t1(word VARCHAR(20)) /*!*/; SET TIMESTAMP=1000000000/*!*/; @@ -205,7 +203,6 @@ DELIMITER ; ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; - --- Test 4 Second Remote test -- DROP TABLE t1; DROP TABLE t2; @@ -276,7 +273,6 @@ c1 c3 c4 c5 3 2006-02-22 00:00:00 Tested in Texas 6.6 4 2006-02-22 00:00:00 Tested in Texas 8.8 5 2006-02-22 00:00:00 Tested in Texas 11 - --- Test 5 LOAD DATA -- /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!40019 SET @@session.max_insert_delayed_threads=0*/; @@ -287,7 +283,6 @@ DELIMITER ; ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; - --- Test 6 reading stdin -- /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!40019 SET @@session.max_insert_delayed_threads=0*/; @@ -304,9 +299,6 @@ SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/ SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; -DROP TABLE IF EXISTS `t1`,`t2`,`t3` /* generated by server */ -/*!*/; -SET TIMESTAMP=1000000000/*!*/; CREATE TABLE t1(word VARCHAR(20)) /*!*/; SET TIMESTAMP=1000000000/*!*/; @@ -320,7 +312,6 @@ DELIMITER ; ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; - --- Test 7 reading stdin w/position -- /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!40019 SET @@session.max_insert_delayed_threads=0*/; @@ -344,7 +335,6 @@ DELIMITER ; ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; - --- Test 8 switch internal charset -- stop slave; include/wait_for_slave_to_stop.inc @@ -380,14 +370,13 @@ HEX(f) select HEX(f) from t5; HEX(f) 835C - --- Test cleanup -- -DROP TABLE IF EXISTS t1; +DROP TABLE t1, t2, t3, t04, t05, t4, t5; CREATE TABLE t1 (a INT NOT NULL KEY, b INT); INSERT INTO t1 VALUES(1,1); SELECT * FROM t1; a b 1 1 FLUSH LOGS; -DROP TABLE IF EXISTS t1, t2, t3, t04, t05, t4, t5; +DROP TABLE t1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_row_mysqlbinlog.test b/mysql-test/suite/rpl/t/rpl_row_mysqlbinlog.test index ed0f31b75be1f..678679f0cf132 100644 --- a/mysql-test/suite/rpl/t/rpl_row_mysqlbinlog.test +++ b/mysql-test/suite/rpl/t/rpl_row_mysqlbinlog.test @@ -4,43 +4,27 @@ # Purpose: To test changes to mysqlbinlog for row based bin logs # # We are using .opt file since we need small binlog size # ################################################################## -# Include Section -# Make sure that we have row based bin log -- source include/have_binlog_format_row.inc -# Embedded server doesn't support binlogging -- source include/not_embedded.inc -# This test requires the cp932 charset compiled in -- source include/have_cp932.inc -# Slow test, don't run during staging part --- source include/not_staging.inc - -- source include/master-slave.inc -# Setup Section -# we need this for getting fixed timestamps inside of this test - ---disable_query_log -select "---Setup Section --" as ""; ---enable_query_log +--echo ---Setup Section -- +# we need this for getting fixed timestamps inside of this test set timestamp=1000000000; ---disable_warnings -DROP TABLE IF EXISTS t1,t2,t3; ---enable_warnings - -connection master; CREATE TABLE t1(word VARCHAR(20)); CREATE TABLE t2(id INT AUTO_INCREMENT NOT NULL PRIMARY KEY); ---let $position= query_get_value(SHOW MASTER STATUS, Position, 1) +--let position= query_get_value(SHOW MASTER STATUS, Position, 1) CREATE TABLE t3(c1 INT NOT NULL PRIMARY KEY, c2 LONGBLOB, c3 TIMESTAMP, c4 TEXT, c5 FLOAT); ---let $stop_position=query_get_value(SHOW MASTER STATUS, Position, 1) ---let $stop_position1=`select $stop_position - 1` ---let $binlog_start_pos=query_get_value(SHOW BINLOG EVENTS LIMIT 1, End_log_pos, 1) +--let stop_position=query_get_value(SHOW MASTER STATUS, Position, 1) +--let stop_position1=`select $stop_position - 1` +--let binlog_start_pos=query_get_value(SHOW BINLOG EVENTS LIMIT 1, End_log_pos, 1) + # Test Section # Lets start by putting some data into the tables. ---disable_query_log INSERT INTO t1 VALUES ("abirvalg"); LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1; LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1; @@ -54,7 +38,8 @@ set @d1 = concat(@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1); set @d1 = concat(@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1); set @d1 = concat(@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1,@d1); -let $count=500; +--disable_query_log +let count=500; while ($count) { INSERT INTO t2 VALUES (NULL); @@ -63,10 +48,7 @@ while ($count) } --enable_query_log - ---disable_query_log -select "---Test1 check table load --" as ""; ---enable_query_log +--echo ---Test 1 check table load -- # Lets Check the tables on the Master SELECT COUNT(*) from t1; @@ -95,34 +77,26 @@ insert into t1 values ("Alas"); flush logs; # delimiters are for easier debugging in future ---disable_query_log -select "--- Test 1 Dump binlog to file --" as ""; ---enable_query_log +--echo --- Test 1 Dump binlog to file -- # # Prepare local temporary file to recreate what we have currently. -let $MYSQLD_DATADIR= `select @@datadir;`; +let MYSQLD_DATADIR= `select @@datadir;`; --exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/master.sql --exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000002 >> $MYSQLTEST_VARDIR/tmp/master.sql # Now that we have our file, lets get rid of the current database. # Cleanup the master and the slave and try to recreate. ---disable_query_log -select "--- Test 1 delete tables, clean master and slave --" as ""; ---enable_query_log +--echo --- Test 1 delete tables, clean master and slave -- DROP TABLE t1; DROP TABLE t2; DROP TABLE t3; sync_slave_with_master; -#we expect STOP SLAVE to produce a warning as the slave is stopped -#(the server was started with skip-slave-start) ---disable_warnings stop slave; --source include/wait_for_slave_to_stop.inc ---enable_warnings connection master; reset master; connection slave; @@ -132,15 +106,11 @@ start slave; connection master; # We should be clean at this point, now we will run in the file from above. ---disable_query_log -select "--- Test 1 Load from Dump binlog file --" as ""; ---enable_query_log +--echo --- Test 1 Load from Dump binlog file -- --exec $MYSQL -e "source $MYSQLTEST_VARDIR/tmp/master.sql" ---disable_query_log -select "--- Test 1 Check Load Results --" as ""; ---enable_query_log +--echo --- Test 1 Check Load Results -- # Lets Check the tables on the Master SELECT COUNT(*) from t1; @@ -168,28 +138,20 @@ remove_file $MYSQLTEST_VARDIR/tmp/master.sql; # this test for start-position option # By setting this position to 416, we should only get the create of t3 ---disable_query_log -select "--- Test 2 position test --" as ""; ---enable_query_log -let $MYSQLD_DATADIR= `select @@datadir;`; +--echo --- Test 2 position test -- --exec $MYSQL_BINLOG --short-form --local-load=$MYSQLTEST_VARDIR/tmp/ --start-position=$position --stop-position=$stop_position $MYSQLD_DATADIR/master-bin.000001 # These are tests for remote binlog. # They should return the same as previous test. ---disable_query_log -select "--- Test 3 First Remote test --" as ""; ---enable_query_log +--echo --- Test 3 First Remote test -- # This is broken now --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --exec $MYSQL_BINLOG --short-form --local-load=$MYSQLTEST_VARDIR/tmp/ --stop-position=$stop_position --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 ---disable_query_log -select "--- Test 4 Second Remote test --" as ""; ---enable_query_log +--echo --- Test 4 Second Remote test -- --exec $MYSQL_BINLOG --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 > $MYSQLTEST_VARDIR/tmp/remote.sql - --exec $MYSQL_BINLOG --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000002 >> $MYSQLTEST_VARDIR/tmp/remote.sql # Now that we have our file, lets get rid of the current database. @@ -201,13 +163,8 @@ DROP TABLE t3; sync_slave_with_master; -#we expect STOP SLAVE to produce a warning as the slave is stopped -#(the server was started with skip-slave-start) - ---disable_warnings stop slave; --source include/wait_for_slave_to_stop.inc ---enable_warnings connection master; reset master; connection slave; @@ -251,40 +208,26 @@ connection master; # transactions. /Matz # LOAD DATA ---disable_query_log -select "--- Test 5 LOAD DATA --" as ""; ---enable_query_log +--echo --- Test 5 LOAD DATA -- --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR --exec $MYSQL_BINLOG --short-form --local-load=$MYSQLTEST_VARDIR/tmp/ --stop-position=$binlog_start_pos --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000002 # Bug#7853 (mysqlbinlog does not accept input from stdin) ---disable_query_log -select "--- Test 6 reading stdin --" as ""; ---enable_query_log -let $MYSQLD_DATADIR= `select @@datadir;`; +--echo --- Test 6 reading stdin -- --replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR --exec $MYSQL_BINLOG --short-form --stop-position=$stop_position1 - < $MYSQLD_DATADIR/master-bin.000001 ---disable_query_log -select "--- Test 7 reading stdin w/position --" as ""; ---enable_query_log +--echo --- Test 7 reading stdin w/position -- --replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR --exec $MYSQL_BINLOG --short-form --start-position=$position --stop-position=$stop_position - < $MYSQLD_DATADIR/master-bin.000001 # Bug#16217 (mysql client did not know how not switch its internal charset) ---disable_query_log -select "--- Test 8 switch internal charset --" as ""; ---enable_query_log +--echo --- Test 8 switch internal charset -- sync_slave_with_master; -#we expect STOP SLAVE to produce a warning as the slave is stopped -#(the server was started with skip-slave-start) - ---disable_warnings stop slave; --source include/wait_for_slave_to_stop.inc ---enable_warnings connection master; reset master; connection slave; @@ -297,7 +240,6 @@ create table t4 (f text character set utf8); create table t5 (f text character set cp932); --exec $MYSQL --default-character-set=utf8 test -e "insert into t4 values(_utf8'ソ')" --exec $MYSQL --default-character-set=cp932 test -e "insert into t5 values(_cp932'ƒ\');" -let $MYSQLD_DATADIR= `select @@datadir;`; flush logs; rename table t4 to t04, t5 to t05; --exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 | $MYSQL --default-character-set=utf8 @@ -314,42 +256,30 @@ select HEX(f) from t4; select HEX(f) from t05; select HEX(f) from t5; ---disable_query_log -select "--- Test cleanup --" as ""; ---enable_query_log +--echo --- Test cleanup -- # clean up connection master; sync_slave_with_master; connection master; +DROP TABLE t1, t2, t3, t04, t05, t4, t5; # BUG#17654 also test mysqlbinlog to ensure it can read the binlog from a remote server # and ensure that the results are the same as if read from a file (the same file). ---disable_warnings -DROP TABLE IF EXISTS t1; ---enable_warnings - CREATE TABLE t1 (a INT NOT NULL KEY, b INT); - INSERT INTO t1 VALUES(1,1); - SELECT * FROM t1; - -let $MYSQLD_DATADIR= `select @@datadir;`; - FLUSH LOGS; --exec $MYSQL_BINLOG --read-from-remote-server --user=root --host=127.0.0.1 --port=$MASTER_MYPORT master-bin.000001 > $MYSQLTEST_VARDIR/tmp/remote.sql --exec $MYSQL_BINLOG $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/local.sql --diff_files $MYSQLTEST_VARDIR/tmp/local.sql $MYSQLTEST_VARDIR/tmp/remote.sql - --remove_file $MYSQLTEST_VARDIR/tmp/remote.sql - --remove_file $MYSQLTEST_VARDIR/tmp/local.sql +DROP TABLE t1; -DROP TABLE IF EXISTS t1, t2, t3, t04, t05, t4, t5; sync_slave_with_master; # End of 4.1 tests From b2b210b891697f999a9f85037462d54f78707e3e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 16 Dec 2016 17:42:21 +0100 Subject: [PATCH 38/73] MDEV-11543 Buildbot tests fail with warnings on server shutdown after rpl.rpl_row_mysqlbinlog double the timeout for threads to die on shutdown --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8eb92cafc03e3..ea4fa823d29b8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1462,7 +1462,7 @@ static void close_connections(void) end_slave(); /* Give threads time to die. */ - for (int i= 0; thread_count && i < 100; i++) + for (int i= 0; thread_count && i < 200; i++) my_sleep(20000); /* From c4d9dc705b781bb155aab8f04cece2b87116d3c1 Mon Sep 17 00:00:00 2001 From: iangilfillan Date: Fri, 16 Dec 2016 14:44:08 +0200 Subject: [PATCH 39/73] Typo, update limit in comment --- sql/item_subselect.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 6427b0ecae407..dda9986f60f8e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -4352,9 +4352,9 @@ bool subselect_hash_sj_engine::init(List *tmp_columns, uint subquery_id) result= result_sink; /* - If the subquery has blobs, or the total key lenght is bigger than + If the subquery has blobs, or the total key length is bigger than some length, or the total number of key parts is more than the - allowed maximum (currently MAX_REF_PARTS == 16), then the created + allowed maximum (currently MAX_REF_PARTS == 32), then the created index cannot be used for lookups and we can't use hash semi join. If this is the case, delete the temporary table since it will not be used, and tell the caller we failed to initialize the From 2f6fede8d5f7e98319b4b7b557bd565fdb42fac3 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 19 Dec 2016 14:28:08 +0400 Subject: [PATCH 40/73] MDEV-10524 Assertion `arg1_int >= 0' failed in Item_func_additive_op::result_precision() This change is a backport from 10.0 to 5.5 for: 1. The full patch for: MDEV-4841 Wrong character set of ADDTIME() and DATE_ADD() 9adb6e991ec87b65d04929f115d9d0c899e4ab19 2. A small fragment of: MDEV-5298 Illegal mix of collations on timestamp 03f6778d61a74bdd7d09103a16473a2a5624cf66 which overrides Item_temporal_hybrid_func::cmp_type(), and adds a new line into cache_temporal_4265.result. --- mysql-test/include/ctype_numconv.inc | 26 ++++++++++- mysql-test/r/cache_temporal_4265.result | 1 + mysql-test/r/ctype_binary.result | 42 ++++++++++++++++- mysql-test/r/ctype_cp1251.result | 46 ++++++++++++++++-- mysql-test/r/ctype_latin1.result | 42 ++++++++++++++++- mysql-test/r/ctype_ucs.result | 48 +++++++++++++++++-- mysql-test/r/ctype_utf8.result | 46 ++++++++++++++++-- mysql-test/r/func_time.result | 6 +++ mysql-test/t/func_time.test | 5 ++ sql/item_func.h | 1 + sql/item_strfunc.cc | 2 +- sql/item_strfunc.h | 1 - sql/item_timefunc.cc | 29 ++++++++++-- sql/item_timefunc.h | 62 ++++++++++++++++++++----- sql/sql_const.h | 2 +- sql/sql_time.cc | 17 +++++++ sql/sql_time.h | 2 + 17 files changed, 341 insertions(+), 37 deletions(-) diff --git a/mysql-test/include/ctype_numconv.inc b/mysql-test/include/ctype_numconv.inc index 9b21e7f38c1e0..47b52be9cecfd 100644 --- a/mysql-test/include/ctype_numconv.inc +++ b/mysql-test/include/ctype_numconv.inc @@ -1743,6 +1743,11 @@ DROP TABLE t1; --echo # Bug #31384 DATE_ADD() and DATE_SUB() return binary data --echo # SELECT @@collation_connection, @@character_set_results; +SELECT + CHARSET(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, + CHARSET(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field_str2, + CHARSET(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, + CHARSET(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; CREATE TABLE t1 AS SELECT DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, @@ -1766,7 +1771,26 @@ SELECT HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, HEX(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; - + +--echo # +--echo # MDEV-4841 Wrong character set of ADDTIME() and DATE_ADD() +--echo # +SELECT @@collation_connection, @@character_set_results; +SELECT + CHARSET(ADDTIME(_latin1'10:01:01',_latin1'10:00:00')) AS addtime1, + CHARSET(ADDTIME('10:01:01','10:00:00')) AS addtime2, + CHARSET(DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second)) AS date_add1, + CHARSET(DATE_ADD('2001-01-01 10:01:01',interval 10 second)) AS date_add2; +CREATE TABLE t1 AS +SELECT + ADDTIME(_latin1'10:01:01',_latin1'10:00:00') AS addtime1, + ADDTIME('10:01:01','10:00:00') AS addtime2, + DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second) AS date_add1, + DATE_ADD('2001-01-01 10:01:01',interval 10 second) AS date_add2; +SHOW CREATE TABLE t1; +SELECT * FROM t1; +DROP TABLE t1; + --echo # --echo # Bug#11926811 / Bug#60625 Illegal mix of collations --echo # diff --git a/mysql-test/r/cache_temporal_4265.result b/mysql-test/r/cache_temporal_4265.result index 7f215de43fb0b..980bb957e1982 100644 --- a/mysql-test/r/cache_temporal_4265.result +++ b/mysql-test/r/cache_temporal_4265.result @@ -7,6 +7,7 @@ a 2002-03-04 Warnings: Note 1003 2000-01-01 +Note 1003 2000-01-06 set debug_dbug=''; drop table t1; create table t1 (id int not null, ut timestamp(6) not null); diff --git a/mysql-test/r/ctype_binary.result b/mysql-test/r/ctype_binary.result index e7bf12382100b..3fc440b8354d7 100644 --- a/mysql-test/r/ctype_binary.result +++ b/mysql-test/r/ctype_binary.result @@ -2777,6 +2777,13 @@ DROP TABLE t1; SELECT @@collation_connection, @@character_set_results; @@collation_connection @@character_set_results binary binary +SELECT +CHARSET(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +CHARSET(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field_str2, +CHARSET(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +CHARSET(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field_str2 field_date field_datetime +binary binary binary binary CREATE TABLE t1 AS SELECT DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, @@ -2786,8 +2793,8 @@ DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_da SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `field_str1` varchar(19) DEFAULT NULL, - `field1_str2` varchar(19) DEFAULT NULL, + `field_str1` varbinary(19) DEFAULT NULL, + `field1_str2` varbinary(19) DEFAULT NULL, `field_date` date DEFAULT NULL, `field_datetime` datetime DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 @@ -2812,6 +2819,37 @@ HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS fie field_str1 field1_str2 field_date field_datetime 323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 # +# MDEV-4841 Wrong character set of ADDTIME() and DATE_ADD() +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +binary binary +SELECT +CHARSET(ADDTIME(_latin1'10:01:01',_latin1'10:00:00')) AS addtime1, +CHARSET(ADDTIME('10:01:01','10:00:00')) AS addtime2, +CHARSET(DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second)) AS date_add1, +CHARSET(DATE_ADD('2001-01-01 10:01:01',interval 10 second)) AS date_add2; +addtime1 addtime2 date_add1 date_add2 +binary binary binary binary +CREATE TABLE t1 AS +SELECT +ADDTIME(_latin1'10:01:01',_latin1'10:00:00') AS addtime1, +ADDTIME('10:01:01','10:00:00') AS addtime2, +DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second) AS date_add1, +DATE_ADD('2001-01-01 10:01:01',interval 10 second) AS date_add2; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `addtime1` varbinary(26) DEFAULT NULL, + `addtime2` varbinary(26) DEFAULT NULL, + `date_add1` varbinary(19) DEFAULT NULL, + `date_add2` varbinary(19) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t1; +addtime1 addtime2 date_add1 date_add2 +20:01:01 20:01:01 2001-01-01 10:01:11 2001-01-01 10:01:11 +DROP TABLE t1; +# # Bug#11926811 / Bug#60625 Illegal mix of collations # SELECT @@collation_connection; diff --git a/mysql-test/r/ctype_cp1251.result b/mysql-test/r/ctype_cp1251.result index 2a7d4ed1fb400..9d5dea2f2867b 100644 --- a/mysql-test/r/ctype_cp1251.result +++ b/mysql-test/r/ctype_cp1251.result @@ -3169,6 +3169,13 @@ DROP TABLE t1; SELECT @@collation_connection, @@character_set_results; @@collation_connection @@character_set_results cp1251_general_ci cp1251 +SELECT +CHARSET(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +CHARSET(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field_str2, +CHARSET(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +CHARSET(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field_str2 field_date field_datetime +cp1251 cp1251 binary binary CREATE TABLE t1 AS SELECT DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, @@ -3178,8 +3185,8 @@ DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_da SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `field_str1` varchar(19) DEFAULT NULL, - `field1_str2` varchar(19) DEFAULT NULL, + `field_str1` varchar(19) CHARACTER SET cp1251 DEFAULT NULL, + `field1_str2` varchar(19) CHARACTER SET cp1251 DEFAULT NULL, `field_date` date DEFAULT NULL, `field_datetime` datetime DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 @@ -3190,8 +3197,8 @@ DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def field_str1 254 19 10 Y 128 0 63 -def field1_str2 254 19 19 Y 128 0 63 +def field_str1 254 19 10 Y 0 0 51 +def field1_str2 254 19 19 Y 0 0 51 def field_date 10 10 10 Y 128 0 63 def field_datetime 12 19 19 Y 128 0 63 field_str1 field1_str2 field_date field_datetime @@ -3204,6 +3211,37 @@ HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS fie field_str1 field1_str2 field_date field_datetime 323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 # +# MDEV-4841 Wrong character set of ADDTIME() and DATE_ADD() +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +cp1251_general_ci cp1251 +SELECT +CHARSET(ADDTIME(_latin1'10:01:01',_latin1'10:00:00')) AS addtime1, +CHARSET(ADDTIME('10:01:01','10:00:00')) AS addtime2, +CHARSET(DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second)) AS date_add1, +CHARSET(DATE_ADD('2001-01-01 10:01:01',interval 10 second)) AS date_add2; +addtime1 addtime2 date_add1 date_add2 +cp1251 cp1251 cp1251 cp1251 +CREATE TABLE t1 AS +SELECT +ADDTIME(_latin1'10:01:01',_latin1'10:00:00') AS addtime1, +ADDTIME('10:01:01','10:00:00') AS addtime2, +DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second) AS date_add1, +DATE_ADD('2001-01-01 10:01:01',interval 10 second) AS date_add2; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `addtime1` varchar(26) CHARACTER SET cp1251 DEFAULT NULL, + `addtime2` varchar(26) CHARACTER SET cp1251 DEFAULT NULL, + `date_add1` varchar(19) CHARACTER SET cp1251 DEFAULT NULL, + `date_add2` varchar(19) CHARACTER SET cp1251 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t1; +addtime1 addtime2 date_add1 date_add2 +20:01:01 20:01:01 2001-01-01 10:01:11 2001-01-01 10:01:11 +DROP TABLE t1; +# # Bug#11926811 / Bug#60625 Illegal mix of collations # SELECT @@collation_connection; diff --git a/mysql-test/r/ctype_latin1.result b/mysql-test/r/ctype_latin1.result index dc96495b1ac63..db9d03a86569c 100644 --- a/mysql-test/r/ctype_latin1.result +++ b/mysql-test/r/ctype_latin1.result @@ -3351,6 +3351,13 @@ DROP TABLE t1; SELECT @@collation_connection, @@character_set_results; @@collation_connection @@character_set_results latin1_swedish_ci latin1 +SELECT +CHARSET(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +CHARSET(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field_str2, +CHARSET(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +CHARSET(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field_str2 field_date field_datetime +latin1 latin1 binary binary CREATE TABLE t1 AS SELECT DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, @@ -3372,8 +3379,8 @@ DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def field_str1 254 19 10 Y 128 0 63 -def field1_str2 254 19 19 Y 128 0 63 +def field_str1 254 19 10 Y 0 0 8 +def field1_str2 254 19 19 Y 0 0 8 def field_date 10 10 10 Y 128 0 63 def field_datetime 12 19 19 Y 128 0 63 field_str1 field1_str2 field_date field_datetime @@ -3386,6 +3393,37 @@ HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS fie field_str1 field1_str2 field_date field_datetime 323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 # +# MDEV-4841 Wrong character set of ADDTIME() and DATE_ADD() +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +latin1_swedish_ci latin1 +SELECT +CHARSET(ADDTIME(_latin1'10:01:01',_latin1'10:00:00')) AS addtime1, +CHARSET(ADDTIME('10:01:01','10:00:00')) AS addtime2, +CHARSET(DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second)) AS date_add1, +CHARSET(DATE_ADD('2001-01-01 10:01:01',interval 10 second)) AS date_add2; +addtime1 addtime2 date_add1 date_add2 +latin1 latin1 latin1 latin1 +CREATE TABLE t1 AS +SELECT +ADDTIME(_latin1'10:01:01',_latin1'10:00:00') AS addtime1, +ADDTIME('10:01:01','10:00:00') AS addtime2, +DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second) AS date_add1, +DATE_ADD('2001-01-01 10:01:01',interval 10 second) AS date_add2; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `addtime1` varchar(26) DEFAULT NULL, + `addtime2` varchar(26) DEFAULT NULL, + `date_add1` varchar(19) DEFAULT NULL, + `date_add2` varchar(19) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t1; +addtime1 addtime2 date_add1 date_add2 +20:01:01 20:01:01 2001-01-01 10:01:11 2001-01-01 10:01:11 +DROP TABLE t1; +# # Bug#11926811 / Bug#60625 Illegal mix of collations # SELECT @@collation_connection; diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index 96e0970e4a6b6..c38a03f76a802 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -4167,6 +4167,13 @@ DROP TABLE t1; SELECT @@collation_connection, @@character_set_results; @@collation_connection @@character_set_results ucs2_general_ci latin1 +SELECT +CHARSET(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +CHARSET(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field_str2, +CHARSET(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +CHARSET(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field_str2 field_date field_datetime +ucs2 ucs2 binary binary CREATE TABLE t1 AS SELECT DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, @@ -4176,8 +4183,8 @@ DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_da SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `field_str1` varchar(19) DEFAULT NULL, - `field1_str2` varchar(19) DEFAULT NULL, + `field_str1` varchar(19) CHARACTER SET ucs2 DEFAULT NULL, + `field1_str2` varchar(19) CHARACTER SET ucs2 DEFAULT NULL, `field_date` date DEFAULT NULL, `field_datetime` datetime DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 @@ -4188,8 +4195,8 @@ DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def field_str1 254 19 10 Y 128 0 63 -def field1_str2 254 19 19 Y 128 0 63 +def field_str1 254 19 10 Y 0 0 8 +def field1_str2 254 19 19 Y 0 0 8 def field_date 10 10 10 Y 128 0 63 def field_datetime 12 19 19 Y 128 0 63 field_str1 field1_str2 field_date field_datetime @@ -4200,7 +4207,38 @@ HEX(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field1_str2, HEX(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; field_str1 field1_str2 field_date field_datetime -323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 +0032003000300037002D00300038002D00300032002000320033003A00350039003A00300030 0032003000300037002D00300038002D00300033002000310037003A00330032003A00300030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 +# +# MDEV-4841 Wrong character set of ADDTIME() and DATE_ADD() +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +ucs2_general_ci latin1 +SELECT +CHARSET(ADDTIME(_latin1'10:01:01',_latin1'10:00:00')) AS addtime1, +CHARSET(ADDTIME('10:01:01','10:00:00')) AS addtime2, +CHARSET(DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second)) AS date_add1, +CHARSET(DATE_ADD('2001-01-01 10:01:01',interval 10 second)) AS date_add2; +addtime1 addtime2 date_add1 date_add2 +ucs2 ucs2 ucs2 ucs2 +CREATE TABLE t1 AS +SELECT +ADDTIME(_latin1'10:01:01',_latin1'10:00:00') AS addtime1, +ADDTIME('10:01:01','10:00:00') AS addtime2, +DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second) AS date_add1, +DATE_ADD('2001-01-01 10:01:01',interval 10 second) AS date_add2; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `addtime1` varchar(26) CHARACTER SET ucs2 DEFAULT NULL, + `addtime2` varchar(26) CHARACTER SET ucs2 DEFAULT NULL, + `date_add1` varchar(19) CHARACTER SET ucs2 DEFAULT NULL, + `date_add2` varchar(19) CHARACTER SET ucs2 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t1; +addtime1 addtime2 date_add1 date_add2 +20:01:01 20:01:01 2001-01-01 10:01:11 2001-01-01 10:01:11 +DROP TABLE t1; # # Bug#11926811 / Bug#60625 Illegal mix of collations # diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 294c2cb2be13b..74fed6a316223 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -4988,6 +4988,13 @@ DROP TABLE t1; SELECT @@collation_connection, @@character_set_results; @@collation_connection @@character_set_results utf8_general_ci utf8 +SELECT +CHARSET(DATE_SUB('2007-08-03', INTERVAL 1 MINUTE)) AS field_str1, +CHARSET(DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE)) AS field_str2, +CHARSET(DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY)) AS field_date, +CHARSET(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS field_datetime; +field_str1 field_str2 field_date field_datetime +utf8 utf8 binary binary CREATE TABLE t1 AS SELECT DATE_SUB('2007-08-03', INTERVAL 1 MINUTE) AS field_str1, @@ -4997,8 +5004,8 @@ DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_da SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `field_str1` varchar(19) DEFAULT NULL, - `field1_str2` varchar(19) DEFAULT NULL, + `field_str1` varchar(19) CHARACTER SET utf8 DEFAULT NULL, + `field1_str2` varchar(19) CHARACTER SET utf8 DEFAULT NULL, `field_date` date DEFAULT NULL, `field_datetime` datetime DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 @@ -5009,8 +5016,8 @@ DATE_SUB('2007-08-03 17:33:00', INTERVAL 1 MINUTE) AS field1_str2, DATE_SUB(DATE('2007-08-03'), INTERVAL 1 DAY) AS field_date, DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE) AS field_datetime; Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr -def field_str1 254 19 10 Y 128 0 63 -def field1_str2 254 19 19 Y 128 0 63 +def field_str1 254 57 10 Y 0 0 33 +def field1_str2 254 57 19 Y 0 0 33 def field_date 10 10 10 Y 128 0 63 def field_datetime 12 19 19 Y 128 0 63 field_str1 field1_str2 field_date field_datetime @@ -5023,6 +5030,37 @@ HEX(DATE_SUB(CAST('2007-08-03 17:33:00' AS DATETIME), INTERVAL 1 MINUTE)) AS fie field_str1 field1_str2 field_date field_datetime 323030372D30382D30322032333A35393A3030 323030372D30382D30332031373A33323A3030 323030372D30382D3032 323030372D30382D30332031373A33323A3030 # +# MDEV-4841 Wrong character set of ADDTIME() and DATE_ADD() +# +SELECT @@collation_connection, @@character_set_results; +@@collation_connection @@character_set_results +utf8_general_ci utf8 +SELECT +CHARSET(ADDTIME(_latin1'10:01:01',_latin1'10:00:00')) AS addtime1, +CHARSET(ADDTIME('10:01:01','10:00:00')) AS addtime2, +CHARSET(DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second)) AS date_add1, +CHARSET(DATE_ADD('2001-01-01 10:01:01',interval 10 second)) AS date_add2; +addtime1 addtime2 date_add1 date_add2 +utf8 utf8 utf8 utf8 +CREATE TABLE t1 AS +SELECT +ADDTIME(_latin1'10:01:01',_latin1'10:00:00') AS addtime1, +ADDTIME('10:01:01','10:00:00') AS addtime2, +DATE_ADD(_latin1'2001-01-01 10:01:01',interval 10 second) AS date_add1, +DATE_ADD('2001-01-01 10:01:01',interval 10 second) AS date_add2; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `addtime1` varchar(26) CHARACTER SET utf8 DEFAULT NULL, + `addtime2` varchar(26) CHARACTER SET utf8 DEFAULT NULL, + `date_add1` varchar(19) CHARACTER SET utf8 DEFAULT NULL, + `date_add2` varchar(19) CHARACTER SET utf8 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +SELECT * FROM t1; +addtime1 addtime2 date_add1 date_add2 +20:01:01 20:01:01 2001-01-01 10:01:11 2001-01-01 10:01:11 +DROP TABLE t1; +# # Bug#11926811 / Bug#60625 Illegal mix of collations # SELECT @@collation_connection; diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index ef5e3487a99ee..68b1e0f04ad40 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -2620,3 +2620,9 @@ id date1 date2 DATE_ADD(a.date1,INTERVAL -10 DAY) TO_DAYS(a.date1)-10 17 NULL NULL NULL NULL 18 2010-10-13 2010-10-03 2010-10-03 734413 DROP TABLE t1; +# +# MDEV-10524 Assertion `arg1_int >= 0' failed in Item_func_additive_op::result_precision() +# +SELECT 1 MOD ADDTIME( '13:58:57', '00:00:01' ) + 2; +1 MOD ADDTIME( '13:58:57', '00:00:01' ) + 2 +3 diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 45214aed6e79c..92e1c38cec209 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -1597,3 +1597,8 @@ INSERT INTO t1 VALUES (17, NULL); INSERT INTO t1 VALUES (18, '2010-10-13'); SELECT a.id,a.date1,FROM_DAYS(TO_DAYS(a.date1)-10) as date2, DATE_ADD(a.date1,INTERVAL -10 DAY),TO_DAYS(a.date1)-10 FROM t1 a ORDER BY a.id; DROP TABLE t1; + +--echo # +--echo # MDEV-10524 Assertion `arg1_int >= 0' failed in Item_func_additive_op::result_precision() +--echo # +SELECT 1 MOD ADDTIME( '13:58:57', '00:00:01' ) + 2; diff --git a/sql/item_func.h b/sql/item_func.h index 667be3c043816..0da38e22c7ff1 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -39,6 +39,7 @@ class Item_func :public Item_result_field 0 means get this number from first argument */ uint allowed_arg_cols; + String *val_str_from_val_str_ascii(String *str, String *str2); void count_only_length(Item **item, uint nitems); void count_real_length(Item **item, uint nitems); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 94370d45cef6e..ec9580bfabd74 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -70,7 +70,7 @@ size_t username_char_length= 16; Normally conversion does not happen, and val_str_ascii() is immediately returned instead. */ -String *Item_str_func::val_str_from_val_str_ascii(String *str, String *str2) +String *Item_func::val_str_from_val_str_ascii(String *str, String *str2) { DBUG_ASSERT(fixed == 1); diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 7606c2815484f..00ae60a7fb129 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -62,7 +62,6 @@ class Item_str_func :public Item_func enum Item_result result_type () const { return STRING_RESULT; } void left_right_max_length(); bool fix_fields(THD *thd, Item **ref); - String *val_str_from_val_str_ascii(String *str, String *str2); }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 28e93683422bf..420fb29f5187f 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1455,25 +1455,29 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) void Item_temporal_func::fix_length_and_dec() { + uint char_length= mysql_temporal_int_part_length(field_type()); /* We set maybe_null to 1 as default as any bad argument with date or time can get us to return NULL. */ maybe_null= 1; - max_length= mysql_temporal_int_part_length(field_type()); + if (decimals) { if (decimals == NOT_FIXED_DEC) - max_length+= TIME_SECOND_PART_DIGITS + 1; + char_length+= TIME_SECOND_PART_DIGITS + 1; else { set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); - max_length+= decimals + 1; + char_length+= decimals + 1; } } sql_mode= current_thd->variables.sql_mode & (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); - collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); + collation.set(field_type() == MYSQL_TYPE_STRING ? + default_charset() : &my_charset_numeric, + DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII); + fix_char_length(char_length); } String *Item_temporal_func::val_str(String *str) @@ -1483,6 +1487,23 @@ String *Item_temporal_func::val_str(String *str) } +String *Item_temporal_hybrid_func::val_str_ascii(String *str) +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + + if (get_date(<ime, 0) || + (null_value= my_TIME_to_str(<ime, str, decimals))) + return (String *) 0; + + /* Check that the returned timestamp type matches to the function type */ + DBUG_ASSERT(cached_field_type == MYSQL_TYPE_STRING || + ltime.time_type == MYSQL_TIMESTAMP_NONE || + mysql_type_to_time_type(cached_field_type) == ltime.time_type); + return str; +} + + bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { longlong value=args[0]->val_int(); diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 3a03ee4b27a9c..0062d50083589 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -506,6 +506,50 @@ class Item_temporal_func: public Item_func }; +/** + Abstract class for functions returning TIME, DATE, DATETIME or string values, + whose data type depends on parameters and is set at fix_fields time. +*/ +class Item_temporal_hybrid_func: public Item_temporal_func +{ +protected: + enum_field_types cached_field_type; // TIME, DATE, DATETIME or STRING + String ascii_buf; // Conversion buffer +public: + Item_temporal_hybrid_func(Item *a,Item *b) + :Item_temporal_func(a,b) {} + enum_field_types field_type() const { return cached_field_type; } + Item_result cmp_type() const + { + return cached_field_type == MYSQL_TYPE_STRING ? + STRING_RESULT : TIME_RESULT; + } + const CHARSET_INFO *charset_for_protocol() const + { + /* + Can return TIME, DATE, DATETIME or VARCHAR depending on arguments. + Send using "binary" when TIME, DATE or DATETIME, + or using collation.collation when VARCHAR + (which is fixed from @@collation_connection in fix_length_and_dec). + */ + DBUG_ASSERT(fixed == 1); + return cached_field_type == MYSQL_TYPE_STRING ? + collation.collation : &my_charset_bin; + } + /** + Return string value in ASCII character set. + */ + String *val_str_ascii(String *str); + /** + Return string value in @@character_set_connection. + */ + String *val_str(String *str) + { + return val_str_from_val_str_ascii(str, &ascii_buf); + } +}; + + class Item_datefunc :public Item_temporal_func { public: @@ -763,17 +807,15 @@ class Item_func_sec_to_time :public Item_timefunc }; -class Item_date_add_interval :public Item_temporal_func +class Item_date_add_interval :public Item_temporal_hybrid_func { - enum_field_types cached_field_type; public: const interval_type int_type; // keep it public const bool date_sub_interval; // keep it public Item_date_add_interval(Item *a,Item *b,interval_type type_arg,bool neg_arg) - :Item_temporal_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} + :Item_temporal_hybrid_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} const char *func_name() const { return "date_add_interval"; } void fix_length_and_dec(); - enum_field_types field_type() const { return cached_field_type; } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); bool eq(const Item *item, bool binary_cmp) const; void print(String *str, enum_query_type query_type); @@ -911,16 +953,14 @@ class Item_func_makedate :public Item_temporal_func }; -class Item_func_add_time :public Item_temporal_func +class Item_func_add_time :public Item_temporal_hybrid_func { const bool is_date; int sign; - enum_field_types cached_field_type; public: Item_func_add_time(Item *a, Item *b, bool type_arg, bool neg_arg) - :Item_temporal_func(a, b), is_date(type_arg) { sign= neg_arg ? -1 : 1; } - enum_field_types field_type() const { return cached_field_type; } + :Item_temporal_hybrid_func(a, b), is_date(type_arg) { sign= neg_arg ? -1 : 1; } void fix_length_and_dec(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); void print(String *str, enum_query_type query_type); @@ -1019,9 +1059,8 @@ class Item_func_get_format :public Item_str_ascii_func }; -class Item_func_str_to_date :public Item_temporal_func +class Item_func_str_to_date :public Item_temporal_hybrid_func { - enum_field_types cached_field_type; timestamp_type cached_timestamp_type; bool const_item; String subject_converter; @@ -1029,12 +1068,11 @@ class Item_func_str_to_date :public Item_temporal_func CHARSET_INFO *internal_charset; public: Item_func_str_to_date(Item *a, Item *b) - :Item_temporal_func(a, b), const_item(false), + :Item_temporal_hybrid_func(a, b), const_item(false), internal_charset(NULL) {} bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); const char *func_name() const { return "str_to_date"; } - enum_field_types field_type() const { return cached_field_type; } void fix_length_and_dec(); }; diff --git a/sql/sql_const.h b/sql/sql_const.h index 9d227601a20d1..3c127a038267e 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -54,7 +54,7 @@ #define MIN_TIME_WIDTH 10 /* -HHH:MM:SS */ #define MAX_TIME_WIDTH 16 /* -DDDDDD HH:MM:SS */ #define MAX_TIME_FULL_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */ -#define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */ +#define MAX_DATETIME_FULL_WIDTH 26 /* YYYY-MM-DD HH:MM:SS.###### */ #define MAX_DATETIME_WIDTH 19 /* YYYY-MM-DD HH:MM:SS */ #define MAX_DATETIME_COMPRESSED_WIDTH 14 /* YYYYMMDDHHMMSS */ #define MAX_DATETIME_PRECISION 6 diff --git a/sql/sql_time.cc b/sql/sql_time.cc index c5c65391758b7..d912a7b78d6c4 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -838,6 +838,23 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, } } + +/** + Convert TIME/DATE/DATETIME value to String. + @param l_time DATE value + @param OUT str String to convert to + @param dec Number of fractional digits. +*/ +bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec) +{ + if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) + return true; + str->set_charset(&my_charset_numeric); + str->length(my_TIME_to_str(ltime, const_cast(str->ptr()), dec)); + return false; +} + + void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, const ErrConv *sval, diff --git a/sql/sql_time.h b/sql/sql_time.h index ad7521210446a..9becdcd42006b 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -74,6 +74,8 @@ extern DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format); const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, timestamp_type type); +bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec); + /* MYSQL_TIME operations */ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval); From 19896d4b3ab459d135aee6ee67cb92bce92f9b87 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 19 Dec 2016 16:09:20 +0400 Subject: [PATCH 41/73] MDEV-10274 Bundling insert with create statement for table with unsigned Decimal primary key issues warning 1194. Flags are important for key_length calculations, so them should be set before it, not after. --- mysql-test/r/create.result | 7 +++++++ mysql-test/t/create.test | 9 +++++++++ sql/sql_table.cc | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index 0164aa4b610aa..829b54dea49dd 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -2498,4 +2498,11 @@ end| create table t1 as select f1(); ERROR 42S02: Table 'test.t1' doesn't exist drop function f1; +# +# MDEV-10274 Bundling insert with create statement +# for table with unsigned Decimal primary key issues warning 1194 +# +create table t1(ID decimal(2,1) unsigned NOT NULL, PRIMARY KEY (ID))engine=memory +select 2.1 ID; +drop table t1; End of 5.5 tests diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 5c90c3e17a4c0..1e77dac9bc991 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -2081,4 +2081,13 @@ DELIMITER ;| create table t1 as select f1(); drop function f1; +--echo # +--echo # MDEV-10274 Bundling insert with create statement +--echo # for table with unsigned Decimal primary key issues warning 1194 +--echo # + +create table t1(ID decimal(2,1) unsigned NOT NULL, PRIMARY KEY (ID))engine=memory + select 2.1 ID; +drop table t1; + --echo End of 5.5 tests diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7d2e67b5cfd18..9bcb4c3f8ccf0 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3167,7 +3167,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->pack_length= dup_field->pack_length; sql_field->key_length= dup_field->key_length; sql_field->decimals= dup_field->decimals; - sql_field->create_length_to_internal_length(); sql_field->unireg_check= dup_field->unireg_check; /* We're making one field from two, the result field will have @@ -3177,6 +3176,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields--; sql_field->flags= dup_field->flags; + sql_field->create_length_to_internal_length(); sql_field->interval= dup_field->interval; sql_field->vcol_info= dup_field->vcol_info; sql_field->stored_in_db= dup_field->stored_in_db; From 268bb69beaec027b9f713d13316aa78c5c292817 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 16 Dec 2016 17:08:31 +0300 Subject: [PATCH 42/73] MDEV-7691: Assertion `outer_context || !*from_field || *from_field == not_found_field' ... The bug occurred when a subquery - has a reference to outside, to grand-parent query or further up - is converted to a semi-join (i.e. merged into its parent). Then the reference to outside had form Item_ref(Item_field(...)). - Conversion to semi-join would call item->fix_after_pullout() for the outside reference. - Item_ref::fix_after_pullout would call Item_field->fix_after_pullout - The Item_field would construct a new Name_resolution_context object This process ignored the fact that the Item_field does not belong to any of the subselects being flattened. The result was crash in the next call to Item_field::fix_fields(), where we would try to use an invalid Name_resolution_context object. Fixed by not creating Name_resolution_context object if the Item_field's context does not belong to the subselect(s) that were flattened. --- mysql-test/r/subselect4.result | 39 ++++++++++++++++++++++++++++++++ mysql-test/t/subselect4.test | 41 ++++++++++++++++++++++++++++++++++ sql/item.cc | 38 +++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index c7f22cf05e528..89fb0902f5305 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -2410,5 +2410,44 @@ SELECT x FROM t1 WHERE id > (SELECT MAX(id) - 1000 FROM t1) ORDER BY x LIMIT 1; x 0 drop table t1; +# +# MDEV-7691: Assertion `outer_context || !*from_field || *from_field == not_found_field' ... +# +set optimizer_switch=default; +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (4),(6); +CREATE TABLE t2 (b INT) ENGINE=MyISAM; +INSERT INTO t2 VALUES (1),(8); +PREPARE stmt FROM " +SELECT * FROM t2 +HAVING 0 IN ( + SELECT a FROM t1 + WHERE a IN ( + SELECT a FROM t1 + WHERE b = a + ) +) +"; +EXECUTE stmt; +b +EXECUTE stmt; +b +# Alternative test case, without HAVING +CREATE TABLE t3 (i INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES (4),(6); +PREPARE stmt FROM " +SELECT * FROM t3 AS t10 +WHERE EXISTS ( + SELECT * FROM t3 AS t20 WHERE t10.i IN ( + SELECT i FROM t3 + ) +)"; +EXECUTE stmt; +i +6 +EXECUTE stmt; +i +6 +drop table t1, t2, t3; SET optimizer_switch= @@global.optimizer_switch; set @@tmp_table_size= @@global.tmp_table_size; diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test index b179ead39d7f1..7a7dd7e492e3e 100644 --- a/mysql-test/t/subselect4.test +++ b/mysql-test/t/subselect4.test @@ -1959,5 +1959,46 @@ SELECT x FROM t1 WHERE id > (SELECT MAX(id) - 1000 FROM t1) ORDER BY x LIMIT 1; drop table t1; +--echo # +--echo # MDEV-7691: Assertion `outer_context || !*from_field || *from_field == not_found_field' ... +--echo # +set optimizer_switch=default; +CREATE TABLE t1 (a INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (4),(6); + +CREATE TABLE t2 (b INT) ENGINE=MyISAM; +INSERT INTO t2 VALUES (1),(8); + +PREPARE stmt FROM " +SELECT * FROM t2 +HAVING 0 IN ( + SELECT a FROM t1 + WHERE a IN ( + SELECT a FROM t1 + WHERE b = a + ) +) +"; + +EXECUTE stmt; +EXECUTE stmt; + +--echo # Alternative test case, without HAVING +CREATE TABLE t3 (i INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES (4),(6); + +PREPARE stmt FROM " +SELECT * FROM t3 AS t10 +WHERE EXISTS ( + SELECT * FROM t3 AS t20 WHERE t10.i IN ( + SELECT i FROM t3 + ) +)"; + +EXECUTE stmt; +EXECUTE stmt; + +drop table t1, t2, t3; + SET optimizer_switch= @@global.optimizer_switch; set @@tmp_table_size= @@global.tmp_table_size; diff --git a/sql/item.cc b/sql/item.cc index 53666aaf83da2..fc9eb31bf5b0c 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2777,6 +2777,44 @@ void Item_field::fix_after_pullout(st_select_lex *new_parent, Item **ref) depended_from= NULL; if (context) { + bool need_change= false; + /* + Suppose there are nested selects: + + select_id=1 + select_id=2 + select_id=3 <----+ + select_id=4 -+ + select_id=5 --+ + + Suppose, pullout operation has moved anything that had select_id=4 or 5 + in to select_id=3. + + If this Item_field had a name resolution context pointing into select_lex + with id=4 or id=5, it needs a new name resolution context. + + However, it could also be that this object is a part of outer reference: + Item_ref(Item_field(field in select with select_id=1))). + - The Item_ref object has a context with select_id=5, and so needs a new + name resolution context. + - The Item_field object has a context with select_id=1, and doesn't need + a new name resolution context. + + So, the following loop walks from Item_field's current context upwards. + If we find that the select we've been pulled out to is up there, we + create the new name resolution context. Otherwise, we don't. + */ + for (Name_resolution_context *ct= context; ct; ct= ct->outer_context) + { + if (new_parent == ct->select_lex) + { + need_change= true; + break; + } + } + if (!need_change) + return; + Name_resolution_context *ctx= new Name_resolution_context(); if (context->select_lex == new_parent) { From f23b41b9b8a30e0e54a1ec7a8923057b0e57e0f5 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 16 Dec 2016 17:16:02 +0300 Subject: [PATCH 43/73] MDEV-10148: Database crashes in the query to the View Fix st_select_lex::is_merged_child_of to work across merged views or derived tables. --- mysql-test/r/subselect2.result | 35 ++++++++++++++++++++++++++++++++ mysql-test/t/subselect2.test | 37 ++++++++++++++++++++++++++++++++++ sql/sql_lex.cc | 6 ++++++ 3 files changed, 78 insertions(+) diff --git a/mysql-test/r/subselect2.result b/mysql-test/r/subselect2.result index 72ca9d33354ed..b6dc940d9fbbb 100644 --- a/mysql-test/r/subselect2.result +++ b/mysql-test/r/subselect2.result @@ -348,4 +348,39 @@ where t1.a = t2.a and ( t1.a = ( select min(a) from t1 ) or 0 ); a a a FRA FRA FRA drop table t1,t2,t3; +# +# MDEV-10148: Database crashes in the query to the View +# +CREATE TABLE t1 ( +key_code INT(11) NOT NULL, +value_string VARCHAR(50) NULL DEFAULT NULL, +PRIMARY KEY (key_code) +) COLLATE='utf8_general_ci' ENGINE=InnoDB ; +CREATE TABLE t2 ( +key_code INT(11) NOT NULL, +target_date DATE NULL DEFAULT NULL, +PRIMARY KEY (key_code) +) COLLATE='utf8_general_ci' ENGINE=InnoDB ; +CREATE TABLE t3 ( +now_date DATE NOT NULL, +PRIMARY KEY (now_date) +) COLLATE='utf8_general_ci' ENGINE=InnoDB ; +CREATE VIEW v1 +AS +SELECT +B.key_code, +B.target_date +FROM +t2 B INNER JOIN t3 C ON +B.target_date = C.now_date +; +SET @s = 'SELECT A.* FROM t1 A WHERE A.key_code IN (SELECT key_code FROM v1)'; +PREPARE stmt FROM @s; +EXECUTE stmt; +key_code value_string +EXECUTE stmt; +key_code value_string +DEALLOCATE PREPARE stmt; +DROP VIEW v1; +DROP TABLE t1,t2,t3; set optimizer_switch=@subselect2_test_tmp; diff --git a/mysql-test/t/subselect2.test b/mysql-test/t/subselect2.test index b3c1322184d10..f795cef648c85 100644 --- a/mysql-test/t/subselect2.test +++ b/mysql-test/t/subselect2.test @@ -359,5 +359,42 @@ where t1.a = t2.a and ( t1.a = ( select min(a) from t1 ) or 0 ); drop table t1,t2,t3; +--echo # +--echo # MDEV-10148: Database crashes in the query to the View +--echo # +CREATE TABLE t1 ( + key_code INT(11) NOT NULL, + value_string VARCHAR(50) NULL DEFAULT NULL, + PRIMARY KEY (key_code) +) COLLATE='utf8_general_ci' ENGINE=InnoDB ; + +CREATE TABLE t2 ( + key_code INT(11) NOT NULL, + target_date DATE NULL DEFAULT NULL, + PRIMARY KEY (key_code) +) COLLATE='utf8_general_ci' ENGINE=InnoDB ; + +CREATE TABLE t3 ( + now_date DATE NOT NULL, + PRIMARY KEY (now_date) +) COLLATE='utf8_general_ci' ENGINE=InnoDB ; + +CREATE VIEW v1 +AS +SELECT + B.key_code, + B.target_date +FROM + t2 B INNER JOIN t3 C ON + B.target_date = C.now_date +; +SET @s = 'SELECT A.* FROM t1 A WHERE A.key_code IN (SELECT key_code FROM v1)'; +PREPARE stmt FROM @s; +EXECUTE stmt; #1st time -> success +EXECUTE stmt; #2nd time -> crash +DEALLOCATE PREPARE stmt; +DROP VIEW v1; +DROP TABLE t1,t2,t3; + set optimizer_switch=@subselect2_test_tmp; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f2e7b4f7c3a8d..fa866bc7008a9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4247,6 +4247,12 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) { continue; } + + if (sl->master_unit()->derived && + sl->master_unit()->derived->is_merged_derived()) + { + continue; + } all_merged= FALSE; break; } From aaff3d6c35f51dde60907f3c0fc4b2a40bc63c38 Mon Sep 17 00:00:00 2001 From: Oleksandr Byelkin Date: Tue, 20 Dec 2016 10:25:25 +0100 Subject: [PATCH 44/73] MDEV-10172: UNION query returns incorrect rows outside conditional evaluation count duplicate of UNION SELECT separately to awoid influence on lokal LIMIT clause. --- mysql-test/r/union.result | 33 ++++++++++++++++++++++++++++----- mysql-test/t/union.test | 22 +++++++++++++++++++++- sql/sql_select.cc | 13 +++++++++---- sql/sql_select.h | 5 +++-- 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 40f5a77e3d055..5a6cd8907e90f 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -362,7 +362,7 @@ a 2 select found_rows(); found_rows() -6 +5 SELECT SQL_CALC_FOUND_ROWS * FROM t1 UNION SELECT * FROM t2 LIMIT 100; a 1 @@ -1169,12 +1169,9 @@ a b select * from ((select * from t1 limit 1) union (select * from t1 limit 1)) a; a b 1 a -2 b select * from ((select * from t1 limit 1) union (select * from t1 limit 1) union (select * from t1 limit 1)) a; a b 1 a -2 b -3 c select * from ((((select * from t1))) union (select * from t1) union (select * from t1)) a; a b 1 a @@ -1553,7 +1550,6 @@ NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Using filesort Warnings: Note 1003 select NULL AS `a` from `test`.`t1` union select NULL AS `a` from `test`.`t1` order by `a` DROP TABLE t1; -End of 5.0 tests # # Bug#32858: Error: "Incorrect usage of UNION and INTO" does not take # subselects into account @@ -1659,6 +1655,14 @@ a 4 5 6 +(select a from t1 where false) UNION (select a from t1) limit 8; +a +10 +2 +3 +4 +5 +6 7 8 drop table t1; @@ -1955,3 +1959,22 @@ cccc bbbb dddd drop table t1; +# +# MDEV-10172: UNION query returns incorrect rows outside +# conditional evaluation +# +create table t1 (d datetime not null primary key); +insert into t1(d) values ('2016-06-01'),('2016-06-02'),('2016-06-03'),('2016-06-04'); +select * from +( +select * from t1 where d between '2016-06-02' and '2016-06-05' + union +(select * from t1 where d < '2016-06-05' order by d desc limit 1) +) onlyJun2toJun4 +order by d; +d +2016-06-02 00:00:00 +2016-06-03 00:00:00 +2016-06-04 00:00:00 +drop table t1; +End of 5.0 tests diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 9204ddd22e5a8..f4dc6a5d449b0 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -1022,7 +1022,6 @@ ORDER BY a; DROP TABLE t1; ---echo End of 5.0 tests -- echo # -- echo # Bug#32858: Error: "Incorrect usage of UNION and INTO" does not take -- echo # subselects into account @@ -1126,6 +1125,8 @@ create table t1 (a int); insert into t1 values (10),(10),(10),(2),(3),(4),(5),(6),(7),(8),(9),(1),(10); --sorted_result select a from t1 where false UNION select a from t1 limit 8; +--sorted_result +(select a from t1 where false) UNION (select a from t1) limit 8; drop table t1; --echo # @@ -1350,3 +1351,22 @@ UNION ; drop table t1; + + +--echo # +--echo # MDEV-10172: UNION query returns incorrect rows outside +--echo # conditional evaluation +--echo # + +create table t1 (d datetime not null primary key); +insert into t1(d) values ('2016-06-01'),('2016-06-02'),('2016-06-03'),('2016-06-04'); +select * from +( + select * from t1 where d between '2016-06-02' and '2016-06-05' + union + (select * from t1 where d < '2016-06-05' order by d desc limit 1) +) onlyJun2toJun4 +order by d; +drop table t1; + +--echo End of 5.0 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8c994964d5980..839665f3a9f98 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2871,7 +2871,7 @@ JOIN::exec() *curr_fields_list), Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); error= do_select(curr_join, curr_fields_list, NULL, procedure); - thd->limit_found_rows= curr_join->send_records; + thd->limit_found_rows= curr_join->send_records - curr_join->duplicate_rows; /* Accumulate the counts from all join iterations of all join parts. */ thd->examined_row_count+= curr_join->examined_rows; @@ -16578,7 +16578,7 @@ do_select(JOIN *join,List *fields,TABLE *table,Procedure *procedure) join->join_tab[join->top_join_tab_count - 1].next_select= end_select; join_tab=join->join_tab+join->const_tables; } - join->send_records=0; + join->duplicate_rows= join->send_records=0; if (join->table_count == join->const_tables) { /* @@ -18089,7 +18089,12 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), int error; /* result < 0 if row was not accepted and should not be counted */ if ((error= join->result->send_data(*join->fields))) - DBUG_RETURN(error < 0 ? NESTED_LOOP_OK : NESTED_LOOP_ERROR); + { + if (error > 0) + DBUG_RETURN(NESTED_LOOP_ERROR); + // error < 0 => duplicate row + join->duplicate_rows++; + } } if (++join->send_records >= join->unit->select_limit_cnt && join->do_send_rows) @@ -18205,7 +18210,7 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (error < 0) { /* Duplicate row, don't count */ - join->send_records--; + join->duplicate_rows++; error= 0; } } diff --git a/sql/sql_select.h b/sql/sql_select.h index 4650bc24c6805..0623672840e15 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1018,7 +1018,8 @@ class JOIN :public Sql_alloc table_map outer_join; /* Bitmap of tables used in the select list items */ table_map select_list_used_tables; - ha_rows send_records,found_records,examined_rows,row_limit, select_limit; + ha_rows send_records, found_records, examined_rows, + row_limit, select_limit, duplicate_rows; /** Used to fetch no more than given amount of rows per one fetch operation of server side cursor. @@ -1272,7 +1273,7 @@ class JOIN :public Sql_alloc sort_and_group= 0; first_record= 0; do_send_rows= 1; - send_records= 0; + duplicate_rows= send_records= 0; found_records= 0; fetch_limit= HA_POS_ERROR; examined_rows= 0; From e025ebcdb538ee6d191d22aee0587f5534080a4b Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 20 Dec 2016 12:45:48 +0000 Subject: [PATCH 45/73] Fix pointer formatting in crash handler output. Do not use 0x%p to output thd address, use %p --- sql/signal_handler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index b0c67fbfe320e..81792cc30ac19 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -143,7 +143,7 @@ extern "C" sig_handler handle_fatal_signal(int sig) if (opt_stack_trace) { - my_safe_printf_stderr("Thread pointer: 0x%p\n", thd); + my_safe_printf_stderr("Thread pointer: %p\n", thd); my_safe_printf_stderr("%s", "Attempting backtrace. You can use the following " "information to find out\n" From cbd7548aff7536940cf6c619c4de4f51c1f9e0bb Mon Sep 17 00:00:00 2001 From: Ronak Jain Date: Thu, 8 Dec 2016 23:27:04 +0530 Subject: [PATCH 46/73] MDEV-11353: fixes Identical logical conditions --- sql/handler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/handler.cc b/sql/handler.cc index 5fc756020398e..bc71aa57fb7ba 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3924,7 +3924,7 @@ void handler::get_dynamic_partition_info(PARTITION_STATS *stat_info, stat_info->update_time= stats.update_time; stat_info->check_time= stats.check_time; stat_info->check_sum= 0; - if (table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_OLD_CHECKSUM)) + if (table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM)) stat_info->check_sum= checksum(); return; } From ef82fd8ca33f0ecab06ddfa11b51dd5ea9019653 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Tue, 20 Dec 2016 17:42:08 +0400 Subject: [PATCH 47/73] MDEV-11353 - Identical logical conditions Added test case. --- mysql-test/r/information_schema_part.result | 8 ++++++++ mysql-test/t/information_schema_part.test | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/mysql-test/r/information_schema_part.result b/mysql-test/r/information_schema_part.result index b34183ebdee85..77959de256eaf 100644 --- a/mysql-test/r/information_schema_part.result +++ b/mysql-test/r/information_schema_part.result @@ -151,3 +151,11 @@ select create_options from information_schema.tables where table_schema="test"; create_options partitioned drop table t1; +# +# MDEV-11353 - Identical logical conditions +# +CREATE TABLE t1(a INT) CHECKSUM=1 SELECT 1; +SELECT CHECKSUM FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'; +CHECKSUM +3036305396 +DROP TABLE t1; diff --git a/mysql-test/t/information_schema_part.test b/mysql-test/t/information_schema_part.test index f1415d12f7909..ea88f364c077e 100644 --- a/mysql-test/t/information_schema_part.test +++ b/mysql-test/t/information_schema_part.test @@ -131,3 +131,10 @@ drop table if exists t1; create table t1 (f1 int key) partition by key(f1) partitions 2; select create_options from information_schema.tables where table_schema="test"; drop table t1; + +--echo # +--echo # MDEV-11353 - Identical logical conditions +--echo # +CREATE TABLE t1(a INT) CHECKSUM=1 SELECT 1; +SELECT CHECKSUM FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1'; +DROP TABLE t1; From 5e051bfa15d201228b103d7f536436a61cde8707 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 21 Dec 2016 15:39:45 +0400 Subject: [PATCH 48/73] MDEV-10386 Assertion `fixed == 1' failed in virtual String* Item_func_conv_charset::val_str(String*) The patch b96c196f1cd5d77e524cbf952539bdd33c65ffc1 added a new call for safe_charset_converter() without a corresponding fix_fields(). In case of a sub-query the created Item remained in non-fixed state. The problem did not show up with literal derived expressions, only subselects were affected. This patch adds a corresponding fix_fields() to the previously added safe_charset_converter(). --- mysql-test/r/subselect.result | 11 +++++++++++ mysql-test/r/subselect_no_mat.result | 11 +++++++++++ mysql-test/r/subselect_no_opts.result | 11 +++++++++++ mysql-test/r/subselect_no_scache.result | 11 +++++++++++ mysql-test/r/subselect_no_semijoin.result | 11 +++++++++++ mysql-test/t/subselect.test | 10 ++++++++++ sql/item.cc | 3 ++- 7 files changed, 67 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 0a599a64f80d1..789cfe2fdcaf6 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -7105,3 +7105,14 @@ group by round((select 1 from t1 limit 1)); round((select 1 from t1 limit 1)) 1 drop table t1; +# +# MDEV-10386 Assertion `fixed == 1' failed in virtual String* Item_func_conv_charset::val_str(String*) +# +CREATE TABLE t1 (f1 CHAR(3) CHARACTER SET utf8 NULL, f2 CHAR(3) CHARACTER SET latin1 NULL); +INSERT INTO t1 VALUES ('foo','bar'); +SELECT * FROM t1 WHERE f2 >= SOME ( SELECT f1 FROM t1 ); +f1 f2 +SELECT * FROM t1 WHERE f2 <= SOME ( SELECT f1 FROM t1 ); +f1 f2 +foo bar +DROP TABLE t1; diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result index b819b1e4ef9dc..c729c17f94f22 100644 --- a/mysql-test/r/subselect_no_mat.result +++ b/mysql-test/r/subselect_no_mat.result @@ -7102,6 +7102,17 @@ group by round((select 1 from t1 limit 1)); round((select 1 from t1 limit 1)) 1 drop table t1; +# +# MDEV-10386 Assertion `fixed == 1' failed in virtual String* Item_func_conv_charset::val_str(String*) +# +CREATE TABLE t1 (f1 CHAR(3) CHARACTER SET utf8 NULL, f2 CHAR(3) CHARACTER SET latin1 NULL); +INSERT INTO t1 VALUES ('foo','bar'); +SELECT * FROM t1 WHERE f2 >= SOME ( SELECT f1 FROM t1 ); +f1 f2 +SELECT * FROM t1 WHERE f2 <= SOME ( SELECT f1 FROM t1 ); +f1 f2 +foo bar +DROP TABLE t1; set optimizer_switch=default; select @@optimizer_switch like '%materialization=on%'; @@optimizer_switch like '%materialization=on%' diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result index e1001a5165828..dc308ea77e594 100644 --- a/mysql-test/r/subselect_no_opts.result +++ b/mysql-test/r/subselect_no_opts.result @@ -7100,4 +7100,15 @@ group by round((select 1 from t1 limit 1)); round((select 1 from t1 limit 1)) 1 drop table t1; +# +# MDEV-10386 Assertion `fixed == 1' failed in virtual String* Item_func_conv_charset::val_str(String*) +# +CREATE TABLE t1 (f1 CHAR(3) CHARACTER SET utf8 NULL, f2 CHAR(3) CHARACTER SET latin1 NULL); +INSERT INTO t1 VALUES ('foo','bar'); +SELECT * FROM t1 WHERE f2 >= SOME ( SELECT f1 FROM t1 ); +f1 f2 +SELECT * FROM t1 WHERE f2 <= SOME ( SELECT f1 FROM t1 ); +f1 f2 +foo bar +DROP TABLE t1; set @optimizer_switch_for_subselect_test=null; diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result index e175e7e007280..e7c85c10f2de7 100644 --- a/mysql-test/r/subselect_no_scache.result +++ b/mysql-test/r/subselect_no_scache.result @@ -7111,6 +7111,17 @@ group by round((select 1 from t1 limit 1)); round((select 1 from t1 limit 1)) 1 drop table t1; +# +# MDEV-10386 Assertion `fixed == 1' failed in virtual String* Item_func_conv_charset::val_str(String*) +# +CREATE TABLE t1 (f1 CHAR(3) CHARACTER SET utf8 NULL, f2 CHAR(3) CHARACTER SET latin1 NULL); +INSERT INTO t1 VALUES ('foo','bar'); +SELECT * FROM t1 WHERE f2 >= SOME ( SELECT f1 FROM t1 ); +f1 f2 +SELECT * FROM t1 WHERE f2 <= SOME ( SELECT f1 FROM t1 ); +f1 f2 +foo bar +DROP TABLE t1; set optimizer_switch=default; select @@optimizer_switch like '%subquery_cache=on%'; @@optimizer_switch like '%subquery_cache=on%' diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result index a211d498762e9..b6261f0509838 100644 --- a/mysql-test/r/subselect_no_semijoin.result +++ b/mysql-test/r/subselect_no_semijoin.result @@ -7100,5 +7100,16 @@ group by round((select 1 from t1 limit 1)); round((select 1 from t1 limit 1)) 1 drop table t1; +# +# MDEV-10386 Assertion `fixed == 1' failed in virtual String* Item_func_conv_charset::val_str(String*) +# +CREATE TABLE t1 (f1 CHAR(3) CHARACTER SET utf8 NULL, f2 CHAR(3) CHARACTER SET latin1 NULL); +INSERT INTO t1 VALUES ('foo','bar'); +SELECT * FROM t1 WHERE f2 >= SOME ( SELECT f1 FROM t1 ); +f1 f2 +SELECT * FROM t1 WHERE f2 <= SOME ( SELECT f1 FROM t1 ); +f1 f2 +foo bar +DROP TABLE t1; set @optimizer_switch_for_subselect_test=null; set @join_cache_level_for_subselect_test=NULL; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 77b6c6c55822f..a8ad3ba52a543 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -5988,3 +5988,13 @@ from t1 group by round((select 1 from t1 limit 1)); drop table t1; + +--echo # +--echo # MDEV-10386 Assertion `fixed == 1' failed in virtual String* Item_func_conv_charset::val_str(String*) +--echo # + +CREATE TABLE t1 (f1 CHAR(3) CHARACTER SET utf8 NULL, f2 CHAR(3) CHARACTER SET latin1 NULL); +INSERT INTO t1 VALUES ('foo','bar'); +SELECT * FROM t1 WHERE f2 >= SOME ( SELECT f1 FROM t1 ); +SELECT * FROM t1 WHERE f2 <= SOME ( SELECT f1 FROM t1 ); +DROP TABLE t1; diff --git a/sql/item.cc b/sql/item.cc index fc9eb31bf5b0c..1df91dc25348a 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1164,7 +1164,8 @@ Item *Item_cache::safe_charset_converter(CHARSET_INFO *tocs) if (conv == example) return this; Item_cache *cache; - if (!conv || !(cache= new Item_cache_str(conv))) + if (!conv || conv->fix_fields(current_thd, (Item **) NULL) || + !(cache= new Item_cache_str(conv))) return NULL; // Safe conversion is not possible, or OEM cache->setup(conv); cache->fixed= false; // Make Item::fix_fields() happy From 706fb790bcf9105a73f34002fe28c75032267c4b Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Thu, 22 Dec 2016 15:51:37 +0530 Subject: [PATCH 49/73] MDEV-10927: Crash When Using sort_union Optimization In file sql/filesort.cc,when merge_buffers() is called then - queue_remove(&queue,0) is called - For the function queue_remove there is assertion states that the element to be removed should have index >=1 - this is causing the assertion to fail. Fixed by removing the top element. --- mysql-test/r/index_merge_innodb.result | 29 ++++++++++++++++++++++ mysql-test/t/index_merge_innodb.test | 33 +++++++++++++++++++++++++- sql/filesort.cc | 2 +- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/index_merge_innodb.result b/mysql-test/r/index_merge_innodb.result index b93d15f7bef36..00cbf35ec6999 100644 --- a/mysql-test/r/index_merge_innodb.result +++ b/mysql-test/r/index_merge_innodb.result @@ -793,3 +793,32 @@ a b c 9 d d DROP TABLE t1; set optimizer_switch= @optimizer_switch_save; +# +# MDEV-10927: Crash When Using sort_union Optimization +# +set @tmp_optimizer_switch=@@optimizer_switch; +SET optimizer_switch='index_merge_sort_intersection=on'; +SET SESSION sort_buffer_size = 1024; +create table t1 ( +pk int(11) NOT NULL AUTO_INCREMENT, +col1 int(11) NOT NULL, +col2 int(11) NOT NULL, +col3 int(11) NOT NULL, +key2 int(11) NOT NULL, +col4 int(11) NOT NULL, +key1 int(11) NOT NULL, +PRIMARY KEY (pk), +KEY key1 (key1), +KEY key2 (key2) +) ENGINE=InnoDB AUTO_INCREMENT=12860259 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT; +create table t2(a int); +insert into t2 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table t3(a int); +insert into t3 select A.a + B.a* 10 + C.a * 100 + D.a*1000 from t2 A, t2 B, t2 C, t2 D; +insert into t1 (key1, key2, col1,col2,col3,col4) +select a,a, a,a,a,a from t3; +SELECT sum(col1) FROM t1 FORCE INDEX (key1,key2) WHERE (key1 between 10 and 8191+10) or (key2= 5); +sum(col1) +33632261 +drop table t1,t2,t3; +set optimizer_switch=@tmp_optimizer_switch; diff --git a/mysql-test/t/index_merge_innodb.test b/mysql-test/t/index_merge_innodb.test index 6a1cb53dc404e..fb56e44b5ae14 100644 --- a/mysql-test/t/index_merge_innodb.test +++ b/mysql-test/t/index_merge_innodb.test @@ -171,6 +171,37 @@ WHERE ( tb.b != ta.b OR tb.a = ta.a ) AND ( tb.b = ta.c OR tb.b = ta.b ); DROP TABLE t1; - set optimizer_switch= @optimizer_switch_save; +--echo # +--echo # MDEV-10927: Crash When Using sort_union Optimization +--echo # + +set @tmp_optimizer_switch=@@optimizer_switch; +SET optimizer_switch='index_merge_sort_intersection=on'; +SET SESSION sort_buffer_size = 1024; + +create table t1 ( +pk int(11) NOT NULL AUTO_INCREMENT, +col1 int(11) NOT NULL, +col2 int(11) NOT NULL, +col3 int(11) NOT NULL, +key2 int(11) NOT NULL, +col4 int(11) NOT NULL, +key1 int(11) NOT NULL, +PRIMARY KEY (pk), +KEY key1 (key1), +KEY key2 (key2) +) ENGINE=InnoDB AUTO_INCREMENT=12860259 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT; + +create table t2(a int); +insert into t2 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table t3(a int); +insert into t3 select A.a + B.a* 10 + C.a * 100 + D.a*1000 from t2 A, t2 B, t2 C, t2 D; + +insert into t1 (key1, key2, col1,col2,col3,col4) +select a,a, a,a,a,a from t3; +SELECT sum(col1) FROM t1 FORCE INDEX (key1,key2) WHERE (key1 between 10 and 8191+10) or (key2= 5); +drop table t1,t2,t3; +set optimizer_switch=@tmp_optimizer_switch; diff --git a/sql/filesort.cc b/sql/filesort.cc index 5bb5c64409ab9..38404b01cf72d 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1411,7 +1411,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, if (!(error= (int) read_to_buffer(from_file, buffpek, rec_length))) { - queue_remove(&queue,0); + (void) queue_remove_top(&queue); reuse_freed_buff(&queue, buffpek, rec_length); } else if (error == -1) From c8e49f2f57b7e8c9dcf3cdb108dc15e6b63b4dc4 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 20 Dec 2016 15:17:59 +0100 Subject: [PATCH 50/73] move check_user/set_user from mysqld.cc to mysys --- include/my_sys.h | 4 +++ mysys/CMakeLists.txt | 4 +-- mysys/my_setuser.c | 81 ++++++++++++++++++++++++++++++++++++++++++ sql/mysqld.cc | 83 ++++++-------------------------------------- 4 files changed, 98 insertions(+), 74 deletions(-) create mode 100644 mysys/my_setuser.c diff --git a/include/my_sys.h b/include/my_sys.h index 001769a0b76fc..5392a94d27e55 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -602,8 +602,12 @@ extern void *my_memmem(const void *haystack, size_t haystacklen, #ifdef _WIN32 extern int my_access(const char *path, int amode); +#define my_check_user(A,B) (NULL) +#define my_set_user(A,B,C) (0) #else #define my_access access +struct passwd *my_check_user(const char *user, myf MyFlags); +int my_set_user(const char *user, struct passwd *user_info, myf MyFlags); #endif extern int check_if_legal_filename(const char *path); diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 06a811f0994f9..cb86850c2def5 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -34,7 +34,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c rijndael.c sha1.c string.c thr_alarm.c thr_lock.c thr_mutex.c thr_rwlock.c tree.c typelib.c base64.c my_memmem.c my_getpagesize.c lf_alloc-pin.c lf_dynarray.c lf_hash.c - safemalloc.c my_new.cc + safemalloc.c my_new.cc my_atomic.c my_getncpus.c my_safehash.c my_chmod.c my_rnd.c my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c my_rdtsc.c my_context.c file_logger.c) @@ -44,7 +44,7 @@ IF (WIN32) ENDIF() IF(UNIX) - SET (MYSYS_SOURCES ${MYSYS_SOURCES} my_addr_resolve.c) + SET (MYSYS_SOURCES ${MYSYS_SOURCES} my_addr_resolve.c my_setuser.c) ENDIF() IF(HAVE_ALARM) diff --git a/mysys/my_setuser.c b/mysys/my_setuser.c new file mode 100644 index 0000000000000..1f3e7770d4cd2 --- /dev/null +++ b/mysys/my_setuser.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif + +struct passwd *my_check_user(const char *user, myf MyFlags) +{ + struct passwd *user_info; + uid_t user_id= geteuid(); + DBUG_ENTER("my_check_user"); + + // Don't bother if we aren't superuser + if (user_id) + { + if (user) + { + /* Don't give a warning, if real user is same as given with --user */ + user_info= getpwnam(user); + if (!user_info || user_id != user_info->pw_uid) + { + my_errno= EPERM; + if (MyFlags & MY_WME) + my_printf_error(my_errno, "One can only use the --user switch if " + "running as root", MYF(ME_JUST_WARNING|ME_NOREFRESH)); + } + } + DBUG_RETURN(NULL); + } + if (!user) + { + if (MyFlags & MY_FAE) + { + my_errno= EINVAL; + my_printf_error(my_errno, "Please consult the Knowledge Base to find " + "out how to run mysqld as root!", MYF(ME_NOREFRESH)); + } + DBUG_RETURN(NULL); + } + if (!strcmp(user,"root")) + DBUG_RETURN(NULL); + + if (!(user_info= getpwnam(user))) + { + // Allow a numeric uid to be used + int err= 0; + user_id= my_strtoll10(user, NULL, &err); + if (err || !(user_info= getpwuid(user_id))) + { + my_errno= EINVAL; + my_printf_error(my_errno, "Can't change to run as user '%s'. Please " + "check that the user exists!", MYF(ME_NOREFRESH), user); + DBUG_RETURN(NULL); + } + } + DBUG_ASSERT(user_info); + DBUG_RETURN(user_info); +} + +int my_set_user(const char *user, struct passwd *user_info, myf MyFlags) +{ + DBUG_ENTER("my_set_user"); + + DBUG_ASSERT(user_info != 0); +#ifdef HAVE_INITGROUPS + initgroups(user, user_info->pw_gid); +#endif + if (setgid(user_info->pw_gid) == -1 || setuid(user_info->pw_uid) == -1) + { + my_errno= errno; + if (MyFlags & MY_WME) + my_error(my_errno, MYF(ME_NOREFRESH)); + DBUG_RETURN(my_errno); + } + DBUG_RETURN(0); +} diff --git a/sql/mysqld.cc b/sql/mysqld.cc index ea4fa823d29b8..11e9176861d43 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -121,10 +121,7 @@ extern "C" { // Because of SCO 3.2V4.2 #include #endif #ifdef HAVE_PWD_H -#include // For getpwent -#endif -#ifdef HAVE_GRP_H -#include +#include // For struct passwd #endif #include @@ -455,9 +452,7 @@ ulong opt_binlog_rows_event_max_size; my_bool opt_master_verify_checksum= 0; my_bool opt_slave_sql_verify_checksum= 1; const char *binlog_format_names[]= {"MIXED", "STATEMENT", "ROW", NullS}; -#ifdef HAVE_INITGROUPS volatile sig_atomic_t calling_initgroups= 0; /**< Used in SIGSEGV handler. */ -#endif uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options; uint mysqld_extra_port; uint mysqld_port_timeout; @@ -2001,59 +1996,18 @@ static void set_ports() static struct passwd *check_user(const char *user) { -#if !defined(__WIN__) - struct passwd *tmp_user_info; - uid_t user_id= geteuid(); + myf flags= 0; + if (global_system_variables.log_warnings) + flags|= MY_WME; + if (!opt_bootstrap && !opt_help) + flags|= MY_FAE; - // Don't bother if we aren't superuser - if (user_id) - { - if (user) - { - /* Don't give a warning, if real user is same as given with --user */ - /* purecov: begin tested */ - tmp_user_info= getpwnam(user); - if ((!tmp_user_info || user_id != tmp_user_info->pw_uid) && - global_system_variables.log_warnings) - sql_print_warning( - "One can only use the --user switch if running as root\n"); - /* purecov: end */ - } - return NULL; - } - if (!user) - { - if (!opt_bootstrap && !opt_help) - { - sql_print_error("Fatal error: Please consult the Knowledge Base " - "to find out how to run mysqld as root!\n"); - unireg_abort(1); - } - return NULL; - } - /* purecov: begin tested */ - if (!strcmp(user,"root")) - return NULL; // Avoid problem with dynamic libraries + struct passwd *tmp_user_info= my_check_user(user, MYF(flags)); - if (!(tmp_user_info= getpwnam(user))) - { - // Allow a numeric uid to be used - const char *pos; - for (pos= user; my_isdigit(mysqld_charset,*pos); pos++) ; - if (*pos) // Not numeric id - goto err; - if (!(tmp_user_info= getpwuid(atoi(user)))) - goto err; - } + if (!tmp_user_info && my_errno==EINVAL && (flags & MY_FAE)) + unireg_abort(1); return tmp_user_info; - /* purecov: end */ - -err: - sql_print_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); - unireg_abort(1); -#endif - return NULL; } static inline void allow_coredumps() @@ -2070,10 +2024,6 @@ static inline void allow_coredumps() static void set_user(const char *user, struct passwd *user_info_arg) { - /* purecov: begin tested */ -#if !defined(__WIN__) - DBUG_ASSERT(user_info_arg != 0); -#ifdef HAVE_INITGROUPS /* We can get a SIGSEGV when calling initgroups() on some systems when NSS is configured to use LDAP and the server is statically linked. We set @@ -2081,22 +2031,11 @@ static void set_user(const char *user, struct passwd *user_info_arg) output a specific message to help the user resolve this problem. */ calling_initgroups= 1; - initgroups((char*) user, user_info_arg->pw_gid); + int res= my_set_user(user, user_info_arg, MYF(MY_WME)); calling_initgroups= 0; -#endif - if (setgid(user_info_arg->pw_gid) == -1) - { - sql_perror("setgid"); - unireg_abort(1); - } - if (setuid(user_info_arg->pw_uid) == -1) - { - sql_perror("setuid"); + if (res) unireg_abort(1); - } allow_coredumps(); -#endif - /* purecov: end */ } From 8fcdd6b0ecbb966f4479856efe93a963a7a422f7 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 20 Dec 2016 21:16:23 +0100 Subject: [PATCH 51/73] Numerous issues in mysqld_safe --- .gitignore | 1 + .../dist/Debian/mariadb-server-5.5.files.in | 1 + .../dist/Ubuntu/mariadb-server-5.5.files.in | 1 + extra/CMakeLists.txt | 3 + extra/mysqld_safe_helper.c | 77 +++++++++++++ scripts/mysqld_safe.sh | 107 +++++++----------- support-files/mysql.server.sh | 8 +- 7 files changed, 128 insertions(+), 70 deletions(-) create mode 100644 extra/mysqld_safe_helper.c diff --git a/.gitignore b/.gitignore index b780ca88de06c..8b6e416ec452e 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ extra/jemalloc/build/ extra/jemalloc/tmp/ extra/my_print_defaults extra/mysql_waitpid +extra/mysqld_safe_helper extra/perror extra/replace extra/resolve_stack_dump diff --git a/debian/dist/Debian/mariadb-server-5.5.files.in b/debian/dist/Debian/mariadb-server-5.5.files.in index c1ea58740e420..47a9887b07536 100644 --- a/debian/dist/Debian/mariadb-server-5.5.files.in +++ b/debian/dist/Debian/mariadb-server-5.5.files.in @@ -32,6 +32,7 @@ usr/bin/mysql_zap usr/bin/mysqlbinlog usr/bin/mysqld_multi usr/bin/mysqld_safe +usr/bin/mysqld_safe_helper usr/bin/mysqlhotcopy usr/bin/perror usr/bin/replace diff --git a/debian/dist/Ubuntu/mariadb-server-5.5.files.in b/debian/dist/Ubuntu/mariadb-server-5.5.files.in index 7f75ccc230340..5182dd76346f1 100644 --- a/debian/dist/Ubuntu/mariadb-server-5.5.files.in +++ b/debian/dist/Ubuntu/mariadb-server-5.5.files.in @@ -34,6 +34,7 @@ usr/bin/mysql_zap usr/bin/mysqlbinlog usr/bin/mysqld_multi usr/bin/mysqld_safe +usr/bin/mysqld_safe_helper usr/bin/mysqlhotcopy usr/bin/perror usr/bin/replace diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index f8f71b0074369..7f47f878110ec 100644 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -82,4 +82,7 @@ IF(UNIX) MYSQL_ADD_EXECUTABLE(mysql_waitpid mysql_waitpid.c COMPONENT Client) TARGET_LINK_LIBRARIES(mysql_waitpid mysys) + + MYSQL_ADD_EXECUTABLE(mysqld_safe_helper mysqld_safe_helper.c COMPONENT Server) + TARGET_LINK_LIBRARIES(mysqld_safe_helper mysys) ENDIF() diff --git a/extra/mysqld_safe_helper.c b/extra/mysqld_safe_helper.c new file mode 100644 index 0000000000000..09e507c6e1cde --- /dev/null +++ b/extra/mysqld_safe_helper.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#ifdef HAVE_PWD_H +#include +#endif +#include +#include + +void my_exit(int c) +{ + my_end(0); + exit(c); +} + +void do_usage() +{ + printf("Usage:\n" + " %s log \n" + " %s exec \n", + my_progname, my_progname); + my_exit(1); +} + +void do_log(const char *logfile) +{ + FILE *f; + uchar buf[4096]; + int size; + + if (!logfile) + do_usage(); + + f= my_fopen(logfile, O_WRONLY|O_APPEND|O_CREAT, MYF(MY_WME)); + if (!f) + my_exit(1); + + while ((size= my_fread(stdin, buf, sizeof(buf), MYF(MY_WME))) > 0) + if ((int)my_fwrite(f, buf, size, MYF(MY_WME)) != size) + my_exit(1); + + my_fclose(f, MYF(0)); + my_exit(0); +} + +void do_exec(char *args[]) +{ + if (!args[0]) + do_usage(); + + my_end(0); + execvp(args[0], args); +} + +int main(int argc, char *argv[]) +{ + struct passwd *user_info; + MY_INIT(argv[0]); + + if (argc < 3) + do_usage(argv[0]); + + user_info= my_check_user(argv[1], MYF(0)); + if (user_info ? my_set_user(argv[1], user_info, MYF(MY_WME)) + : my_errno == EINVAL) + my_exit(1); + + if (strcmp(argv[2], "log") == 0) + do_log(argv[3]); + + if (strcmp(argv[2], "exec") == 0) + do_exec(argv+3); + + my_end(0); + return 1; +} diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 7cadce725d1fd..059263fad51b9 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -20,6 +20,7 @@ mysqld_ld_preload= mysqld_ld_library_path= flush_caches=0 numa_interleave=0 +unsafe_my_cnf=0 # Initial logging status: error log is not open, and not using syslog logging=init @@ -128,6 +129,18 @@ my_which () return $ret # Success } +find_in_bin() { + if test -x "$MY_BASEDIR_VERSION/bin/$1" + then + echo "$MY_BASEDIR_VERSION/bin/$1" + elif test -x "@bindir@/$1" + then + echo "@bindir@/$1" + else + echo "$1" + fi +} + log_generic () { priority="$1" shift @@ -136,7 +149,7 @@ log_generic () { echo "$msg" case $logging in init) ;; # Just echo the message, don't save it anywhere - file) echo "$msg" >> "$err_log" ;; + file) echo "$msg" | "$helper" "$user" log "$err_log" ;; syslog) logger -t "$syslog_tag_mysqld_safe" -p "$priority" "$*" ;; *) echo "Internal program error (non-fatal):" \ @@ -156,7 +169,7 @@ log_notice () { eval_log_error () { cmd="$1" case $logging in - file) cmd="$cmd >> "`shell_quote_string "$err_log"`" 2>&1" ;; + file) cmd="$cmd 2>&1 | "`shell_quote_string "$helper"`" $user log "`shell_quote_string "$err_log"` ;; syslog) # mysqld often prefixes its messages with a timestamp, which is # redundant when logging to syslog (which adds its own timestamp) @@ -190,6 +203,13 @@ shell_quote_string() { echo "$1" | sed -e 's,\([^a-zA-Z0-9/_.=-]\),\\\1,g' } +check_executable_location() { + if test "$unsafe_my_cnf" = 1 -a "$unrecognized_handling" != collect; then + log_error "Cannot accept $1 from a config file, when my.cnf is in the datadir" + exit 1 + fi +} + parse_arguments() { for arg do # the parameter after "=", or the whole $arg if no match @@ -200,7 +220,6 @@ parse_arguments() { optname_subst=`echo "$optname" | sed 's/_/-/g'` arg=`echo $arg | sed "s/^$optname/$optname_subst/"` case "$arg" in - --crash-script=*) CRASH_SCRIPT="$val" ;; # these get passed explicitly to mysqld --basedir=*) MY_BASEDIR_VERSION="$val" ;; --datadir=*|--data=*) DATADIR="$val" ;; @@ -220,12 +239,14 @@ parse_arguments() { # mysqld_safe-specific options - must be set in my.cnf ([mysqld_safe])! --core-file-size=*) core_file_size="$val" ;; - --ledir=*) ledir="$val" ;; - --malloc-lib=*) set_malloc_lib "$val" ;; - --mysqld=*) MYSQLD="$val" ;; + --ledir=*) check_executable_location "$arg" ; ledir="$val" ;; + --malloc-lib=*) check_executable_location "$arg"; set_malloc_lib "$val" ;; + --crash-script=*) check_executable_location "$arg"; crash_script="$val" ;; + --mysqld=*) check_executable_location "$arg"; MYSQLD="$val" ;; --mysqld-version=*) if test -n "$val" then + check_executable_location "$arg" MYSQLD="mysqld-$val" PLUGIN_VARIANT="/$val" else @@ -385,15 +406,8 @@ set_malloc_lib() { # First, try to find BASEDIR and ledir (where mysqld is) # -if echo '@pkgdatadir@' | grep '^@prefix@' > /dev/null -then - relpkgdata=`echo '@pkgdatadir@' | sed -e 's,^@prefix@,,' -e 's,^/,,' -e 's,^,./,'` -else - # pkgdatadir is not relative to prefix - relpkgdata='@pkgdatadir@' -fi - -MY_PWD=`pwd` +MY_PWD=`dirname $0` +MY_PWD=`cd "$MY_PWD"/.. && pwd` # Check for the directories we would expect from a binary release install if test -n "$MY_BASEDIR_VERSION" -a -d "$MY_BASEDIR_VERSION" then @@ -409,16 +423,16 @@ then else ledir="$MY_BASEDIR_VERSION/bin" fi -elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/bin/mysqld" +elif test -x "$MY_PWD/bin/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where bin, share and data are ledir="$MY_PWD/bin" # Where mysqld is # Check for the directories we would expect from a source install -elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/libexec/mysqld" +elif test -x "$MY_PWD/libexec/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where libexec, share and var are ledir="$MY_PWD/libexec" # Where mysqld is -elif test -f "$relpkgdata"/english/errmsg.sys -a -x "$MY_PWD/sbin/mysqld" +elif test -x "$MY_PWD/sbin/mysqld" then MY_BASEDIR_VERSION="$MY_PWD" # Where sbin, share and var are ledir="$MY_PWD/sbin" # Where mysqld is @@ -428,6 +442,8 @@ else ledir='@libexecdir@' fi +helper=`find_in_bin mysqld_safe_helper` +print_defaults=`find_in_bin my_print_defaults` # # Second, try to find the data directory @@ -465,6 +481,7 @@ IGNORING $DATADIR/my.cnf" log_error "WARNING: Found $DATADIR/my.cnf The data directory is a deprecated location for my.cnf, please move it to $MY_BASEDIR_VERSION/my.cnf" + unsafe_my_cnf=1 MYSQL_HOME=$DATADIR else MYSQL_HOME=$MY_BASEDIR_VERSION @@ -472,34 +489,15 @@ $MY_BASEDIR_VERSION/my.cnf" fi export MYSQL_HOME - -# Get first arguments from the my.cnf file, groups [mysqld] and [mysqld_safe] -# and then merge with the command line arguments -if test -x "$MY_BASEDIR_VERSION/bin/my_print_defaults" -then - print_defaults="$MY_BASEDIR_VERSION/bin/my_print_defaults" -elif test -x `dirname $0`/my_print_defaults -then - print_defaults="`dirname $0`/my_print_defaults" -elif test -x ./bin/my_print_defaults -then - print_defaults="./bin/my_print_defaults" -elif test -x @bindir@/my_print_defaults -then - print_defaults="@bindir@/my_print_defaults" -elif test -x @bindir@/mysql_print_defaults -then - print_defaults="@bindir@/mysql_print_defaults" -else - print_defaults="my_print_defaults" -fi - append_arg_to_args () { args="$args "`shell_quote_string "$1"` } args= +# Get first arguments from the my.cnf file, groups [mysqld] and [mysqld_safe] +# and then merge with the command line arguments + SET_USER=2 parse_arguments `$print_defaults $defaults --loose-verbose --mysqld` if test $SET_USER -eq 2 @@ -603,11 +601,6 @@ then log_notice "Logging to '$err_log'." logging=file - if [ ! -f "$err_log" ]; then # if error log already exists, - touch "$err_log" # we just append. otherwise, - chmod "$fmode" "$err_log" # fix the permissions here! - fi - else if [ -n "$syslog_tag" ] then @@ -620,10 +613,6 @@ else logging=syslog fi -# close stdout and stderr, everything goes to $logging now -exec 1>&- -exec 2>&- - USER_OPTION="" if test -w / -o "$USER" = "root" then @@ -631,11 +620,6 @@ then then USER_OPTION="--user=$user" fi - # Change the err log to the right user, if it is in use - if [ $want_syslog -eq 0 ]; then - touch "$err_log" - chown $user "$err_log" - fi if test -n "$open_files" then ulimit -n $open_files @@ -879,6 +863,10 @@ max_fast_restarts=5 # flag whether a usable sleep command exists have_sleep=1 +# close stdout and stderr, everything goes to $logging now +exec 1>&- +exec 2>&- + while true do rm -f "$pid_file" # Some extra safety @@ -886,13 +874,6 @@ do start_time=`date +%M%S` eval_log_error "$cmd" - - if [ $want_syslog -eq 0 -a ! -f "$err_log" ]; then - touch "$err_log" # hypothetical: log was renamed but not - chown $user "$err_log" # flushed yet. we'd recreate it with - chmod "$fmode" "$err_log" # wrong owner next time we log, so set - fi # it up correctly while we can! - end_time=`date +%M%S` if test ! -f "$pid_file" # This is removed if normal shutdown @@ -956,9 +937,9 @@ do done fi log_notice "mysqld restarted" - if test -n "$CRASH_SCRIPT" + if test -n "$crash_script" then - crash_script_output=`$CRASH_SCRIPT 2>&1` + crash_script_output=`$crash_script 2>&1` log_error "$crash_script_output" fi done diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index d4fff33af13d3..a4034dec385c1 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -157,15 +157,9 @@ parse_server_arguments() { # Get arguments from the my.cnf file, # the only group, which is read from now on is [mysqld] -if test -x ./bin/my_print_defaults -then - print_defaults="./bin/my_print_defaults" -elif test -x $bindir/my_print_defaults +if test -x $bindir/my_print_defaults then print_defaults="$bindir/my_print_defaults" -elif test -x $bindir/mysql_print_defaults -then - print_defaults="$bindir/mysql_print_defaults" else # Try to find basedir in /etc/my.cnf conf=/etc/my.cnf From 48655ce6985490fdf6c5c8be7c75b37f83f7738e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 22 Dec 2016 12:23:48 +0100 Subject: [PATCH 52/73] test case for Bug #23303485 : HANDLE_FATAL_SIGNAL (SIG=11) IN SUBSELECT_UNION_ENGINE::NO_ROWS --- mysql-test/r/subselect2.result | 10 ++++++++++ mysql-test/t/subselect2.test | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/mysql-test/r/subselect2.result b/mysql-test/r/subselect2.result index b6dc940d9fbbb..64bd86707cc28 100644 --- a/mysql-test/r/subselect2.result +++ b/mysql-test/r/subselect2.result @@ -384,3 +384,13 @@ DEALLOCATE PREPARE stmt; DROP VIEW v1; DROP TABLE t1,t2,t3; set optimizer_switch=@subselect2_test_tmp; +create table t1 (a int); +create table t2 (a int); +create table t3(a int); +insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +insert into t2 select a from t1; +insert into t3 select a from t1; +select null in (select a from t1 where a < out3.a union select a from t2 where +(select a from t3) +1 < out3.a+1) from t3 out3; +ERROR 21000: Subquery returns more than 1 row +drop table t1, t2, t3; diff --git a/mysql-test/t/subselect2.test b/mysql-test/t/subselect2.test index f795cef648c85..ae210b865a26c 100644 --- a/mysql-test/t/subselect2.test +++ b/mysql-test/t/subselect2.test @@ -398,3 +398,16 @@ DROP TABLE t1,t2,t3; set optimizer_switch=@subselect2_test_tmp; +# +# Bug #23303485 : HANDLE_FATAL_SIGNAL (SIG=11) IN SUBSELECT_UNION_ENGINE::NO_ROWS +# +create table t1 (a int); +create table t2 (a int); +create table t3(a int); +insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +insert into t2 select a from t1; +insert into t3 select a from t1; +--error ER_SUBQUERY_NO_1_ROW +select null in (select a from t1 where a < out3.a union select a from t2 where + (select a from t3) +1 < out3.a+1) from t3 out3; +drop table t1, t2, t3; From e7d7910b7a926ccc6f5b8d73d55ac511f1c03c3d Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 22 Dec 2016 11:13:07 +0100 Subject: [PATCH 53/73] add an assert and use is_supported_parser_charset() instead of direct check --- sql/sql_connect.cc | 16 +++++++--------- sql/sys_vars.cc | 3 ++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 61f8b4081ebc8..e2e56c48b3b9e 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2007, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2014, SkySQL Ab. + Copyright (c) 2008, 2016, 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 @@ -821,6 +821,7 @@ void update_global_user_stats(THD *thd, bool create_user, time_t now) bool thd_init_client_charset(THD *thd, uint cs_number) { + SV *gv=&global_system_variables; CHARSET_INFO *cs; /* Use server character set and collation if @@ -831,16 +832,13 @@ bool thd_init_client_charset(THD *thd, uint cs_number) */ if (!opt_character_set_client_handshake || !(cs= get_charset(cs_number, MYF(0))) || - !my_strcasecmp(&my_charset_latin1, - global_system_variables.character_set_client->name, + !my_strcasecmp(&my_charset_latin1, gv->character_set_client->name, cs->name)) { - thd->variables.character_set_client= - global_system_variables.character_set_client; - thd->variables.collation_connection= - global_system_variables.collation_connection; - thd->variables.character_set_results= - global_system_variables.character_set_results; + DBUG_ASSERT(is_supported_parser_charset(gv->character_set_client)); + thd->variables.character_set_client= gv->character_set_client; + thd->variables.collation_connection= gv->collation_connection; + thd->variables.character_set_results= gv->character_set_results; } else { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 7b89890618486..359975254560b 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -31,6 +31,7 @@ #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "sql_class.h" // set_var.h: THD +#include "sql_parse.h" #include "sys_vars.h" #include "events.h" @@ -445,7 +446,7 @@ static bool check_cs_client(sys_var *self, THD *thd, set_var *var) return true; // Currently, UCS-2 cannot be used as a client character set - if (((CHARSET_INFO *)(var->save_result.ptr))->mbminlen > 1) + if (!is_supported_parser_charset((CHARSET_INFO *)(var->save_result.ptr))) return true; return false; From ec6d8dadc01269451332e5b24b12a5350a2a4896 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 22 Dec 2016 13:02:32 +0100 Subject: [PATCH 54/73] reduce code duplication a little --- sql/event_db_repository.cc | 15 +++++++-------- sql/sp_head.cc | 6 +----- sql/sql_parse.cc | 23 ++++++++++++++--------- sql/sql_parse.h | 1 + sql/sql_table.cc | 6 +----- sql/sql_udf.cc | 6 +----- sql/sql_yacc.yy | 12 ++---------- 7 files changed, 27 insertions(+), 42 deletions(-) diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 37dff0da714f2..673250ffd222a 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -17,6 +17,7 @@ #include "sql_priv.h" #include "unireg.h" #include "sql_base.h" // close_thread_tables +#include "sql_parse.h" #include "event_db_repository.h" #include "key.h" // key_copy #include "sql_db.h" // get_default_db_collation @@ -702,19 +703,17 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, restore_record(table, s->default_values); // Get default values for fields - if (system_charset_info->cset-> - numchars(system_charset_info, parse_data->dbname.str, - parse_data->dbname.str + parse_data->dbname.length) > - table->field[ET_FIELD_DB]->char_length()) + if (check_string_char_length(&parse_data->dbname, 0, + table->field[ET_FIELD_DB]->char_length(), + system_charset_info, 1)) { my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->dbname.str); goto end; } - if (system_charset_info->cset-> - numchars(system_charset_info, parse_data->name.str, - parse_data->name.str + parse_data->name.length) > - table->field[ET_FIELD_NAME]->char_length()) + if (check_string_char_length(&parse_data->name, 0, + table->field[ET_FIELD_NAME]->char_length(), + system_charset_info, 1)) { my_error(ER_TOO_LONG_IDENT, MYF(0), parse_data->name.str); goto end; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 9bfa60a07d38a..69364eaa43f32 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -488,12 +488,8 @@ check_routine_name(LEX_STRING *ident) my_error(ER_SP_WRONG_NAME, MYF(0), ident->str); return TRUE; } - if (check_string_char_length(ident, "", NAME_CHAR_LEN, - system_charset_info, 1)) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), ident->str); + if (check_ident_length(ident)) return TRUE; - } return FALSE; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a3114aba7d373..f000fe1a37d26 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4329,11 +4329,8 @@ case SQLCOM_PREPARE: } case SQLCOM_SHOW_CREATE_TRIGGER: { - if (lex->spname->m_name.length > NAME_LEN) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); + if (check_ident_length(&lex->spname->m_name)) goto error; - } if (show_create_trigger(thd, lex->spname)) goto error; /* Error has been already logged. */ @@ -6019,12 +6016,9 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, LEX *lex= thd->lex; DBUG_ENTER("add_field_to_list"); - if (check_string_char_length(field_name, "", NAME_CHAR_LEN, - system_charset_info, 1)) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */ + if (check_ident_length(field_name)) DBUG_RETURN(1); /* purecov: inspected */ - } + if (type_modifier & PRI_KEY_FLAG) { Key *key; @@ -7688,6 +7682,17 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg, } +bool check_ident_length(LEX_STRING *ident) +{ + if (check_string_char_length(ident, 0, NAME_CHAR_LEN, system_charset_info, 1)) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), ident->str); + return 1; + } + return 0; +} + + /* Check if path does not contain mysql data home directory diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 4c3070d197d98..66a8f6efc7da1 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -75,6 +75,7 @@ bool check_string_byte_length(LEX_STRING *str, const char *err_msg, bool check_string_char_length(LEX_STRING *str, const char *err_msg, uint max_char_length, CHARSET_INFO *cs, bool no_error); +bool check_ident_length(LEX_STRING *ident); CHARSET_INFO* merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl); bool check_host_name(LEX_STRING *str); bool check_identifier_name(LEX_STRING *str, uint max_char_length, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 9bcb4c3f8ccf0..2cec480d23b08 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3312,12 +3312,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp); DBUG_RETURN(TRUE); } - if (check_string_char_length(&key->name, "", NAME_CHAR_LEN, - system_charset_info, 1)) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), key->name.str); + if (check_ident_length(&key->name)) DBUG_RETURN(TRUE); - } key_iterator2.rewind (); if (key->type != Key::FOREIGN_KEY) { diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 626e5569cccdb..d18498de7847b 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -455,12 +455,8 @@ int mysql_create_function(THD *thd,udf_func *udf) my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0)); DBUG_RETURN(1); } - if (check_string_char_length(&udf->name, "", NAME_CHAR_LEN, - system_charset_info, 1)) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), udf->name.str); + if (check_ident_length(&udf->name)) DBUG_RETURN(1); - } /* Turn off row binlogging of this statement and use statement-based diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7d981e03aea05..35c7203ca0d4e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4670,12 +4670,8 @@ part_name: { partition_info *part_info= Lex->part_info; partition_element *p_elem= part_info->curr_part_elem; - if (check_string_char_length(&$1, "", NAME_CHAR_LEN, - system_charset_info, true)) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), $1.str); + if (check_ident_length(&$1)) MYSQL_YYABORT; - } p_elem->partition_name= $1.str; } ; @@ -4971,12 +4967,8 @@ sub_part_definition: sub_name: ident_or_text { - if (check_string_char_length(&$1, "", NAME_CHAR_LEN, - system_charset_info, true)) - { - my_error(ER_TOO_LONG_IDENT, MYF(0), $1.str); + if (check_ident_length(&$1)) MYSQL_YYABORT; - } Lex->part_info->curr_part_elem->partition_name= $1.str; } ; From e6b563f8be68d57df2a4c9b8e2b6c130855b18e4 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Fri, 23 Dec 2016 16:58:32 +0100 Subject: [PATCH 55/73] Fix some XML table type bugs: - in DOMNODELIST::DropItem if (Listp == NULL || Listp->length <= n) return true; is wrong, should be: if (Listp == NULL || Listp->length < n) return true; - Crash in discovery with libxml2 in XMLColumns because: if (!tdp->Usedom) // nl was destroyed vp->nl = vp->pn->GetChildElements(g); is executed with vp->pn uninitialized. Fixed by adding: vp->pn = node; line 264. -In discovery with libxml2 some columns are not found. Because list was not recovered properly, nodes being modified and not reallocated. Fixed lines 214 and 277. modified: storage/connect/domdoc.cpp modified: storage/connect/tabxml.cpp Add support for zipped table files modified: storage/connect/domdoc.cpp modified: storage/connect/domdoc.h modified: storage/connect/filamap.cpp modified: storage/connect/filamap.h modified: storage/connect/filamzip.cpp modified: storage/connect/filamzip.h modified: storage/connect/ha_connect.cc modified: storage/connect/libdoc.cpp modified: storage/connect/plgdbutl.cpp modified: storage/connect/plgxml.cpp modified: storage/connect/plgxml.h modified: storage/connect/tabdos.cpp modified: storage/connect/tabdos.h modified: storage/connect/tabfmt.cpp modified: storage/connect/tabjson.cpp modified: storage/connect/tabxml.cpp --- storage/connect/domdoc.cpp | 40 ++-- storage/connect/domdoc.h | 4 +- storage/connect/filamap.cpp | 49 ++-- storage/connect/filamap.h | 3 +- storage/connect/filamzip.cpp | 426 ++++++++++++++++++++++------------ storage/connect/filamzip.h | 86 ++++--- storage/connect/ha_connect.cc | 6 +- storage/connect/libdoc.cpp | 30 ++- storage/connect/plgdbutl.cpp | 2 +- storage/connect/plgxml.cpp | 55 ++++- storage/connect/plgxml.h | 16 +- storage/connect/tabdos.cpp | 34 ++- storage/connect/tabdos.h | 13 +- storage/connect/tabfmt.cpp | 11 +- storage/connect/tabjson.cpp | 33 ++- storage/connect/tabxml.cpp | 56 +++-- storage/connect/tabxml.h | 18 +- 17 files changed, 604 insertions(+), 278 deletions(-) diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp index 64a0a172956ce..eb9660b439d12 100644 --- a/storage/connect/domdoc.cpp +++ b/storage/connect/domdoc.cpp @@ -89,30 +89,43 @@ DOMDOC::DOMDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp) /******************************************************************/ /* Initialize XML parser and check library compatibility. */ /******************************************************************/ -bool DOMDOC::Initialize(PGLOBAL g) - { - if (TestHr(g, CoInitialize(NULL))) +bool DOMDOC::Initialize(PGLOBAL g, char *entry, bool zipped) +{ + if (zipped && InitZip(g, entry)) + return true; + + if (TestHr(g, CoInitialize(NULL))) return true; if (TestHr(g, Docp.CreateInstance("msxml2.domdocument"))) return true; return MakeNSlist(g); - } // end of Initialize +} // end of Initialize /******************************************************************/ /* Parse the XML file and construct node tree in memory. */ /******************************************************************/ -bool DOMDOC::ParseFile(char *fn) - { - // Load the document +bool DOMDOC::ParseFile(PGLOBAL g, char *fn) +{ + bool b; + Docp->async = false; - if (!(bool)Docp->load((_bstr_t)fn)) + if (zip) { + // Parse an in memory document + char *xdoc = GetMemDoc(g, fn); + + b = (xdoc) ? (bool)Docp->loadXML((_bstr_t)xdoc) : false; + } else + // Load the document + b = (bool)Docp->load((_bstr_t)fn); + + if (!b) return true; return false; - } // end of ParseFile +} // end of ParseFile /******************************************************************/ /* Create or reuse an Xblock for this document. */ @@ -239,6 +252,7 @@ int DOMDOC::DumpDoc(PGLOBAL g, char *ofn) void DOMDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) { CloseXMLFile(g, xp, false); + CloseZip(); } // end of Close /* ----------------------- class DOMNODE ------------------------ */ @@ -616,13 +630,13 @@ PXNODE DOMNODELIST::GetItem(PGLOBAL g, int n, PXNODE np) /* Reset the pointer on the deleted item. */ /******************************************************************/ bool DOMNODELIST::DropItem(PGLOBAL g, int n) - { - if (Listp == NULL || Listp->length <= n) - return true; +{ + if (Listp == NULL || Listp->length < n) + return true; //Listp->item[n] = NULL; La propriété n'a pas de méthode 'set' return false; - } // end of DeleteItem +} // end of DeleteItem /* ----------------------- class DOMATTR ------------------------ */ diff --git a/storage/connect/domdoc.h b/storage/connect/domdoc.h index 2cffec499e286..cfec98a94221a 100644 --- a/storage/connect/domdoc.h +++ b/storage/connect/domdoc.h @@ -37,8 +37,8 @@ class DOMDOC : public XMLDOCUMENT { virtual void SetNofree(bool b) {} // Only libxml2 // Methods - virtual bool Initialize(PGLOBAL g); - virtual bool ParseFile(char *fn); + virtual bool Initialize(PGLOBAL g, char *entry, bool zipped); + virtual bool ParseFile(PGLOBAL g, char *fn); virtual bool NewDoc(PGLOBAL g, char *ver); virtual void AddComment(PGLOBAL g, char *com); virtual PXNODE GetRoot(PGLOBAL g); diff --git a/storage/connect/filamap.cpp b/storage/connect/filamap.cpp index 3c5b3ae7592fe..94c562a998132 100644 --- a/storage/connect/filamap.cpp +++ b/storage/connect/filamap.cpp @@ -319,11 +319,13 @@ int MAPFAM::SkipRecord(PGLOBAL g, bool header) /***********************************************************************/ int MAPFAM::ReadBuffer(PGLOBAL g) { - int len; + int rc, len; // Are we at the end of the memory - if (Mempos >= Top) - return RC_EF; + if (Mempos >= Top) + if ((rc = GetNext(g)) != RC_OK) + return rc; + if (!Placed) { /*******************************************************************/ @@ -341,8 +343,10 @@ int MAPFAM::ReadBuffer(PGLOBAL g) /*******************************************************************/ switch (Tdbp->TestBlock(g)) { case RC_EF: - return RC_EF; - case RC_NF: + if ((rc = GetNext(g)) != RC_OK) + return rc; + + case RC_NF: // Skip this record if ((rc = SkipRecord(g, false)) != RC_OK) return rc; @@ -569,7 +573,7 @@ int MBKFAM::GetRowID(void) /***********************************************************************/ int MBKFAM::ReadBuffer(PGLOBAL g) { - int len; + int rc, len; /*********************************************************************/ /* Sequential block reading when Placed is not true. */ @@ -577,8 +581,10 @@ int MBKFAM::ReadBuffer(PGLOBAL g) if (Placed) { Placed = false; } else if (Mempos >= Top) { // Are we at the end of the memory - return RC_EF; - } else if (++CurNum < Nrec) { + if ((rc = GetNext(g)) != RC_OK) + return rc; + + } else if (++CurNum < Nrec) { Fpos = Mempos; } else { /*******************************************************************/ @@ -588,7 +594,8 @@ int MBKFAM::ReadBuffer(PGLOBAL g) next: if (++CurBlk >= Block) - return RC_EF; + if ((rc = GetNext(g)) != RC_OK) + return rc; /*******************************************************************/ /* Before reading a new block, check whether block optimization */ @@ -596,8 +603,11 @@ int MBKFAM::ReadBuffer(PGLOBAL g) /*******************************************************************/ switch (Tdbp->TestBlock(g)) { case RC_EF: - return RC_EF; - case RC_NF: + if ((rc = GetNext(g)) != RC_OK) + return rc; + + break; + case RC_NF: goto next; } // endswitch rc @@ -697,14 +707,18 @@ int MPXFAM::InitDelete(PGLOBAL, int fpos, int) /***********************************************************************/ int MPXFAM::ReadBuffer(PGLOBAL g) { + int rc; + /*********************************************************************/ /* Sequential block reading when Placed is not true. */ /*********************************************************************/ if (Placed) { Placed = false; } else if (Mempos >= Top) { // Are we at the end of the memory - return RC_EF; - } else if (++CurNum < Nrec) { + if ((rc = GetNext(g)) != RC_OK) + return rc; + + } else if (++CurNum < Nrec) { Fpos = Mempos; } else { /*******************************************************************/ @@ -714,7 +728,7 @@ int MPXFAM::ReadBuffer(PGLOBAL g) next: if (++CurBlk >= Block) - return RC_EF; + return GetNext(g); /*******************************************************************/ /* Before reading a new block, check whether block optimization */ @@ -722,8 +736,11 @@ int MPXFAM::ReadBuffer(PGLOBAL g) /*******************************************************************/ switch (Tdbp->TestBlock(g)) { case RC_EF: - return RC_EF; - case RC_NF: + if ((rc = GetNext(g)) != RC_OK) + return rc; + + break; + case RC_NF: goto next; } // endswitch rc diff --git a/storage/connect/filamap.h b/storage/connect/filamap.h index b9c8ad965fd47..774eb8b91b300 100644 --- a/storage/connect/filamap.h +++ b/storage/connect/filamap.h @@ -41,7 +41,8 @@ class DllExport MAPFAM : public TXTFAM { virtual int SkipRecord(PGLOBAL g, bool header); virtual bool OpenTableFile(PGLOBAL g); virtual bool DeferReading(void) {return false;} - virtual int ReadBuffer(PGLOBAL g); + virtual int GetNext(PGLOBAL g) {return RC_EF;} + virtual int ReadBuffer(PGLOBAL g); virtual int WriteBuffer(PGLOBAL g); virtual int DeleteRecords(PGLOBAL g, int irc); virtual void CloseTableFile(PGLOBAL g, bool abort); diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp index 8386e5be48120..65013e170e4e1 100644 --- a/storage/connect/filamzip.cpp +++ b/storage/connect/filamzip.cpp @@ -40,19 +40,21 @@ //#include "tabzip.h" #include "filamzip.h" -/* -------------------------- class ZIPFAM --------------------------- */ +/* -------------------------- class ZIPUTIL -------------------------- */ /***********************************************************************/ /* Constructors. */ /***********************************************************************/ -ZIPFAM::ZIPFAM(PDOSDEF tdp) : MAPFAM(tdp) +ZIPUTIL::ZIPUTIL(PSZ tgt, bool mul) { zipfile = NULL; -//zfn = tdp->Fn; - target = tdp->Entry; -//*fn = 0; + target = tgt; + fp = NULL; + memory = NULL; + size = 0; entryopen = false; - multiple = (target && !(strchr(target, '*') || strchr(target, '?'))) ? 0 : 1; + multiple = mul; + memset(fn, 0, sizeof(fn)); // Init the case mapping table. #if defined(__WIN__) @@ -60,29 +62,30 @@ ZIPFAM::ZIPFAM(PDOSDEF tdp) : MAPFAM(tdp) #else for (int i = 0; i < 256; ++i) mapCaseTable[i] = i; #endif -} // end of ZIPFAM standard constructor +} // end of ZIPUTIL standard constructor -ZIPFAM::ZIPFAM(PZIPFAM txfp) : MAPFAM(txfp) +#if 0 +ZIPUTIL::ZIPUTIL(PZIPUTIL zutp) { - zipfile = txfp->zipfile; -//zfn = txfp->zfn; - target = txfp->target; -//strcpy(fn, txfp->fn); - finfo = txfp->finfo; - entryopen = txfp->entryopen; - multiple = txfp->multiple; - for (int i = 0; i < 256; ++i) mapCaseTable[i] = txfp->mapCaseTable[i]; -} // end of ZIPFAM copy constructor + zipfile = zutp->zipfile; + target = zutp->target; + fp = zutp->fp; + finfo = zutp->finfo; + entryopen = zutp->entryopen; + multiple = zutp->multiple; + for (int i = 0; i < 256; ++i) mapCaseTable[i] = zutp->mapCaseTable[i]; +} // end of ZIPUTIL copy constructor +#endif // 0 /***********************************************************************/ /* This code is the copyright property of Alessandro Felice Cantatore. */ /* http://xoomer.virgilio.it/acantato/dev/wildcard/wildmatch.html */ /***********************************************************************/ -bool ZIPFAM::WildMatch(PSZ pat, PSZ str) { +bool ZIPUTIL::WildMatch(PSZ pat, PSZ str) { PSZ s, p; bool star = FALSE; - loopStart: +loopStart: for (s = str, p = pat; *s; ++s, ++p) { switch (*p) { case '?': @@ -102,31 +105,18 @@ bool ZIPFAM::WildMatch(PSZ pat, PSZ str) { if (*p == '*') ++p; return (!*p); - starCheck: +starCheck: if (!star) return FALSE; str++; goto loopStart; } // end of WildMatch -/***********************************************************************/ -/* ZIP GetFileLength: returns file size in number of bytes. */ -/***********************************************************************/ -int ZIPFAM::GetFileLength(PGLOBAL g) -{ - int len = (entryopen) ? Top - Memory : 100; // not 0 to avoid ASSERT - - if (trace) - htrc("Zipped file length=%d\n", len); - - return len; -} // end of GetFileLength - /***********************************************************************/ /* open a zip file. */ /* param: filename path and the filename of the zip file to open. */ /* return: true if open, false otherwise. */ /***********************************************************************/ -bool ZIPFAM::open(PGLOBAL g, const char *filename) +bool ZIPUTIL::open(PGLOBAL g, char *filename) { if (!zipfile && !(zipfile = unzOpen64(filename))) sprintf(g->Message, "Zipfile open error on %s", filename); @@ -137,7 +127,7 @@ bool ZIPFAM::open(PGLOBAL g, const char *filename) /***********************************************************************/ /* Close the zip file. */ /***********************************************************************/ -void ZIPFAM::close() +void ZIPUTIL::close() { if (zipfile) { closeEntry(); @@ -150,10 +140,9 @@ void ZIPFAM::close() /***********************************************************************/ /* Find next entry matching target pattern. */ /***********************************************************************/ -int ZIPFAM::findEntry(PGLOBAL g, bool next) +int ZIPUTIL::findEntry(PGLOBAL g, bool next) { int rc; - char fn[FILENAME_MAX]; // The current entry file name do { if (next) { @@ -188,37 +177,53 @@ int ZIPFAM::findEntry(PGLOBAL g, bool next) strcpy(g->Message, "FindNext logical error"); return RC_FX; -} // end of FindNext +} // end of FindEntry + /***********************************************************************/ -/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ +/* Get the next used entry. */ /***********************************************************************/ -bool ZIPFAM::OpenTableFile(PGLOBAL g) +int ZIPUTIL::nextEntry(PGLOBAL g) { - char filename[_MAX_PATH]; - MODE mode = Tdbp->GetMode(); - PFBLOCK fp; - PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + if (multiple) { + int rc; + + closeEntry(); + + if ((rc = findEntry(g, true)) != RC_OK) + return rc; + + if (openEntry(g)) + return RC_FX; + return RC_OK; + } else + return RC_EF; + +} // end of nextEntry + + +/***********************************************************************/ +/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ +/***********************************************************************/ +bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn) +{ /*********************************************************************/ /* The file will be decompressed into virtual memory. */ /*********************************************************************/ - if (mode == MODE_READ) { - // We used the file name relative to recorded datapath - PlugSetPath(filename, To_File, Tdbp->GetPath()); - - bool b = open(g, filename); + if (mode == MODE_READ || mode == MODE_ANY) { + bool b = open(g, fn); if (!b) { int rc; - + if (target && *target) { if (!multiple) { rc = unzLocateFile(zipfile, target, 0); if (rc == UNZ_END_OF_LIST_OF_FILE) { - sprintf(g->Message, "Target file %s not in %s", target, filename); - return false; + sprintf(g->Message, "Target file %s not in %s", target, fn); + return true; } else if (rc != UNZ_OK) { sprintf(g->Message, "unzLocateFile rc=%d", rc); return true; @@ -227,9 +232,9 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) } else { if ((rc = findEntry(g, false)) == RC_FX) return true; - else if (rc == RC_NF) { - sprintf(g->Message, "No match of %s in %s", target, filename); - return false; + else if (rc == RC_EF) { + sprintf(g->Message, "No match of %s in %s", target, fn); + return true; } // endif rc } // endif multiple @@ -239,25 +244,26 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) if (openEntry(g)) return true; - if (Top > Memory) { + if (size > 0) { /*******************************************************************/ /* 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, filename); + fp->Fname = PlugDup(g, fn); fp->Next = dbuserp->Openlist; dbuserp->Openlist = fp; fp->Count = 1; - fp->Length = Top - Memory; - fp->Memory = Memory; + fp->Length = size; + fp->Memory = memory; fp->Mode = mode; fp->File = this; fp->Handle = NULL; } // endif fp - To_Fb = fp; // Useful when closing } else return true; @@ -267,65 +273,161 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) } // endif mode return false; - } // end of OpenTableFile +} // end of OpenTableFile /***********************************************************************/ /* Open target in zip file. */ /***********************************************************************/ -bool ZIPFAM::openEntry(PGLOBAL g) +bool ZIPUTIL::openEntry(PGLOBAL g) { - int rc; - uint size; + int rc; - rc = unzGetCurrentFileInfo(zipfile, &finfo, 0, 0, 0, 0, 0, 0); + rc = unzGetCurrentFileInfo(zipfile, &finfo, fn, sizeof(fn), + NULL, 0, NULL, 0); if (rc != UNZ_OK) { sprintf(g->Message, "unzGetCurrentFileInfo64 rc=%d", rc); return true; } else if ((rc = unzOpenCurrentFile(zipfile)) != UNZ_OK) { - sprintf(g->Message, "unzOpenCurrentFile rc=%d", rc); + sprintf(g->Message, "unzOpen fn=%s rc=%d", fn, rc); return true; } // endif rc size = finfo.uncompressed_size; - Memory = new char[size]; + memory = new char[size + 1]; - if ((rc = unzReadCurrentFile(zipfile, Memory, size)) < 0) { + if ((rc = unzReadCurrentFile(zipfile, memory, size)) < 0) { sprintf(g->Message, "unzReadCurrentFile rc = ", rc); unzCloseCurrentFile(zipfile); - free(Memory); + free(memory); + memory = NULL; entryopen = false; } else { - // The pseudo "buffer" is here the entire real buffer - Fpos = Mempos = Memory; - Top = Memory + size; - - if (trace) - htrc("Memory=%p size=%ud Top=%p\n", Memory, size, Top); - + memory[size] = 0; // Required by some table types (XML) entryopen = true; } // endif rc + if (trace) + htrc("Openning entry%s %s\n", fn, (entryopen) ? "oked" : "failed"); + return !entryopen; } // end of openEntry /***********************************************************************/ /* Close the zip file. */ /***********************************************************************/ -void ZIPFAM::closeEntry() +void ZIPUTIL::closeEntry() { if (entryopen) { unzCloseCurrentFile(zipfile); entryopen = false; } // endif entryopen - if (Memory) { - free(Memory); - Memory = NULL; - } // endif Memory + if (memory) { + free(memory); + memory = NULL; + } // endif memory } // end of closeEntry +/* -------------------------- class ZIPFAM --------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +ZIPFAM::ZIPFAM(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 + +ZIPFAM::ZIPFAM(PDOSDEF tdp, PZPXFAM txfp) : MAPFAM(tdp) +{ + zutp = txfp->zutp; + target = txfp->target; + mul = txfp->mul; +} // end of ZIPFAM constructor used in ResetTableOpt + +/***********************************************************************/ +/* ZIP GetFileLength: returns file size in number of bytes. */ +/***********************************************************************/ +int ZIPFAM::GetFileLength(PGLOBAL g) +{ + int len = (zutp && zutp->entryopen) ? Top - Memory + : TXTFAM::GetFileLength(g) * 3; + + if (trace) + htrc("Zipped file length=%d\n", len); + + return len; +} // end of GetFileLength + +/***********************************************************************/ +/* ZIP Cardinality: return the number of rows if possible. */ +/***********************************************************************/ +int ZIPFAM::Cardinality(PGLOBAL g) +{ + if (!g) + return 1; + + int card = -1; + int len = GetFileLength(g); + + card = (len / (int)Lrecl) * 2; // Estimated ??? + return card; +} // end of Cardinality + +/***********************************************************************/ +/* 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, mul); + + // We used the file name relative to recorded datapath + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!zutp->OpenTable(g, mode, filename)) { + // The pseudo "buffer" is here the entire real buffer + Fpos = Mempos = Memory = zutp->memory; + Top = Memory + zutp->size; + To_Fb = zutp->fp; // Useful when closing + } else + return true; + + return false; + } // end of OpenTableFile + +/***********************************************************************/ +/* GetNext: go to next entry. */ +/***********************************************************************/ +int ZIPFAM::GetNext(PGLOBAL g) +{ + int rc = zutp->nextEntry(g); + + if (rc != RC_OK) + return rc; + + Mempos = Memory = zutp->memory; + Top = Memory + zutp->size; + return RC_OK; +} // end of GetNext + +#if 0 /***********************************************************************/ /* ReadBuffer: Read one line for a ZIP file. */ /***********************************************************************/ @@ -335,19 +437,12 @@ int ZIPFAM::ReadBuffer(PGLOBAL g) // Are we at the end of the memory if (Mempos >= Top) { - if (multiple) { - closeEntry(); - - if ((rc = findEntry(g, true)) != RC_OK) - return rc; - - if (openEntry(g)) - return RC_FX; + if ((rc = zutp->nextEntry(g)) != RC_OK) + return rc; - } else - return RC_EF; - - } // endif Mempos + Mempos = Memory = zutp->memory; + Top = Memory + zutp->size; + } // endif Mempos #if 0 if (!Placed) { @@ -399,7 +494,6 @@ int ZIPFAM::ReadBuffer(PGLOBAL g) return RC_OK; } // end of ReadBuffer -#if 0 /***********************************************************************/ /* Table file close routine for MAP access method. */ /***********************************************************************/ @@ -414,89 +508,115 @@ void ZIPFAM::CloseTableFile(PGLOBAL g, bool) /***********************************************************************/ /* Constructors. */ /***********************************************************************/ -ZPXFAM::ZPXFAM(PDOSDEF tdp) : ZIPFAM(tdp) +ZPXFAM::ZPXFAM(PDOSDEF tdp) : MPXFAM(tdp) { - Lrecl = tdp->GetLrecl(); + zutp = NULL; + target = tdp->GetEntry(); + mul = tdp->GetMul(); + //Lrecl = tdp->GetLrecl(); } // end of ZPXFAM standard constructor -ZPXFAM::ZPXFAM(PZPXFAM txfp) : ZIPFAM(txfp) +ZPXFAM::ZPXFAM(PZPXFAM txfp) : MPXFAM(txfp) { - Lrecl = txfp->Lrecl; + zutp = txfp->zutp; + target = txfp->target; + mul = txfp->mul; +//Lrecl = txfp->Lrecl; } // end of ZPXFAM copy constructor /***********************************************************************/ -/* ReadBuffer: Read one line for a fixed ZIP file. */ +/* ZIP GetFileLength: returns file size in number of bytes. */ /***********************************************************************/ -int ZPXFAM::ReadBuffer(PGLOBAL g) +int ZPXFAM::GetFileLength(PGLOBAL g) { - int rc, len; + int len; - // Are we at the end of the memory - if (Mempos >= Top) { - if (multiple) { - closeEntry(); + if (!zutp && OpenTableFile(g)) + return 0; - if ((rc = findEntry(g, true)) != RC_OK) - return rc; + if (zutp->entryopen) + len = zutp->size; + else + len = 0; - if (openEntry(g)) - return RC_FX; + return len; +} // end of GetFileLength - } else - return RC_EF; +/***********************************************************************/ +/* ZIP Cardinality: return the number of rows if possible. */ +/***********************************************************************/ +int ZPXFAM::Cardinality(PGLOBAL g) +{ + if (!g) + return 1; - } // endif Mempos + int card = -1; + int len = GetFileLength(g); -#if 0 - if (!Placed) { - /*******************************************************************/ - /* Record file position in case of UPDATE or DELETE. */ - /*******************************************************************/ - int rc; + if (!(len % Lrecl)) + card = len / (int)Lrecl; // Fixed length file + else + sprintf(g->Message, MSG(NOT_FIXED_LEN), zutp->fn, len, Lrecl); - next: - Fpos = Mempos; - CurBlk = (int)Rows++; + // Set number of blocks for later use + Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0; + return card; +} // end of Cardinality - /*******************************************************************/ - /* Check whether optimization on ROWID */ - /* can be done, as well as for join as for local filtering. */ - /*******************************************************************/ - switch (Tdbp->TestBlock(g)) { - case RC_EF: - return RC_EF; - case RC_NF: - // Skip this record - if ((rc = SkipRecord(g, false)) != RC_OK) - return rc; +/***********************************************************************/ +/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ +/***********************************************************************/ +bool ZPXFAM::OpenTableFile(PGLOBAL g) +{ + // May have been already opened in GetFileLength + if (!zutp || !zutp->zipfile) { + char filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); - goto next; - } // endswitch rc + /*********************************************************************/ + /* Allocate the ZIP utility class. */ + /*********************************************************************/ + if (!zutp) + zutp = new(g)ZIPUTIL(target, mul); + + // We used the file name relative to recorded datapath + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!zutp->OpenTable(g, mode, filename)) { + // The pseudo "buffer" is here the entire real buffer + Memory = zutp->memory; + Fpos = Mempos = Memory + Headlen; + Top = Memory + zutp->size; + To_Fb = zutp->fp; // Useful when closing + } else + return true; } else - Placed = false; -#else - // Perhaps unuseful - Fpos = Mempos; - CurBlk = (int)Rows++; - Placed = false; -#endif + Reset(); - // Immediately calculate next position (Used by DeleteDB) - Mempos += Lrecl; + return false; +} // end of OpenTableFile - // Set caller line buffer - len = Lrecl; +/***********************************************************************/ +/* GetNext: go to next entry. */ +/***********************************************************************/ +int ZPXFAM::GetNext(PGLOBAL g) +{ + int rc = zutp->nextEntry(g); - // Don't rely on ENDING setting - if (len > 0 && *(Mempos - 1) == '\n') - len--; // Line ends by LF + if (rc != RC_OK) + return rc; - if (len > 0 && *(Mempos - 2) == '\r') - len--; // Line ends by CRLF + int len = zutp->size; - memcpy(Tdbp->GetLine(), Fpos, len); - Tdbp->GetLine()[len] = '\0'; + if (len % Lrecl) { + sprintf(g->Message, MSG(NOT_FIXED_LEN), zutp->fn, len, Lrecl); + return RC_FX; + } // endif size + + Memory = zutp->memory; + Top = Memory + len; + Rewind(); return RC_OK; -} // end of ReadBuffer +} // end of GetNext diff --git a/storage/connect/filamzip.h b/storage/connect/filamzip.h index c3c04b2b3bb16..9312fb2f70eb1 100644 --- a/storage/connect/filamzip.h +++ b/storage/connect/filamzip.h @@ -17,67 +17,101 @@ typedef class ZIPFAM *PZIPFAM; typedef class ZPXFAM *PZPXFAM; +/***********************************************************************/ +/* This is the ZIP utility fonctions class. */ +/***********************************************************************/ +class DllExport ZIPUTIL : public BLOCK { +public: + // Constructor + ZIPUTIL(PSZ tgt, bool mul); +//ZIPUTIL(ZIPUTIL *zutp); + + // Implementation +//PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)ZIPFAM(this); } + + // Methods + virtual bool OpenTable(PGLOBAL g, MODE mode, char *fn); + bool open(PGLOBAL g, char *fn); + bool openEntry(PGLOBAL g); + void close(void); + void closeEntry(void); + bool WildMatch(PSZ pat, PSZ str); + int findEntry(PGLOBAL g, bool next); + int nextEntry(PGLOBAL g); + + // Members + unzFile 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 ZIPFAM + /***********************************************************************/ /* This is the ZIP file access method. */ /***********************************************************************/ class DllExport ZIPFAM : public MAPFAM { + friend class ZPXFAM; public: - // Constructor + // Constructors ZIPFAM(PDOSDEF tdp); ZIPFAM(PZIPFAM txfp); + ZIPFAM(PDOSDEF tdp, PZPXFAM 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)ZIPFAM(this); } // Methods + virtual int Cardinality(PGLOBAL g); virtual int GetFileLength(PGLOBAL g); - virtual int Cardinality(PGLOBAL g) {return (g) ? 10 : 1;} //virtual int MaxBlkSize(PGLOBAL g, int s) {return s;} virtual bool OpenTableFile(PGLOBAL g); - virtual bool DeferReading(void) {return false;} - virtual int ReadBuffer(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); - void close(void); protected: - bool open(PGLOBAL g, const char *filename); - bool openEntry(PGLOBAL g); - void closeEntry(void); - bool WildMatch(PSZ pat, PSZ str); - int findEntry(PGLOBAL g, bool next); - // Members - unzFile zipfile; // The ZIP container file -//PSZ zfn; // The ZIP file name - PSZ target; // The target file name - unz_file_info finfo; // The current file info -//char fn[FILENAME_MAX]; // The current file name - bool entryopen; // True when open current entry - int multiple; // Multiple targets - char mapCaseTable[256]; + ZIPUTIL *zutp; + PSZ target; + bool mul; }; // end of ZIPFAM /***********************************************************************/ /* This is the fixed ZIP file access method. */ /***********************************************************************/ -class DllExport ZPXFAM : public ZIPFAM { +class DllExport ZPXFAM : public MPXFAM { + friend class ZIPFAM; public: - // Constructor + // Constructors ZPXFAM(PDOSDEF tdp); ZPXFAM(PZPXFAM txfp); // Implementation - virtual PTXF Duplicate(PGLOBAL g) {return (PTXF) new(g) ZPXFAM(this);} + virtual AMT GetAmType(void) { return TYPE_AM_ZIP; } + virtual PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)ZPXFAM(this); } // Methods - virtual int ReadBuffer(PGLOBAL g); + 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); protected: // Members - int Lrecl; + ZIPUTIL *zutp; + PSZ target; + bool mul; }; // end of ZPXFAM #endif // __FILAMZIP_H diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 45ca546ad4e84..6590902bcd49b 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -1242,8 +1242,10 @@ char *ha_connect::GetStringOption(char *opname, char *sdef) if (opval && (!stricmp(opname, "connect") || !stricmp(opname, "tabname") - || !stricmp(opname, "filename"))) - opval = GetRealString(opval); + || !stricmp(opname, "filename") + || !stricmp(opname, "optname") + || !stricmp(opname, "entry"))) + opval = GetRealString(opval); if (!opval) { if (sdef && !strcmp(sdef, "*")) { diff --git a/storage/connect/libdoc.cpp b/storage/connect/libdoc.cpp index c2882fc0d7caa..2470d37c35358 100644 --- a/storage/connect/libdoc.cpp +++ b/storage/connect/libdoc.cpp @@ -1,6 +1,6 @@ /******************************************************************/ /* Implementation of XML document processing using libxml2 */ -/* Author: Olivier Bertrand 2007-2015 */ +/* Author: Olivier Bertrand 2007-2016 */ /******************************************************************/ #include "my_global.h" #include @@ -68,8 +68,8 @@ class LIBXMLDOC : public XMLDOCUMENT { virtual void SetNofree(bool b) {Nofreelist = b;} // Methods - virtual bool Initialize(PGLOBAL g); - virtual bool ParseFile(char *fn); + virtual bool Initialize(PGLOBAL g, char *entry, bool zipped); + virtual bool ParseFile(PGLOBAL g, char *fn); virtual bool NewDoc(PGLOBAL g, char *ver); virtual void AddComment(PGLOBAL g, char *com); virtual PXNODE GetRoot(PGLOBAL g); @@ -373,22 +373,33 @@ LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp) /******************************************************************/ /* Initialize XML parser and check library compatibility. */ /******************************************************************/ -bool LIBXMLDOC::Initialize(PGLOBAL g) - { +bool LIBXMLDOC::Initialize(PGLOBAL g, char *entry, bool zipped) +{ + if (zipped && InitZip(g, entry)) + return true; + int n = xmlKeepBlanksDefault(1); return MakeNSlist(g); - } // end of Initialize +} // end of Initialize /******************************************************************/ /* Parse the XML file and construct node tree in memory. */ /******************************************************************/ -bool LIBXMLDOC::ParseFile(char *fn) +bool LIBXMLDOC::ParseFile(PGLOBAL g, char *fn) { if (trace) htrc("ParseFile\n"); - if ((Docp = xmlParseFile(fn))) { - if (Docp->encoding) + if (zip) { + // Parse an in memory document + char *xdoc = GetMemDoc(g, fn); + + Docp = (xdoc) ? xmlParseDoc((const xmlChar *)xdoc) : NULL; + } else + Docp = xmlParseFile(fn); + + if (Docp) { + if (Docp->encoding) Encoding = (char*)Docp->encoding; return false; @@ -609,6 +620,7 @@ void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) } // endif xp CloseXML2File(g, xp, false); + CloseZip(); } // end of Close /******************************************************************/ diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp index 31c040c6957ea..83975c6d8fa24 100644 --- a/storage/connect/plgdbutl.cpp +++ b/storage/connect/plgdbutl.cpp @@ -939,7 +939,7 @@ int PlugCloseFile(PGLOBAL g __attribute__((unused)), PFBLOCK fp, bool all) #endif // LIBXML2_SUPPORT #ifdef ZIP_SUPPORT case TYPE_FB_ZIP: - ((PZIPFAM)fp->File)->close(); + ((ZIPUTIL*)fp->File)->close(); fp->Memory = NULL; fp->Mode = MODE_ANY; fp->Count = 0; diff --git a/storage/connect/plgxml.cpp b/storage/connect/plgxml.cpp index 3061a6d697e54..71b72621b0671 100644 --- a/storage/connect/plgxml.cpp +++ b/storage/connect/plgxml.cpp @@ -30,19 +30,51 @@ PXDOC GetLibxmlDoc(PGLOBAL g, char *nsl, char *nsdf, /* XMLDOCUMENT constructor. */ /******************************************************************/ XMLDOCUMENT::XMLDOCUMENT(char *nsl, char *nsdf, char *enc) - { - Namespaces = NULL; +{ +#if defined(ZIP_SUPPORT) + zip = NULL; +#else // !ZIP_SUPPORT + zip = false; +#endif // !ZIP_SUPPORT + Namespaces = NULL; Encoding = enc; Nslist = nsl; DefNs = nsdf; - } // end of XMLDOCUMENT constructor +} // end of XMLDOCUMENT constructor + +/******************************************************************/ +/* Initialize zipped file processing. */ +/******************************************************************/ +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); + return zip == NULL; +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return true; +#endif // !ZIP_SUPPORT +} // end of InitZip + +/******************************************************************/ +/* Make the namespace structure list. */ +/******************************************************************/ +char* XMLDOCUMENT::GetMemDoc(PGLOBAL g, char *fn) +{ +#if defined(ZIP_SUPPORT) + return (zip->OpenTable(g, MODE_ANY, fn)) ? NULL : zip->memory; +#else // !ZIP_SUPPORT + return NULL; +#endif // !ZIP_SUPPORT +} // end of GetMemDoc /******************************************************************/ /* Make the namespace structure list. */ /******************************************************************/ bool XMLDOCUMENT::MakeNSlist(PGLOBAL g) - { - char *prefix, *href, *next = Nslist; +{ + char *prefix, *href, *next = Nslist; PNS nsp, *ppns = &Namespaces; while (next) { @@ -83,6 +115,19 @@ bool XMLDOCUMENT::MakeNSlist(PGLOBAL g) return false; } // end of MakeNSlist +/******************************************************************/ +/* Close ZIP file. */ +/******************************************************************/ +void XMLDOCUMENT::CloseZip(void) +{ +#if defined(ZIP_SUPPORT) + if (zip) { + zip->close(); + zip = NULL; + } // endif zip +#endif // ZIP_SUPPORT +} // end of CloseZip + /******************************************************************/ /* XMLNODE constructor. */ /******************************************************************/ diff --git a/storage/connect/plgxml.h b/storage/connect/plgxml.h index b8e914e0bf10d..db7dfa6bda55d 100644 --- a/storage/connect/plgxml.h +++ b/storage/connect/plgxml.h @@ -1,3 +1,7 @@ +#if defined(ZIP_SUPPORT) +#include "filamzip.h" +#endif // ZIP_SUPPORT + /******************************************************************/ /* Dual XML implementation base classes defines. */ /******************************************************************/ @@ -72,8 +76,8 @@ class XMLDOCUMENT : public BLOCK { virtual void SetNofree(bool b) = 0; // Methods - virtual bool Initialize(PGLOBAL) = 0; - virtual bool ParseFile(char *) = 0; + virtual bool Initialize(PGLOBAL, char *, bool) = 0; + virtual bool ParseFile(PGLOBAL, char *) = 0; virtual bool NewDoc(PGLOBAL, char *) = 0; virtual void AddComment(PGLOBAL, char *) = 0; virtual PXNODE GetRoot(PGLOBAL) = 0; @@ -91,8 +95,16 @@ class XMLDOCUMENT : public BLOCK { // Utility bool MakeNSlist(PGLOBAL g); + bool InitZip(PGLOBAL g, char *entry); + char *GetMemDoc(PGLOBAL g, char *fn); + void CloseZip(void); // Members +#if defined(ZIP_SUPPORT) + ZIPUTIL *zip; /* Used for zipped file */ +#else // !ZIP_SUPPORT + bool zip; /* Always false */ +#endif // !ZIP_SUPPORT PNS Namespaces; /* To the namespaces */ char *Encoding; /* The document encoding */ char *Nslist; /* Namespace list */ diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp index f47e66b014b79..16cc6c33b4404 100644 --- a/storage/connect/tabdos.cpp +++ b/storage/connect/tabdos.cpp @@ -101,6 +101,7 @@ DOSDEF::DOSDEF(void) Recfm = RECFM_VAR; Mapped = false; Zipped = false; + Mulentries = false; Padded = false; Huge = false; Accept = false; @@ -131,12 +132,13 @@ bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int) : (am && (*am == 'B' || *am == 'b')) ? "B" : (am && !stricmp(am, "DBF")) ? "D" : "V"; - if (*dfm != 'D') - Zipped = GetBoolCatInfo("Zipped", false); + if ((Zipped = GetBoolCatInfo("Zipped", false))) + Mulentries = ((Entry = GetStringCatInfo(g, "Entry", NULL))) + ? strchr(Entry, '*') || strchr(Entry, '?') + : GetBoolCatInfo("Mulentries", false); Desc = Fn = GetStringCatInfo(g, "Filename", NULL); Ofn = GetStringCatInfo(g, "Optname", Fn); - Entry = GetStringCatInfo(g, "Entry", NULL); GetCharCatInfo("Recfm", (PSZ)dfm, buf, sizeof(buf)); Recfm = (toupper(*buf) == 'F') ? RECFM_FIX : (toupper(*buf) == 'B') ? RECFM_BIN : @@ -344,14 +346,16 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) /*********************************************************************/ if (Zipped) { #if defined(ZIP_SUPPORT) - if (Recfm == RECFM_VAR) - txfp = new(g) ZIPFAM(this); - else - txfp = new(g) ZPXFAM(this); + if (Recfm == RECFM_VAR) { + txfp = new(g)ZIPFAM(this); + tdbp = new(g)TDBDOS(this, txfp); + } else { + txfp = new(g)ZPXFAM(this); + tdbp = new(g)TDBFIX(this, txfp); + } // endif Recfm - tdbp = new(g) TDBDOS(this, txfp); #else // !ZIP_SUPPORT - strcpy(g->Message, "ZIP not supported"); + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); return NULL; #endif // !ZIP_SUPPORT } else if (Recfm == RECFM_DBF) { @@ -559,7 +563,7 @@ int TDBDOS::ResetTableOpt(PGLOBAL g, bool dop, bool dox) Txfp->Reset(); ((PZLBFAM)Txfp)->SetOptimized(false); #endif // GZ_SUPPORT - } else if (Txfp->GetAmType() == TYPE_AM_BLK) + } else if (Txfp->GetAmType() == TYPE_AM_BLK) Txfp = new(g) DOSFAM((PDOSDEF)To_Def); Txfp->SetTdbp(this); @@ -630,7 +634,12 @@ int TDBDOS::MakeBlockValues(PGLOBAL g) defp->SetOptimized(0); // Estimate the number of needed blocks - block = (int)((MaxSize + (int)nrec - 1) / (int)nrec); + if ((block = (int)((MaxSize + (int)nrec - 1) / (int)nrec)) < 2) { + // This may be wrong to do in some cases + defp->RemoveOptValues(g); + strcpy(g->Message, MSG(TABLE_NOT_OPT)); + return RC_INFO; // Not to be optimized + } // endif block // We have to use local variables because Txfp->CurBlk is set // to Rows+1 by unblocked variable length table access methods. @@ -973,13 +982,14 @@ bool TDBDOS::GetBlockValues(PGLOBAL g) PCOLDEF cdp; PDOSDEF defp = (PDOSDEF)To_Def; PCATLG cat = defp->GetCat(); + PDBUSER dup = PlgGetUser(g); #if 0 if (Mode == MODE_INSERT && Txfp->GetAmType() == TYPE_AM_DOS) return false; #endif // __WIN__ - if (defp->Optimized) + if (defp->Optimized || !(dup->Check & CHK_OPT)) return false; // Already done or to be redone if (Ftype == RECFM_VAR || defp->Compressed == 2) { diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h index 623adcfed0d6d..4c8eb438a265c 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 ZIPFAM; + friend class ZIPUTIL; public: // Constructor DOSDEF(void); @@ -41,7 +41,9 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ virtual bool IsHuge(void) {return Huge;} PSZ GetFn(void) {return Fn;} PSZ GetOfn(void) {return Ofn;} - void SetBlock(int block) {Block = block;} + PSZ GetEntry(void) {return Entry;} + bool GetMul(void) {return Mulentries;} + void SetBlock(int block) {Block = block;} int GetBlock(void) {return Block;} int GetLast(void) {return Last;} void SetLast(int last) {Last = last;} @@ -58,9 +60,9 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ int *GetTo_Pos(void) {return To_Pos;} // Methods - virtual int Indexable(void) - {return (!Multiple && !Zipped && Compressed != 1) ? 1 : 0;} - virtual bool DeleteIndexFile(PGLOBAL g, PIXDEF pxdf); + virtual int Indexable(void) + {return (!Multiple && !Mulentries && Compressed != 1) ? 1 : 0;} + virtual bool DeleteIndexFile(PGLOBAL g, PIXDEF pxdf); virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); virtual PTDB GetTable(PGLOBAL g, MODE mode); bool InvalidateIndex(PGLOBAL g); @@ -78,6 +80,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ RECFM Recfm; /* 0:VAR, 1:FIX, 2:BIN, 3:VCT, 6:DBF */ bool Mapped; /* 0: disk file, 1: memory mapped file */ bool Zipped; /* true for zipped table file */ + bool Mulentries; /* true for multiple entries */ bool Padded; /* true for padded table file */ bool Huge; /* true for files larger than 2GB */ bool Accept; /* true if wrong lines are accepted */ diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index 2c4d605e66c79..b24375443f611 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -177,9 +177,14 @@ 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) - tdbp = new(g) TDBCSV(tdp, new(g) ZIPFAM(tdp)); - else + 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)); tdbp->SetMode(MODE_READ); diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp index eff95445a3a22..1b9ce8b64c973 100644 --- a/storage/connect/tabjson.cpp +++ b/storage/connect/tabjson.cpp @@ -127,9 +127,14 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) tdp->Fn, tdp->Objname, tdp->Pretty, lvl); if (tdp->Pretty == 2) { - if (tdp->Zipped) - tjsp = new(g) TDBJSON(tdp, new(g) ZIPFAM(tdp)); - else + if (tdp->Zipped) { +#if defined(ZIP_SUPPORT) + tjsp = new(g) TDBJSON(tdp, new(g) ZIPFAM(tdp)); +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif // !ZIP_SUPPORT + } else tjsp = new(g) TDBJSON(tdp, new(g) MAPFAM(tdp)); if (tjsp->MakeDocument(g)) @@ -144,9 +149,14 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) tdp->Ending = GetIntegerTableOption(g, topt, "Ending", CRLF); - if (tdp->Zipped) - tjnp = new(g) TDBJSN(tdp, new(g) ZIPFAM(tdp)); - else + if (tdp->Zipped) { +#if defined(ZIP_SUPPORT) + tjnp = new(g)TDBJSN(tdp, new(g)ZIPFAM(tdp)); +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif // !ZIP_SUPPORT + } else tjnp = new(g) TDBJSN(tdp, new(g) DOSFAM(tdp)); tjnp->SetMode(MODE_READ); @@ -467,9 +477,14 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) ((TDBJSN*)tdbp)->G = g; #endif } else { - if (Zipped) - txfp = new(g) ZIPFAM(this); - else + if (Zipped) { +#if defined(ZIP_SUPPORT) + txfp = new(g)ZIPFAM(this); +#else // !ZIP_SUPPORT + sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); + return NULL; +#endif // !ZIP_SUPPORT + } else txfp = new(g) MAPFAM(this); tdbp = new(g) TDBJSON(this, txfp); diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp index 1993b07eb7a0a..3b8229fcf51aa 100644 --- a/storage/connect/tabxml.cpp +++ b/storage/connect/tabxml.cpp @@ -1,9 +1,9 @@ /************* Tabxml C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABXML */ /* ------------- */ -/* Version 2.8 */ +/* Version 2.9 */ /* */ -/* Author Olivier BERTRAND 2007 - 2015 */ +/* Author Olivier BERTRAND 2007 - 2016 */ /* */ /* This program are the XML tables classes using MS-DOM or libxml2. */ /***********************************************************************/ @@ -159,6 +159,8 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info) tdp->Fn = fn; tdp->Database = SetPath(g, db); tdp->Tabname = tab; + tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false); + tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL); if (!(op = GetStringTableOption(g, topt, "Xmlsup", NULL))) #if defined(__WIN__) @@ -209,7 +211,8 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info) while (true) { if (!vp->atp && - !(node = (vp->nl) ? vp->nl->GetItem(g, vp->k++, node) : NULL)) + !(node = (vp->nl) ? vp->nl->GetItem(g, vp->k++, tdp->Usedom ? node : NULL) + : NULL)) if (j) { vp = lvlp[--j]; @@ -259,7 +262,8 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info) if (j < lvl && ok) { vp = lvlp[j+1]; vp->k = 0; - vp->atp = node->GetAttribute(g, NULL); + vp->pn = node; + vp->atp = node->GetAttribute(g, NULL); vp->nl = node->GetChildElements(g); if (tdp->Usedom && vp->nl->GetLength() == 1) { @@ -270,7 +274,7 @@ PQRYRES XMLColumns(PGLOBAL g, char *db, char *tab, PTOS topt, bool info) if (vp->atp || vp->b) { if (!vp->atp) - node = vp->nl->GetItem(g, vp->k++, node); + node = vp->nl->GetItem(g, vp->k++, tdp->Usedom ? node : NULL); strncat(fmt, colname, XLEN(fmt)); strncat(fmt, "/", XLEN(fmt)); @@ -429,11 +433,14 @@ XMLDEF::XMLDEF(void) DefNs = NULL; Attrib = NULL; Hdattr = NULL; + Entry = NULL; Coltype = 1; Limit = 0; Header = 0; Xpand = false; Usedom = false; + Zipped = false; + Mulentries = false; } // end of XMLDEF constructor /***********************************************************************/ @@ -512,7 +519,14 @@ bool XMLDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) // Get eventual table node attribute Attrib = GetStringCatInfo(g, "Attribute", NULL); Hdattr = GetStringCatInfo(g, "HeadAttr", NULL); - return false; + + // Specific for zipped files + if ((Zipped = GetBoolCatInfo("Zipped", false))) + Mulentries = ((Entry = GetStringCatInfo(g, "Entry", NULL))) + ? strchr(Entry, '*') || strchr(Entry, '?') + : GetBoolCatInfo("Mulentries", false); + + return false; } // end of DefineAM /***********************************************************************/ @@ -552,6 +566,7 @@ TDBXML::TDBXML(PXMLDEF tdp) : TDBASE(tdp) Xfile = tdp->Fn; Enc = tdp->Encoding; Tabname = tdp->Tabname; +#if 0 // why all these? Rowname = (tdp->Rowname) ? tdp->Rowname : NULL; Colname = (tdp->Colname) ? tdp->Colname : NULL; Mulnode = (tdp->Mulnode) ? tdp->Mulnode : NULL; @@ -560,10 +575,22 @@ TDBXML::TDBXML(PXMLDEF tdp) : TDBASE(tdp) DefNs = (tdp->DefNs) ? tdp->DefNs : NULL; Attrib = (tdp->Attrib) ? tdp->Attrib : NULL; Hdattr = (tdp->Hdattr) ? tdp->Hdattr : NULL; - Coltype = tdp->Coltype; +#endif // 0 + Rowname = tdp->Rowname; + Colname = tdp->Colname; + Mulnode = tdp->Mulnode; + XmlDB = tdp->XmlDB; + Nslist = tdp->Nslist; + DefNs = tdp->DefNs; + Attrib = tdp->Attrib; + Hdattr = tdp->Hdattr; + Entry = tdp->Entry; + Coltype = tdp->Coltype; Limit = tdp->Limit; Xpand = tdp->Xpand; - Changed = false; + Zipped = tdp->Zipped; + Mulentries = tdp->Mulentries; + Changed = false; Checked = false; NextSame = false; NewRow = false; @@ -605,10 +632,13 @@ TDBXML::TDBXML(PTDBXML tdbp) : TDBASE(tdbp) DefNs = tdbp->DefNs; Attrib = tdbp->Attrib; Hdattr = tdbp->Hdattr; - Coltype = tdbp->Coltype; + Entry = tdbp->Entry; + Coltype = tdbp->Coltype; Limit = tdbp->Limit; Xpand = tdbp->Xpand; - Changed = tdbp->Changed; + Zipped = tdbp->Zipped; + Mulentries = tdbp->Mulentries; + Changed = tdbp->Changed; Checked = tdbp->Checked; NextSame = tdbp->NextSame; NewRow = tdbp->NewRow; @@ -686,7 +716,7 @@ int TDBXML::LoadTableFile(PGLOBAL g, char *filename) /*********************************************************************/ /* Firstly we check whether this file have been already loaded. */ /*********************************************************************/ - if (Mode == MODE_READ || Mode == MODE_ANY) + if ((Mode == MODE_READ || Mode == MODE_ANY) && !Zipped) for (fp = dup->Openlist; fp; fp = fp->Next) if (fp->Type == type && fp->Length && fp->Count) if (!stricmp(fp->Fname, filename)) @@ -708,7 +738,7 @@ int TDBXML::LoadTableFile(PGLOBAL g, char *filename) return RC_FX; // Initialize the implementation - if (Docp->Initialize(g)) { + if (Docp->Initialize(g, Entry, Zipped)) { sprintf(g->Message, MSG(INIT_FAILED), (Usedom) ? "DOM" : "libxml2"); return RC_FX; } // endif init @@ -717,7 +747,7 @@ int TDBXML::LoadTableFile(PGLOBAL g, char *filename) htrc("TDBXML: parsing %s rc=%d\n", filename, rc); // Parse the XML file - if (Docp->ParseFile(filename)) { + if (Docp->ParseFile(g, filename)) { // Does the file exist? int h= global_open(g, MSGID_NONE, filename, _O_RDONLY); diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h index 7ba3166881df3..6c586d79dec3d 100644 --- a/storage/connect/tabxml.h +++ b/storage/connect/tabxml.h @@ -1,7 +1,7 @@ /*************** Tabxml H Declares Source Code File (.H) ***************/ -/* Name: TABXML.H Version 1.6 */ +/* Name: TABXML.H Version 1.7 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2007-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2007-2016 */ /* */ /* This file contains the XML table classes declares. */ /***********************************************************************/ @@ -42,12 +42,15 @@ class DllExport XMLDEF : public TABDEF { /* Logical table description */ char *DefNs; /* Dummy name of default namespace */ char *Attrib; /* Table node attributes */ char *Hdattr; /* Header node attributes */ - int Coltype; /* Default column type */ + char *Entry; /* Zip entry name or pattern */ + int Coltype; /* Default column type */ int Limit; /* Limit of multiple values */ int Header; /* n first rows are header rows */ bool Xpand; /* Put multiple tags in several rows */ bool Usedom; /* True: DOM, False: libxml2 */ - }; // end of XMLDEF + bool Zipped; /* True: Zipped XML file(s) */ + bool Mulentries; /* True: multiple entries in zip file*/ +}; // end of XMLDEF #if defined(INCLUDE_TDBXML) /***********************************************************************/ @@ -122,7 +125,9 @@ class DllExport TDBXML : public TDBASE { bool Bufdone; // True when column buffers allocated bool Nodedone; // True when column nodes allocated bool Void; // True if the file does not exist - char *Xfile; // The XML file + bool Zipped; // True if Zipped XML file(s) + bool Mulentries; // True if multiple entries in zip file + char *Xfile; // The XML file char *Enc; // New XML table file encoding char *Tabname; // Name of Table node char *Rowname; // Name of first level nodes @@ -133,7 +138,8 @@ class DllExport TDBXML : public TDBASE { char *DefNs; // Dummy name of default namespace char *Attrib; // Table node attribut(s) char *Hdattr; // Header node attribut(s) - int Coltype; // Default column type + char *Entry; // Zip entry name or pattern + int Coltype; // Default column type int Limit; // Limit of multiple values int Header; // n first rows are header rows int Multiple; // If multiple files From 2718225b2626eeb8df9ab71547a81e43104d52ab Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Sat, 24 Dec 2016 09:47:55 -0500 Subject: [PATCH 56/73] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4f1ecb3a1972b..bc0b19220b64e 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=54 +MYSQL_VERSION_PATCH=55 MYSQL_VERSION_EXTRA= From 43147681503299ccdf31e23e84dd39aeff52b2df Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Sun, 25 Dec 2016 12:32:05 +0100 Subject: [PATCH 57/73] Modified version number --- storage/connect/ha_connect.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 6590902bcd49b..33f02d8338c18 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -6955,7 +6955,7 @@ maria_declare_plugin(connect) 0x0104, /* version number (1.04) */ NULL, /* status variables */ connect_system_variables, /* system variables */ - "1.04.0008", /* string version */ + "1.05.0001", /* string version */ MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */ } maria_declare_plugin_end; From 0912fbbce179cba38bd41de797ba5934e63dbaad Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Wed, 4 Jan 2017 03:33:39 +0200 Subject: [PATCH 58/73] MDEV-11719 main.subselect_no_exists_to_in failed in buildbot main.log_slow might leave mysql.slow_log table non-empty, and tests which later use it might fail. Make sure that the table is properly truncated --- mysql-test/r/log_slow.result | 2 +- mysql-test/t/log_slow.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/log_slow.result b/mysql-test/r/log_slow.result index 89846dc698c3c..b396de8a3d857 100644 --- a/mysql-test/r/log_slow.result +++ b/mysql-test/r/log_slow.result @@ -67,9 +67,9 @@ sleep(0.5) select count(*) FROM mysql.slow_log; count(*) 1 -truncate mysql.slow_log; set @@long_query_time=default; set global slow_query_log= @org_slow_query_log; set @@log_slow_filter=default; set @@log_slow_verbosity=default; set global log_output= default; +truncate mysql.slow_log; diff --git a/mysql-test/t/log_slow.test b/mysql-test/t/log_slow.test index 8d5a09d7a94a0..56e35bd5a207a 100644 --- a/mysql-test/t/log_slow.test +++ b/mysql-test/t/log_slow.test @@ -50,7 +50,6 @@ set global slow_query_log=1; set global log_output='TABLE'; select sleep(0.5); select count(*) FROM mysql.slow_log; -truncate mysql.slow_log; # Reset used variables set @@long_query_time=default; @@ -58,3 +57,4 @@ set global slow_query_log= @org_slow_query_log; set @@log_slow_filter=default; set @@log_slow_verbosity=default; set global log_output= default; +truncate mysql.slow_log; From e5d7fc967ede53407a65bfde3faec3181e35f19f Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Wed, 4 Jan 2017 13:03:30 +0200 Subject: [PATCH 59/73] MDEV-10100 main.pool_of_threads fails sporadically in buildbot Backport the fix to 5.5, because it fails there too The patch fixes two test failures: - on slow builders, sometimes a connection attempt which should fail due to the exceeded number of thread_pool_max_threads actually succeeds; - on even slow builders, MTR sometimes cannot establish the initial connection, and check-testcase fails prior to the test start The problem with check-testcase was caused by connect-timeout=2 which was set for all clients in the test config file. On slow builders it might be not enough. There is no way to override it for the pre-test check, so it needed to be substantially increased or removed. The other problem was caused by a race condition between sleeps that the test performs in existing connections and the connect timeout for the connection attempt which was expected to fail. If sleeps finished before the connect-timeout was exceeded, it would allow the connection to succeed. To solve each problem without making the other one worse, connect-timeout should be configured dynamically during the test. Due to the nature of the test (all connections must be busy at the moment when we need to change the timeout, and cannot execute SET GLOBAL ...), it needs to be done independently from the server. The solution: - recognize 'connect_timeout' as a connection option in mysqltest's "connect" command; - remove connect-timeout from the test configuration file; - use the new connect_timeout option for those connections which are expected to fail; - re-arrange the test flow to allow running a huge SLEEP without affecting the test execution time (because it would be interrupted after the main test flow is finished). The test is still subject to false negatives, e.g. if the connection fails due to timeout rather than due to the exceeded number of allowed threads, or if the connection on extra port succeeds due to a race condition and not because the special logic for the extra port. But those false negatives have always been possible there on slow builders, they should not be critical because faster builders should catch such failures if they appear. Conflicts: client/mysqltest.cc mysql-test/r/pool_of_threads.result mysql-test/t/pool_of_threads.test --- client/mysqltest.cc | 8 +++++ mysql-test/r/pool_of_threads.result | 21 +++++++------ mysql-test/t/pool_of_threads.cnf | 3 -- mysql-test/t/pool_of_threads.test | 46 ++++++++++++++++------------- 4 files changed, 44 insertions(+), 34 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 5daa0e72270a1..d9fa9bc0bef24 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -5900,6 +5900,7 @@ void do_connect(struct st_command *command) my_bool con_ssl= 0, con_compress= 0; my_bool con_pipe= 0; my_bool con_shm __attribute__ ((unused))= 0; + int connect_timeout= 0; struct st_connection* con_slot; static DYNAMIC_STRING ds_connection_name; @@ -5996,6 +5997,9 @@ void do_connect(struct st_command *command) con_pipe= 1; else if (length == 3 && !strncmp(con_options, "SHM", 3)) con_shm= 1; + else if (strncasecmp(con_options, "connect_timeout=", + sizeof("connect_timeout=")-1) == 0) + connect_timeout= atoi(con_options + sizeof("connect_timeout=")-1); else die("Illegal option to connect: %.*s", (int) (end - con_options), con_options); @@ -6066,6 +6070,10 @@ void do_connect(struct st_command *command) if (opt_protocol) mysql_options(con_slot->mysql, MYSQL_OPT_PROTOCOL, (char*) &opt_protocol); + if (connect_timeout) + mysql_options(con_slot->mysql, MYSQL_OPT_CONNECT_TIMEOUT, + (char*)&connect_timeout); + #ifdef HAVE_SMEM if (con_shm) { diff --git a/mysql-test/r/pool_of_threads.result b/mysql-test/r/pool_of_threads.result index 7acb45121d6d0..f153ebc48f45c 100644 --- a/mysql-test/r/pool_of_threads.result +++ b/mysql-test/r/pool_of_threads.result @@ -2157,23 +2157,22 @@ Warnings: Warning 1052 Column 'kundentyp' in group statement is ambiguous drop table t1; SET optimizer_switch=@save_optimizer_switch; -SELECT sleep(5); -SELECT sleep(5); +SELECT sleep(50); +SELECT sleep(50); # -- Success: more than --thread_pool_max_threads normal connections not possible -sleep(5) -0 -sleep(5) -0 -SELECT sleep(5); -SELECT sleep(5); SELECT 'Connection on extra port ok'; Connection on extra port ok Connection on extra port ok +SELECT sleep(5.5); SELECT 'Connection on extra port 2 ok'; Connection on extra port 2 ok Connection on extra port 2 ok # -- Success: more than --extra-max-connections + 1 normal connections not possible -sleep(5) -0 -sleep(5) +KILL QUERY ; +KILL QUERY ; +sleep(50) +1 +sleep(50) +1 +sleep(5.5) 0 diff --git a/mysql-test/t/pool_of_threads.cnf b/mysql-test/t/pool_of_threads.cnf index c03e1da64502e..f6651c878deb9 100644 --- a/mysql-test/t/pool_of_threads.cnf +++ b/mysql-test/t/pool_of_threads.cnf @@ -7,8 +7,5 @@ loose-thread_pool_max_threads= 2 extra-port= @ENV.MASTER_EXTRA_PORT extra-max-connections=1 -[client] -connect-timeout= 2 - [ENV] MASTER_EXTRA_PORT= @OPT.port diff --git a/mysql-test/t/pool_of_threads.test b/mysql-test/t/pool_of_threads.test index 4600128ff43d7..f13a096985cf4 100644 --- a/mysql-test/t/pool_of_threads.test +++ b/mysql-test/t/pool_of_threads.test @@ -15,20 +15,26 @@ SET optimizer_switch=@save_optimizer_switch; # connections on the extra port. # First set two connections running, and check that extra connection -# on normal port fails due to--thread-pool-max_threads=2 +# on normal port fails due to --thread-pool-max-threads=2. +# We can afford using a really long sleep, because we won't wait +# till it ends, we'll interrupt it as soon as we don't need it anymore + connection default; -send SELECT sleep(5); +--let $con1_id= `SELECT CONNECTION_ID()` + +send SELECT sleep(50); --sleep 1 connect(con2,localhost,root,,); -connection con2; -send SELECT sleep(5); +--let $con2_id= `SELECT CONNECTION_ID()` + +send SELECT sleep(50); --sleep 0.5 --disable_abort_on_error --disable_result_log --disable_query_log -connect(con3,localhost,root,,); +connect(con3,localhost,root,,,,,connect_timeout=2); --enable_query_log --enable_result_log --enable_abort_on_error @@ -42,24 +48,15 @@ if ($error) --echo # -- Success: more than --thread_pool_max_threads normal connections not possible } -connection default; ---reap -connection con2; ---reap - -# Now try again, but this time use the extra port to successfully connect. - -connection default; -send SELECT sleep(5); - -connection con2; -send SELECT sleep(5); ---sleep 1 - connect(extracon,127.0.0.1,root,,test,$MASTER_EXTRA_PORT,); connection extracon; SELECT 'Connection on extra port ok'; +# Here, sleep just for slightly longer than 5 sec to trigger MDEV-4566 +# (abort in interruptible wait connection check). +send SELECT sleep(5.5); + + connect(extracon2,127.0.0.1,root,,test,$MASTER_EXTRA_PORT,); connection extracon2; SELECT 'Connection on extra port 2 ok'; @@ -67,7 +64,7 @@ SELECT 'Connection on extra port 2 ok'; --disable_abort_on_error --disable_result_log --disable_query_log -connect(extracon3,127.0.0.1,root,,test,$MASTER_EXTRA_PORT,); +connect(extracon3,127.0.0.1,root,,test,$MASTER_EXTRA_PORT,,connect_timeout=2); --enable_query_log --enable_result_log --enable_abort_on_error @@ -81,7 +78,16 @@ if ($error) --echo # -- Success: more than --extra-max-connections + 1 normal connections not possible } +connection extracon2; +--replace_result $con1_id +eval KILL QUERY $con1_id; +--replace_result $con2_id +eval KILL QUERY $con2_id; + connection default; --reap connection con2; --reap + +connection extracon; +--reap From f4d12c1d3fd79db8d30a7de3de3b851d2fa0a397 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 4 Jan 2017 13:36:55 +0100 Subject: [PATCH 60/73] MDEV-11676 Starting service with mysqld_safe_helper fails in SELINUX "enforcing" mode correct the error message in case of setuid/setgid failures --- mysys/my_setuser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mysys/my_setuser.c b/mysys/my_setuser.c index 1f3e7770d4cd2..14ab04dd10f5b 100644 --- a/mysys/my_setuser.c +++ b/mysys/my_setuser.c @@ -74,7 +74,8 @@ int my_set_user(const char *user, struct passwd *user_info, myf MyFlags) { my_errno= errno; if (MyFlags & MY_WME) - my_error(my_errno, MYF(ME_NOREFRESH)); + my_printf_error(errno, "Cannot change uid/gid (errno: %d)", MYF(ME_NOREFRESH), + errno); DBUG_RETURN(my_errno); } DBUG_RETURN(0); From f1ee011a6cbda1069a6ec9b5e2428451a64861fd Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Wed, 4 Jan 2017 23:05:22 +0200 Subject: [PATCH 61/73] MDEV-11722 main.join_cache fails in buildbot on very slow builders The guilty part of the test checks for performance degradation on a query with numerous joins on an empty table. The test expects the query to take less than 1 second, and fails if it is not so (which can happen on very slow builders). The solution is to add more JOINs to the query. On a fixed server, it should not have any noticeable impact on the query execution, while on the unfixed version the query would take several times longer (e.g. 6.5 sec vs 1.5 sec). Thus, we can increase the margin for the error, and make the test fail when the query takes longer than 5 seconds. --- mysql-test/r/join_cache.result | 16 ++++++++++++---- mysql-test/t/join_cache.test | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result index 8a2bdcec8a21a..cc64393f97543 100644 --- a/mysql-test/r/join_cache.result +++ b/mysql-test/r/join_cache.result @@ -5702,11 +5702,13 @@ LEFT JOIN t2 c1 ON c1.parent_id = t.id AND c1.col2 = "val" LEFT JOIN t2 c23 ON c23.parent_id = t.id AND c23.col2 = "val" LEFT JOIN t2 c24 ON c24.parent_id = t.id AND c24.col2 = "val" LEFT JOIN t2 c25 ON c25.parent_id = t.id AND c25.col2 = "val" + LEFT JOIN t2 c26 ON c26.parent_id = t.id AND c26.col2 = "val" + LEFT JOIN t2 c27 ON c27.parent_id = t.id AND c27.col2 = "val" ORDER BY col1; id col1 -select timestampdiff(second, @init_time, now()) <= 1; -timestampdiff(second, @init_time, now()) <= 1 +select timestampdiff(second, @init_time, now()) <= 5; +timestampdiff(second, @init_time, now()) <= 5 1 set join_cache_level=2; set @init_time:=now(); @@ -5738,11 +5740,13 @@ LEFT JOIN t2 c1 ON c1.parent_id = t.id AND c1.col2 = "val" LEFT JOIN t2 c23 ON c23.parent_id = t.id AND c23.col2 = "val" LEFT JOIN t2 c24 ON c24.parent_id = t.id AND c24.col2 = "val" LEFT JOIN t2 c25 ON c25.parent_id = t.id AND c25.col2 = "val" + LEFT JOIN t2 c26 ON c26.parent_id = t.id AND c26.col2 = "val" + LEFT JOIN t2 c27 ON c27.parent_id = t.id AND c27.col2 = "val" ORDER BY col1; id col1 -select timestampdiff(second, @init_time, now()) <= 1; -timestampdiff(second, @init_time, now()) <= 1 +select timestampdiff(second, @init_time, now()) <= 5; +timestampdiff(second, @init_time, now()) <= 5 1 EXPLAIN SELECT t.* @@ -5773,6 +5777,8 @@ LEFT JOIN t2 c1 ON c1.parent_id = t.id AND c1.col2 = "val" LEFT JOIN t2 c23 ON c23.parent_id = t.id AND c23.col2 = "val" LEFT JOIN t2 c24 ON c24.parent_id = t.id AND c24.col2 = "val" LEFT JOIN t2 c25 ON c25.parent_id = t.id AND c25.col2 = "val" + LEFT JOIN t2 c26 ON c26.parent_id = t.id AND c26.col2 = "val" + LEFT JOIN t2 c27 ON c27.parent_id = t.id AND c27.col2 = "val" ORDER BY col1; id select_type table type possible_keys key key_len ref rows Extra @@ -5802,6 +5808,8 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE c23 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (incremental, BNL join) 1 SIMPLE c24 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (incremental, BNL join) 1 SIMPLE c25 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE c26 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (incremental, BNL join) +1 SIMPLE c27 ALL NULL NULL NULL NULL 1 Using where; Using join buffer (incremental, BNL join) set join_buffer_size=default; set join_cache_level = default; DROP TABLE t1,t2; diff --git a/mysql-test/t/join_cache.test b/mysql-test/t/join_cache.test index 1d22de8679947..77e8fce0d2776 100644 --- a/mysql-test/t/join_cache.test +++ b/mysql-test/t/join_cache.test @@ -3709,9 +3709,11 @@ FROM LEFT JOIN t2 c23 ON c23.parent_id = t.id AND c23.col2 = "val" LEFT JOIN t2 c24 ON c24.parent_id = t.id AND c24.col2 = "val" LEFT JOIN t2 c25 ON c25.parent_id = t.id AND c25.col2 = "val" + LEFT JOIN t2 c26 ON c26.parent_id = t.id AND c26.col2 = "val" + LEFT JOIN t2 c27 ON c27.parent_id = t.id AND c27.col2 = "val" ORDER BY col1; -select timestampdiff(second, @init_time, now()) <= 1; +select timestampdiff(second, @init_time, now()) <= 5; set join_cache_level=2; @@ -3744,9 +3746,11 @@ FROM LEFT JOIN t2 c23 ON c23.parent_id = t.id AND c23.col2 = "val" LEFT JOIN t2 c24 ON c24.parent_id = t.id AND c24.col2 = "val" LEFT JOIN t2 c25 ON c25.parent_id = t.id AND c25.col2 = "val" + LEFT JOIN t2 c26 ON c26.parent_id = t.id AND c26.col2 = "val" + LEFT JOIN t2 c27 ON c27.parent_id = t.id AND c27.col2 = "val" ORDER BY col1; -select timestampdiff(second, @init_time, now()) <= 1; +select timestampdiff(second, @init_time, now()) <= 5; EXPLAIN SELECT t.* @@ -3777,6 +3781,8 @@ FROM LEFT JOIN t2 c23 ON c23.parent_id = t.id AND c23.col2 = "val" LEFT JOIN t2 c24 ON c24.parent_id = t.id AND c24.col2 = "val" LEFT JOIN t2 c25 ON c25.parent_id = t.id AND c25.col2 = "val" + LEFT JOIN t2 c26 ON c26.parent_id = t.id AND c26.col2 = "val" + LEFT JOIN t2 c27 ON c27.parent_id = t.id AND c27.col2 = "val" ORDER BY col1; From 5302ef2c955d98bf3b1fb1f94c036d904c8af922 Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Sun, 1 Jan 2017 23:13:04 +0200 Subject: [PATCH 62/73] MDEV-11700 funcs_2.innodb_charset fails in buldbot on valgrind builder with timeout When the test is run as a part of the suite with valgrind, only allow it to be executed if --big-test is set. If the test is run by specifying its name explicitly, it will still be executed, even with valgrind without big-test, MTR has special logic for that --- mysql-test/suite/funcs_2/t/innodb_charset.test | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/suite/funcs_2/t/innodb_charset.test b/mysql-test/suite/funcs_2/t/innodb_charset.test index b77bacfc01c35..da4dea44ad7d3 100644 --- a/mysql-test/suite/funcs_2/t/innodb_charset.test +++ b/mysql-test/suite/funcs_2/t/innodb_charset.test @@ -6,6 +6,7 @@ # Checking of other prerequisites is in charset_master.test # ################################################################################ +--source include/no_valgrind_without_big.inc --source include/have_innodb.inc let $engine_type= InnoDB; From 9e528d4fdeab83fcc75ec788da939937ce27b98d Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Thu, 5 Jan 2017 17:38:55 +0200 Subject: [PATCH 63/73] MDEV-11727 Sequences of tests fail with valgrind warnings in buildbot The warning is "blocks are still reachable in loss record", happens in malloc / _dl_close_worker. Suppression added --- mysql-test/valgrind.supp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index b86cbd23408fc..154031feb0d9d 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -923,6 +923,14 @@ fun:backtrace } +{ + memory leak in mysqld_exit + Memcheck:Leak + fun:malloc + fun:_dl_close_worker + fun:_dl_close +} + # # Bug in Glibc 2.9: http://sourceware.org/bugzilla/show_bug.cgi?id=10391 # Fixed in latest Glibc, but suppressed here for running tests on hosts From ae1b3d1991b679bb38095711de27934d7683deda Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 5 Jan 2017 13:54:31 -0800 Subject: [PATCH 64/73] Fixed bug mdev-10705. The fix for bug mdev-5104 did not take into account that for any call of setup_order the size of ref_array must be big enough. This patch fixes this problem. --- mysql-test/r/order_by.result | 11 +++++++++++ mysql-test/t/order_by.test | 10 ++++++++++ sql/sql_select.cc | 7 ++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/order_by.result b/mysql-test/r/order_by.result index 6ce12651dbaa7..94a38ca78275a 100644 --- a/mysql-test/r/order_by.result +++ b/mysql-test/r/order_by.result @@ -2050,4 +2050,15 @@ t2 A, t2 B where A.b = B.b order by A.col2, B.col2 limit 10, 1000000; drop table t1,t2,t3; +# +# mdev-10705 : long order by list that can be skipped +# +SELECT 1 +UNION +( SELECT 2 +ORDER BY NULL, @a0 := 3, @a1 := 3, @a2 := 3, @a3 := 3, @a4 := 3, +@a5 := 3, @a6 := 3, @a7 := 3, @a8 := 3, @a9 := 3, @a10 := 3 ); +1 +1 +2 End of 5.5 tests diff --git a/mysql-test/t/order_by.test b/mysql-test/t/order_by.test index 2ebf8ba5af164..c96d5c2996a15 100644 --- a/mysql-test/t/order_by.test +++ b/mysql-test/t/order_by.test @@ -1746,6 +1746,16 @@ order by A.col2, B.col2 limit 10, 1000000; drop table t1,t2,t3; +--echo # +--echo # mdev-10705 : long order by list that can be skipped +--echo # + +SELECT 1 +UNION +( SELECT 2 + ORDER BY NULL, @a0 := 3, @a1 := 3, @a2 := 3, @a3 := 3, @a4 := 3, + @a5 := 3, @a6 := 3, @a7 := 3, @a8 := 3, @a9 := 3, @a10 := 3 ); + --echo End of 5.5 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c406fab5a3aec..2c65c59ad7e86 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -714,10 +714,15 @@ JOIN::prepare(Item ***rref_pointer_array, if (mixed_implicit_grouping && tbl->table) tbl->table->maybe_null= 1; } + + uint real_og_num= og_num; + if (skip_order_by && + select_lex != select_lex->master_unit()->global_parameters) + real_og_num+= select_lex->order_list.elements; if ((wild_num && setup_wild(thd, tables_list, fields_list, &all_fields, wild_num)) || - select_lex->setup_ref_array(thd, og_num) || + select_lex->setup_ref_array(thd, real_og_num) || setup_fields(thd, (*rref_pointer_array), fields_list, MARK_COLUMNS_READ, &all_fields, 1) || setup_without_group(thd, (*rref_pointer_array), tables_list, From 6ac84d9824ec384c4489b68b8087369aef147ff9 Mon Sep 17 00:00:00 2001 From: vicentiu Date: Sat, 7 Jan 2017 14:24:42 +0200 Subject: [PATCH 65/73] 5.6.35 --- storage/innobase/dict/dict0stats.cc | 13 +++- storage/innobase/fts/fts0opt.cc | 7 +- storage/innobase/handler/ha_innodb.cc | 38 +++++++++++ storage/innobase/handler/handler0alter.cc | 79 +++++++++++++---------- storage/innobase/include/os0thread.h | 13 +++- storage/innobase/include/srv0srv.h | 1 + storage/innobase/mach/mach0data.cc | 53 +++++++++++---- storage/innobase/os/os0thread.cc | 32 ++++++++- storage/innobase/row/row0ftsort.cc | 2 +- storage/innobase/row/row0merge.cc | 7 ++ storage/innobase/row/row0mysql.cc | 2 + storage/innobase/srv/srv0srv.cc | 1 + 12 files changed, 188 insertions(+), 60 deletions(-) diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 9aa63caa579a8..b0ba98308be71 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/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 @@ -1095,7 +1095,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)))) { @@ -1281,8 +1282,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 */ }; /* @} */ @@ -1558,6 +1563,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/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index 0d45a195c95c8..19098dc00ef93 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -578,9 +578,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; @@ -655,10 +652,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); @@ -669,7 +665,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/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index be5e74e1617bb..41c767a4bfc8c 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -13359,6 +13359,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) { @@ -15880,6 +15911,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_ULONG(io_capacity, srv_io_capacity, PLUGIN_VAR_RQCMDARG, "Number of IOPs the server can do. Tunes the background IO rate", @@ -16681,6 +16718,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/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 2261754a4f5e1..e772208ed7275 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -1709,6 +1709,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: @@ -1775,26 +1776,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 @@ -1812,8 +1798,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++; @@ -2900,6 +2892,8 @@ prepare_inplace_alter_table_dict( ctx->add_cols = add_cols; } else { DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info)); + DBUG_ASSERT(old_table->s->primary_key + == altered_table->s->primary_key); if (!ctx->new_table->fts && innobase_fulltext_exist(altered_table)) { @@ -3892,6 +3886,27 @@ ha_innobase::prepare_inplace_alter_table( add_fts_doc_id_idx)); } +/** 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 @@ -4009,17 +4024,13 @@ ha_innobase::inplace_alter_table( 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; default: my_error_innodb(error, @@ -4829,7 +4840,6 @@ innobase_update_foreign_cache( "Foreign key constraints for table '%s'" " are loaded with charset check off", user_table->name); - } } @@ -4929,14 +4939,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/innobase/include/os0thread.h b/storage/innobase/include/os0thread.h index 9a1ada8fa0dff..54f3d7554bf95 100644 --- a/storage/innobase/include/os0thread.h +++ b/storage/innobase/include/os0thread.h @@ -117,14 +117,25 @@ 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[in,out] thread thread to join */ +UNIV_INTERN +void +os_thread_join( + os_thread_t thread); + /*****************************************************************//** Exits the current thread. */ UNIV_INTERN void os_thread_exit( /*===========*/ - void* exit_value) /*!< in: exit value; in Windows this void* + void* exit_value, /*!< in: exit value; in Windows this void* is cast as a DWORD */ + bool detach = true) /*!< in: if true, the thread will be detached + right before exiting. If false, another thread + is responsible for joining this thread. */ UNIV_COLD MY_ATTRIBUTE((noreturn)); /*****************************************************************//** Returns the thread identifier of current thread. diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index aaea21b264b4a..042ced75d105b 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -347,6 +347,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 ibool srv_use_doublewrite_buf; extern ulong srv_doublewrite_batch_size; diff --git a/storage/innobase/mach/mach0data.cc b/storage/innobase/mach/mach0data.cc index df68aab8a1874..feeedb0160948 100644 --- a/storage/innobase/mach/mach0data.cc +++ b/storage/innobase/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,8 +55,22 @@ mach_parse_compressed( if (flag < 0x80UL) { *val = flag; return(ptr + 1); + } + + /* Workaround GCC bug + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77673: + the compiler moves mach_read_from_4 right to the beginning of the + 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__) +#define DEPLOY_FENCE +#endif + +#ifdef DEPLOY_FENCE + __atomic_thread_fence(__ATOMIC_ACQUIRE); +#endif - } else if (flag < 0xC0UL) { + if (flag < 0xC0UL) { if (end_ptr < ptr + 2) { return(NULL); } @@ -64,8 +78,13 @@ mach_parse_compressed( *val = mach_read_from_2(ptr) & 0x7FFFUL; return(ptr + 2); + } + +#ifdef DEPLOY_FENCE + __atomic_thread_fence(__ATOMIC_ACQUIRE); +#endif - } else if (flag < 0xE0UL) { + if (flag < 0xE0UL) { if (end_ptr < ptr + 3) { return(NULL); } @@ -73,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); } @@ -81,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 + +#undef DEPLOY_FENCE + + ut_ad(flag == 0xF0UL); - *val = mach_read_from_4(ptr + 1); - return(ptr + 5); + if (end_ptr < ptr + 5) { + return(NULL); } + + *val = mach_read_from_4(ptr + 1); + return(ptr + 5); } diff --git a/storage/innobase/os/os0thread.cc b/storage/innobase/os/os0thread.cc index 772336215c9ed..d6f897ca46add 100644 --- a/storage/innobase/os/os0thread.cc +++ b/storage/innobase/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 @@ -191,14 +191,38 @@ os_thread_create_func( #endif } +/** 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) +{ +#ifdef __WIN__ + /* Do nothing. */ +#else +#ifdef UNIV_DEBUG + const int ret = +#endif /* UNIV_DEBUG */ + pthread_join(thread, NULL); + + /* Waiting on already-quit threads is allowed. */ + ut_ad(ret == 0 || ret == ESRCH); +#endif /* __WIN__ */ +} + /*****************************************************************//** Exits the current thread. */ UNIV_INTERN void os_thread_exit( /*===========*/ - void* exit_value) /*!< in: exit value; in Windows this void* + void* exit_value, /*!< in: exit value; in Windows this void* is cast as a DWORD */ + bool detach) /*!< in: if true, the thread will be detached + right before exiting. If false, another thread + is responsible for joining this thread. */ { #ifdef UNIV_DEBUG_THREAD_CREATION fprintf(stderr, "Thread exits, id %lu\n", @@ -216,7 +240,9 @@ os_thread_exit( #ifdef __WIN__ ExitThread((DWORD) exit_value); #else - pthread_detach(pthread_self()); + if (detach) { + pthread_detach(pthread_self()); + } pthread_exit(exit_value); #endif } diff --git a/storage/innobase/row/row0ftsort.cc b/storage/innobase/row/row0ftsort.cc index 4fd3a51040a0b..7d3fcf675ece2 100644 --- a/storage/innobase/row/row0ftsort.cc +++ b/storage/innobase/row/row0ftsort.cc @@ -957,7 +957,7 @@ fts_parallel_merge( CloseHandle(psort_info->thread_hdl); #endif /*__WIN__ */ - os_thread_exit(NULL); + os_thread_exit(NULL, false); OS_THREAD_DUMMY_RETURN; } diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index c094be8a23b2f..f8bea67906c3f 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -3774,6 +3774,13 @@ row_merge_build_indexes( " exited when creating FTS" " index '%s'", indexes[i]->name); + } else { + for (j = 0; j < FTS_NUM_AUX_INDEX; + j++) { + + os_thread_join(merge_info[j] + .thread_hdl); + } } } else { /* This cannot report duplicates; an diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 11bef1064d6f5..09c20911ec9e3 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1362,6 +1362,8 @@ row_insert_for_mysql( row_ins_step(thr); + DEBUG_SYNC_C("ib_after_row_insert_step"); + err = trx->error_state; if (err != DB_SUCCESS) { diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 7a7783b962ca5..a67f3a776c5af 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -340,6 +340,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; From c33db2cdc0ab70a874060d58710895f6dac3dea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Sat, 7 Jan 2017 15:53:37 +0200 Subject: [PATCH 66/73] 5.6.35 --- storage/perfschema/pfs.cc | 34 ++++-- storage/perfschema/pfs_digest.cc | 118 +++++++++++++-------- storage/perfschema/pfs_digest.h | 5 +- storage/perfschema/pfs_lock.h | 14 ++- storage/perfschema/table_esms_by_digest.cc | 22 ++-- 5 files changed, 129 insertions(+), 64 deletions(-) diff --git a/storage/perfschema/pfs.cc b/storage/perfschema/pfs.cc index cf0fa82ba3f86..3bbc0a4602e85 100644 --- a/storage/perfschema/pfs.cc +++ b/storage/perfschema/pfs.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 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 @@ -2560,10 +2560,7 @@ start_table_io_wait_v1(PSI_table_locker_state *state, if (! pfs_table->m_io_enabled) return NULL; - PFS_thread *pfs_thread= pfs_table->m_thread_owner; - - DBUG_ASSERT(pfs_thread == - my_pthread_getspecific_ptr(PFS_thread*, THR_PFS)); + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); register uint flags; ulonglong timer_start= 0; @@ -2666,7 +2663,7 @@ start_table_lock_wait_v1(PSI_table_locker_state *state, if (! pfs_table->m_lock_enabled) return NULL; - PFS_thread *pfs_thread= pfs_table->m_thread_owner; + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); PFS_TL_LOCK_TYPE lock_type; @@ -3068,7 +3065,12 @@ start_socket_wait_v1(PSI_socket_locker_state *state, if (flag_thread_instrumentation) { - PFS_thread *pfs_thread= pfs_socket->m_thread_owner; + /* + Do not use pfs_socket->m_thread_owner here, + as different threads may use concurrently the same socket, + for example during a KILL. + */ + PFS_thread *pfs_thread= my_pthread_getspecific_ptr(PFS_thread*, THR_PFS); if (unlikely(pfs_thread == NULL)) return NULL; @@ -3436,6 +3438,8 @@ static void end_idle_wait_v1(PSI_idle_locker* locker) if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } @@ -3517,6 +3521,8 @@ static void end_mutex_wait_v1(PSI_mutex_locker* locker, int rc) if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } } @@ -3596,6 +3602,8 @@ static void end_rwlock_rdwait_v1(PSI_rwlock_locker* locker, int rc) if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } } @@ -3668,6 +3676,8 @@ static void end_rwlock_wrwait_v1(PSI_rwlock_locker* locker, int rc) if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } } @@ -3732,6 +3742,8 @@ static void end_cond_wait_v1(PSI_cond_locker* locker, int rc) if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } } @@ -3826,6 +3838,8 @@ static void end_table_io_wait_v1(PSI_table_locker* locker) if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } @@ -3895,6 +3909,8 @@ static void end_table_lock_wait_v1(PSI_table_locker* locker) if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } @@ -4143,6 +4159,8 @@ static void end_file_wait_v1(PSI_file_locker *locker, if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } } @@ -5070,6 +5088,8 @@ static void end_socket_wait_v1(PSI_socket_locker *locker, size_t byte_count) if (flag_events_waits_history_long) insert_events_waits_history_long(wait); thread->m_events_waits_current--; + + DBUG_ASSERT(wait == thread->m_events_waits_current); } } diff --git a/storage/perfschema/pfs_digest.cc b/storage/perfschema/pfs_digest.cc index 1053bd59571c2..5886c379b2f49 100644 --- a/storage/perfschema/pfs_digest.cc +++ b/storage/perfschema/pfs_digest.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2008, 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 @@ -45,7 +45,7 @@ bool flag_statements_digest= true; Current index in Stat array where new record is to be inserted. index 0 is reserved for "all else" case when entire array is full. */ -volatile uint32 digest_index; +volatile uint32 PFS_ALIGNED digest_monotonic_index; bool digest_full= false; LF_HASH digest_hash; @@ -63,7 +63,7 @@ int init_digest(const PFS_global_param *param) */ digest_max= param->m_digest_sizing; digest_lost= 0; - digest_index= 1; + PFS_atomic::store_u32(& digest_monotonic_index, 1); digest_full= false; if (digest_max == 0) @@ -105,6 +105,9 @@ int init_digest(const PFS_global_param *param) + index * pfs_max_digest_length, pfs_max_digest_length); } + /* Set record[0] as allocated. */ + statements_digest_stat_array[0].m_lock.set_allocated(); + return 0; } @@ -207,9 +210,10 @@ find_or_create_digest(PFS_thread *thread, memcpy(hash_key.m_schema_name, schema_name, schema_name_length); int res; - ulong safe_index; uint retry_count= 0; const uint retry_max= 3; + size_t safe_index; + size_t attempts= 0; PFS_statements_digest_stat **entry; PFS_statements_digest_stat *pfs= NULL; @@ -245,55 +249,70 @@ find_or_create_digest(PFS_thread *thread, return & pfs->m_stat; } - safe_index= PFS_atomic::add_u32(& digest_index, 1); - if (safe_index >= digest_max) + while (++attempts <= digest_max) { - /* The digest array is now full. */ - digest_full= true; - pfs= &statements_digest_stat_array[0]; - - if (pfs->m_first_seen == 0) - pfs->m_first_seen= now; - pfs->m_last_seen= now; - return & pfs->m_stat; - } - - /* Add a new record in digest stat array. */ - pfs= &statements_digest_stat_array[safe_index]; - - /* Copy digest hash/LF Hash search key. */ - memcpy(& pfs->m_digest_key, &hash_key, sizeof(PFS_digest_key)); - - /* - Copy digest storage to statement_digest_stat_array so that it could be - used later to generate digest text. - */ - pfs->m_digest_storage.copy(digest_storage); - - pfs->m_first_seen= now; - pfs->m_last_seen= now; + safe_index= PFS_atomic::add_u32(& digest_monotonic_index, 1) % digest_max; + if (safe_index == 0) + { + /* Record [0] is reserved. */ + safe_index= 1; + } - res= lf_hash_insert(&digest_hash, pins, &pfs); - if (likely(res == 0)) - { - return & pfs->m_stat; - } + /* Add a new record in digest stat array. */ + pfs= &statements_digest_stat_array[safe_index]; - if (res > 0) - { - /* Duplicate insert by another thread */ - if (++retry_count > retry_max) + if (pfs->m_lock.is_free()) { - /* Avoid infinite loops */ - digest_lost++; - return NULL; + if (pfs->m_lock.free_to_dirty()) + { + /* Copy digest hash/LF Hash search key. */ + memcpy(& pfs->m_digest_key, &hash_key, sizeof(PFS_digest_key)); + + /* + Copy digest storage to statement_digest_stat_array so that it could be + used later to generate digest text. + */ + pfs->m_digest_storage.copy(digest_storage); + + pfs->m_first_seen= now; + pfs->m_last_seen= now; + + res= lf_hash_insert(&digest_hash, pins, &pfs); + if (likely(res == 0)) + { + pfs->m_lock.dirty_to_allocated(); + return & pfs->m_stat; + } + + pfs->m_lock.dirty_to_free(); + + if (res > 0) + { + /* Duplicate insert by another thread */ + if (++retry_count > retry_max) + { + /* Avoid infinite loops */ + digest_lost++; + return NULL; + } + goto search; + } + + /* OOM in lf_hash_insert */ + digest_lost++; + return NULL; + } } - goto search; } - /* OOM in lf_hash_insert */ - digest_lost++; - return NULL; + /* The digest array is now full. */ + digest_full= true; + pfs= &statements_digest_stat_array[0]; + + if (pfs->m_first_seen == 0) + pfs->m_first_seen= now; + pfs->m_last_seen= now; + return & pfs->m_stat; } void purge_digest(PFS_thread* thread, PFS_digest_key *hash_key) @@ -320,10 +339,12 @@ void purge_digest(PFS_thread* thread, PFS_digest_key *hash_key) void PFS_statements_digest_stat::reset_data(unsigned char *token_array, uint length) { + m_lock.set_dirty(); m_digest_storage.reset(token_array, length); m_stat.reset(); m_first_seen= 0; m_last_seen= 0; + m_lock.dirty_to_free(); } void PFS_statements_digest_stat::reset_index(PFS_thread *thread) @@ -351,11 +372,14 @@ void reset_esms_by_digest() statements_digest_stat_array[index].reset_data(statements_digest_token_array + index * pfs_max_digest_length, pfs_max_digest_length); } + /* Mark record[0] as allocated again. */ + statements_digest_stat_array[0].m_lock.set_allocated(); + /* Reset index which indicates where the next calculated digest information to be inserted in statements_digest_stat_array. */ - digest_index= 1; + PFS_atomic::store_u32(& digest_monotonic_index, 1); digest_full= false; } diff --git a/storage/perfschema/pfs_digest.h b/storage/perfschema/pfs_digest.h index 76d6c33d984fb..429a9f4250a5b 100644 --- a/storage/perfschema/pfs_digest.h +++ b/storage/perfschema/pfs_digest.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2011, 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 @@ -44,6 +44,9 @@ struct PFS_digest_key /** A statement digest stat record. */ struct PFS_ALIGNED PFS_statements_digest_stat { + /** Internal lock. */ + pfs_lock m_lock; + /** Digest Schema + MD5 Hash. */ PFS_digest_key m_digest_key; diff --git a/storage/perfschema/pfs_lock.h b/storage/perfschema/pfs_lock.h index c429d9347029c..339a893c83370 100644 --- a/storage/perfschema/pfs_lock.h +++ b/storage/perfschema/pfs_lock.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010, 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 @@ -155,6 +155,18 @@ struct pfs_lock PFS_atomic::store_u32(&m_version_state, new_val); } + /** + Initialize a lock to dirty. + */ + void set_dirty(void) + { + /* Do not set the version to 0, read the previous value. */ + uint32 copy= PFS_atomic::load_u32(&m_version_state); + /* Increment the version, set the DIRTY state */ + uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_DIRTY; + PFS_atomic::store_u32(&m_version_state, new_val); + } + /** Execute a dirty to free transition. This transition should be executed by the writer that owns the record. diff --git a/storage/perfschema/table_esms_by_digest.cc b/storage/perfschema/table_esms_by_digest.cc index 99e24316cbb9d..002a7f0104b2d 100644 --- a/storage/perfschema/table_esms_by_digest.cc +++ b/storage/perfschema/table_esms_by_digest.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 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 @@ -238,11 +238,14 @@ int table_esms_by_digest::rnd_next(void) m_pos.next()) { digest_stat= &statements_digest_stat_array[m_pos.m_index]; - if (digest_stat->m_first_seen != 0) + if (digest_stat->m_lock.is_populated()) { - make_row(digest_stat); - m_next_pos.set_after(&m_pos); - return 0; + if (digest_stat->m_first_seen != 0) + { + make_row(digest_stat); + m_next_pos.set_after(&m_pos); + return 0; + } } } @@ -260,10 +263,13 @@ table_esms_by_digest::rnd_pos(const void *pos) set_position(pos); digest_stat= &statements_digest_stat_array[m_pos.m_index]; - if (digest_stat->m_first_seen != 0) + if (digest_stat->m_lock.is_populated()) { - make_row(digest_stat); - return 0; + if (digest_stat->m_first_seen != 0) + { + make_row(digest_stat); + return 0; + } } return HA_ERR_RECORD_DELETED; From 3e63fde52eb42ff23a9b260ed42b18284628ea42 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 9 Jan 2017 14:19:02 +0400 Subject: [PATCH 67/73] Adding LOAD DATA tests for MDEV-11079 and MDEV-11631 c3cf7f47f0f4a1ec314001aaf0c3d9c1c1f62097 reverted the patch for BUG#24487120. After merging the reverting patch from MySQL to MariaDB the problems described in MDEV-11079 and MDEV-11631 disappeared. Adding test cases only. --- mysql-test/r/loaddata.result | 18 ++++++++++++++++++ mysql-test/std_data/loaddata/mdev-11079.txt | 1 + mysql-test/std_data/loaddata/mdev-11631.txt | 1 + mysql-test/t/loaddata.test | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 mysql-test/std_data/loaddata/mdev-11079.txt create mode 100644 mysql-test/std_data/loaddata/mdev-11631.txt diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result index 8ccc1a3da3de8..ce26abea66d5e 100644 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -532,3 +532,21 @@ FIELDS TERMINATED BY 't' LINES TERMINATED BY ''; Got one of the listed errors SET @@sql_mode= @old_mode; DROP TABLE t1; +# +# MDEV-11079 Regression: LOAD DATA INFILE lost BLOB support using utf8 load files +# +CREATE TABLE t1 (a mediumblob NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci; +LOAD DATA INFILE '../../std_data/loaddata/mdev-11079.txt' INTO TABLE t1 CHARSET utf8 FIELDS TERMINATED BY ';' ENCLOSED BY '"' ESCAPED BY '\\' LINES TERMINATED BY '\n'; +SELECT HEX(a) FROM t1; +HEX(a) +25AAABAC +DROP TABLE t1; +# +# MDEV-11631 LOAD DATA INFILE fails to load data with an escape character followed by a multi-byte character +# +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8); +LOAD DATA INFILE '../../std_data/loaddata/mdev-11631.txt' INTO TABLE t1 CHARACTER SET utf8; +SELECT HEX(a) FROM t1; +HEX(a) +C3A4 +DROP TABLE t1; diff --git a/mysql-test/std_data/loaddata/mdev-11079.txt b/mysql-test/std_data/loaddata/mdev-11079.txt new file mode 100644 index 0000000000000..a792f984d5f19 --- /dev/null +++ b/mysql-test/std_data/loaddata/mdev-11079.txt @@ -0,0 +1 @@ +"%ª«¬" diff --git a/mysql-test/std_data/loaddata/mdev-11631.txt b/mysql-test/std_data/loaddata/mdev-11631.txt new file mode 100644 index 0000000000000..87b824b71ae97 --- /dev/null +++ b/mysql-test/std_data/loaddata/mdev-11631.txt @@ -0,0 +1 @@ +\ä diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test index 35243864c04bb..1bc7eb139b9b6 100644 --- a/mysql-test/t/loaddata.test +++ b/mysql-test/t/loaddata.test @@ -658,3 +658,21 @@ SET @@sql_mode= @old_mode; --remove_file $MYSQLTEST_VARDIR/mysql DROP TABLE t1; + +--echo # +--echo # MDEV-11079 Regression: LOAD DATA INFILE lost BLOB support using utf8 load files +--echo # + +CREATE TABLE t1 (a mediumblob NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci; +LOAD DATA INFILE '../../std_data/loaddata/mdev-11079.txt' INTO TABLE t1 CHARSET utf8 FIELDS TERMINATED BY ';' ENCLOSED BY '"' ESCAPED BY '\\' LINES TERMINATED BY '\n'; +SELECT HEX(a) FROM t1; +DROP TABLE t1; + +--echo # +--echo # MDEV-11631 LOAD DATA INFILE fails to load data with an escape character followed by a multi-byte character +--echo # + +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET utf8); +LOAD DATA INFILE '../../std_data/loaddata/mdev-11631.txt' INTO TABLE t1 CHARACTER SET utf8; +SELECT HEX(a) FROM t1; +DROP TABLE t1; From ecdb39a9f51ebbdafae167889ec65ee817e07de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Tue, 10 Jan 2017 12:08:36 +0200 Subject: [PATCH 68/73] Fix problems from 5.5 merge * Update mysqld_safe script to remove duplicated parameter --crash-script * Make --core-file-size accept underscores as well as dashes correctly. * Add mysqld_safe_helper to Debian and Ubuntu files. * Update innodb minor version to 35 --- debian/dist/Debian/mariadb-server-10.0.files.in | 1 + debian/dist/Ubuntu/mariadb-server-10.0.files.in | 1 + scripts/mysqld_safe.sh | 3 +-- storage/innobase/include/univ.i | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/debian/dist/Debian/mariadb-server-10.0.files.in b/debian/dist/Debian/mariadb-server-10.0.files.in index 88516bb6089b1..9feec684ff0b9 100644 --- a/debian/dist/Debian/mariadb-server-10.0.files.in +++ b/debian/dist/Debian/mariadb-server-10.0.files.in @@ -38,6 +38,7 @@ usr/bin/mysql_zap usr/bin/mysqlbinlog usr/bin/mysqld_multi usr/bin/mysqld_safe +usr/bin/mysqld_safe_helper usr/bin/mysqlhotcopy usr/bin/perror usr/bin/replace diff --git a/debian/dist/Ubuntu/mariadb-server-10.0.files.in b/debian/dist/Ubuntu/mariadb-server-10.0.files.in index 849a763dccd17..a5e8bd6e717c8 100644 --- a/debian/dist/Ubuntu/mariadb-server-10.0.files.in +++ b/debian/dist/Ubuntu/mariadb-server-10.0.files.in @@ -40,6 +40,7 @@ usr/bin/mysql_zap usr/bin/mysqlbinlog usr/bin/mysqld_multi usr/bin/mysqld_safe +usr/bin/mysqld_safe_helper usr/bin/mysqlhotcopy usr/bin/perror usr/bin/replace diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 10f88885068f9..bbd99e92cedcc 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -215,7 +215,6 @@ parse_arguments() { for arg do val=`echo "$arg" | sed -e "s;--[^=]*=;;"` case "$arg" in - --crash[-_]script=*) CRASH_SCRIPT="$val" ;; # these get passed explicitly to mysqld --basedir=*) MY_BASEDIR_VERSION="$val" ;; --datadir=*|--data=*) DATADIR="$val" ;; @@ -241,7 +240,7 @@ parse_arguments() { --socket=*) mysql_unix_port="$val" ;; # mysqld_safe-specific options - must be set in my.cnf ([mysqld_safe])! - --core[-_]file-size=*) core_file_size="$val" ;; + --core[-_]file[-_]size=*) core_file_size="$val" ;; --ledir=*) check_executable_location "$arg" ; ledir="$val" ;; --malloc[-_]lib=*) check_executable_location "$arg"; set_malloc_lib "$val" ;; --crash[-_]script=*) check_executable_location "$arg"; crash_script="$val" ;; diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index e6ddf45fabaa9..e4b2044174971 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -44,7 +44,7 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 5 #define INNODB_VERSION_MINOR 6 -#define INNODB_VERSION_BUGFIX 33 +#define INNODB_VERSION_BUGFIX 35 /* The following is the InnoDB version as shown in SELECT plugin_version FROM information_schema.plugins; From 4799af092574e7957d7143c7751acef74a95a495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Tue, 10 Jan 2017 14:20:43 +0200 Subject: [PATCH 69/73] Fix unit test after merge from mysql 5.5.35 perfschema The problem in MariaDB is introduced by this merge commit: c33db2cdc0ab70a874060d58710895f6dac3dea3 The merge comes from mysql and the original author comes from this commit from MySQL: ------------------------------------------------ commit 160b823d146288d66638e4a740d6d2da72f9a689 Author: Marc Alff Date: Tue Aug 30 12:14:07 2016 +0200 Bug#22551677 SIGNAL 11 IN LF_PINBOX_PUT_PINS Backport to 5.6 ------------------------------------------------ The breaking change is in start_socket_wait_v1 where instead of using m_thread_owner, we make use of my_pthread_getspecific_ptr to fetch a thread local storage value. Unfortunately this invalidates the "m_thread_owner" member when a socket is created. The internals of the socket structure have m_thread_owner set to NULL, but when checking for ownership we actually look at the current thread's key store. This seems incorrect however it is not immediately apparent why. To not diverge from MySQL's reasoning as it is not described what the actual problem was that this commit is trying to fix, I have adjusted the unittest to account for this new behaviour. We destroy the current thread in the unit test, such that the newly created socket actually has no thread owner. The m_thread_owner is untouched in all this. --- storage/perfschema/unittest/pfs-t.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/perfschema/unittest/pfs-t.cc b/storage/perfschema/unittest/pfs-t.cc index 6121fac098fd0..f76b1aa2e75ff 100644 --- a/storage/perfschema/unittest/pfs-t.cc +++ b/storage/perfschema/unittest/pfs-t.cc @@ -1316,6 +1316,7 @@ void test_locker_disabled() /* Pretend the socket does not have a thread owner */ /* ---------------------------------------------- */ + psi->delete_current_thread(); socket_class_A->m_enabled= true; socket_A1= psi->init_socket(socket_key_A, NULL, NULL, 0); ok(socket_A1 != NULL, "instrumented"); From 6ad3dd6054b34afea1f1f13dfd925dc7e73f3b16 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 10 Jan 2017 14:19:11 +0100 Subject: [PATCH 70/73] mysqld_safe: don't close stdout if set -x --- scripts/mysqld_safe.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 059263fad51b9..a93a18c729c22 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -864,8 +864,13 @@ max_fast_restarts=5 have_sleep=1 # close stdout and stderr, everything goes to $logging now -exec 1>&- -exec 2>&- +if expr "${-}" : '.*x' > /dev/null +then + : +else + exec 1>&- + exec 2>&- +fi while true do From 9a4bc0d098a9fba03e88b80db0f921b5ee597123 Mon Sep 17 00:00:00 2001 From: iangilfillan Date: Tue, 3 Jan 2017 16:38:56 +0200 Subject: [PATCH 71/73] Update mysql_secure_installation man page --- man/mysql_secure_installation.1 | 72 +++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/man/mysql_secure_installation.1 b/man/mysql_secure_installation.1 index 20a77bc3f1ef1..2a04e5d0b9865 100644 --- a/man/mysql_secure_installation.1 +++ b/man/mysql_secure_installation.1 @@ -1,6 +1,6 @@ '\" t .\" -.TH "\FBMYSQL_SECURE_INST" "1" "04/08/2015" "MariaDB 10\&.0" "MariaDB Database System" +.TH "\FBMYSQL_SECURE_INST" "1" "3 January 2017" "MariaDB 10\&.0" "MariaDB Database System" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -71,9 +71,8 @@ test database, which by default can be accessed by anonymous users\&. .RE .PP -Invoke \fBmysql_secure_installation\fR -without arguments: +can be invoked without arguments: .sp .if n \{\ .RS 4 @@ -86,10 +85,75 @@ shell> \fBmysql_secure_installation\fR .\} .PP The script will prompt you to determine which actions to perform\&. +.PP +\fBmysql_secure_installation\fR +accepts some options: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +.\" mysql_secure_installation: basedir option +.\" basedir option: mysql_secure_installation +\fB\-\-basedir=\fR\fB\fIdir_name\fR\fR +.sp +Base directory\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +.\" mysql_secure_installation: defaults-extra-file option +.\" defaults-extra-file option: mysql_secure_installation +\fB\-\-defaults\-extra\-file=\fR\fB\fIfile_name\fR\fR +.sp +Additional option file\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +.\" mysql_secure_installation: defaults-file option +.\" defaults-file option: mysql_secure_installation +\fB\-\-defaults\-file=\fR\fB\fIfile_name\fR\fR +.sp +Option file\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +.\" mysql_secure_installation: no-defaults option +.\" no-defaults option: mysql_secure_installation +\fB\-\-no\-defaults\fR +.sp +Don't read any defaults file\&. +.RE +.sp +Other unrecognized options will be passed on to the server\&. .SH "COPYRIGHT" .br .PP -Copyright 2007-2008 MySQL AB, 2008-2010 Sun Microsystems, Inc., 2010-2015 MariaDB Foundation +Copyright 2007-2008 MySQL AB, 2008-2010 Sun Microsystems, Inc., 2010-2017 MariaDB Foundation .PP This documentation is free software; you can redistribute it and/or modify it only under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. .PP From c1a23cd4e5f96c51064f9569bfa4f87d76d53fb6 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 10 Jan 2017 18:31:03 +0100 Subject: [PATCH 72/73] MDEV-11676 Starting service with mysqld_safe_helper fails in SELINUX "enforcing" mode compile, and install selinux policy for mysqld_safe_helper on centos6. the policy was created as described in https://mariadb.com/kb/en/mariadb/what-to-do-if-mariadb-doesnt-start/#other-selinux-changes --- .gitignore | 1 + support-files/CMakeLists.txt | 2 +- support-files/SELinux/CMakeLists.txt | 35 +++++++++++++++++++ support-files/SELinux/centos6-mariadb.te | 9 +++++ .../mysql.fc => SELinux/rhel4-mysql.fc} | 0 .../mysql.te => SELinux/rhel4-mysql.te} | 0 support-files/rpm/server-postin.sh | 7 +++- 7 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 support-files/SELinux/CMakeLists.txt create mode 100644 support-files/SELinux/centos6-mariadb.te rename support-files/{RHEL4-SElinux/mysql.fc => SELinux/rhel4-mysql.fc} (100%) rename support-files/{RHEL4-SElinux/mysql.te => SELinux/rhel4-mysql.te} (100%) diff --git a/.gitignore b/.gitignore index 8b6e416ec452e..4d96bfe65a38b 100644 --- a/.gitignore +++ b/.gitignore @@ -221,6 +221,7 @@ support-files/mysql.spec support-files/mysqld_multi.server support-files/wsrep.cnf support-files/wsrep_notify +support-files/SELinux/centos6-mariadb.pp tags tests/async_queries tests/bug25714 diff --git a/support-files/CMakeLists.txt b/support-files/CMakeLists.txt index 4677bd5941563..67a7b508e3346 100644 --- a/support-files/CMakeLists.txt +++ b/support-files/CMakeLists.txt @@ -67,7 +67,7 @@ IF(UNIX) ENDFOREACH() IF(INSTALL_SUPPORTFILESDIR) INSTALL(FILES magic DESTINATION ${inst_location} COMPONENT SupportFiles) - INSTALL(DIRECTORY RHEL4-SElinux/ DESTINATION ${inst_location}/SELinux/RHEL4 COMPONENT SupportFiles) + ADD_SUBDIRECTORY(SELinux) ENDIF() INSTALL(FILES mysql.m4 DESTINATION ${INSTALL_SHAREDIR}/aclocal COMPONENT Development) diff --git a/support-files/SELinux/CMakeLists.txt b/support-files/SELinux/CMakeLists.txt new file mode 100644 index 0000000000000..e3cdb26ca8fa9 --- /dev/null +++ b/support-files/SELinux/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) 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 +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +FIND_PROGRAM(CHECKMODULE checkmodule) +FIND_PROGRAM(SEMODULE_PACKAGE semodule_package) +MARK_AS_ADVANCED(CHECKMODULE SEMODULE_PACKAGE) + +SET(params DESTINATION ${INSTALL_SUPPORTFILESDIR}/SELinux COMPONENT SupportFiles) + +IF(CHECKMODULE AND SEMODULE_PACKAGE) + FOREACH(pol centos6-mariadb) + SET(src ${CMAKE_CURRENT_SOURCE_DIR}/${pol}.te) + SET(mod ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${pol}-pp.dir/${pol}.mod) + SET(out ${CMAKE_CURRENT_BINARY_DIR}/${pol}.pp) + ADD_CUSTOM_COMMAND(OUTPUT ${out} + COMMAND ${CHECKMODULE} -M -m ${src} -o ${mod} + COMMAND ${SEMODULE_PACKAGE} -m ${mod} -o ${out} + DEPENDS ${src}) + ADD_CUSTOM_TARGET(${pol}-pp ALL DEPENDS ${out}) + INSTALL(FILES ${out} ${params}) + ENDFOREACH() +ENDIF() +INSTALL(FILES centos6-mariadb.te rhel4-mysql.fc rhel4-mysql.te ${params}) diff --git a/support-files/SELinux/centos6-mariadb.te b/support-files/SELinux/centos6-mariadb.te new file mode 100644 index 0000000000000..1d3de52c7004e --- /dev/null +++ b/support-files/SELinux/centos6-mariadb.te @@ -0,0 +1,9 @@ +module mariadb 1.0; + +require { + type mysqld_safe_t; + class capability { setuid setgid }; +} + +#============= mysqld_safe_t ============== +allow mysqld_safe_t self:capability { setuid setgid }; diff --git a/support-files/RHEL4-SElinux/mysql.fc b/support-files/SELinux/rhel4-mysql.fc similarity index 100% rename from support-files/RHEL4-SElinux/mysql.fc rename to support-files/SELinux/rhel4-mysql.fc diff --git a/support-files/RHEL4-SElinux/mysql.te b/support-files/SELinux/rhel4-mysql.te similarity index 100% rename from support-files/RHEL4-SElinux/mysql.te rename to support-files/SELinux/rhel4-mysql.te diff --git a/support-files/rpm/server-postin.sh b/support-files/rpm/server-postin.sh index cd2aec4d84ae7..377a752824d11 100644 --- a/support-files/rpm/server-postin.sh +++ b/support-files/rpm/server-postin.sh @@ -79,7 +79,12 @@ if [ -f /etc/redhat-release ] ; then echo ' make load' echo echo - fi + 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/SELinux/centos6-mariadb.pp + fi + fi fi if [ -x sbin/restorecon ] ; then From ab93a4d4df7206833fa4a8eb0aa47b8ba185da60 Mon Sep 17 00:00:00 2001 From: Nirbhay Choubey Date: Wed, 11 Jan 2017 09:05:36 -0500 Subject: [PATCH 73/73] MDEV-11685: sql_mode can't be set with non-ascii connection charset The supplied sql_mode(s) should be converted to ASCII first, before comparing it with the sql_mode set. --- mysql-test/r/ctype_ucs.result | 10 ++++++++++ mysql-test/r/ctype_utf16.result | 10 ++++++++++ mysql-test/r/ctype_utf32.result | 10 ++++++++++ mysql-test/t/ctype_ucs.test | 8 ++++++++ mysql-test/t/ctype_utf16.test | 9 +++++++++ mysql-test/t/ctype_utf32.test | 9 +++++++++ sql/sys_vars.h | 2 +- 7 files changed, 57 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ctype_ucs.result b/mysql-test/r/ctype_ucs.result index c38a03f76a802..7a93c5524ef35 100644 --- a/mysql-test/r/ctype_ucs.result +++ b/mysql-test/r/ctype_ucs.result @@ -4357,5 +4357,15 @@ SELECT CHAR_LENGTH(TRIM(BOTH 0x00 FROM _ucs2 0x0061)); CHAR_LENGTH(TRIM(BOTH 0x00 FROM _ucs2 0x0061)) 1 # +# MDEV-11685: sql_mode can't be set with non-ascii connection charset +# +SET character_set_connection=ucs2; +SET sql_mode='NO_ENGINE_SUBSTITUTION'; +SELECT @@sql_mode; +@@sql_mode +NO_ENGINE_SUBSTITUTION +SET sql_mode=DEFAULT; +SET NAMES utf8; +# # End of 5.5 tests # diff --git a/mysql-test/r/ctype_utf16.result b/mysql-test/r/ctype_utf16.result index bfb2d6a498cff..0846811e2e639 100644 --- a/mysql-test/r/ctype_utf16.result +++ b/mysql-test/r/ctype_utf16.result @@ -1207,5 +1207,15 @@ DO LPAD(_utf16 0x0061 COLLATE utf16_unicode_ci, 10000, 0x0061DE989999); Warnings: Warning 1300 Invalid utf16 character string: 'DE9899' # +# MDEV-11685: sql_mode can't be set with non-ascii connection charset +# +SET character_set_connection=utf16; +SET sql_mode='NO_ENGINE_SUBSTITUTION'; +SELECT @@sql_mode; +@@sql_mode +NO_ENGINE_SUBSTITUTION +SET sql_mode=DEFAULT; +SET NAMES utf8; +# # End of 5.5 tests # diff --git a/mysql-test/r/ctype_utf32.result b/mysql-test/r/ctype_utf32.result index ae55f2c101ed9..9b062f9480f07 100644 --- a/mysql-test/r/ctype_utf32.result +++ b/mysql-test/r/ctype_utf32.result @@ -1273,5 +1273,15 @@ select hex(lower(cast(0xffff0000 as char character set utf32))) as c; c FFFF0000 # +# MDEV-11685: sql_mode can't be set with non-ascii connection charset +# +SET character_set_connection=utf32; +SET sql_mode='NO_ENGINE_SUBSTITUTION'; +SELECT @@sql_mode; +@@sql_mode +NO_ENGINE_SUBSTITUTION +SET sql_mode=DEFAULT; +SET NAMES utf8; +# # End of 5.5 tests # diff --git a/mysql-test/t/ctype_ucs.test b/mysql-test/t/ctype_ucs.test index 7fd3768aa5f64..d94c9ae62ac6e 100644 --- a/mysql-test/t/ctype_ucs.test +++ b/mysql-test/t/ctype_ucs.test @@ -853,6 +853,14 @@ SELECT CHAR_LENGTH(TRIM(BOTH 0x0001 FROM _ucs2 0x0061)); SELECT CHAR_LENGTH(TRIM(BOTH 0x61 FROM _ucs2 0x0061)); SELECT CHAR_LENGTH(TRIM(BOTH 0x00 FROM _ucs2 0x0061)); +--echo # +--echo # MDEV-11685: sql_mode can't be set with non-ascii connection charset +--echo # +SET character_set_connection=ucs2; +SET sql_mode='NO_ENGINE_SUBSTITUTION'; +SELECT @@sql_mode; +SET sql_mode=DEFAULT; +SET NAMES utf8; --echo # --echo # End of 5.5 tests diff --git a/mysql-test/t/ctype_utf16.test b/mysql-test/t/ctype_utf16.test index 3f9e5eece2170..ab87c7da0d8ba 100644 --- a/mysql-test/t/ctype_utf16.test +++ b/mysql-test/t/ctype_utf16.test @@ -792,6 +792,15 @@ SELECT 'a' AS id, REPEAT('bla bla', 100) AS body) t1; DO RPAD(_utf16 0x0061 COLLATE utf16_unicode_ci, 10000, 0x0061DE989999); DO LPAD(_utf16 0x0061 COLLATE utf16_unicode_ci, 10000, 0x0061DE989999); +--echo # +--echo # MDEV-11685: sql_mode can't be set with non-ascii connection charset +--echo # +SET character_set_connection=utf16; +SET sql_mode='NO_ENGINE_SUBSTITUTION'; +SELECT @@sql_mode; +SET sql_mode=DEFAULT; +SET NAMES utf8; + --echo # --echo # End of 5.5 tests --echo # diff --git a/mysql-test/t/ctype_utf32.test b/mysql-test/t/ctype_utf32.test index 8cbb8e2e55e19..2b3d3b3bdc59d 100644 --- a/mysql-test/t/ctype_utf32.test +++ b/mysql-test/t/ctype_utf32.test @@ -881,6 +881,15 @@ SELECT CHAR_LENGTH(TRIM(BOTH 0x00 FROM _utf32 0x00000061)); # select hex(lower(cast(0xffff0000 as char character set utf32))) as c; +--echo # +--echo # MDEV-11685: sql_mode can't be set with non-ascii connection charset +--echo # +SET character_set_connection=utf32; +SET sql_mode='NO_ENGINE_SUBSTITUTION'; +SELECT @@sql_mode; +SET sql_mode=DEFAULT; +SET NAMES utf8; + --echo # --echo # End of 5.5 tests --echo # diff --git a/sql/sys_vars.h b/sql/sys_vars.h index 3cbd24f1c89d8..dbe27ab107eca 100644 --- a/sql/sys_vars.h +++ b/sql/sys_vars.h @@ -1141,7 +1141,7 @@ class Sys_var_set: public Sys_var_typelib if (var->value->result_type() == STRING_RESULT) { - if (!(res=var->value->val_str(&str))) + if (!(res=var->value->val_str_ascii(&str))) return true; else {