Commit 90004e34 authored by monty@hundin.mysql.fi's avatar monty@hundin.mysql.fi

Fixed bug in LEFT JOIN

parent 4b513347
...@@ -46784,6 +46784,7 @@ users use this code as the rest of the code and because of this we are ...@@ -46784,6 +46784,7 @@ users use this code as the rest of the code and because of this we are
not yet 100% confident in this code. not yet 100% confident in this code.
@menu @menu
* News-3.23.47::
* News-3.23.46:: Changes in release 3.23.46 * News-3.23.46:: Changes in release 3.23.46
* News-3.23.45:: Changes in release 3.23.45 * News-3.23.45:: Changes in release 3.23.45
* News-3.23.44:: Changes in release 3.23.44 * News-3.23.44:: Changes in release 3.23.44
...@@ -46834,7 +46835,13 @@ not yet 100% confident in this code. ...@@ -46834,7 +46835,13 @@ not yet 100% confident in this code.
* News-3.23.0:: Changes in release 3.23.0 * News-3.23.0:: Changes in release 3.23.0
@end menu @end menu
@node News-3.23.46, News-3.23.45, News-3.23.x, News-3.23.x @node News-3.23.47, News-3.23.46, News-3.23.x, News-3.23.x
@appendixsubsec Changes in release 3.23.46
@itemize @bullet
Fixed bug when using @code{t1 LEFT JOIN t2 ON t2.key=constant}.
@end itemize
@node News-3.23.46, News-3.23.45, News-3.23.47, News-3.23.x
@appendixsubsec Changes in release 3.23.46 @appendixsubsec Changes in release 3.23.46
@itemize @bullet @itemize @bullet
@item @item
...@@ -303,10 +303,9 @@ int _mi_readinfo(register MI_INFO *info, int lock_type, int check_keybuffer) ...@@ -303,10 +303,9 @@ int _mi_readinfo(register MI_INFO *info, int lock_type, int check_keybuffer)
{ {
if (!share->r_locks && !share->w_locks) if (!share->r_locks && !share->w_locks)
{ {
if ((info->tmp_lock_type=lock_type) != F_RDLCK) if (my_lock(share->kfile,lock_type,0L,F_TO_EOF,
if (my_lock(share->kfile,lock_type,0L,F_TO_EOF, info->lock_wait | MY_SEEK_NOT_DONE))
info->lock_wait | MY_SEEK_NOT_DONE)) DBUG_RETURN(1);
DBUG_RETURN(1);
if (mi_state_info_read_dsk(share->kfile, &share->state, 1)) if (mi_state_info_read_dsk(share->kfile, &share->state, 1))
{ {
int error=my_errno ? my_errno : -1; int error=my_errno ? my_errno : -1;
......
...@@ -320,3 +320,14 @@ id name id idx ...@@ -320,3 +320,14 @@ id name id idx
2 no NULL NULL 2 no NULL NULL
bug_id reporter bug_id who bug_id reporter bug_id who
1 1 1 2 1 1 1 2
table type possible_keys key key_len ref rows Extra
t2 index NULL PRIMARY 4 NULL 3 Using index
t1 eq_ref PRIMARY PRIMARY 2 const 1 where used; Using index
fooID barID fooID
10 1 NULL
20 2 NULL
30 3 30
fooID barID fooID
10 1 NULL
20 2 NULL
30 3 30
...@@ -404,3 +404,15 @@ insert into t2 values (1,1),(1,2); ...@@ -404,3 +404,15 @@ insert into t2 values (1,1),(1,2);
insert into t1 values (1,1),(2,1); insert into t1 values (1,1),(2,1);
SELECT * FROM t1 LEFT JOIN t2 ON (t1.bug_id = t2.bug_id AND t2.who = 2) WHERE (t1.reporter = 2 OR t2.who = 2); SELECT * FROM t1 LEFT JOIN t2 ON (t1.bug_id = t2.bug_id AND t2.who = 2) WHERE (t1.reporter = 2 OR t2.who = 2);
drop table t1,t2; drop table t1,t2;
#
# Test problem with LEFT JOIN
create table t1 (fooID smallint unsigned auto_increment, primary key (fooID));
create table t2 (fooID smallint unsigned not null, barID smallint unsigned not null, primary key (fooID,barID));
insert into t1 (fooID) values (10),(20),(30);
insert into t2 values (10,1),(20,2),(30,3);
explain select * from t2 left join t1 on t1.fooID = t2.fooID and t1.fooID = 30;
select * from t2 left join t1 on t1.fooID = t2.fooID and t1.fooID = 30;
select * from t2 left join t1 ignore index(primary) on t1.fooID = t2.fooID and t1.fooID = 30;
drop table t1,t2;
...@@ -126,6 +126,7 @@ void my_end(int infoflag) ...@@ -126,6 +126,7 @@ void my_end(int infoflag)
DBUG_PRINT("error",("%s",errbuff[0])); DBUG_PRINT("error",("%s",errbuff[0]));
} }
} }
free_charsets();
if (infoflag & MY_GIVE_INFO || info_file != stderr) if (infoflag & MY_GIVE_INFO || info_file != stderr)
{ {
#ifdef HAVE_GETRUSAGE #ifdef HAVE_GETRUSAGE
...@@ -150,7 +151,6 @@ Voluntary context switches %ld, Involuntary context switches %ld\n", ...@@ -150,7 +151,6 @@ Voluntary context switches %ld, Involuntary context switches %ld\n",
#if defined(MSDOS) && !defined(__WIN__) #if defined(MSDOS) && !defined(__WIN__)
fprintf(info_file,"\nRun time: %.1f\n",(double) clock()/CLOCKS_PER_SEC); fprintf(info_file,"\nRun time: %.1f\n",(double) clock()/CLOCKS_PER_SEC);
#endif #endif
free_charsets();
#if defined(SAFEMALLOC) #if defined(SAFEMALLOC)
TERMINATE(stderr); /* Give statistic on screen */ TERMINATE(stderr); /* Give statistic on screen */
#elif defined(__WIN__) && defined(_MSC_VER) #elif defined(__WIN__) && defined(_MSC_VER)
......
...@@ -37,7 +37,7 @@ WARNING: THIS PROGRAM IS STILL IN BETA. Comments/patches welcome. ...@@ -37,7 +37,7 @@ WARNING: THIS PROGRAM IS STILL IN BETA. Comments/patches welcome.
# Documentation continued at end of file # Documentation continued at end of file
my $VERSION = "1.13"; my $VERSION = "1.14";
my $opt_tmpdir = $ENV{TMPDIR} || "/tmp"; my $opt_tmpdir = $ENV{TMPDIR} || "/tmp";
...@@ -120,6 +120,7 @@ GetOptions( \%opt, ...@@ -120,6 +120,7 @@ GetOptions( \%opt,
# 'target' - destination directory of the copy # 'target' - destination directory of the copy
# 'tables' - array-ref to list of tables in the db # 'tables' - array-ref to list of tables in the db
# 'files' - array-ref to list of files to be copied # 'files' - array-ref to list of files to be copied
# (RAID files look like 'nn/name.MYD')
# 'index' - array-ref to list of indexes to be copied # 'index' - array-ref to list of indexes to be copied
# #
...@@ -265,11 +266,23 @@ foreach my $rdb ( @db_desc ) { ...@@ -265,11 +266,23 @@ foreach my $rdb ( @db_desc ) {
or die "Cannot open dir '$db_dir': $!"; or die "Cannot open dir '$db_dir': $!";
my %db_files; my %db_files;
map { ( /(.+)\.\w+$/ ? ( $db_files{$_} = $1 ) : () ) } readdir(DBDIR); my @raid_dir = ();
while ( defined( my $name = readdir DBDIR ) ) {
if ( $name =~ /^\d\d$/ && -d "$db_dir/$name" ) {
push @raid_dir, $name;
}
else {
$db_files{$name} = $1 if ( $name =~ /(.+)\.\w+$/ );
}
}
closedir( DBDIR );
scan_raid_dir( \%db_files, $db_dir, @raid_dir );
unless( keys %db_files ) { unless( keys %db_files ) {
warn "'$db' is an empty database\n"; warn "'$db' is an empty database\n";
} }
closedir( DBDIR );
## filter (out) files specified in t_regex ## filter (out) files specified in t_regex
my @db_files; my @db_files;
...@@ -297,6 +310,8 @@ foreach my $rdb ( @db_desc ) { ...@@ -297,6 +310,8 @@ foreach my $rdb ( @db_desc ) {
my @hc_tables = map { "$db.$_" } @dbh_tables; my @hc_tables = map { "$db.$_" } @dbh_tables;
$rdb->{tables} = [ @hc_tables ]; $rdb->{tables} = [ @hc_tables ];
$rdb->{raid_dirs} = [ get_raid_dirs( $rdb->{files} ) ];
$hc_locks .= ", " if ( length $hc_locks && @hc_tables ); $hc_locks .= ", " if ( length $hc_locks && @hc_tables );
$hc_locks .= join ", ", map { "$_ READ" } @hc_tables; $hc_locks .= join ", ", map { "$_ READ" } @hc_tables;
$hc_tables .= ", " if ( length $hc_tables && @hc_tables ); $hc_tables .= ", " if ( length $hc_tables && @hc_tables );
...@@ -370,17 +385,20 @@ if ($opt{method} =~ /^cp\b/) ...@@ -370,17 +385,20 @@ if ($opt{method} =~ /^cp\b/)
retire_directory( @existing ) if ( @existing ); retire_directory( @existing ) if ( @existing );
foreach my $rdb ( @db_desc ) { foreach my $rdb ( @db_desc ) {
my $tgt_dirpath = $rdb->{target}; foreach my $td ( '', @{$rdb->{raid_dirs}} ) {
if ( $opt{dryrun} ) {
print "mkdir $tgt_dirpath, 0750\n"; my $tgt_dirpath = "$rdb->{target}/$td";
} if ( $opt{dryrun} ) {
elsif ($opt{method} =~ /^scp\b/) { print "mkdir $tgt_dirpath, 0750\n";
## assume it's there? }
## ... elsif ($opt{method} =~ /^scp\b/) {
} ## assume it's there?
else { ## ...
mkdir($tgt_dirpath, 0750) }
or die "Can't create '$tgt_dirpath': $!\n"; else {
mkdir($tgt_dirpath, 0750)
or die "Can't create '$tgt_dirpath': $!\n";
}
} }
} }
...@@ -438,7 +456,7 @@ foreach my $rdb ( @db_desc ) ...@@ -438,7 +456,7 @@ foreach my $rdb ( @db_desc )
my @files = map { "$datadir/$rdb->{src}/$_" } @{$rdb->{files}}; my @files = map { "$datadir/$rdb->{src}/$_" } @{$rdb->{files}};
next unless @files; next unless @files;
eval { copy_files($opt{method}, \@files, $rdb->{target} ); }; eval { copy_files($opt{method}, \@files, $rdb->{target}, $rdb->{raid_dirs} ); };
push @failed, "$rdb->{src} -> $rdb->{target} failed: $@" push @failed, "$rdb->{src} -> $rdb->{target} failed: $@"
if ( $@ ); if ( $@ );
...@@ -531,27 +549,33 @@ exit 0; ...@@ -531,27 +549,33 @@ exit 0;
# --- # ---
sub copy_files { sub copy_files {
my ($method, $files, $target) = @_; my ($method, $files, $target, $raid_dirs) = @_;
my @cmd; my @cmd;
print "Copying ".@$files." files...\n" unless $opt{quiet}; print "Copying ".@$files." files...\n" unless $opt{quiet};
if ($method =~ /^s?cp\b/) { # cp or scp with optional flags if ($method =~ /^s?cp\b/) { # cp or scp with optional flags
@cmd = ($method); my @cp = ($method);
# add option to preserve mod time etc of copied files # add option to preserve mod time etc of copied files
# not critical, but nice to have # not critical, but nice to have
push @cmd, "-p" if $^O =~ m/^(solaris|linux|freebsd)$/; push @cp, "-p" if $^O =~ m/^(solaris|linux|freebsd)$/;
# add recursive option for scp # add recursive option for scp
push @cmd, "-r" if $^O =~ /m^(solaris|linux|freebsd)$/ && $method =~ /^scp\b/; push @cp, "-r" if $^O =~ /m^(solaris|linux|freebsd)$/ && $method =~ /^scp\b/;
my @non_raid = grep { $_ !~ m:\d\d/: } @$files;
# add files to copy and the destination directory # add files to copy and the destination directory
push @cmd, @$files, $target; safe_system( @cp, @non_raid, $target );
foreach my $rd ( @$raid_dirs ) {
my @raid = grep { m:$rd/: } @$files;
safe_system( @cp, @raid, "$target/$rd" ) if ( @raid );
}
} }
else else
{ {
die "Can't use unsupported method '$method'\n"; die "Can't use unsupported method '$method'\n";
} }
safe_system (@cmd);
} }
# #
...@@ -682,6 +706,35 @@ sub get_row { ...@@ -682,6 +706,35 @@ sub get_row {
return $sth->fetchrow_array(); return $sth->fetchrow_array();
} }
sub scan_raid_dir {
my ( $r_db_files, $data_dir, @raid_dir ) = @_;
local(*RAID_DIR);
foreach my $rd ( @raid_dir ) {
opendir(RAID_DIR, "$data_dir/$rd" )
or die "Cannot open dir '$data_dir/$rd': $!";
while ( defined( my $name = readdir RAID_DIR ) ) {
$r_db_files->{"$rd/$name"} = $1 if ( $name =~ /(.+)\.\w+$/ );
}
closedir( RAID_DIR );
}
}
sub get_raid_dirs {
my ( $r_files ) = @_;
my %dirs = ();
foreach my $f ( @$r_files ) {
if ( $f =~ m:^(\d\d)/: ) {
$dirs{$1} = 1;
}
}
return sort keys %dirs;
}
__END__ __END__
=head1 DESCRIPTION =head1 DESCRIPTION
...@@ -905,6 +958,7 @@ Tim Bunce ...@@ -905,6 +958,7 @@ Tim Bunce
Martin Waite - added checkpoint, flushlog, regexp and dryrun options Martin Waite - added checkpoint, flushlog, regexp and dryrun options
Fixed cleanup of targets when hotcopy fails. Fixed cleanup of targets when hotcopy fails.
Added --record_log_pos. Added --record_log_pos.
RAID tables are now copied (don't know if this works over scp).
Ralph Corderoy - added synonyms for commands Ralph Corderoy - added synonyms for commands
......
...@@ -2119,10 +2119,17 @@ get_best_combination(JOIN *join) ...@@ -2119,10 +2119,17 @@ get_best_combination(JOIN *join)
j->type=JT_REF; /* Must read with repeat */ j->type=JT_REF; /* Must read with repeat */
else if (ref_key == j->ref.key_copy) else if (ref_key == j->ref.key_copy)
{ /* Should never be reached */ { /* Should never be reached */
j->type=JT_CONST; /* purecov: deadcode */ /*
This happen if we are using a constant expression in the ON part
of an LEFT JOIN.
SELECT * FROM a LEFT JOIN b ON b.key=30
Here we should not mark the table as a 'const' as a field may
have a 'normal' value or a NULL value.
*/
j->type=JT_CONST;
if (join->const_tables == tablenr) if (join->const_tables == tablenr)
{ {
join->const_tables++; /* purecov: deadcode */ join->const_tables++;
join->const_table_map|=form->map; join->const_table_map|=form->map;
} }
} }
...@@ -2251,7 +2258,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) ...@@ -2251,7 +2258,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
used_tables|=current_map; used_tables|=current_map;
if (tab->type == JT_REF && tab->quick && if (tab->type == JT_REF && tab->quick &&
tab->ref.key == tab->quick->index && (uint) tab->ref.key == tab->quick->index &&
tab->ref.key_length < tab->quick->max_used_key_length) tab->ref.key_length < tab->quick->max_used_key_length)
{ {
/* Range uses longer key; Use this instead of ref on key */ /* Range uses longer key; Use this instead of ref on key */
...@@ -2689,8 +2696,8 @@ static void update_depend_map(JOIN *join, ORDER *order) ...@@ -2689,8 +2696,8 @@ static void update_depend_map(JOIN *join, ORDER *order)
/* /*
** simple_order is set to 1 if sort_order only uses fields from head table simple_order is set to 1 if sort_order only uses fields from head table
** and the head table is not a LEFT JOIN table and the head table is not a LEFT JOIN table
*/ */
static ORDER * static ORDER *
...@@ -4331,8 +4338,11 @@ join_read_const(JOIN_TAB *tab) ...@@ -4331,8 +4338,11 @@ join_read_const(JOIN_TAB *tab)
} }
store_record(table,1); store_record(table,1);
} }
else if (!table->status) // Only happens with left join else if (!(table->status & ~STATUS_NULL_ROW)) // Only happens with left join
{
table->status=0;
restore_record(table,1); // restore old record restore_record(table,1); // restore old record
}
table->null_row=0; table->null_row=0;
return table->status ? -1 : 0; return table->status ? -1 : 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment