From cd5ec6cb36a450ab378ded6889db72131d81c9be Mon Sep 17 00:00:00 2001 From: Karen Langford Date: Sun, 10 Apr 2011 23:58:04 +0200 Subject: Raise version number after cloning 5.1.57 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index dc944386f22..5bd823ab879 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ dnl dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MySQL Server], [5.1.57], [], [mysql]) +AC_INIT([MySQL Server], [5.1.58], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM -- cgit v1.2.1 From 25096933df752a13a3392cfe8669cecb9426a5cf Mon Sep 17 00:00:00 2001 From: Sunanda Menon Date: Mon, 11 Apr 2011 09:27:07 +0200 Subject: Raise version number after cloning 5.0.93 --- configure.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 523d36afaea..fdfb7eae871 100644 --- a/configure.in +++ b/configure.in @@ -7,7 +7,7 @@ AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! # remember to also change ndb version below and update version.c in ndb -AM_INIT_AUTOMAKE(mysql, 5.0.93) +AM_INIT_AUTOMAKE(mysql, 5.0.94) AM_CONFIG_HEADER([include/config.h:config.h.in]) PROTOCOL_VERSION=10 @@ -23,7 +23,7 @@ NDB_SHARED_LIB_VERSION=$NDB_SHARED_LIB_MAJOR_VERSION:0:0 # ndb version NDB_VERSION_MAJOR=5 NDB_VERSION_MINOR=0 -NDB_VERSION_BUILD=93 +NDB_VERSION_BUILD=94 NDB_VERSION_STATUS="" # Set all version vars based on $VERSION. How do we do this more elegant ? -- cgit v1.2.1 From 96e572974ab4590db9ada4d4af8a5e73017ea7e0 Mon Sep 17 00:00:00 2001 From: MySQL Build Team Date: Mon, 11 Apr 2011 11:13:25 +0200 Subject: Increased the VERSION tag by 2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 952cd09f84b..f0a5c1203ed 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=5 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=12 +MYSQL_VERSION_PATCH=14 MYSQL_VERSION_EXTRA= -- cgit v1.2.1 From b3ea1d1febb93db6e24ef17c58d4ad235ff3064d Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Mon, 11 Apr 2011 12:24:50 +0200 Subject: Bug#11882603 SELECT_ACL ON ANY COLUMN IN MYSQL.PROC ALLOWS TO SEE DEFINITION OF ANY ROUTINE. The problem was that having the SELECT privilege any column of the mysql.proc table by mistake allowed the user to see the definition of all routines (using SHOW CREATE PROCEDURE/FUNCTION and SHOW PROCEDURE/FUNCTION CODE). This patch fixes the problem by making sure that those commands are only allowed if the user has the SELECT privilege on the mysql.proc table itself. Test case added to sp-security.test. --- mysql-test/r/sp-security.result | 30 ++++++++++++++++++++++++++++++ mysql-test/t/sp-security.test | 40 ++++++++++++++++++++++++++++++++++++++++ sql/sp_head.cc | 3 ++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 1451f8e88fd..04d11a35266 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -576,3 +576,33 @@ DROP USER 'tester'; DROP USER 'Tester'; DROP DATABASE B48872; End of 5.0 tests. +# +# Bug#11882603 SELECT_ACL ON ANY COLUMN IN MYSQL.PROC ALLOWS TO SEE +# DEFINITION OF ANY ROUTINE. +# +DROP DATABASE IF EXISTS db1; +CREATE DATABASE db1; +CREATE PROCEDURE db1.p1() SELECT 1; +CREATE USER user2@localhost IDENTIFIED BY ''; +GRANT SELECT(db) ON mysql.proc TO user2@localhost; +# Connection con2 as user2 +# The below statements before disclosed info from body_utf8 column. +SHOW CREATE PROCEDURE db1.p1; +ERROR 42000: PROCEDURE p1 does not exist +SHOW PROCEDURE CODE db1.p1; +ERROR 42000: PROCEDURE p1 does not exist +# Check that SHOW works with SELECT grant on whole table +# Connection default +GRANT SELECT ON mysql.proc TO user2@localhost; +# Connection con2 +# This should work +SHOW CREATE PROCEDURE db1.p1; +Procedure sql_mode Create Procedure +p1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() +SELECT 1 +SHOW PROCEDURE CODE db1.p1; +Pos Instruction +0 stmt 0 "SELECT 1" +# Connection default +DROP USER user2@localhost; +DROP DATABASE db1; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 3d41d90404d..dcbae756be9 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -950,6 +950,46 @@ DROP DATABASE B48872; --echo End of 5.0 tests. +--echo # +--echo # Bug#11882603 SELECT_ACL ON ANY COLUMN IN MYSQL.PROC ALLOWS TO SEE +--echo # DEFINITION OF ANY ROUTINE. +--echo # + +--disable_warnings +DROP DATABASE IF EXISTS db1; +--enable_warnings + +CREATE DATABASE db1; +CREATE PROCEDURE db1.p1() SELECT 1; +CREATE USER user2@localhost IDENTIFIED BY ''; +GRANT SELECT(db) ON mysql.proc TO user2@localhost; + +--echo # Connection con2 as user2 +connect (con2, localhost, user2); +--echo # The below statements before disclosed info from body_utf8 column. +--error ER_SP_DOES_NOT_EXIST +SHOW CREATE PROCEDURE db1.p1; +--error ER_SP_DOES_NOT_EXIST +SHOW PROCEDURE CODE db1.p1; + +--echo # Check that SHOW works with SELECT grant on whole table +--echo # Connection default +connection default; +GRANT SELECT ON mysql.proc TO user2@localhost; + +--echo # Connection con2 +connection con2; +--echo # This should work +SHOW CREATE PROCEDURE db1.p1; +SHOW PROCEDURE CODE db1.p1; + +--echo # Connection default +connection default; +disconnect con2; +DROP USER user2@localhost; +DROP DATABASE db1; + + # Wait till all disconnects are completed --source include/wait_until_count_sessions.inc diff --git a/sql/sp_head.cc b/sql/sp_head.cc index e32dd75486b..713b0b43ae1 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2168,7 +2168,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) bzero((char*) &tables,sizeof(tables)); tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "proc"; - *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1) || + *full_access= ((!check_table_access(thd, SELECT_ACL, &tables, TRUE) && + (tables.grant.privilege & SELECT_ACL) != 0) || (!strcmp(sp->m_definer_user.str, thd->security_ctx->priv_user) && !strcmp(sp->m_definer_host.str, -- cgit v1.2.1 From c75923e0795c61cb0eafcf799c91091bad80f6dc Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Mon, 11 Apr 2011 18:09:23 +0200 Subject: backport of Bug#11867664 from mysql-5.5 3381 Mattias Jonsson 2011-03-18 Bug#11867664: SERVER CRASHES ON UPDATE WITH JOIN ON PARTITIONED TABLE Regression from bug#11766232. m_last_part could be set beyond the last partition. Fixed by only setting it if within the limit. Also added check in print_error. --- sql/ha_partition.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index f55c48189fe..bd8e0d397c4 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -4317,7 +4317,8 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index, break; } } - m_last_part= part; + if (part <= m_part_spec.end_part) + m_last_part= part; } else { @@ -6237,7 +6238,14 @@ void ha_partition::print_error(int error, myf errflag) { /* In case m_file has not been initialized, like in bug#42438 */ if (m_file) + { + if (m_last_part >= m_tot_parts) + { + DBUG_ASSERT(0); + m_last_part= 0; + } m_file[m_last_part]->print_error(error, errflag); + } else handler::print_error(error, errflag); } -- cgit v1.2.1 From 7f5180598fc6c5222a675274d0a10d6bde04d114 Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Wed, 13 Apr 2011 09:54:51 +0200 Subject: Bug#11882603 SELECT_ACL ON ANY COLUMN IN MYSQL.PROC ALLOWS TO SEE DEFINITION OF ANY ROUTINE. This follow-up patch removes SHOW PROCEDURE CODE from the test case as this command is only available on debug versions of the server and therefore caused the test to fail on release builds. --- mysql-test/r/sp-security.result | 7 +------ mysql-test/t/sp-security.test | 5 +---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 04d11a35266..61ec3e80688 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -586,11 +586,9 @@ CREATE PROCEDURE db1.p1() SELECT 1; CREATE USER user2@localhost IDENTIFIED BY ''; GRANT SELECT(db) ON mysql.proc TO user2@localhost; # Connection con2 as user2 -# The below statements before disclosed info from body_utf8 column. +# The statement below before disclosed info from body_utf8 column. SHOW CREATE PROCEDURE db1.p1; ERROR 42000: PROCEDURE p1 does not exist -SHOW PROCEDURE CODE db1.p1; -ERROR 42000: PROCEDURE p1 does not exist # Check that SHOW works with SELECT grant on whole table # Connection default GRANT SELECT ON mysql.proc TO user2@localhost; @@ -600,9 +598,6 @@ SHOW CREATE PROCEDURE db1.p1; Procedure sql_mode Create Procedure p1 CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`() SELECT 1 -SHOW PROCEDURE CODE db1.p1; -Pos Instruction -0 stmt 0 "SELECT 1" # Connection default DROP USER user2@localhost; DROP DATABASE db1; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index dcbae756be9..3120cb76d14 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -966,11 +966,9 @@ GRANT SELECT(db) ON mysql.proc TO user2@localhost; --echo # Connection con2 as user2 connect (con2, localhost, user2); ---echo # The below statements before disclosed info from body_utf8 column. +--echo # The statement below before disclosed info from body_utf8 column. --error ER_SP_DOES_NOT_EXIST SHOW CREATE PROCEDURE db1.p1; ---error ER_SP_DOES_NOT_EXIST -SHOW PROCEDURE CODE db1.p1; --echo # Check that SHOW works with SELECT grant on whole table --echo # Connection default @@ -981,7 +979,6 @@ GRANT SELECT ON mysql.proc TO user2@localhost; connection con2; --echo # This should work SHOW CREATE PROCEDURE db1.p1; -SHOW PROCEDURE CODE db1.p1; --echo # Connection default connection default; -- cgit v1.2.1 From 8f449c36defd0ad1e25f212026a4929899458336 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 18 Apr 2011 17:04:01 +0300 Subject: Bug #11810224: CORRECT INVALID LGPL NOTICE IN CLUSTER FILES The 5.0 fix. Removed unreferenced files and the directory that has them. --- ndb/test/sql/test_create_drop.pl | 196 ------------------------------- ndb/test/sql/test_range_bounds.pl | 235 -------------------------------------- 2 files changed, 431 deletions(-) delete mode 100644 ndb/test/sql/test_create_drop.pl delete mode 100644 ndb/test/sql/test_range_bounds.pl diff --git a/ndb/test/sql/test_create_drop.pl b/ndb/test/sql/test_create_drop.pl deleted file mode 100644 index eb9d5e31dc8..00000000000 --- a/ndb/test/sql/test_create_drop.pl +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (C) 2005 MySQL AB -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU Library 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 -# Library General Public License for more details. -# -# You should have received a copy of the GNU Library General Public -# License along with this library; if not, write to the Free -# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, -# MA 02111-1307, USA - -use strict; -use IO::Socket; -use DBI; - -# mgm info -my $mgmhost = "localhost"; -my $mgmport = 38101; - -# location of ndb_x_fs -my $datadir = "c2"; -my @schemafiles = <$datadir/ndb_*_fs/D[12]/DBDICT/P0.SchemaLog>; -@schemafiles or die "no schemafiles in $datadir"; - -my $dsn; -$dsn = "dbi:mysql:test:localhost;port=38100"; - -# this works better for me -my $cnf = $ENV{MYSQL_HOME} . "/var/my.cnf"; -$dsn = "dbi:mysql:database=test;host=localhost;mysql_read_default_file=$cnf"; - -my $dbh; -$dbh = DBI->connect($dsn, 'root', undef, { RaiseError => 0, PrintError => 0 }); -$dbh or die $DBI::errstr; - -# mgm commands - -my $mgm = undef; - -sub mgmconnect { - $mgm = IO::Socket::INET->new( - Proto => "tcp", - PeerHost => $mgmhost, - PeerPort => $mgmport); - $mgm or die "connect to mgm failed: $!"; - $mgm->autoflush(1); -}; - -mgmconnect(); -warn "connected to mgm $mgmhost $mgmport\n"; - -my $nodeinfo = {}; - -sub getnodeinfo { - $nodeinfo = {}; - $mgm->print("get status\n"); - $mgm->print("\n"); - while (defined($_ = $mgm->getline)) { - /^node\s+status/ && last; - } - while (defined($_ = $mgm->getline)) { - /^\s*$/ && last; - /^node\.(\d+)\.(\w+):\s*(\S+)/ && ($nodeinfo->{$1}{$2} = $3); - } -} - -getnodeinfo(); - -my @dbnode = (); -for my $n (keys %$nodeinfo) { - my $p = $nodeinfo->{$n}; - ($p->{type} eq 'NDB') && push(@dbnode, $n); -} -@dbnode = sort { $a <=> $b } @dbnode; -@dbnode or die "mgm error, found no db nodes"; -warn "db nodes: @dbnode\n"; - -sub restartnode { - my($n, $initialstart) = @_; - warn "restart node $n initialstart=$initialstart\n"; - $mgm->print("restart node\n"); - $mgm->print("node: $n\n"); - $mgm->print("initialstart: $initialstart\n"); - $mgm->print("\n"); - while (1) { - sleep 5; - getnodeinfo(); - my $status = $nodeinfo->{$n}{status}; - my $sp = $nodeinfo->{$n}{startphase}; - warn "node $n status: $status sp: $sp\n"; - last if $status eq 'STARTED'; - } -} - -sub restartall { - warn "restart all\n"; - $mgm->print("restart all\n"); - $mgm->print("\n"); - while (1) { - sleep 5; - getnodeinfo(); - my $ok = 1; - for my $n (@dbnode) { - my $status = $nodeinfo->{$n}{status}; - my $sp = $nodeinfo->{$n}{startphase}; - warn "node $n status: $status sp: $sp\n"; - $ok = 0 if $status ne 'STARTED'; - } - last if $ok; - } -} - -# the sql stuff - -my $maxtab = 300; -my @tab = (); - -sub create { - my($n) = @_; - my $sql = "create table t$n (a int primary key, b varchar(20), key (b)) engine=ndb"; - warn "create t$n\n"; - $dbh->do($sql) or die "$sql\n$DBI::errstr"; -} - -sub drop { - my($n) = @_; - my $sql = "drop table t$n"; - warn "drop t$n\n"; - $dbh->do($sql) or die "$sql\n$DBI::errstr"; -} - -sub dropall { - for my $n (0..($maxtab-1)) { - my $sql = "drop table if exists t$n"; - $dbh->do($sql) or die "$sql\n$DBI::errstr"; - } -} - -sub createdrop { - my $n = int(rand($maxtab)); - if (! $tab[$n]) { - create($n); - $tab[$n] = 1; - } else { - drop($n); - $tab[$n] = 0; - } -} - -sub checkschemafiles { - system("printSchemaFile -ce @schemafiles"); - $? == 0 or die "schemafiles check failed"; -} - -sub randomrestart { - my($k) = @_; - my $s = int(rand(500)); - if ($s < 2) { - my $i = $k % scalar(@dbnode); - my $n = $dbnode[$i]; - my $initialstart = ($s < 1 ? 0 : 1); - restartnode($n, $initialstart); - return 1; - } - if ($s < 3) { - restartall(); - return 1; - } - return 0; -} - -# deterministic -srand(1); - -warn "drop any old tables\n"; -dropall(); - -my $loop = 1000000; -for my $k (0..($loop-1)) { - warn "$k\n"; - createdrop(); - checkschemafiles(); - if (randomrestart($k)) { - checkschemafiles(); - } -} - -$dbh->disconnect or die $DBI::errstr; - -# vim: set sw=2: diff --git a/ndb/test/sql/test_range_bounds.pl b/ndb/test/sql/test_range_bounds.pl deleted file mode 100644 index 964847044de..00000000000 --- a/ndb/test/sql/test_range_bounds.pl +++ /dev/null @@ -1,235 +0,0 @@ -# Copyright (C) 2005 MySQL AB -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU Library 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 -# Library General Public License for more details. -# -# You should have received a copy of the GNU Library General Public -# License along with this library; if not, write to the Free -# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, -# MA 02111-1307, USA - -# -# test range scan bounds -# give option --all to test all cases -# set MYSQL_HOME to installation top -# - -use strict; -use integer; -use Getopt::Long; -use DBI; - -my $opt_all = 0; -my $opt_cnt = 5; -my $opt_verbose = 0; -GetOptions("all" => \$opt_all, "cnt=i" => \$opt_cnt, "verbose" => \$opt_verbose) - or die "options are: --all --cnt=N --verbose"; - -my $mysql_home = $ENV{MYSQL_HOME}; -defined($mysql_home) or die "no MYSQL_HOME"; -my $dsn = "dbi:mysql:database=test;host=localhost;mysql_read_default_file=$mysql_home/var/my.cnf"; -my $opts = { RaiseError => 0, PrintError => 0, AutoCommit => 1, }; - -my $dbh; -my $sth; -my $sql; - -$dbh = DBI->connect($dsn, "root", undef, $opts) or die $DBI::errstr; - -my $table = 't'; - -$sql = "drop table if exists $table"; -$dbh->do($sql) or die $DBI::errstr; - -sub cut ($$$) { - my($op, $key, $val) = @_; - $op = '==' if $op eq '='; - my(@w) = @$val; - eval "\@w = grep(\$_ $op $key, \@w)"; - $@ and die $@; - return [ @w ]; -} - -sub mkdummy ($) { - my ($val) = @_; - return { - 'dummy' => 1, - 'exp' => '9 = 9', - 'res' => $val, - }; -} - -sub mkone ($$$$) { - my($col, $op, $key, $val) = @_; - my $res = cut($op, $key, $val); - return { - 'exp' => "$col $op $key", - 'res' => $res, - }; -} - -sub mktwo ($$$$$$) { - my($col, $op1, $key1, $op2, $key2, $val) = @_; - my $res = cut($op2, $key2, cut($op1, $key1, $val)); - return { - 'exp' => "$col $op1 $key1 and $col $op2 $key2", - 'res' => $res, - }; -} - -sub mkall ($$$$) { - my($col, $key1, $key2, $val) = @_; - my @a = (); - my $p = mkdummy($val); - push(@a, $p) if $opt_all; - my @ops = qw(< <= = >= >); - for my $op (@ops) { - my $p = mkone($col, $op, $key1, $val); - push(@a, $p) if $opt_all || @{$p->{res}} != 0; - } - my @ops1 = $opt_all ? @ops : qw(= >= >); - my @ops2 = $opt_all ? @ops : qw(<= <); - for my $op1 (@ops1) { - for my $op2 (@ops2) { - my $p = mktwo($col, $op1, $key1, $op2, $key2, $val); - push(@a, $p) if $opt_all || @{$p->{res}} != 0; - } - } - warn scalar(@a)." cases\n" if $opt_verbose; - return \@a; -} - -my $casecnt = 0; - -sub verify ($$$) { - my($sql, $ord, $res) = @_; - warn "$sql\n" if $opt_verbose; - $sth = $dbh->prepare($sql) or die "prepare: $sql: $DBI::errstr"; - $sth->execute() or die "execute: $sql: $DBI::errstr"; - # - # BUG: execute can return success on error so check again - # - $sth->err and die "execute: $sql: $DBI::errstr"; - my @out = (); - for my $b (@{$res->[0]}) { - for my $c (@{$res->[1]}) { - for my $d (@{$res->[2]}) { - push(@out, [$b, $c, $d]); - } - } - } - if ($ord) { - @out = sort { - $ord * ($a->[0] - $b->[0]) || - $ord * ($a->[1] - $b->[1]) || - $ord * ($a->[2] - $b->[2]) || - 0 - } @out; - } - my $cnt = scalar @out; - my $n = 0; - while (1) { - my $row = $sth->fetchrow_arrayref; - $row || last; - @$row == 3 or die "bad row: $sql: @$row"; - for my $v (@$row) { - $v =~ s/^\s+|\s+$//g; - $v =~ /^\d+$/ or die "bad value: $sql: $v"; - } - if ($ord) { - my $out = $out[$n]; - $row->[0] == $out->[0] && - $row->[1] == $out->[1] && - $row->[2] == $out->[2] or - die "$sql: row $n: got row @$row != @$out"; - } - $n++; - } - $sth->err and die "fetch: $sql: $DBI::errstr"; - $n == $cnt or die "verify: $sql: got row count $n != $cnt"; - $casecnt++; -} - -for my $nn ("bcd", "") { - my %nn; - for my $x (qw(b c d)) { - $nn{$x} = $nn =~ /$x/ ? "not null" : "null"; - } - warn "create table\n"; - $sql = <do($sql) or die $DBI::errstr; - warn "insert\n"; - $sql = "insert into $table values(?, ?, ?, ?)"; - $sth = $dbh->prepare($sql) or die $DBI::errstr; - my @val = (0..($opt_cnt-1)); - my $v0 = 0; - for my $v1 (@val) { - for my $v2 (@val) { - for my $v3 (@val) { - $sth->bind_param(1, $v0) or die $DBI::errstr; - $sth->bind_param(2, $v1) or die $DBI::errstr; - $sth->bind_param(3, $v2) or die $DBI::errstr; - $sth->bind_param(4, $v3) or die $DBI::errstr; - $sth->execute or die $DBI::errstr; - $v0++; - } - } - } - warn "generate cases\n"; - my $key1 = 1; - my $key2 = 3; - my $a1 = mkall('b', $key1, $key2, \@val); - my $a2 = mkall('c', $key1, $key2, \@val); - my $a3 = mkall('d', $key1, $key2, \@val); - warn "select\n"; - for my $ord (0, +1, -1) { - my $orderby = - $ord == 0 ? "" : - $ord == +1 ? " order by b, c, d" : - $ord == -1 ? " order by b desc, c desc, d desc" : die "not here"; - for my $p1 (@$a1) { - my $res = [ $p1->{res}, \@val, \@val ]; - $sql = "select b, c, d from $table" . - " where $p1->{exp}" . - $orderby; - verify($sql, $ord, $res); - for my $p2 (@$a2) { - my $res = [ $p1->{res}, $p2->{res}, \@val ]; - $sql = "select b, c, d from $table" . - " where $p1->{exp} and $p2->{exp}" . - $orderby; - verify($sql, $ord, $res); - for my $p3 (@$a3) { - my $res = [ $p1->{res}, $p2->{res}, $p3->{res} ]; - $sql = "select b, c, d from $table" . - " where $p1->{exp} and $p2->{exp} and $p3->{exp}" . - $orderby; - verify($sql, $ord, $res); - } - } - } - } - warn "drop table\n"; - $sql = "drop table $table"; - $dbh->do($sql) or die $DBI::errstr; -} - -warn "verified $casecnt cases\n"; -warn "done\n"; - -# vim: set sw=2: -- cgit v1.2.1 From 1019b95877bd1a8f581e58a9f2d52ec52d23e5b7 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Thu, 28 Apr 2011 09:03:56 +0200 Subject: Bug#12340997 - DATE_ADD/DATE_SUB WITH INTERVAL CRASHES IN GET_INTERVAL_VALUE() get_interval_value() was trying to parse the input string, looking for leading '-' while skipping whitespace. The macro my_isspace() does not work for utf16 character set, since my_charset_utf16_general_ci.ctype == NULL. Solution: convert input to ASCII before parsing. --- mysql-test/r/ctype_utf16.result | 9 +++++++++ mysql-test/t/ctype_utf16.test | 7 +++++++ sql/item_timefunc.cc | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ctype_utf16.result b/mysql-test/r/ctype_utf16.result index ebb1bd443a2..dfffd8142de 100644 --- a/mysql-test/r/ctype_utf16.result +++ b/mysql-test/r/ctype_utf16.result @@ -1117,5 +1117,14 @@ CASE s1 WHEN 'a' THEN 'b' ELSE 'c' END b DROP TABLE t1; # +# Bug#12340997 +# DATE_ADD/DATE_SUB WITH INTERVAL CRASHES IN GET_INTERVAL_VALUE() +# +SELECT space(date_add(101, INTERVAL CHAR('1' USING utf16) hour_second)); +space(date_add(101, INTERVAL CHAR('1' USING utf16) hour_second)) +NULL +Warnings: +Warning 1301 Result of repeat() was larger than max_allowed_packet (1048576) - truncated +# # End of 5.5 tests # diff --git a/mysql-test/t/ctype_utf16.test b/mysql-test/t/ctype_utf16.test index 2f1651d0f40..4ac47a4ac53 100644 --- a/mysql-test/t/ctype_utf16.test +++ b/mysql-test/t/ctype_utf16.test @@ -755,6 +755,13 @@ INSERT INTO t1 VALUES ('a'); SELECT CASE s1 WHEN 'a' THEN 'b' ELSE 'c' END FROM t1; DROP TABLE t1; +--echo # +--echo # Bug#12340997 +--echo # DATE_ADD/DATE_SUB WITH INTERVAL CRASHES IN GET_INTERVAL_VALUE() +--echo # + +SELECT space(date_add(101, INTERVAL CHAR('1' USING utf16) hour_second)); + # ## TODO: add tests for all engines # diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 19e7b3f52ef..36b85f2411f 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1430,7 +1430,7 @@ bool get_interval_value(Item *args,interval_type int_type, else { String *res; - if (!(res=args->val_str(str_value))) + if (!(res= args->val_str_ascii(str_value))) return (1); /* record negative intervalls in interval->neg */ -- cgit v1.2.1 From 4c5dfc00f7a2ea3cf9475455e2749a96bfc38344 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 28 Apr 2011 12:22:41 +0300 Subject: Bug #11764517: 57359: POSSIBLE TO CIRCUMVENT SECURE_FILE_PRIV USING '..' ON WINDOWS Backport of the fix to 5.0 (to be null-merged to 5.1). Moved the test into the main test suite. Made mysql-test-run.pl to not use symlinks for sdtdata as the symlinks are now properly recognized by secure_file_priv. Made sure the paths in load_file(), LOAD DATA and SELECT .. INTO OUTFILE that are checked against secure_file_priv in a correct way similarly to 5.1 by the extended is_secure_file_path() backport before the comparison. Added an extensive test with all the variants of upper/lower case, slash/backslash and case sensitivity. Added few comments to the code. --- mysql-test/mysql-test-run.pl | 14 ++--- mysql-test/r/secure_file_priv_win.result | 38 +++++++++++++ mysql-test/t/secure_file_priv_win-master.opt | 1 + mysql-test/t/secure_file_priv_win.test | 79 ++++++++++++++++++++++++++++ mysys/my_symlink.c | 17 +++++- sql/item_strfunc.cc | 3 +- sql/mysql_priv.h | 2 + sql/mysqld.cc | 58 ++++++++++++++++++++ sql/sql_class.cc | 3 +- sql/sql_load.cc | 48 ++++++++--------- 10 files changed, 223 insertions(+), 40 deletions(-) create mode 100644 mysql-test/r/secure_file_priv_win.result create mode 100644 mysql-test/t/secure_file_priv_win-master.opt create mode 100644 mysql-test/t/secure_file_priv_win.test diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index f0100593516..f43cadd3784 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -2412,17 +2412,9 @@ sub setup_vardir() { mkpath("$data_dir/test"); } - # Make a link std_data_ln in var/ that points to std_data - if ( ! $glob_win32 ) - { - symlink("$glob_mysql_test_dir/std_data", "$opt_vardir/std_data_ln"); - } - else - { - # on windows, copy all files from std_data into var/std_data_ln - mkpath("$opt_vardir/std_data_ln"); - mtr_copy_dir("$glob_mysql_test_dir/std_data", "$opt_vardir/std_data_ln"); - } + # copy all files from std_data into var/std_data_ln + mkpath("$opt_vardir/std_data_ln"); + mtr_copy_dir("$glob_mysql_test_dir/std_data", "$opt_vardir/std_data_ln"); # Remove old log files foreach my $name (glob("r/*.progress r/*.log r/*.warnings")) diff --git a/mysql-test/r/secure_file_priv_win.result b/mysql-test/r/secure_file_priv_win.result new file mode 100644 index 00000000000..497a5d04b1f --- /dev/null +++ b/mysql-test/r/secure_file_priv_win.result @@ -0,0 +1,38 @@ +CREATE TABLE t1 (c1 longtext); +INSERT INTO t1 values ('a'); +SELECT * FROM t1 INTO OUTFILE 'd:/mysql/work/test-5.0-security/mysql-test/var/tmp/B11764517.tmp'; +show global variables like 'secure_file_priv'; +Variable_name Value +secure_file_priv MYSQL_TMP_DIR/ +SELECT load_file('MYSQL_TMP_DIR\\B11764517.tmp') AS x; +x +a + +SELECT load_file('MYSQL_TMP_DIR/B11764517.tmp') AS x; +x +a + +SELECT load_file('MYSQL_TMP_DIR_UCASE/B11764517.tmp') AS x; +x +a + +SELECT load_file('MYSQL_TMP_DIR_LCASE/B11764517.tmp') AS x; +x +a + +SELECT load_file('MYSQL_TMP_DIR\\..a..\\..\\..\\B11764517.tmp') AS x; +x +NULL +LOAD DATA INFILE 'MYSQL_TMP_DIR\\B11764517.tmp' INTO TABLE t1; +LOAD DATA INFILE 'MYSQL_TMP_DIR/B11764517.tmp' INTO TABLE t1; +LOAD DATA INFILE 'MYSQL_TMP_DIR_UCASE/B11764517.tmp' INTO TABLE t1; +LOAD DATA INFILE 'MYSQL_TMP_DIR_LCASE/B11764517.tmp' INTO TABLE t1; +LOAD DATA INFILE "MYSQL_TMP_DIR\\..a..\\..\\..\\B11764517.tmp" into table t1; +ERROR HY000: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement +SELECT * FROM t1 INTO OUTFILE 'MYSQL_TMP_DIR\\..a..\\..\\..\\B11764517-2.tmp'; +ERROR HY000: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement +SELECT * FROM t1 INTO OUTFILE 'MYSQL_TMP_DIR\\B11764517-2.tmp'; +SELECT * FROM t1 INTO OUTFILE 'MYSQL_TMP_DIR/B11764517-3.tmp'; +SELECT * FROM t1 INTO OUTFILE 'MYSQL_TMP_DIR_UCASE/B11764517-4.tmp'; +SELECT * FROM t1 INTO OUTFILE 'MYSQL_TMP_DIR_LCASE/B11764517-5.tmp'; +DROP TABLE t1; diff --git a/mysql-test/t/secure_file_priv_win-master.opt b/mysql-test/t/secure_file_priv_win-master.opt new file mode 100644 index 00000000000..e9a43a5584d --- /dev/null +++ b/mysql-test/t/secure_file_priv_win-master.opt @@ -0,0 +1 @@ +--secure_file_priv=$MYSQL_TMP_DIR diff --git a/mysql-test/t/secure_file_priv_win.test b/mysql-test/t/secure_file_priv_win.test new file mode 100644 index 00000000000..07e012e42b4 --- /dev/null +++ b/mysql-test/t/secure_file_priv_win.test @@ -0,0 +1,79 @@ +# +# Bug58747 breaks secure_file_priv+not secure yet+still accesses other folders +# + +# we do the windows specific relative directory testing + +--source include/windows.inc + +CREATE TABLE t1 (c1 longtext); +INSERT INTO t1 values ('a'); + +LET $MYSQL_TMP_DIR_UCASE= `SELECT upper('$MYSQL_TMP_DIR')`; +LET $MYSQL_TMP_DIR_LCASE= `SELECT lower('$MYSQL_TMP_DIR')`; + +#create the file +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SELECT * FROM t1 INTO OUTFILE '$MYSQL_TMP_DIR_LCASE/B11764517.tmp'; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +show global variables like 'secure_file_priv'; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SELECT load_file('$MYSQL_TMP_DIR\\\\B11764517.tmp') AS x; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SELECT load_file('$MYSQL_TMP_DIR/B11764517.tmp') AS x; + +--replace_result $MYSQL_TMP_DIR_UCASE MYSQL_TMP_DIR_UCASE +eval SELECT load_file('$MYSQL_TMP_DIR_UCASE/B11764517.tmp') AS x; + +--replace_result $MYSQL_TMP_DIR_LCASE MYSQL_TMP_DIR_LCASE +eval SELECT load_file('$MYSQL_TMP_DIR_LCASE/B11764517.tmp') AS x; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SELECT load_file('$MYSQL_TMP_DIR\\\\..a..\\\\..\\\\..\\\\B11764517.tmp') AS x; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval LOAD DATA INFILE '$MYSQL_TMP_DIR\\\\B11764517.tmp' INTO TABLE t1; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval LOAD DATA INFILE '$MYSQL_TMP_DIR/B11764517.tmp' INTO TABLE t1; + +--replace_result $MYSQL_TMP_DIR_UCASE MYSQL_TMP_DIR_UCASE +eval LOAD DATA INFILE '$MYSQL_TMP_DIR_UCASE/B11764517.tmp' INTO TABLE t1; + +--replace_result $MYSQL_TMP_DIR_LCASE MYSQL_TMP_DIR_LCASE +eval LOAD DATA INFILE '$MYSQL_TMP_DIR_LCASE/B11764517.tmp' INTO TABLE t1; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--error ER_OPTION_PREVENTS_STATEMENT +eval LOAD DATA INFILE "$MYSQL_TMP_DIR\\\\..a..\\\\..\\\\..\\\\B11764517.tmp" into table t1; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--error ER_OPTION_PREVENTS_STATEMENT +eval SELECT * FROM t1 INTO OUTFILE '$MYSQL_TMP_DIR\\\\..a..\\\\..\\\\..\\\\B11764517-2.tmp'; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SELECT * FROM t1 INTO OUTFILE '$MYSQL_TMP_DIR\\\\B11764517-2.tmp'; + +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +eval SELECT * FROM t1 INTO OUTFILE '$MYSQL_TMP_DIR/B11764517-3.tmp'; + +--replace_result $MYSQL_TMP_DIR_UCASE MYSQL_TMP_DIR_UCASE +eval SELECT * FROM t1 INTO OUTFILE '$MYSQL_TMP_DIR_UCASE/B11764517-4.tmp'; + +--replace_result $MYSQL_TMP_DIR_LCASE MYSQL_TMP_DIR_LCASE +eval SELECT * FROM t1 INTO OUTFILE '$MYSQL_TMP_DIR_LCASE/B11764517-5.tmp'; + +--error 0,1 +--remove_file $MYSQL_TMP_DIR/B11764517.tmp; +--error 0,1 +--remove_file $MYSQL_TMP_DIR/B11764517-2.tmp; +--error 0,1 +--remove_file $MYSQL_TMP_DIR/B11764517-3.tmp; +--error 0,1 +--remove_file $MYSQL_TMP_DIR/B11764517-4.tmp; +--error 0,1 +--remove_file $MYSQL_TMP_DIR/B11764517-5.tmp; +DROP TABLE t1; diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c index 7f2be5644e8..e17cd8bbe0c 100644 --- a/mysys/my_symlink.c +++ b/mysys/my_symlink.c @@ -149,8 +149,23 @@ int my_realpath(char *to, const char *filename, result= -1; } DBUG_RETURN(result); +#elif defined(_WIN32) + int ret= GetFullPathName(filename,FN_REFLEN, to, NULL); + if (ret == 0 || ret > FN_REFLEN) + { + my_errno= (ret > FN_REFLEN) ? ENAMETOOLONG : GetLastError(); + if (MyFlags & MY_WME) + my_error(EE_REALPATH, MYF(0), filename, my_errno); + /* + GetFullPathName didn't work : use my_load_path() which is a poor + substitute original name but will at least be able to resolve + paths that starts with '.'. + */ + my_load_path(to, filename, NullS); + return -1; + } #else my_load_path(to, filename, NullS); +#endif return 0; -#endif } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 6f697a1665a..8f9a04329d3 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2843,8 +2843,7 @@ String *Item_load_file::val_str(String *str) MY_RELATIVE_PATH | MY_UNPACK_FILENAME); /* Read only allowed from within dir specified by secure_file_priv */ - if (opt_secure_file_priv && - strncmp(opt_secure_file_priv, path, strlen(opt_secure_file_priv))) + if (!is_secure_file_path(path)) goto err; if (!my_stat(path, &stat_info, MYF(0))) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index d88e629b91b..a811bbafdb6 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1264,6 +1264,8 @@ bool init_errmessage(void); bool fn_format_relative_to_data_home(my_string to, const char *name, const char *dir, const char *extension); +bool is_secure_file_path(char *path); + File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f026bab1c32..3291085f380 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7855,6 +7855,64 @@ fn_format_relative_to_data_home(my_string to, const char *name, } +/** + Test a file path to determine if the path is compatible with the secure file + path restriction. + + @param path null terminated character string + + @return + @retval TRUE The path is secure + @retval FALSE The path isn't secure +*/ + +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 + */ + if (!opt_secure_file_priv) + return TRUE; + + opt_secure_file_priv_len= strlen(opt_secure_file_priv); + + if (strlen(path) >= FN_REFLEN) + return FALSE; + + if (my_realpath(buff1, path, 0)) + { + /* + The supplied file path might have been a file and not a directory. + */ + int length= (int) dirname_length(path); + if (length >= FN_REFLEN) + return FALSE; + memcpy(buff2, path, length); + buff2[length]= '\0'; + if (length == 0 || my_realpath(buff1, buff2, 0)) + return FALSE; + } + convert_dirname(buff2, buff1, NullS); + if (!lower_case_file_system) + { + if (strncmp(opt_secure_file_priv, buff2, opt_secure_file_priv_len)) + return FALSE; + } + else + { + if (files_charset_info->coll->strnncoll(files_charset_info, + (uchar *) buff2, strlen(buff2), + (uchar *) opt_secure_file_priv, + opt_secure_file_priv_len, + TRUE)) + return FALSE; + } + return TRUE; +} + + static void fix_paths(void) { char buff[FN_REFLEN],*pos; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 06f2229a050..cd2f2029ca2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1211,8 +1211,7 @@ static File create_file(THD *thd, char *path, sql_exchange *exchange, else (void) fn_format(path, exchange->file_name, mysql_real_data_home, "", option); - if (opt_secure_file_priv && - strncmp(opt_secure_file_priv, path, strlen(opt_secure_file_priv))) + if (!is_secure_file_path(path)) { /* Write only allowed to dir or subdir specified by secure_file_priv */ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 83af6d477db..9cead8c0ff1 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -287,36 +287,36 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { (void) fn_format(name, ex->file_name, mysql_real_data_home, "", MY_RELATIVE_PATH | MY_UNPACK_FILENAME); + } + + if (!is_secure_file_path(name)) + { + /* Read only allowed from within dir specified by secure_file_priv */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); + DBUG_RETURN(TRUE); + } + #if !defined(__WIN__) && !defined(OS2) && ! defined(__NETWARE__) - MY_STAT stat_info; - if (!my_stat(name,&stat_info,MYF(MY_WME))) - DBUG_RETURN(TRUE); + MY_STAT stat_info; + if (!my_stat(name, &stat_info, MYF(MY_WME))) + DBUG_RETURN(TRUE); - // if we are not in slave thread, the file must be: - if (!thd->slave_thread && - !((stat_info.st_mode & S_IROTH) == S_IROTH && // readable by others + // if we are not in slave thread, the file must be: + if (!thd->slave_thread && + !((stat_info.st_mode & S_IROTH) == S_IROTH && // readable by others #ifndef __EMX__ - (stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink + (stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink #endif - ((stat_info.st_mode & S_IFREG) == S_IFREG || - (stat_info.st_mode & S_IFIFO) == S_IFIFO))) - { - my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), name); - DBUG_RETURN(TRUE); - } - if ((stat_info.st_mode & S_IFIFO) == S_IFIFO) - is_fifo = 1; + ((stat_info.st_mode & S_IFREG) == S_IFREG || // and a regular file + (stat_info.st_mode & S_IFIFO) == S_IFIFO))) // or FIFO + { + my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), name); + DBUG_RETURN(TRUE); + } + if ((stat_info.st_mode & S_IFIFO) == S_IFIFO) + is_fifo= 1; #endif - if (opt_secure_file_priv && - strncmp(opt_secure_file_priv, name, strlen(opt_secure_file_priv))) - { - /* Read only allowed from within dir specified by secure_file_priv */ - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv"); - DBUG_RETURN(TRUE); - } - - } if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) < 0) DBUG_RETURN(TRUE); } -- cgit v1.2.1 From d33bed3c23259bd4a45d909ffa26b88176a02978 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Tue, 3 May 2011 10:48:24 +0300 Subject: Bug #12375190: UPDATEXML CRASHES ON SIMPLE INPUTS The XPATH implementation was not handling correctly the XPATH production #19 (http://www.w3.org/TR/1999/REC-xpath-19991116/#node-sets), namely PathExpr ::= | FilterExpr '/' RelativeLocationPath | FilterExpr '//' RelativeLocationPath It was lacking context for the RelativeLocationPath and it was just ignoring the second slash instead of treating it as a different axis specifier. Fixed the above two problems and added a test case. --- mysql-test/r/xml.result | 18 ++++++++++++++++++ mysql-test/t/xml.test | 9 +++++++++ sql/item_xmlfunc.cc | 21 +++++++++++++++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/xml.result b/mysql-test/r/xml.result index 8ca9ab84bf7..30aa77ddf37 100644 --- a/mysql-test/r/xml.result +++ b/mysql-test/r/xml.result @@ -1144,5 +1144,23 @@ SELECT UPDATEXML(CONVERT('' USING swe7), TRUNCATE('',1), 0); UPDATEXML(CONVERT('' USING swe7), TRUNCATE('',1), 0) NULL # +# Bug#12375190: UPDATEXML CRASHES ON SIMPLE INPUTS +# +SELECT UPDATEXML('','(a)/a',''); +UPDATEXML('','(a)/a','') + +SELECT UPDATEXML('x','(a)/a',''); +UPDATEXML('x','(a)/a','') + +SELECT UPDATEXML('x','(a)/a',''); +UPDATEXML('x','(a)/a','') +x +SELECT UPDATEXML('x','(a)//a',''); +UPDATEXML('x','(a)//a','') + +SELECT ExtractValue('aabb','(a)/a|(a)/b'); +ExtractValue('aabb','(a)/a|(a)/b') +aa bb +# # End of 5.5 tests # diff --git a/mysql-test/t/xml.test b/mysql-test/t/xml.test index 89c0b8992b1..b36bd2067cc 100644 --- a/mysql-test/t/xml.test +++ b/mysql-test/t/xml.test @@ -664,6 +664,15 @@ SELECT ExtractValue(CONVERT('<\"', BINARY(10)), 1); SET NAMES latin1; SELECT UPDATEXML(CONVERT('' USING swe7), TRUNCATE('',1), 0); +--echo # +--echo # Bug#12375190: UPDATEXML CRASHES ON SIMPLE INPUTS +--echo # +SELECT UPDATEXML('','(a)/a',''); +SELECT UPDATEXML('x','(a)/a',''); +SELECT UPDATEXML('x','(a)/a',''); +SELECT UPDATEXML('x','(a)//a',''); +SELECT ExtractValue('aabb','(a)/a|(a)/b'); + --echo # --echo # End of 5.5 tests --echo # diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 49d18b1bb0f..531b9e11cb5 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -1973,6 +1973,9 @@ static int my_xpath_parse_UnionExpr(MY_XPATH *xpath) static int my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath) { + Item *context= xpath->context; + int rc; + if (!my_xpath_parse_FilterExpr(xpath)) return 0; @@ -1986,8 +1989,22 @@ my_xpath_parse_FilterExpr_opt_slashes_RelativeLocationPath(MY_XPATH *xpath) return 0; } - my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH); - return my_xpath_parse_RelativeLocationPath(xpath); + /* + The context for the next relative path is the nodeset + returned by FilterExpr + */ + xpath->context= xpath->item; + + /* treat double slash (//) as /descendant-or-self::node()/ */ + if (my_xpath_parse_term(xpath, MY_XPATH_LEX_SLASH)) + xpath->context= new Item_nodeset_func_descendantbyname(xpath->context, + "*", 1, xpath->pxml, 1); + rc= my_xpath_parse_RelativeLocationPath(xpath); + + /* push back the context and restore the item */ + xpath->item= xpath->context; + xpath->context= context; + return rc; } static int my_xpath_parse_PathExpr(MY_XPATH *xpath) { -- cgit v1.2.1 From faad8227408e42ab2c31cf97a5a9592b24890330 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 4 May 2011 15:47:29 +0300 Subject: Addendum to the fix for bug #11764517 : replaced an absolute path. --- mysql-test/r/secure_file_priv_win.result | 2 +- mysql-test/t/secure_file_priv_win.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/secure_file_priv_win.result b/mysql-test/r/secure_file_priv_win.result index 497a5d04b1f..d6636aad5d4 100644 --- a/mysql-test/r/secure_file_priv_win.result +++ b/mysql-test/r/secure_file_priv_win.result @@ -1,6 +1,6 @@ CREATE TABLE t1 (c1 longtext); INSERT INTO t1 values ('a'); -SELECT * FROM t1 INTO OUTFILE 'd:/mysql/work/test-5.0-security/mysql-test/var/tmp/B11764517.tmp'; +SELECT * FROM t1 INTO OUTFILE 'MYSQL_TMP_DIR/B11764517.tmp'; show global variables like 'secure_file_priv'; Variable_name Value secure_file_priv MYSQL_TMP_DIR/ diff --git a/mysql-test/t/secure_file_priv_win.test b/mysql-test/t/secure_file_priv_win.test index 07e012e42b4..a12510974ce 100644 --- a/mysql-test/t/secure_file_priv_win.test +++ b/mysql-test/t/secure_file_priv_win.test @@ -14,7 +14,7 @@ LET $MYSQL_TMP_DIR_LCASE= `SELECT lower('$MYSQL_TMP_DIR')`; #create the file --replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR -eval SELECT * FROM t1 INTO OUTFILE '$MYSQL_TMP_DIR_LCASE/B11764517.tmp'; +eval SELECT * FROM t1 INTO OUTFILE '$MYSQL_TMP_DIR/B11764517.tmp'; --replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR show global variables like 'secure_file_priv'; -- cgit v1.2.1 From a32df762d43c29545718e8a78a1721a9a7a1a35f Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Wed, 4 May 2011 16:18:21 +0200 Subject: Bug#12329653 - EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY The query was re-written *after* we had tagged it with NON_AGG_FIELD_USED. Remove the flag before continuing. --- mysql-test/r/explain.result | 11 ++++++----- mysql-test/r/subselect.result | 26 ++++++++++++++++++++++++++ mysql-test/t/explain.test | 9 +++++---- mysql-test/t/subselect.test | 34 ++++++++++++++++++++++++++++++++++ sql/item.cc | 4 ++-- sql/item_subselect.cc | 8 ++++++++ sql/item_sum.cc | 6 +++--- sql/mysql_priv.h | 7 ------- sql/sql_lex.cc | 5 ++++- sql/sql_lex.h | 26 ++++++++++++++++---------- sql/sql_select.cc | 12 ++++++------ 11 files changed, 110 insertions(+), 38 deletions(-) diff --git a/mysql-test/r/explain.result b/mysql-test/r/explain.result index 90a4136d030..da2cc51b59b 100644 --- a/mysql-test/r/explain.result +++ b/mysql-test/r/explain.result @@ -176,11 +176,12 @@ SELECT @@session.sql_mode INTO @old_sql_mode; SET SESSION sql_mode='ONLY_FULL_GROUP_BY'; EXPLAIN EXTENDED SELECT 1 FROM t1 WHERE f1 > ALL( SELECT t.f1 FROM t1,t1 AS t ); -ERROR 42000: Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause -SHOW WARNINGS; -Level Code Message -Error 1140 Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause -Note 1003 select 1 AS `1` from `test`.`t1` where ((...)) +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +2 SUBQUERY t1 system NULL NULL NULL NULL 0 const row not found +2 SUBQUERY t system NULL NULL NULL NULL 0 const row not found +Warnings: +Note 1003 select 1 AS `1` from `test`.`t1` where 0 SET SESSION sql_mode=@old_sql_mode; DROP TABLE t1; End of 5.0 tests. diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 7fbe4c08b08..7652cd7323b 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -4528,6 +4528,32 @@ pk int_key 7 3 DROP TABLE t1,t2; # +# Bug#12329653 +# EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY +# +CREATE TABLE t1(a1 int); +INSERT INTO t1 VALUES (1),(2); +SELECT @@session.sql_mode INTO @old_sql_mode; +SET SESSION sql_mode='ONLY_FULL_GROUP_BY'; +SELECT 1 FROM t1 WHERE 1 < SOME (SELECT a1 FROM t1); +1 +1 +1 +PREPARE stmt FROM +'SELECT 1 UNION ALL +SELECT 1 FROM t1 +ORDER BY +(SELECT 1 FROM t1 AS t1_0 + WHERE 1 < SOME (SELECT a1 FROM t1) +)' ; +EXECUTE stmt ; +ERROR 21000: Subquery returns more than 1 row +EXECUTE stmt ; +ERROR 21000: Subquery returns more than 1 row +SET SESSION sql_mode=@old_sql_mode; +DEALLOCATE PREPARE stmt; +DROP TABLE t1; +# # Bug #52711: Segfault when doing EXPLAIN SELECT with # union...order by (select... where...) # diff --git a/mysql-test/t/explain.test b/mysql-test/t/explain.test index 1b16c811dbd..2ae36152b73 100644 --- a/mysql-test/t/explain.test +++ b/mysql-test/t/explain.test @@ -1,5 +1,5 @@ # -# Test of different EXPLAIN's +# Test of different EXPLAINs --disable_warnings drop table if exists t1; @@ -157,11 +157,12 @@ CREATE TABLE t1 (f1 INT); SELECT @@session.sql_mode INTO @old_sql_mode; SET SESSION sql_mode='ONLY_FULL_GROUP_BY'; -# EXPLAIN EXTENDED (with subselect). used to crash. should give NOTICE. ---error ER_MIX_OF_GROUP_FUNC_AND_FIELDS +# EXPLAIN EXTENDED (with subselect). used to crash. +# This is actually a valid query for this sql_mode, +# but it was transformed in such a way that it failed, see +# Bug#12329653 - EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY EXPLAIN EXTENDED SELECT 1 FROM t1 WHERE f1 > ALL( SELECT t.f1 FROM t1,t1 AS t ); -SHOW WARNINGS; SET SESSION sql_mode=@old_sql_mode; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 0956f91619d..b4cee13d3ac 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -3506,6 +3506,40 @@ ORDER BY outr.pk; DROP TABLE t1,t2; +--echo # +--echo # Bug#12329653 +--echo # EXPLAIN, UNION, PREPARED STATEMENT, CRASH, SQL_FULL_GROUP_BY +--echo # + +CREATE TABLE t1(a1 int); +INSERT INTO t1 VALUES (1),(2); + +SELECT @@session.sql_mode INTO @old_sql_mode; +SET SESSION sql_mode='ONLY_FULL_GROUP_BY'; + +## First a simpler query, illustrating the transformation +## '1 < some (...)' => '1 < max(...)' +SELECT 1 FROM t1 WHERE 1 < SOME (SELECT a1 FROM t1); + +## The query which made the server crash. +PREPARE stmt FROM +'SELECT 1 UNION ALL +SELECT 1 FROM t1 +ORDER BY +(SELECT 1 FROM t1 AS t1_0 + WHERE 1 < SOME (SELECT a1 FROM t1) +)' ; + +--error ER_SUBQUERY_NO_1_ROW +EXECUTE stmt ; +--error ER_SUBQUERY_NO_1_ROW +EXECUTE stmt ; + +SET SESSION sql_mode=@old_sql_mode; + +DEALLOCATE PREPARE stmt; +DROP TABLE t1; + --echo # --echo # Bug #52711: Segfault when doing EXPLAIN SELECT with --echo # union...order by (select... where...) diff --git a/sql/item.cc b/sql/item.cc index 03d752a85d9..aaeb21f9948 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4080,14 +4080,14 @@ mark_non_agg_field: aggregated or not. */ if (!thd->lex->in_sum_func) - cached_table->select_lex->full_group_by_flag|= NON_AGG_FIELD_USED; + cached_table->select_lex->set_non_agg_field_used(true); else { if (outer_fixed) thd->lex->in_sum_func->outer_fields.push_back(this); else if (thd->lex->in_sum_func->nest_level != thd->lex->current_select->nest_level) - cached_table->select_lex->full_group_by_flag|= NON_AGG_FIELD_USED; + cached_table->select_lex->set_non_agg_field_used(true); } } return FALSE; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 44d1378839b..6666525a270 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -936,6 +936,14 @@ Item_in_subselect::single_value_transformer(JOIN *join, it.replace(item); } + DBUG_EXECUTE("where", + print_where(item, "rewrite with MIN/MAX");); + if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) + { + DBUG_ASSERT(select_lex->non_agg_field_used()); + select_lex->set_non_agg_field_used(false); + } + save_allow_sum_func= thd->lex->allow_sum_func; thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level; /* diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 43102213b9b..9942ae199cb 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -246,10 +246,10 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) in_sum_func->outer_fields.push_back(field); } else - sel->full_group_by_flag|= NON_AGG_FIELD_USED; + sel->set_non_agg_field_used(true); } if (sel->nest_level > aggr_level && - (sel->full_group_by_flag & SUM_FUNC_USED) && + (sel->agg_func_used()) && !sel->group_list.elements) { my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, @@ -258,7 +258,7 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) } } } - aggr_sel->full_group_by_flag|= SUM_FUNC_USED; + aggr_sel->set_agg_func_used(true); update_used_tables(); thd->lex->in_sum_func= in_sum_func; return FALSE; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index a811bbafdb6..97cad9e4b19 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1086,13 +1086,6 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables, bool allow_null_cond, int *error); extern Item **not_found_item; -/* - A set of constants used for checking non aggregated fields and sum - functions mixture in the ONLY_FULL_GROUP_BY_MODE. -*/ -#define NON_AGG_FIELD_USED 1 -#define SUM_FUNC_USED 2 - /* This enumeration type is used only by the function find_item_in_list to return the info on how an item has been resolved against a list diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 97d9fe99eb3..87916b201d2 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1232,6 +1232,8 @@ void st_select_lex::init_query() exclude_from_table_unique_test= no_wrap_view_item= FALSE; nest_level= 0; link_next= 0; + m_non_agg_field_used= false; + m_agg_func_used= false; } void st_select_lex::init_select() @@ -1266,7 +1268,8 @@ void st_select_lex::init_select() non_agg_fields.empty(); cond_value= having_value= Item::COND_UNDEF; inner_refs_list.empty(); - full_group_by_flag= 0; + m_non_agg_field_used= false; + m_agg_func_used= false; } /* diff --git a/sql/sql_lex.h b/sql/sql_lex.h index b3822f91afe..7b2227a9678 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -617,16 +617,7 @@ public: joins on the right. */ List *prev_join_using; - /* - Bitmap used in the ONLY_FULL_GROUP_BY_MODE to prevent mixture of aggregate - functions and non aggregated fields when GROUP BY list is absent. - Bits: - 0 - non aggregated fields are used in this select, - defined as NON_AGG_FIELD_USED. - 1 - aggregate functions are used in this select, - defined as SUM_FUNC_USED. - */ - uint8 full_group_by_flag; + void init_query(); void init_select(); st_select_lex_unit* master_unit(); @@ -714,6 +705,21 @@ public: select lexes. */ void cleanup_all_joins(bool full); + /* + For MODE_ONLY_FULL_GROUP_BY we need to maintain two flags: + - Non-aggregated fields are used in this select. + - Aggregate functions are used in this select. + In MODE_ONLY_FULL_GROUP_BY only one of these may be true. + */ + bool non_agg_field_used() const { return m_non_agg_field_used; } + bool agg_func_used() const { return m_agg_func_used; } + + void set_non_agg_field_used(bool val) { m_non_agg_field_used= val; } + void set_agg_func_used(bool val) { m_agg_func_used= val; } + +private: + bool m_non_agg_field_used; + bool m_agg_func_used; }; typedef class st_select_lex SELECT_LEX; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index cb7add3a874..0d19dcb576b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -391,19 +391,18 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array, int res; nesting_map save_allow_sum_func=thd->lex->allow_sum_func ; /* - Need to save the value, so we can turn off only the new NON_AGG_FIELD + Need to save the value, so we can turn off only any new non_agg_field_used additions coming from the WHERE */ - uint8 saved_flag= thd->lex->current_select->full_group_by_flag; + const bool saved_non_agg_field_used= + thd->lex->current_select->non_agg_field_used(); DBUG_ENTER("setup_without_group"); thd->lex->allow_sum_func&= ~(1 << thd->lex->current_select->nest_level); res= setup_conds(thd, tables, leaves, conds); /* it's not wrong to have non-aggregated columns in a WHERE */ - if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY) - thd->lex->current_select->full_group_by_flag= saved_flag | - (thd->lex->current_select->full_group_by_flag & ~NON_AGG_FIELD_USED); + thd->lex->current_select->set_non_agg_field_used(saved_non_agg_field_used); thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level; res= res || setup_order(thd, ref_pointer_array, tables, fields, all_fields, @@ -593,7 +592,8 @@ JOIN::prepare(Item ***rref_pointer_array, aggregate functions with implicit grouping (there is no GROUP BY). */ if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list && - select_lex->full_group_by_flag == (NON_AGG_FIELD_USED | SUM_FUNC_USED)) + select_lex->non_agg_field_used() && + select_lex->agg_func_used()) { my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0)); -- cgit v1.2.1 From 10afe0993ed3f547b74fcc637b9553eeb3ab6992 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 4 May 2011 19:23:23 +0300 Subject: Addendum to bug #11764517 : don't create links for --mem directory, move the --vardir instead. The new --secure-file-priv checks dereference any symlinks in the paths and compare the resolved paths. Thus the 5.0 test suite must do as the 5.1 and up and avoid using symlinks. --- mysql-test/mysql-test-run.pl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index f43cadd3784..203f964c72e 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -873,6 +873,10 @@ sub command_line_setup () { last; } } + + # point vardir to the mem location + $opt_vardir= $opt_mem; + undef $opt_mem; } # -------------------------------------------------------------------------- -- cgit v1.2.1 From 693fee5d561177e67223300015e5f16817c369e9 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 5 May 2011 12:10:49 +0300 Subject: Addendum 3 for bug #BUG#11764517 : expand secure_file_priv to the real patch so that it can later be compared with patchs with expanded symlinks --- mysql-test/mysql-test-run.pl | 4 ---- mysql-test/r/loaddata.result | 6 ------ mysql-test/t/loaddata.test | 14 ++++++++++---- sql/mysqld.cc | 20 +++++++++++++++++--- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 203f964c72e..f43cadd3784 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -873,10 +873,6 @@ sub command_line_setup () { last; } } - - # point vardir to the mem location - $opt_vardir= $opt_mem; - undef $opt_mem; } # -------------------------------------------------------------------------- diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result index 30f4dbfc6ef..39b4e35495f 100644 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -193,12 +193,6 @@ select * from t1; a b c 10 NULL Ten 15 NULL Fifteen -show variables like "secure_file_pri%"; -Variable_name Value -secure_file_priv MYSQLTEST_VARDIR/ -select @@secure_file_priv; -@@secure_file_priv -MYSQLTEST_VARDIR/ set @@secure_file_priv= 0; ERROR HY000: Variable 'secure_file_priv' is a read only variable truncate table t1; diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test index d86c395e436..4a538716133 100644 --- a/mysql-test/t/loaddata.test +++ b/mysql-test/t/loaddata.test @@ -149,10 +149,16 @@ select * from t1; # # It should not be possible to load from a file outside of vardir ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR -show variables like "secure_file_pri%"; ---replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR -select @@secure_file_priv; +# The following lines were disabled because of patch for +# bug 50373. MYSQLTEST_VARDIR doesn't rewrite symlinks +# to real paths, but this is done for secure_file_priv. +# Because of this the result can't be replaced if the +# test suite runs with the --mem option which creates +# symlinks to the ramdisk. +#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +#show variables like "secure_file_pri%"; +#--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +#select @@secure_file_priv; --error 1238 set @@secure_file_priv= 0; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 3291085f380..caeac5be100 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7974,9 +7974,23 @@ static void fix_paths(void) */ if (opt_secure_file_priv) { - convert_dirname(buff, opt_secure_file_priv, NullS); - my_free(opt_secure_file_priv, MYF(0)); - opt_secure_file_priv= my_strdup(buff, MYF(MY_FAE)); + if (*opt_secure_file_priv == 0) + { + opt_secure_file_priv= 0; + } + else + { + 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)) + { + sql_print_warning("Failed to normalize the argument for --secure-file-priv."); + exit(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, MYF(0)); + opt_secure_file_priv= secure_file_real_path; + } } } -- cgit v1.2.1 From 8a3beb46ee4684caf0f558f598016a5185f150eb Mon Sep 17 00:00:00 2001 From: Kristofer Pettersson Date: Mon, 9 May 2011 12:57:17 +0200 Subject: bug#10064164 Certain fields in the protcol required a strict formatting. If off bound values were sent to the server this could under some circumstances lead to a crash on the Windows platform. --- sql/sql_connect.cc | 199 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 119 insertions(+), 80 deletions(-) diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 406998537e4..26e8c9c3c76 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -20,23 +20,9 @@ #include "mysql_priv.h" -#ifdef HAVE_OPENSSL -/* - Without SSL the handshake consists of one packet. This packet - has both client capabilites and scrambled password. - With SSL the handshake might consist of two packets. If the first - packet (client capabilities) has CLIENT_SSL flag set, we have to - switch to SSL and read the second packet. The scrambled password - is in the second packet and client_capabilites field will be ignored. - Maybe it is better to accept flags other than CLIENT_SSL from the - second packet? -*/ -#define SSL_HANDSHAKE_SIZE 2 -#define NORMAL_HANDSHAKE_SIZE 6 -#define MIN_HANDSHAKE_SIZE 2 -#else -#define MIN_HANDSHAKE_SIZE 6 -#endif /* HAVE_OPENSSL */ +/** Size of the header fields of an authentication packet. */ +#define AUTH_PACKET_HEADER_SIZE_PROTO_41 32 +#define AUTH_PACKET_HEADER_SIZE_PROTO_40 5 #ifdef __WIN__ extern void win_install_sigabrt_handler(); @@ -761,6 +747,14 @@ static int check_connection(THD *thd) ulong pkt_len= 0; char *end; + bool packet_has_required_size= false; + char *db; + size_t db_len; + char *passwd; + size_t passwd_len; + char *user; + size_t user_len; + DBUG_PRINT("info", ("New connection received on %s", vio_description(net->vio))); #ifdef SIGNAL_WITH_VIO_CLOSE @@ -869,8 +863,7 @@ static int check_connection(THD *thd) /* At this point we write connection message and read reply */ if (net_write_command(net, (uchar) protocol_version, (uchar*) "", 0, (uchar*) buff, (size_t) (end-buff)) || - (pkt_len= my_net_read(net)) == packet_error || - pkt_len < MIN_HANDSHAKE_SIZE) + (pkt_len= my_net_read(net)) == packet_error) { inc_host_errors(&thd->remote.sin_addr); my_error(ER_HANDSHAKE_ERROR, MYF(0), @@ -886,22 +879,63 @@ static int check_connection(THD *thd) if (thd->packet.alloc(thd->variables.net_buffer_length)) return 1; /* The error is set by alloc(). */ - thd->client_capabilities= uint2korr(net->read_pos); + uint charset_code= 0; + end= (char *)net->read_pos; + /* + In order to safely scan a head for '\0' string terminators + we must keep track of how many bytes remain in the allocated + buffer or we might read past the end of the buffer. + */ + size_t bytes_remaining_in_packet= pkt_len; + + /* + Peek ahead on the client capability packet and determine which version of + the protocol should be used. + */ + if (bytes_remaining_in_packet < 2) + goto error; + + thd->client_capabilities= uint2korr(end); + + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + packet_has_required_size= bytes_remaining_in_packet >= + AUTH_PACKET_HEADER_SIZE_PROTO_41; + else + packet_has_required_size= bytes_remaining_in_packet >= + AUTH_PACKET_HEADER_SIZE_PROTO_40; + + if (!packet_has_required_size) + goto error; + if (thd->client_capabilities & CLIENT_PROTOCOL_41) { - thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; - thd->max_client_packet_length= uint4korr(net->read_pos+4); - DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); - if (thd_init_client_charset(thd, (uint) net->read_pos[8])) - return 1; - thd->update_charset(); - end= (char*) net->read_pos+32; + thd->client_capabilities= uint4korr(end); + thd->max_client_packet_length= uint4korr(end + 4); + charset_code= (uint)(uchar)*(end + 8); + /* + Skip 23 remaining filler bytes which have no particular meaning. + */ + end+= AUTH_PACKET_HEADER_SIZE_PROTO_41; + bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_41; } else { - thd->max_client_packet_length= uint3korr(net->read_pos+2); - end= (char*) net->read_pos+5; + thd->client_capabilities= uint2korr(end); + thd->max_client_packet_length= uint3korr(end + 2); + end+= AUTH_PACKET_HEADER_SIZE_PROTO_40; + bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_40; + /** + 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; } + + DBUG_PRINT("info", ("client_character_set: %u", charset_code)); + if (thd_init_client_charset(thd, charset_code)) + goto error; + thd->update_charset(); + /* Disable those bits which are not supported by the server. This is a precautionary measure, if the client lies. See Bug#27944. @@ -912,42 +946,63 @@ static int check_connection(THD *thd) thd->variables.sql_mode|= MODE_IGNORE_SPACE; #ifdef HAVE_OPENSSL DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities)); + + /* + If client requested SSL then we must stop parsing, try to switch to SSL, + and wait for the client to send a new handshake packet. + The client isn't expected to send any more bytes until SSL is initialized. + */ if (thd->client_capabilities & CLIENT_SSL) { /* Do the SSL layering. */ if (!ssl_acceptor_fd) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } + goto error; + DBUG_PRINT("info", ("IO layer change in progress...")); if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout)) { DBUG_PRINT("error", ("Failed to accept new SSL connection")); - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; + goto error; } + DBUG_PRINT("info", ("Reading user information over SSL layer")); - if ((pkt_len= my_net_read(net)) == packet_error || - pkt_len < NORMAL_HANDSHAKE_SIZE) + if ((pkt_len= my_net_read(net)) == packet_error) { DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", pkt_len)); - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; + goto error; } - } -#endif /* HAVE_OPENSSL */ + /* + A new packet was read and the statistics reflecting the remaining bytes + in the packet must be updated. + */ + bytes_remaining_in_packet= pkt_len; - if (end > (char *)net->read_pos + pkt_len) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; + /* + After the SSL handshake is performed the client resends the handshake + packet but because of legacy reasons we chose not to parse the packet + fields a second time and instead only assert the length of the packet. + */ + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + { + + packet_has_required_size= bytes_remaining_in_packet >= + AUTH_PACKET_HEADER_SIZE_PROTO_41; + end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_41; + bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_41; + } + else + { + packet_has_required_size= bytes_remaining_in_packet >= + AUTH_PACKET_HEADER_SIZE_PROTO_40; + end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_40; + bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_40; + } + + if (!packet_has_required_size) + goto error; } +#endif /* HAVE_OPENSSL */ if (thd->client_capabilities & CLIENT_INTERACTIVE) thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; @@ -955,30 +1010,18 @@ static int check_connection(THD *thd) opt_using_transactions) net->return_status= &thd->server_status; - /* - In order to safely scan a head for '\0' string terminators - we must keep track of how many bytes remain in the allocated - buffer or we might read past the end of the buffer. - */ - size_t bytes_remaining_in_packet= pkt_len - (end - (char *)net->read_pos); - - size_t user_len; - char *user= get_null_terminated_string(&end, &bytes_remaining_in_packet, - &user_len); + user= get_null_terminated_string(&end, &bytes_remaining_in_packet, + &user_len); if (user == NULL) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } + goto error; /* Old clients send a null-terminated string as password; new clients send the size (1 byte) + string (not null-terminated). Hence in case of empty password both send '\0'. */ - size_t passwd_len= 0; - char *passwd= NULL; + passwd_len= 0; + passwd= NULL; if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) { @@ -998,25 +1041,17 @@ static int check_connection(THD *thd) } if (passwd == NULL) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } + goto error; - size_t db_len= 0; - char *db= NULL; + db_len= 0; + db= NULL; if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) { db= get_null_terminated_string(&end, &bytes_remaining_in_packet, &db_len); if (db == NULL) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } + goto error; } char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 @@ -1059,11 +1094,14 @@ static int check_connection(THD *thd) user[user_len]= '\0'; } - if (thd->main_security_ctx.user) - x_free(thd->main_security_ctx.user); if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME)))) return 1; /* The error is set by my_strdup(). */ return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); + +error: + inc_host_errors(&thd->remote.sin_addr); + my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); + return 1; } @@ -1313,3 +1351,4 @@ end_thread: } } #endif /* EMBEDDED_LIBRARY */ + -- cgit v1.2.1 From cad931d6282a17af3f4bc5f91ecaf3ce7b9469d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 19 May 2011 16:12:27 +0300 Subject: Bug#12429576 Assertion failure on purge of column prefix index --- .../suite/innodb_plugin/r/innodb-index.result | 44 +++++++++++++++ mysql-test/suite/innodb_plugin/t/innodb-index.test | 63 +++++++++++++++++++++- storage/innodb_plugin/ChangeLog | 5 ++ storage/innodb_plugin/row/row0row.c | 2 - 4 files changed, 110 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/innodb_plugin/r/innodb-index.result b/mysql-test/suite/innodb_plugin/r/innodb-index.result index f86fcd4a8ef..b9ca8a8da49 100644 --- a/mysql-test/suite/innodb_plugin/r/innodb-index.result +++ b/mysql-test/suite/innodb_plugin/r/innodb-index.result @@ -1,3 +1,46 @@ +set global innodb_file_per_table=on; +set global innodb_file_format='Barracuda'; +CREATE TABLE t1_purge ( +A INT, +B BLOB, C BLOB, D BLOB, E BLOB, +F BLOB, G BLOB, H BLOB, +PRIMARY KEY (B(767), C(767), D(767), E(767), A), +INDEX (A) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +INSERT INTO t1_purge VALUES (1, +REPEAT('b', 766), REPEAT('c', 766), REPEAT('d', 766), REPEAT('e', 766), +REPEAT('f', 766), REPEAT('g', 766), REPEAT('h', 766)); +CREATE TABLE t2_purge ( +A INT PRIMARY KEY, +B BLOB, C BLOB, D BLOB, E BLOB, +F BLOB, G BLOB, H BLOB, I BLOB, +J BLOB, K BLOB, L BLOB, +INDEX (B(767))) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +INSERT INTO t2_purge VALUES (1, +REPEAT('b', 766), REPEAT('c', 766), REPEAT('d', 766), REPEAT('e', 766), +REPEAT('f', 766), REPEAT('g', 766), REPEAT('h', 766), REPEAT('i', 766), +REPEAT('j', 766), REPEAT('k', 766), REPEAT('l', 766)); +CREATE TABLE t3_purge ( +A INT, +B VARCHAR(800), C VARCHAR(800), D VARCHAR(800), E VARCHAR(800), +F VARCHAR(800), G VARCHAR(800), H VARCHAR(800), +PRIMARY KEY (B(767), C(767), D(767), E(767), A), +INDEX (A) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +INSERT INTO t3_purge SELECT * FROM t1_purge; +CREATE TABLE t4_purge ( +A INT PRIMARY KEY, +B VARCHAR(800), C VARCHAR(800), D VARCHAR(800), E VARCHAR(800), +F VARCHAR(800), G VARCHAR(800), H VARCHAR(800), I VARCHAR(800), +J VARCHAR(800), K VARCHAR(800), L VARCHAR(800), +INDEX (B(767))) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; +INSERT INTO t4_purge SELECT * FROM t2_purge; +DELETE FROM t1_purge; +DELETE FROM t2_purge; +DELETE FROM t3_purge; +DELETE FROM t4_purge; +set global innodb_file_per_table=0; +set global innodb_file_format=Antelope; create table t1(a int not null, b int, c char(10) not null, d varchar(20)) engine = innodb; insert into t1 values (5,5,'oo','oo'),(4,4,'tr','tr'),(3,4,'ad','ad'),(2,3,'ak','ak'); commit; @@ -1170,3 +1213,4 @@ a b 3 a 3 b DROP TABLE t1; +DROP TABLE t1_purge, t2_purge, t3_purge, t4_purge; diff --git a/mysql-test/suite/innodb_plugin/t/innodb-index.test b/mysql-test/suite/innodb_plugin/t/innodb-index.test index 717c7d4e032..947a60abb80 100644 --- a/mysql-test/suite/innodb_plugin/t/innodb-index.test +++ b/mysql-test/suite/innodb_plugin/t/innodb-index.test @@ -4,6 +4,65 @@ let $MYSQLD_DATADIR= `select @@datadir`; let $innodb_file_format_check_orig=`select @@innodb_file_format_check`; +let $per_table=`select @@innodb_file_per_table`; +let $format=`select @@innodb_file_format`; +set global innodb_file_per_table=on; +set global innodb_file_format='Barracuda'; + +# Test an assertion failure on purge. +CREATE TABLE t1_purge ( +A INT, +B BLOB, C BLOB, D BLOB, E BLOB, +F BLOB, G BLOB, H BLOB, +PRIMARY KEY (B(767), C(767), D(767), E(767), A), +INDEX (A) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; + +INSERT INTO t1_purge VALUES (1, +REPEAT('b', 766), REPEAT('c', 766), REPEAT('d', 766), REPEAT('e', 766), +REPEAT('f', 766), REPEAT('g', 766), REPEAT('h', 766)); + +CREATE TABLE t2_purge ( +A INT PRIMARY KEY, +B BLOB, C BLOB, D BLOB, E BLOB, +F BLOB, G BLOB, H BLOB, I BLOB, +J BLOB, K BLOB, L BLOB, +INDEX (B(767))) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; + +INSERT INTO t2_purge VALUES (1, +REPEAT('b', 766), REPEAT('c', 766), REPEAT('d', 766), REPEAT('e', 766), +REPEAT('f', 766), REPEAT('g', 766), REPEAT('h', 766), REPEAT('i', 766), +REPEAT('j', 766), REPEAT('k', 766), REPEAT('l', 766)); + +CREATE TABLE t3_purge ( +A INT, +B VARCHAR(800), C VARCHAR(800), D VARCHAR(800), E VARCHAR(800), +F VARCHAR(800), G VARCHAR(800), H VARCHAR(800), +PRIMARY KEY (B(767), C(767), D(767), E(767), A), +INDEX (A) +) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; + +INSERT INTO t3_purge SELECT * FROM t1_purge; + +CREATE TABLE t4_purge ( +A INT PRIMARY KEY, +B VARCHAR(800), C VARCHAR(800), D VARCHAR(800), E VARCHAR(800), +F VARCHAR(800), G VARCHAR(800), H VARCHAR(800), I VARCHAR(800), +J VARCHAR(800), K VARCHAR(800), L VARCHAR(800), +INDEX (B(767))) ENGINE=InnoDB ROW_FORMAT=DYNAMIC; + +INSERT INTO t4_purge SELECT * FROM t2_purge; + +# This would trigger the failure (Bug #12429576) +# if purge gets a chance to run before DROP TABLE t1_purge, .... +DELETE FROM t1_purge; +DELETE FROM t2_purge; +DELETE FROM t3_purge; +DELETE FROM t4_purge; + +eval set global innodb_file_per_table=$per_table; +eval set global innodb_file_format=$format; + create table t1(a int not null, b int, c char(10) not null, d varchar(20)) engine = innodb; insert into t1 values (5,5,'oo','oo'),(4,4,'tr','tr'),(3,4,'ad','ad'),(2,3,'ak','ak'); commit; @@ -360,8 +419,6 @@ disconnect b; drop table t1; -let $per_table=`select @@innodb_file_per_table`; -let $format=`select @@innodb_file_format`; set global innodb_file_per_table=on; set global innodb_file_format='Barracuda'; # Test creating a table that could lead to undo log overflow. @@ -404,6 +461,7 @@ alter table t1 row_format=compact; create index t1u on t1 (u(1)); drop table t1; + eval set global innodb_file_per_table=$per_table; eval set global innodb_file_format=$format; eval set global innodb_file_format_check=$format; @@ -541,6 +599,7 @@ disconnect a; disconnect b; DROP TABLE t1; +DROP TABLE t1_purge, t2_purge, t3_purge, t4_purge; # # restore environment to the state it was before this test execution diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 0b201816819..7c6c47a39ed 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2011-05-19 The InnoDB Team + + * row/row0row.c: + Fix Bug#12429576 Assertion failure on purge of column prefix index + 2011-04-07 The InnoDB Team * handler/ha_innodb.cc, handler/ha_innodb.h, handler/handler0alter.cc: diff --git a/storage/innodb_plugin/row/row0row.c b/storage/innodb_plugin/row/row0row.c index 8e806a14a98..682d8699270 100644 --- a/storage/innodb_plugin/row/row0row.c +++ b/storage/innodb_plugin/row/row0row.c @@ -151,8 +151,6 @@ row_build_index_entry( } else if (dfield_is_ext(dfield)) { ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE); len -= BTR_EXTERN_FIELD_REF_SIZE; - ut_a(ind_field->prefix_len <= len - || dict_index_is_clust(index)); } len = dtype_get_at_most_n_mbchars( -- cgit v1.2.1 From 8ae2bb6eaaa5e2bf99390e498e5efe3090eb1d46 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Wed, 1 Jun 2011 16:08:13 +0300 Subject: BUG 12610784: SET PASSWORD INCORRECTLY KEEP AN OLD EMPTY PASSWORD The check for empty password in the user account was checking the wrong field. Fixed to check the proper password hash. Test case added. Fixed native_password and old_password plugins that suffered from the same problems. Unambuguated the auth_string ACL_USER member : previously it was used for both password and the authentication string (depending on the plugin). Now fixed to contain either the authentication string specified or empty string. --- mysql-test/r/plugin_auth.result | 7 +++++++ mysql-test/t/plugin_auth.test | 15 +++++++++++++++ sql/sql_acl.cc | 16 +++++++--------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/plugin_auth.result b/mysql-test/r/plugin_auth.result index 91a5d2d8478..4d85da4569f 100644 --- a/mysql-test/r/plugin_auth.result +++ b/mysql-test/r/plugin_auth.result @@ -447,4 +447,11 @@ ORDER BY COLUMN_NAME; IS_NULLABLE COLUMN_NAME YES authentication_string YES plugin +# +# Bug #12610784: SET PASSWORD INCORRECTLY KEEP AN OLD EMPTY PASSWORD +# +CREATE USER bug12610784@localhost; +SET PASSWORD FOR bug12610784@localhost = PASSWORD('secret'); +ERROR 28000: Access denied for user 'bug12610784'@'localhost' (using password: NO) +DROP USER bug12610784@localhost; End of 5.5 tests diff --git a/mysql-test/t/plugin_auth.test b/mysql-test/t/plugin_auth.test index a81cf4e4783..0ec4a62afbd 100644 --- a/mysql-test/t/plugin_auth.test +++ b/mysql-test/t/plugin_auth.test @@ -512,4 +512,19 @@ SELECT IS_NULLABLE, COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS ORDER BY COLUMN_NAME; +--echo # +--echo # Bug #12610784: SET PASSWORD INCORRECTLY KEEP AN OLD EMPTY PASSWORD +--echo # + +CREATE USER bug12610784@localhost; +SET PASSWORD FOR bug12610784@localhost = PASSWORD('secret'); +--disable_query_log +--error ER_ACCESS_DENIED_ERROR +connect(b12610784,localhost,bug12610784,,test); +--enable_query_log +connect(b12610784,localhost,bug12610784,secret,test); +connection default; +disconnect b12610784; +DROP USER bug12610784@localhost; + --echo End of 5.5 tests diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 18758130767..7d7595899d7 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -825,8 +825,6 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) char *password= get_field(&mem, table->field[2]); uint password_len= password ? strlen(password) : 0; - user.auth_string.str= password ? password : const_cast(""); - user.auth_string.length= password_len; set_user_salt(&user, password, password_len); if (set_user_plugin(&user, password_len)) @@ -915,7 +913,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) char *tmpstr= get_field(&mem, table->field[next_field++]); if (tmpstr) { - if (user.auth_string.length) + if (password_len) { sql_print_warning("'user' entry '%s@%s' has both a password " "and an authentication plugin specified. The " @@ -1483,8 +1481,8 @@ static void acl_insert_user(const char *user, const char *host, { acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ? old_password_plugin_name : native_password_plugin_name; - acl_user.auth_string.str= strmake_root(&mem, password, password_len); - acl_user.auth_string.length= password_len; + acl_user.auth_string.str= const_cast(""); + acl_user.auth_string.length= 0; } acl_user.access=privileges; @@ -8380,7 +8378,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) old_password_plugin, otherwise MySQL will think that server and client plugins don't match. */ - if (mpvio->acl_user->auth_string.length == 0) + if (mpvio->acl_user->salt_len == 0) mpvio->acl_user_plugin= old_password_plugin_name; } } @@ -8685,7 +8683,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, old_password_plugin, otherwise MySQL will think that server and client plugins don't match. */ - if (mpvio->acl_user->auth_string.length == 0) + if (mpvio->acl_user->salt_len == 0) mpvio->acl_user_plugin= old_password_plugin_name; } } @@ -9473,7 +9471,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, #endif if (pkt_len == 0) /* no password */ - DBUG_RETURN(info->auth_string[0] ? CR_ERROR : CR_OK); + DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_ERROR : CR_OK); info->password_used= PASSWORD_USED_YES; if (pkt_len == SCRAMBLE_LENGTH) @@ -9522,7 +9520,7 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, pkt_len= strnlen((char*)pkt, pkt_len); if (pkt_len == 0) /* no password */ - return info->auth_string[0] ? CR_ERROR : CR_OK; + return mpvio->acl_user->salt_len != 0 ? CR_ERROR : CR_OK; if (secure_auth(mpvio)) return CR_ERROR; -- cgit v1.2.1 From 47d9773c5f30f305952ebb006095bdeb7eb830a6 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Fri, 10 Jun 2011 17:37:05 +0200 Subject: Bug 12608543 - CRASHES WITH DECIMALS AND STATEMENT NEEDS TO BE REPREPARED ERRORS --- sql/my_decimal.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/sql/my_decimal.h b/sql/my_decimal.h index 6ac9d25b87d..10fe431f40e 100644 --- a/sql/my_decimal.h +++ b/sql/my_decimal.h @@ -144,8 +144,6 @@ public: void swap(my_decimal &rhs) { swap_variables(my_decimal, *this, rhs); - /* Swap the buffer pointers back */ - swap_variables(decimal_digit_t *, buf, rhs.buf); } }; -- cgit v1.2.1 From bdb10545f1430e5df6bbc0f7d347cbc9596372ad Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 13 Jun 2011 15:08:28 +0300 Subject: Bug #12633140 : AUDIT PLUGIN SOURCE FILES HAVE OUT OF DATE COPYRIGHT NOTICE Fixed copyright headers of updated files. --- include/mysql/plugin_audit.h | 13 +++++++------ plugin/audit_null/audit_null.c | 11 ++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/mysql/plugin_audit.h b/include/mysql/plugin_audit.h index 5072ad2b44c..1d968ddb3f2 100644 --- a/include/mysql/plugin_audit.h +++ b/include/mysql/plugin_audit.h @@ -1,17 +1,18 @@ -/* Copyright (C) 2007 MySQL AB +/* Copyright (c) 2007, 2011, 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 Foundation; version 2 of the License. + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _my_audit_h #define _my_audit_h diff --git a/plugin/audit_null/audit_null.c b/plugin/audit_null/audit_null.c index 161bd1cea70..3fe5fa993f0 100644 --- a/plugin/audit_null/audit_null.c +++ b/plugin/audit_null/audit_null.c @@ -1,12 +1,13 @@ -/* Copyright (C) 2006-2007 MySQL AB +/* Copyright (c) 2006, 2011, 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 Foundation; version 2 of the License. + 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 + 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 -- cgit v1.2.1 From 1c810152963f770fae011b29ae30fbafbc516b23 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Tue, 21 Jun 2011 19:24:44 +0400 Subject: Patch for Bug 12652769 - 61470: CASE OPERATOR IN STORED ROUTINE RETAINS OLD VALUE OF INPUT PARAMETER. The user-visible problem was that CASE-control-flow function (not CASE-statement) misbehaved in stored routines under some circumstances. The problem resulted in a crash or wrong data returned. The error happened when expressions in CASE-function were not of the same character set. A CASE-function should return values of the same character set for all branches. Internally, that means a new Item-instance for the CONVERT(... USING )-function is added to the item tree when needed. The problem was that such changes were not properly recorded using THD::change_item_tree(), thus dangling pointers remain in the item tree after THD::rollback_item_tree_changes(), which lead to undefined behavior (i.e. crash / wrong data) for subsequent executions of the stored routine. This bug was introduced by a patch for Bug 11753363 (44793 - CHARACTER SETS: CASE CLAUSE, UCS2 OR UTF32, FAILURE). The fixed function is Item_func_case::fix_length_and_dec(). New CONVERT-items are added in agg_item_set_converter(), which calls THD::change_item_tree(). The problem was that an intermediate array was passed to agg_item_set_converter(). Thus, THD::change_item_tree() there was called on intermediate objects. Note: those intermediate objects are allocated on THD's memory root, so it's Ok to put them into "changed item lists". The fix is to track changes on the correct objects. --- mysql-test/r/sp.result | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ mysql-test/t/sp.test | 56 +++++++++++++++++++++++++++++++++++++++ sql/item_cmpfunc.cc | 34 +++++++++++++++++++++--- 3 files changed, 158 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 6b4215e6b09..8512407aaf3 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -7500,4 +7500,76 @@ CALL p1(); DROP TABLE t1, t2, t3; DROP PROCEDURE p1; + +# -- +# -- Bug#12652769 - 61470: case operator in stored routine retains old +# -- value of input parameter +# --- +DROP TABLE IF EXISTS t1; +DROP PROCEDURE IF EXISTS p1; +CREATE TABLE t1 (s1 CHAR(5) CHARACTER SET utf8); +INSERT INTO t1 VALUES ('a'); +CREATE PROCEDURE p1(dt DATETIME, i INT) +BEGIN +SELECT +CASE +WHEN i = 1 THEN 2 +ELSE dt +END AS x1; +SELECT +CASE _latin1'a' + WHEN _utf8'a' THEN 'A' + END AS x2; +SELECT +CASE _utf8'a' + WHEN _latin1'a' THEN _utf8'A' + END AS x3; +SELECT +CASE s1 +WHEN _latin1'a' THEN _latin1'b' + ELSE _latin1'c' + END AS x4 +FROM t1; +END| + +CALL p1('2011-04-03 05:14:10', 1); +x1 +2 +x2 +A +x3 +A +x4 +b +CALL p1('2011-04-03 05:14:11', 2); +x1 +2011-04-03 05:14:11 +x2 +A +x3 +A +x4 +b +CALL p1('2011-04-03 05:14:12', 2); +x1 +2011-04-03 05:14:12 +x2 +A +x3 +A +x4 +b +CALL p1('2011-04-03 05:14:13', 2); +x1 +2011-04-03 05:14:13 +x2 +A +x3 +A +x4 +b + +DROP TABLE t1; +DROP PROCEDURE p1; + # End of 5.5 test diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index b052b181d70..a9220aa04e6 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -8778,4 +8778,60 @@ DROP TABLE t1, t2, t3; DROP PROCEDURE p1; --echo + +--echo +--echo # -- +--echo # -- Bug#12652769 - 61470: case operator in stored routine retains old +--echo # -- value of input parameter +--echo # --- + +--disable_warnings +DROP TABLE IF EXISTS t1; +DROP PROCEDURE IF EXISTS p1; +--enable_warnings + +CREATE TABLE t1 (s1 CHAR(5) CHARACTER SET utf8); +INSERT INTO t1 VALUES ('a'); + +delimiter |; + +CREATE PROCEDURE p1(dt DATETIME, i INT) +BEGIN + SELECT + CASE + WHEN i = 1 THEN 2 + ELSE dt + END AS x1; + + SELECT + CASE _latin1'a' + WHEN _utf8'a' THEN 'A' + END AS x2; + + SELECT + CASE _utf8'a' + WHEN _latin1'a' THEN _utf8'A' + END AS x3; + + SELECT + CASE s1 + WHEN _latin1'a' THEN _latin1'b' + ELSE _latin1'c' + END AS x4 + FROM t1; +END| + +delimiter ;| + +--echo +CALL p1('2011-04-03 05:14:10', 1); +CALL p1('2011-04-03 05:14:11', 2); +CALL p1('2011-04-03 05:14:12', 2); +CALL p1('2011-04-03 05:14:13', 2); + +--echo +DROP TABLE t1; +DROP PROCEDURE p1; +--echo + --echo # End of 5.5 test diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e0057d1550b..e28221109d9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -3007,11 +3007,35 @@ void Item_func_case::agg_num_lengths(Item *arg) } +/** + Check if (*place) and new_value points to different Items and call + THD::change_item_tree() if needed. + + This function is a workaround for implementation deficiency in + Item_func_case. The problem there is that the 'args' attribute contains + Items from different expressions. + + The function must not be used elsewhere and will be remove eventually. +*/ + +static void change_item_tree_if_needed(THD *thd, + Item **place, + Item *new_value) +{ + if (*place == new_value) + return; + + thd->change_item_tree(place, new_value); +} + + void Item_func_case::fix_length_and_dec() { Item **agg; uint nagg; uint found_types= 0; + THD *thd= current_thd; + if (!(agg= (Item**) sql_alloc(sizeof(Item*)*(ncases+1)))) return; @@ -3036,9 +3060,10 @@ void Item_func_case::fix_length_and_dec() Some of the items might have been changed to Item_func_conv_charset. */ for (nagg= 0 ; nagg < ncases / 2 ; nagg++) - args[nagg * 2 + 1]= agg[nagg]; + change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg]); + if (else_expr_num != -1) - args[else_expr_num]= agg[nagg++]; + change_item_tree_if_needed(thd, &args[else_expr_num], agg[nagg++]); } else collation.set_numeric(); @@ -3098,9 +3123,10 @@ void Item_func_case::fix_length_and_dec() arrray, because some of the items might have been changed to converters (e.g. Item_func_conv_charset, or Item_string for constants). */ - args[first_expr_num]= agg[0]; + change_item_tree_if_needed(thd, &args[first_expr_num], agg[0]); + for (nagg= 0; nagg < ncases / 2; nagg++) - args[nagg * 2]= agg[nagg + 1]; + change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]); } for (i= 0; i <= (uint)DECIMAL_RESULT; i++) { -- cgit v1.2.1