Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mariadb
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
ca6c4cc3
Commit
ca6c4cc3
authored
Nov 13, 2003
by
psergey@psergey.(none)
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added index_merge access method
parent
0466c250
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1247 additions
and
158 deletions
+1247
-158
BitKeeper/etc/logging_ok
BitKeeper/etc/logging_ok
+1
-0
sql/opt_ft.cc
sql/opt_ft.cc
+1
-1
sql/opt_ft.h
sql/opt_ft.h
+6
-5
sql/opt_range.cc
sql/opt_range.cc
+901
-104
sql/opt_range.h
sql/opt_range.h
+174
-17
sql/sql_class.cc
sql/sql_class.cc
+2
-2
sql/sql_list.h
sql/sql_list.h
+7
-0
sql/sql_select.cc
sql/sql_select.cc
+86
-18
sql/sql_select.h
sql/sql_select.h
+3
-2
sql/sql_test.cc
sql/sql_test.cc
+31
-1
sql/sql_union.cc
sql/sql_union.cc
+3
-2
sql/sql_update.cc
sql/sql_update.cc
+32
-6
No files found.
BitKeeper/etc/logging_ok
View file @
ca6c4cc3
...
...
@@ -95,6 +95,7 @@ peter@linux.local
peter@mysql.com
peterg@mysql.com
pgulutzan@linux.local
psergey@psergey.(none)
ram@gw.udmsearch.izhnet.ru
ram@mysql.r18.ru
ram@ram.(none)
...
...
sql/opt_ft.cc
View file @
ca6c4cc3
...
...
@@ -26,7 +26,7 @@
** Create a FT or QUICK RANGE based on a key
****************************************************************************/
QUICK_SELECT
*
get_ft_or_quick_select_for_ref
(
TABLE
*
table
,
JOIN_TAB
*
tab
)
QUICK_
RANGE_
SELECT
*
get_ft_or_quick_select_for_ref
(
TABLE
*
table
,
JOIN_TAB
*
tab
)
{
if
(
tab
->
type
==
JT_FT
)
return
new
FT_SELECT
(
table
,
&
tab
->
ref
);
...
...
sql/opt_ft.h
View file @
ca6c4cc3
...
...
@@ -24,17 +24,18 @@
#pragma interface
/* gcc class implementation */
#endif
class
FT_SELECT
:
public
QUICK_SELECT
{
class
FT_SELECT
:
public
QUICK_
RANGE_
SELECT
{
public:
TABLE_REF
*
ref
;
FT_SELECT
(
TABLE
*
table
,
TABLE_REF
*
tref
)
:
QUICK_SELECT
(
table
,
tref
->
key
,
1
),
ref
(
tref
)
{
init
();
}
QUICK_
RANGE_
SELECT
(
table
,
tref
->
key
,
1
),
ref
(
tref
)
{
init
();
}
int
init
()
{
return
error
=
file
->
ft_init
(
);
}
int
init
()
{
QUICK_RANGE_SELECT
::
init
();
return
(
error
=
file
->
ft_init
()
);
}
int
get_next
()
{
return
error
=
file
->
ft_read
(
record
);
}
int
get_type
()
{
return
QS_TYPE_FULLTEXT
;
}
};
QUICK_SELECT
*
get_ft_or_quick_select_for_ref
(
TABLE
*
table
,
JOIN_TAB
*
tab
);
QUICK_
RANGE_
SELECT
*
get_ft_or_quick_select_for_ref
(
TABLE
*
table
,
JOIN_TAB
*
tab
);
#endif
sql/opt_range.cc
View file @
ca6c4cc3
...
...
@@ -267,14 +267,17 @@ public:
SEL_ARG
*
clone_tree
();
};
class
SEL_IMERGE
;
class
SEL_TREE
:
public
Sql_alloc
{
public:
enum
Type
{
IMPOSSIBLE
,
ALWAYS
,
MAYBE
,
KEY
,
KEY_SMALLER
}
type
;
SEL_TREE
(
enum
Type
type_arg
)
:
type
(
type_arg
)
{}
SEL_TREE
()
:
type
(
KEY
)
{
bzero
((
char
*
)
keys
,
sizeof
(
keys
));}
SEL_TREE
()
:
type
(
KEY
)
,
keys_map
(
0
)
{
bzero
((
char
*
)
keys
,
sizeof
(
keys
));}
SEL_ARG
*
keys
[
MAX_KEY
];
key_map
keys_map
;
/* bitmask of non-NULL elements in keys */
List
<
SEL_IMERGE
>
merges
;
/* possible ways to read rows using index_merge */
};
...
...
@@ -301,10 +304,19 @@ static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree,
char
*
min_key
,
uint
min_key_flag
,
char
*
max_key
,
uint
max_key_flag
);
static
QUICK_SELECT
*
get_quick_select
(
PARAM
*
param
,
uint
index
,
SEL_ARG
*
key_tree
);
QUICK_RANGE_SELECT
*
get_quick_select
(
PARAM
*
param
,
uint
index
,
SEL_ARG
*
key_tree
,
MEM_ROOT
*
alloc
=
NULL
);
static
int
get_quick_select_params
(
SEL_TREE
*
tree
,
PARAM
&
param
,
key_map
&
needed_reg
,
TABLE
*
head
,
bool
index_read_can_be_used
,
double
*
read_time
,
ha_rows
*
records
,
SEL_ARG
***
key_to_read
);
#ifndef DBUG_OFF
static
void
print_quick
(
QUICK_SELECT
*
quick
,
key_map
needed_reg
);
void
print_quick_sel_imerge
(
QUICK_INDEX_MERGE_SELECT
*
quick
,
key_map
needed_reg
);
void
print_quick_sel_range
(
QUICK_RANGE_SELECT
*
quick
,
key_map
needed_reg
);
#endif
static
SEL_TREE
*
tree_and
(
PARAM
*
param
,
SEL_TREE
*
tree1
,
SEL_TREE
*
tree2
);
static
SEL_TREE
*
tree_or
(
PARAM
*
param
,
SEL_TREE
*
tree1
,
SEL_TREE
*
tree2
);
...
...
@@ -312,16 +324,234 @@ static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2);
static
SEL_ARG
*
key_or
(
SEL_ARG
*
key1
,
SEL_ARG
*
key2
);
static
SEL_ARG
*
key_and
(
SEL_ARG
*
key1
,
SEL_ARG
*
key2
,
uint
clone_flag
);
static
bool
get_range
(
SEL_ARG
**
e1
,
SEL_ARG
**
e2
,
SEL_ARG
*
root1
);
static
bool
get_quick_keys
(
PARAM
*
param
,
QUICK
_SELECT
*
quick
,
KEY_PART
*
key
,
bool
get_quick_keys
(
PARAM
*
param
,
QUICK_RANGE
_SELECT
*
quick
,
KEY_PART
*
key
,
SEL_ARG
*
key_tree
,
char
*
min_key
,
uint
min_key_flag
,
char
*
max_key
,
uint
max_key_flag
);
static
bool
eq_tree
(
SEL_ARG
*
a
,
SEL_ARG
*
b
);
static
SEL_ARG
null_element
(
SEL_ARG
::
IMPOSSIBLE
);
static
bool
null_part_in_key
(
KEY_PART
*
key_part
,
const
char
*
key
,
uint
length
);
bool
sel_trees_can_be_ored
(
SEL_TREE
*
tree1
,
SEL_TREE
*
tree2
,
PARAM
*
param
);
/*
SEL_IMERGE is a list of possible ways to do index merge, i.e. it is
a condition in the following form:
(t_1||t_2||...||t_N) && (next)
where all t_i are SEL_TREEs, next is another SEL_IMERGE and no pair
(t_i,t_j) contains SEL_ARGS for the same index.
SEL_TREE contained in SEL_IMERGE always has merges=NULL.
This class relies on memory manager to do the cleanup.
*/
class
SEL_IMERGE
:
public
Sql_alloc
{
enum
{
PREALLOCED_TREES
=
10
};
public:
SEL_TREE
*
trees_prealloced
[
PREALLOCED_TREES
];
SEL_TREE
**
trees
;
/* trees used to do index_merge */
SEL_TREE
**
trees_next
;
/* last of these trees */
SEL_TREE
**
trees_end
;
/* end of allocated space */
SEL_ARG
***
best_keys
;
/* best keys to read in SEL_TREEs */
SEL_IMERGE
()
:
trees
(
&
trees_prealloced
[
0
]),
trees_next
(
trees
),
trees_end
(
trees
+
PREALLOCED_TREES
)
{}
int
or_sel_tree
(
PARAM
*
param
,
SEL_TREE
*
tree
);
int
or_sel_tree_with_checks
(
PARAM
*
param
,
SEL_TREE
*
new_tree
);
int
or_sel_imerge_with_checks
(
PARAM
*
param
,
SEL_IMERGE
*
imerge
);
};
/*
Add SEL_TREE to this index_merge without any checks,
NOTES
This function implements the following:
(x_1||...||x_N) || t = (x_1||...||x_N||t), where x_i, t are SEL_TREEs
RETURN
0 - OK
-1 - Out of memory.
*/
int
SEL_IMERGE
::
or_sel_tree
(
PARAM
*
param
,
SEL_TREE
*
tree
)
{
if
(
trees_next
==
trees_end
)
{
const
int
realloc_ratio
=
2
;
/* Double size for next round */
uint
old_elements
=
(
trees_end
-
trees
);
uint
old_size
=
sizeof
(
SEL_TREE
**
)
*
old_elements
;
uint
new_size
=
old_size
*
realloc_ratio
;
SEL_TREE
**
new_trees
;
if
(
!
(
new_trees
=
(
SEL_TREE
**
)
alloc_root
(
param
->
mem_root
,
new_size
)))
return
-
1
;
memcpy
(
new_trees
,
trees
,
old_size
);
trees
=
new_trees
;
trees_next
=
trees
+
old_elements
;
trees_end
=
trees
+
old_elements
*
realloc_ratio
;
}
*
(
trees_next
++
)
=
tree
;
return
0
;
}
/*
Perform OR operation on this SEL_IMERGE and supplied SEL_TREE new_tree,
combining new_tree with one of the trees in this SEL_IMERGE if they both
have SEL_ARGs for the same key.
SYNOPSIS
or_sel_tree_with_checks()
param PARAM from SQL_SELECT::test_quick_select
new_tree SEL_TREE with type KEY or KEY_SMALLER.
NOTES
This does the following:
(t_1||...||t_k)||new_tree =
either
= (t_1||...||t_k||new_tree)
or
= (t_1||....||(t_j|| new_tree)||...||t_k),
where t_i, y are SEL_TREEs.
new_tree is combined with the first t_j it has a SEL_ARG on common
key with. As a consequence of this, choice of keys to do index_merge
read may depend on the order of conditions in WHERE part of the query.
RETURN
0 OK
1 One of the trees was combined with new_tree to SEL_TREE::ALWAYS,
and (*this) should be discarded.
-1 An error occurred.
*/
int
SEL_IMERGE
::
or_sel_tree_with_checks
(
PARAM
*
param
,
SEL_TREE
*
new_tree
)
{
for
(
SEL_TREE
**
tree
=
trees
;
tree
!=
trees_next
;
tree
++
)
{
if
(
sel_trees_can_be_ored
(
*
tree
,
new_tree
,
param
))
{
*
tree
=
tree_or
(
param
,
*
tree
,
new_tree
);
if
(
!*
tree
)
return
1
;
if
(((
*
tree
)
->
type
==
SEL_TREE
::
MAYBE
)
||
((
*
tree
)
->
type
==
SEL_TREE
::
ALWAYS
))
return
1
;
/* SEL_TREE::IMPOSSIBLE is impossible here */
return
0
;
}
}
/* new tree cannot be combined with any of existing trees */
return
or_sel_tree
(
param
,
new_tree
);
}
/*
Perform OR operation on this index_merge and supplied index_merge list.
RETURN
0 - OK
1 - One of conditions in result is always TRUE and this SEL_IMERGE
should be discarded.
-1 - An error occurred
*/
int
SEL_IMERGE
::
or_sel_imerge_with_checks
(
PARAM
*
param
,
SEL_IMERGE
*
imerge
)
{
for
(
SEL_TREE
**
tree
=
imerge
->
trees
;
tree
!=
imerge
->
trees_next
;
tree
++
)
{
if
(
or_sel_tree_with_checks
(
param
,
*
tree
))
return
1
;
}
return
0
;
}
/*
Perform AND operation on two index_merge lists, storing result in *im1.
*/
inline
void
imerge_list_and_list
(
List
<
SEL_IMERGE
>
*
im1
,
List
<
SEL_IMERGE
>
*
im2
)
{
im1
->
concat
(
im2
);
}
/*
Perform OR operation on 2 index_merge lists, storing result in first list.
NOTES
The following conversion is implemented:
(a_1 &&...&& a_N)||(b_1 &&...&& b_K) = AND_i,j(a_i || b_j) =>
=> (a_1||b_1).
i.e. all conjuncts except the first one are currently dropped.
This is done to avoid producing N*K ways to do index_merge.
If (a_1||b_1) produce a condition that is always true, NULL is
returned and index_merge is discarded. (while it is actually
possible to try harder).
As a consequence of this, choice of keys to do index_merge
read may depend on the order of conditions in WHERE part of
the query.
RETURN
0 OK, result is stored in *im1
other Error, both passed lists are unusable
*/
int
imerge_list_or_list
(
PARAM
*
param
,
List
<
SEL_IMERGE
>
*
im1
,
List
<
SEL_IMERGE
>
*
im2
)
{
SEL_IMERGE
*
imerge
=
im1
->
head
();
im1
->
empty
();
im1
->
push_back
(
imerge
);
return
imerge
->
or_sel_imerge_with_checks
(
param
,
im2
->
head
());
}
/*
Perform OR operation on index_merge list and key tree.
RETURN
0 OK, result is stored in *im1
other Error
*/
int
imerge_list_or_tree
(
PARAM
*
param
,
List
<
SEL_IMERGE
>
*
im1
,
SEL_TREE
*
tree
)
{
SEL_IMERGE
*
imerge
;
List_iterator
<
SEL_IMERGE
>
it
(
*
im1
);
while
((
imerge
=
it
++
))
{
if
(
imerge
->
or_sel_tree_with_checks
(
param
,
tree
))
it
.
remove
();
}
return
im1
->
is_empty
();
}
/***************************************************************************
** Basic functions for SQL_SELECT and QUICK_SELECT
** Basic functions for SQL_SELECT and QUICK_
RANGE_
SELECT
***************************************************************************/
/* make a select from mysql info
...
...
@@ -378,23 +608,34 @@ SQL_SELECT::~SQL_SELECT()
#undef index // Fix for Unixware 7
QUICK_SELECT
::
QUICK_SELECT
(
TABLE
*
table
,
uint
key_nr
,
bool
no_alloc
)
:
dont_free
(
0
),
error
(
0
),
index
(
key_nr
),
max_used_key_length
(
0
),
used_key_parts
(
0
),
head
(
table
),
it
(
ranges
),
range
(
0
)
QUICK_SELECT_I
::
QUICK_SELECT_I
()
:
max_used_key_length
(
0
),
used_key_parts
(
0
)
{}
QUICK_RANGE_SELECT
::
QUICK_RANGE_SELECT
(
TABLE
*
table
,
uint
key_nr
,
bool
no_alloc
,
MEM_ROOT
*
parent_alloc
)
:
dont_free
(
0
),
error
(
0
),
it
(
ranges
),
range
(
0
)
{
if
(
!
no_alloc
)
index
=
key_nr
;
head
=
table
;
if
(
!
no_alloc
&&
!
parent_alloc
)
{
init_sql_alloc
(
&
alloc
,
1024
,
0
);
// Allocates everything here
my_pthread_setspecific_ptr
(
THR_MALLOC
,
&
alloc
);
}
else
bzero
((
char
*
)
&
alloc
,
sizeof
(
alloc
));
file
=
head
->
file
;
record
=
head
->
record
[
0
];
init
();
file
=
head
->
file
;
record
=
head
->
record
[
0
];
}
int
QUICK_RANGE_SELECT
::
init
()
{
return
(
error
=
file
->
index_init
(
index
));
}
QUICK_
SELECT
::~
QUICK
_SELECT
()
QUICK_
RANGE_SELECT
::~
QUICK_RANGE
_SELECT
()
{
if
(
!
dont_free
)
{
...
...
@@ -403,6 +644,42 @@ QUICK_SELECT::~QUICK_SELECT()
}
}
QUICK_INDEX_MERGE_SELECT
::
QUICK_INDEX_MERGE_SELECT
(
THD
*
thd
,
TABLE
*
table
)
:
cur_quick_it
(
quick_selects
),
index_merge
(
thd
)
{
index
=
MAX_KEY
;
head
=
table
;
init_sql_alloc
(
&
alloc
,
1024
,
0
);
}
int
QUICK_INDEX_MERGE_SELECT
::
init
()
{
int
error
;
cur_quick_it
.
rewind
();
cur_quick_select
=
cur_quick_it
++
;
if
(
error
=
index_merge
.
init
(
head
))
return
error
;
return
cur_quick_select
->
init
();
}
void
QUICK_INDEX_MERGE_SELECT
::
reset
()
{
cur_quick_select
->
reset
();
}
bool
QUICK_INDEX_MERGE_SELECT
::
push_quick_back
(
QUICK_RANGE_SELECT
*
quick_sel_range
)
{
return
quick_selects
.
push_back
(
quick_sel_range
);
}
QUICK_INDEX_MERGE_SELECT
::~
QUICK_INDEX_MERGE_SELECT
()
{
quick_selects
.
delete_elements
();
free_root
(
&
alloc
,
MYF
(
0
));
}
QUICK_RANGE
::
QUICK_RANGE
()
:
min_key
(
0
),
max_key
(
0
),
min_length
(
0
),
max_length
(
0
),
flag
(
NO_MIN_RANGE
|
NO_MAX_RANGE
)
...
...
@@ -581,6 +858,8 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables,
uint
basflag
;
uint
idx
;
double
scan_time
;
QUICK_INDEX_MERGE_SELECT
*
quick_imerge
;
THD
*
thd
=
current_thd
;
DBUG_ENTER
(
"test_quick_select"
);
DBUG_PRINT
(
"enter"
,(
"keys_to_use: %lu prev_tables: %lu const_tables: %lu"
,
(
ulong
)
keys_to_use
,
(
ulong
)
prev_tables
,
...
...
@@ -626,13 +905,13 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables,
param
.
keys
=
0
;
param
.
mem_root
=
&
alloc
;
current_
thd
->
no_errors
=
1
;
// Don't warn about NULL
thd
->
no_errors
=
1
;
// Don't warn about NULL
init_sql_alloc
(
&
alloc
,
2048
,
0
);
if
(
!
(
param
.
key_parts
=
(
KEY_PART
*
)
alloc_root
(
&
alloc
,
sizeof
(
KEY_PART
)
*
head
->
key_parts
)))
{
current_
thd
->
no_errors
=
0
;
thd
->
no_errors
=
0
;
free_root
(
&
alloc
,
MYF
(
0
));
// Return memory & allocator
DBUG_RETURN
(
0
);
// Can't use range
}
...
...
@@ -675,10 +954,239 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables,
else
if
(
tree
->
type
==
SEL_TREE
::
KEY
||
tree
->
type
==
SEL_TREE
::
KEY_SMALLER
)
{
SEL_ARG
**
key
,
**
end
,
**
best_key
=
0
;
/*
It is possible to use a quick select (but maybe it would be slower
than 'all' table scan).
*/
SEL_ARG
**
best_key
=
0
;
ha_rows
found_records
;
double
found_read_time
=
read_time
;
if
(
!
get_quick_select_params
(
tree
,
param
,
needed_reg
,
head
,
true
,
&
found_read_time
,
&
found_records
,
&
best_key
))
{
/*
Ok, quick select is better than 'all' table scan and we have its
parameters, so construct it.
*/
read_time
=
found_read_time
;
records
=
found_records
;
if
((
quick
=
get_quick_select
(
&
param
,(
uint
)
(
best_key
-
tree
->
keys
),
*
best_key
))
&&
(
!
quick
->
init
()))
{
quick
->
records
=
records
;
quick
->
read_time
=
read_time
;
}
}
/*
btw, tree type SEL_TREE::INDEX_MERGE was not introduced
intentionally
*/
/* if no range select could be built, try using index_merge */
if
(
!
quick
&&
!
tree
->
merges
.
is_empty
())
{
DBUG_PRINT
(
"info"
,(
"No range reads possible,"
" trying to construct index_merge"
));
SEL_IMERGE
*
imerge
;
SEL_IMERGE
*
min_imerge
=
NULL
;
double
min_imerge_cost
=
DBL_MAX
;
ha_rows
min_imerge_records
;
List_iterator_fast
<
SEL_IMERGE
>
it
(
tree
->
merges
);
while
((
imerge
=
it
++
))
{
double
imerge_cost
=
0
;
ha_rows
imerge_total_records
=
0
;
double
tree_read_time
;
ha_rows
tree_records
;
imerge
->
best_keys
=
(
SEL_ARG
***
)
alloc_root
(
&
alloc
,
(
imerge
->
trees_next
-
imerge
->
trees
)
*
sizeof
(
void
*
));
for
(
SEL_TREE
**
ptree
=
imerge
->
trees
;
ptree
!=
imerge
->
trees_next
;
ptree
++
)
{
tree_read_time
=
read_time
;
if
(
get_quick_select_params
(
*
ptree
,
param
,
needed_reg
,
head
,
false
,
&
tree_read_time
,
&
tree_records
,
&
(
imerge
->
best_keys
[
ptree
-
imerge
->
trees
])))
goto
imerge_fail
;
imerge_cost
+=
tree_read_time
;
imerge_total_records
+=
tree_records
;
}
imerge_total_records
=
min
(
imerge_total_records
,
head
->
file
->
records
);
imerge_cost
+=
imerge_total_records
/
TIME_FOR_COMPARE
;
if
(
imerge_cost
<
min_imerge_cost
)
{
min_imerge
=
imerge
;
min_imerge_cost
=
imerge_cost
;
min_imerge_records
=
imerge_total_records
;
}
imerge_fail:
;
}
if
(
!
min_imerge
)
goto
end_free
;
records
=
min_imerge_records
;
/* ok, got minimal imerge, *min_imerge, with cost min_imerge_cost */
if
(
head
->
used_keys
)
{
/* check if "ALL" +"using index" read would be faster */
int
key_for_use
=
find_shortest_key
(
head
,
head
->
used_keys
);
ha_rows
total_table_records
=
(
0
==
head
->
file
->
records
)
?
1
:
head
->
file
->
records
;
uint
keys_per_block
=
(
head
->
file
->
block_size
/
2
/
(
head
->
key_info
[
key_for_use
].
key_length
+
head
->
file
->
ref_length
)
+
1
);
double
all_index_scan_read_time
=
((
double
)(
total_table_records
+
keys_per_block
-
1
)
/
(
double
)
keys_per_block
);
DBUG_PRINT
(
"info"
,
(
"'all' scan will be using key %d, read time %g"
,
key_for_use
,
all_index_scan_read_time
));
if
(
all_index_scan_read_time
<
min_imerge_cost
)
{
DBUG_PRINT
(
"info"
,
(
"index merge would be slower, "
"will do full 'index' scan"
));
goto
end_free
;
}
}
else
{
/* check if "ALL" would be faster */
if
(
read_time
<
min_imerge_cost
)
{
DBUG_PRINT
(
"info"
,
(
"index merge would be slower, "
"will do full table scan"
));
goto
end_free
;
}
}
if
(
!
(
quick
=
quick_imerge
=
new
QUICK_INDEX_MERGE_SELECT
(
thd
,
head
)))
goto
end_free
;
quick
->
records
=
min_imerge_records
;
quick
->
read_time
=
min_imerge_cost
;
my_pthread_setspecific_ptr
(
THR_MALLOC
,
&
quick_imerge
->
alloc
);
QUICK_RANGE_SELECT
*
new_quick
;
for
(
SEL_TREE
**
ptree
=
min_imerge
->
trees
;
ptree
!=
min_imerge
->
trees_next
;
ptree
++
)
{
SEL_ARG
**
tree_best_key
=
min_imerge
->
best_keys
[
ptree
-
min_imerge
->
trees
];
if
((
new_quick
=
get_quick_select
(
&
param
,
(
uint
)(
tree_best_key
-
(
*
ptree
)
->
keys
),
*
tree_best_key
,
&
quick_imerge
->
alloc
)))
{
new_quick
->
records
=
min_imerge_records
;
new_quick
->
read_time
=
min_imerge_cost
;
/*
QUICK_RANGE_SELECT::QUICK_RANGE_SELECT leaves THR_MALLOC
pointing to its allocator, restore it back
*/
//my_pthread_setspecific_ptr(THR_MALLOC,old_root);
quick_imerge
->
last_quick_select
=
new_quick
;
if
(
quick_imerge
->
push_quick_back
(
new_quick
))
{
delete
new_quick
;
delete
quick
;
quick
=
quick_imerge
=
NULL
;
goto
end_free
;
}
}
else
{
delete
quick
;
quick
=
quick_imerge
=
NULL
;
goto
end_free
;
}
}
free_root
(
&
alloc
,
MYF
(
0
));
my_pthread_setspecific_ptr
(
THR_MALLOC
,
old_root
);
if
(
quick
->
init
())
{
delete
quick
;
quick
=
quick_imerge
=
NULL
;
DBUG_PRINT
(
"error"
,
(
"Failed to allocate index merge structures,"
"falling back to full scan."
));
}
else
{
/* with 'using filesort' quick->reset() is not called */
quick
->
reset
();
}
goto
end
;
}
}
}
end_free:
free_root
(
&
alloc
,
MYF
(
0
));
// Return memory & allocator
my_pthread_setspecific_ptr
(
THR_MALLOC
,
old_root
);
end:
thd
->
no_errors
=
0
;
}
for
(
idx
=
0
,
key
=
tree
->
keys
,
end
=
key
+
param
.
keys
;
DBUG_EXECUTE
(
"info"
,
{
if
(
quick_imerge
)
print_quick_sel_imerge
(
quick_imerge
,
needed_reg
);
else
print_quick_sel_range
((
QUICK_RANGE_SELECT
*
)
quick
,
needed_reg
);
}
);
/*
Assume that if the user is using 'limit' we will only need to scan
limit rows if we are using a key
*/
DBUG_RETURN
(
records
?
test
(
quick
)
:
-
1
);
}
/*
Calculate quick select read time, # of records, and best key to use
without constructing QUICK_SELECT
*/
static
int
get_quick_select_params
(
SEL_TREE
*
tree
,
PARAM
&
param
,
key_map
&
needed_reg
,
TABLE
*
head
,
bool
index_read_can_be_used
,
double
*
read_time
,
ha_rows
*
records
,
SEL_ARG
***
key_to_read
)
{
int
idx
;
int
result
=
1
;
/*
Note that there may be trees that have type SEL_TREE::KEY but contain
no key reads at all. For example, tree for expression "key1 is not null"
where key1 is defined as "not null".
*/
SEL_ARG
**
key
,
**
end
;
for
(
idx
=
0
,
key
=
tree
->
keys
,
end
=
key
+
param
.
keys
;
key
!=
end
;
key
++
,
idx
++
)
{
...
...
@@ -691,9 +1199,12 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables,
(
*
key
)
->
maybe_flag
)
needed_reg
|=
(
key_map
)
1
<<
keynr
;
key_map
usable_keys
=
index_read_can_be_used
?
(
head
->
used_keys
&
((
key_map
)
1
<<
keynr
))
:
0
;
found_records
=
check_quick_select
(
&
param
,
idx
,
*
key
);
if
(
found_records
!=
HA_POS_ERROR
&&
found_records
>
2
&&
head
->
used_keys
&
((
table_map
)
1
<<
keynr
)
&&
usable_keys
&&
(
head
->
file
->
index_flags
(
keynr
)
&
HA_KEY_READ_ONLY
))
{
/*
...
...
@@ -713,35 +1224,16 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables,
param
.
range_count
,
found_records
)
+
(
double
)
found_records
/
TIME_FOR_COMPARE
);
if
(
read_time
>
found_read_time
)
if
(
*
read_time
>
found_read_time
)
{
read_time
=
found_read_time
;
records
=
found_records
;
best_key
=
key
;
*
read_time
=
found_read_time
;
*
records
=
found_records
;
*
key_to_read
=
key
;
result
=
0
;
}
}
}
if
(
best_key
&&
records
)
{
if
((
quick
=
get_quick_select
(
&
param
,(
uint
)
(
best_key
-
tree
->
keys
),
*
best_key
)))
{
quick
->
records
=
records
;
quick
->
read_time
=
read_time
;
}
}
}
}
free_root
(
&
alloc
,
MYF
(
0
));
// Return memory & allocator
my_pthread_setspecific_ptr
(
THR_MALLOC
,
old_root
);
current_thd
->
no_errors
=
0
;
}
DBUG_EXECUTE
(
"info"
,
print_quick
(
quick
,
needed_reg
););
/*
Assume that if the user is using 'limit' we will only need to scan
limit rows if we are using a key
*/
DBUG_RETURN
(
records
?
test
(
quick
)
:
-
1
);
return
result
;
}
/* make a select tree of all keys in condition */
...
...
@@ -911,6 +1403,7 @@ get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value,
sel_arg
=
new
SEL_ARG
(
SEL_ARG
::
MAYBE_KEY
);
// This key may be used later
sel_arg
->
part
=
(
uchar
)
key_part
->
part
;
tree
->
keys
[
key_part
->
key
]
=
sel_add
(
tree
->
keys
[
key_part
->
key
],
sel_arg
);
tree
->
keys_map
|=
1
<<
key_part
->
key
;
}
}
DBUG_RETURN
(
tree
);
...
...
@@ -1176,6 +1669,8 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
DBUG_RETURN
(
tree1
);
}
bool
trees_have_key
=
false
;
key_map
result_keys
=
0
;
/* Join the trees key per key */
SEL_ARG
**
key1
,
**
key2
,
**
end
;
for
(
key1
=
tree1
->
keys
,
key2
=
tree2
->
keys
,
end
=
key1
+
param
->
keys
;
...
...
@@ -1184,6 +1679,7 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
uint
flag
=
0
;
if
(
*
key1
||
*
key2
)
{
trees_have_key
=
true
;
if
(
*
key1
&&
!
(
*
key1
)
->
simple_key
())
flag
|=
CLONE_KEY1_MAYBE
;
if
(
*
key2
&&
!
(
*
key2
)
->
simple_key
())
...
...
@@ -1192,17 +1688,57 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if
((
*
key1
)
->
type
==
SEL_ARG
::
IMPOSSIBLE
)
{
tree1
->
type
=
SEL_TREE
::
IMPOSSIBLE
;
break
;
DBUG_RETURN
(
tree1
)
;
}
result_keys
|=
1
<<
(
key1
-
tree1
->
keys
);
#ifdef EXTRA_DEBUG
(
*
key1
)
->
test_use_count
(
*
key1
);
#endif
}
}
tree1
->
keys_map
=
result_keys
;
/* dispose index_merge if there is a "range" option */
if
(
trees_have_key
)
{
tree1
->
merges
.
empty
();
DBUG_RETURN
(
tree1
);
}
/* ok, both trees are index_merge trees */
imerge_list_and_list
(
&
tree1
->
merges
,
&
tree2
->
merges
);
DBUG_RETURN
(
tree1
);
}
/*
Check if two SEL_TREES can be combined into one without using index_merge
*/
bool
sel_trees_can_be_ored
(
SEL_TREE
*
tree1
,
SEL_TREE
*
tree2
,
PARAM
*
param
)
{
key_map
common_keys
=
tree1
->
keys_map
&
tree2
->
keys_map
;
DBUG_ENTER
(
"sel_trees_can_be_ored"
);
if
(
!
common_keys
)
DBUG_RETURN
(
false
);
/* trees have a common key, check if they refer to same key part */
SEL_ARG
**
key1
,
**
key2
;
for
(
uint
key_no
=
0
;
key_no
<
param
->
keys
;
key_no
++
,
common_keys
=
common_keys
>>
1
)
{
if
(
common_keys
&
1
)
{
key1
=
tree1
->
keys
+
key_no
;
key2
=
tree2
->
keys
+
key_no
;
if
((
*
key1
)
->
part
==
(
*
key2
)
->
part
)
{
DBUG_RETURN
(
true
);
}
}
}
DBUG_RETURN
(
false
);
}
static
SEL_TREE
*
tree_or
(
PARAM
*
param
,
SEL_TREE
*
tree1
,
SEL_TREE
*
tree2
)
...
...
@@ -1219,21 +1755,63 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if
(
tree2
->
type
==
SEL_TREE
::
MAYBE
)
DBUG_RETURN
(
tree2
);
SEL_TREE
*
result
=
0
;
key_map
result_keys
=
0
;
if
(
sel_trees_can_be_ored
(
tree1
,
tree2
,
param
))
{
/* Join the trees key per key */
SEL_ARG
**
key1
,
**
key2
,
**
end
;
SEL_TREE
*
result
=
0
;
for
(
key1
=
tree1
->
keys
,
key2
=
tree2
->
keys
,
end
=
key1
+
param
->
keys
;
for
(
key1
=
tree1
->
keys
,
key2
=
tree2
->
keys
,
end
=
key1
+
param
->
keys
;
key1
!=
end
;
key1
++
,
key2
++
)
{
*
key1
=
key_or
(
*
key1
,
*
key2
);
if
(
*
key1
)
{
result
=
tree1
;
// Added to tree1
result_keys
|=
1
<<
(
key1
-
tree1
->
keys
);
#ifdef EXTRA_DEBUG
(
*
key1
)
->
test_use_count
(
*
key1
);
#endif
}
}
if
(
result
)
result
->
keys_map
=
result_keys
;
}
else
{
/* ok, two trees have KEY type but cannot be used without index merge */
if
(
tree1
->
merges
.
is_empty
()
&&
tree2
->
merges
.
is_empty
())
{
SEL_IMERGE
*
merge
;
/* both trees are "range" trees, produce new index merge structure */
if
(
!
(
result
=
new
SEL_TREE
())
||
!
(
merge
=
new
SEL_IMERGE
())
||
(
result
->
merges
.
push_back
(
merge
))
||
(
merge
->
or_sel_tree
(
param
,
tree1
))
||
(
merge
->
or_sel_tree
(
param
,
tree2
)))
result
=
NULL
;
else
result
->
type
=
tree1
->
type
;
}
else
if
(
!
tree1
->
merges
.
is_empty
()
&&
!
tree2
->
merges
.
is_empty
())
{
if
(
imerge_list_or_list
(
param
,
&
tree1
->
merges
,
&
tree2
->
merges
))
result
=
new
SEL_TREE
(
SEL_TREE
::
ALWAYS
);
else
result
=
tree1
;
}
else
{
/* one tree is index merge tree and another is range tree */
if
(
tree1
->
merges
.
is_empty
())
swap
(
SEL_TREE
*
,
tree1
,
tree2
);
/* add tree2 to tree1->merges, checking if it collapses to ALWAYS */
if
(
imerge_list_or_tree
(
param
,
&
tree1
->
merges
,
tree2
))
result
=
new
SEL_TREE
(
SEL_TREE
::
ALWAYS
);
else
result
=
tree1
;
}
}
DBUG_RETURN
(
result
);
}
...
...
@@ -2201,14 +2779,17 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
/****************************************************************************
** change a tree to a structure to be used by quick_select
** This uses it's own malloc tree
** The caller should call QUICK_SELCT::init for returned quick select
****************************************************************************/
static
QUICK_SELECT
*
get_quick_select
(
PARAM
*
param
,
uint
idx
,
SEL_ARG
*
key_tree
)
QUICK_RANGE_SELECT
*
get_quick_select
(
PARAM
*
param
,
uint
idx
,
SEL_ARG
*
key_tree
,
MEM_ROOT
*
parent_alloc
)
{
QUICK_SELECT
*
quick
;
QUICK_
RANGE_
SELECT
*
quick
;
DBUG_ENTER
(
"get_quick_select"
);
if
((
quick
=
new
QUICK_SELECT
(
param
->
table
,
param
->
real_keynr
[
idx
])))
if
((
quick
=
new
QUICK_RANGE_SELECT
(
param
->
table
,
param
->
real_keynr
[
idx
],
test
(
parent_alloc
),
parent_alloc
)))
{
if
(
quick
->
error
||
get_quick_keys
(
param
,
quick
,
param
->
key
[
idx
],
key_tree
,
param
->
min_key
,
0
,
...
...
@@ -2220,7 +2801,8 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
else
{
quick
->
key_parts
=
(
KEY_PART
*
)
memdup_root
(
&
quick
->
alloc
,(
char
*
)
param
->
key
[
idx
],
memdup_root
(
parent_alloc
?
parent_alloc
:
&
quick
->
alloc
,
(
char
*
)
param
->
key
[
idx
],
sizeof
(
KEY_PART
)
*
param
->
table
->
key_info
[
param
->
real_keynr
[
idx
]].
key_parts
);
}
...
...
@@ -2232,9 +2814,8 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
/*
** Fix this to get all possible sub_ranges
*/
static
bool
get_quick_keys
(
PARAM
*
param
,
QUICK_SELECT
*
quick
,
KEY_PART
*
key
,
bool
get_quick_keys
(
PARAM
*
param
,
QUICK_RANGE_SELECT
*
quick
,
KEY_PART
*
key
,
SEL_ARG
*
key_tree
,
char
*
min_key
,
uint
min_key_flag
,
char
*
max_key
,
uint
max_key_flag
)
{
...
...
@@ -2343,7 +2924,7 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
Return 1 if there is only one range and this uses the whole primary key
*/
bool
QUICK_SELECT
::
unique_key_range
()
bool
QUICK_
RANGE_
SELECT
::
unique_key_range
()
{
if
(
ranges
.
elements
==
1
)
{
...
...
@@ -2380,16 +2961,22 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
** Create a QUICK RANGE based on a key
****************************************************************************/
QUICK_SELECT
*
get_quick_select_for_ref
(
TABLE
*
table
,
TABLE_REF
*
ref
)
QUICK_
RANGE_
SELECT
*
get_quick_select_for_ref
(
TABLE
*
table
,
TABLE_REF
*
ref
)
{
table
->
file
->
index_end
();
// Remove old cursor
QUICK_
SELECT
*
quick
=
new
QUICK_SELECT
(
table
,
ref
->
key
,
1
);
QUICK_
RANGE_SELECT
*
quick
=
new
QUICK_RANGE_SELECT
(
table
,
ref
->
key
,
1
);
KEY
*
key_info
=
&
table
->
key_info
[
ref
->
key
];
KEY_PART
*
key_part
;
uint
part
;
if
(
!
quick
)
return
0
;
if
(
quick
->
init
())
{
delete
quick
;
return
0
;
}
if
(
cp_buffer_from_ref
(
ref
))
{
if
(
current_thd
->
is_fatal_error
)
...
...
@@ -2427,11 +3014,204 @@ err:
return
0
;
}
INDEX_MERGE
::
INDEX_MERGE
(
THD
*
thd_arg
)
:
dont_save
(
false
),
thd
(
thd_arg
)
{}
String
*
INDEX_MERGE
::
Item_rowid
::
val_str
(
String
*
str
)
{
str
->
set_quick
((
char
*
)
head
->
file
->
ref
,
head
->
file
->
ref_length
,
collation
.
collation
);
return
str
;
}
/*
Initialize index_merge operation.
RETURN
0 - OK
other - error.
*/
int
INDEX_MERGE
::
init
(
TABLE
*
table
)
{
DBUG_ENTER
(
"INDEX_MERGE::init"
);
head
=
table
;
if
(
!
(
rowid_item
=
new
Item_rowid
(
table
)))
DBUG_RETURN
(
1
);
tmp_table_param
.
copy_field
=
0
;
tmp_table_param
.
end_write_records
=
HA_POS_ERROR
;
tmp_table_param
.
group_length
=
table
->
file
->
ref_length
;
tmp_table_param
.
group_parts
=
1
;
tmp_table_param
.
group_null_parts
=
0
;
tmp_table_param
.
hidden_field_count
=
0
;
tmp_table_param
.
field_count
=
0
;
tmp_table_param
.
func_count
=
1
;
tmp_table_param
.
sum_func_count
=
0
;
tmp_table_param
.
quick_group
=
1
;
bzero
(
&
order
,
sizeof
(
ORDER
));
order
.
item
=
(
Item
**
)
&
rowid_item
;
order
.
asc
=
1
;
fields
.
push_back
(
rowid_item
);
temp_table
=
create_tmp_table
(
thd
,
&
tmp_table_param
,
fields
,
&
order
,
false
,
0
,
SELECT_DISTINCT
,
HA_POS_ERROR
,
(
char
*
)
""
);
DBUG_RETURN
(
!
temp_table
);
}
/*
Check if record with ROWID record_pos has already been processed and
if not - store the ROWID value.
RETURN
0 - record has not been processed yet
1 - record has already been processed.
-1 - an error occurred and query processing should be terminated.
Error code is stored in INDEX_MERGE::error
*/
int
INDEX_MERGE
::
check_record_in
()
{
return
(
dont_save
)
?
check_record
()
:
put_record
();
}
/*
Stop remembering records in check().
(this should be called just before the last key scan)
RETURN
0 - OK
1 - error occurred initializing table index.
*/
int
INDEX_MERGE
::
start_last_quick_select
()
{
int
result
=
0
;
if
(
!
temp_table
->
uniques
)
{
dont_save
=
true
;
result
=
temp_table
->
file
->
index_init
(
0
);
}
return
result
;
}
inline
int
INDEX_MERGE
::
put_record
()
{
DBUG_ENTER
(
"INDEX_MERGE::put_record"
);
copy_funcs
(
tmp_table_param
.
items_to_copy
);
if
((
error
=
temp_table
->
file
->
write_row
(
temp_table
->
record
[
0
])))
{
if
(
error
==
HA_ERR_FOUND_DUPP_KEY
||
error
==
HA_ERR_FOUND_DUPP_UNIQUE
)
DBUG_RETURN
(
1
);
DBUG_PRINT
(
"info"
,
(
"Error writing row to temp. table: %d, converting to myisam"
,
error
));
if
(
create_myisam_from_heap
(
current_thd
,
temp_table
,
&
tmp_table_param
,
error
,
1
))
{
DBUG_PRINT
(
"info"
,
(
"Table conversion failed, bailing out"
));
DBUG_RETURN
(
-
1
);
}
}
DBUG_RETURN
(
0
);
}
inline
int
INDEX_MERGE
::
check_record
()
{
int
result
=
1
;
DBUG_ENTER
(
"INDEX_MERGE::check_record"
);
if
((
error
=
temp_table
->
file
->
index_read
(
temp_table
->
record
[
0
],
head
->
file
->
ref
,
head
->
file
->
ref_length
,
HA_READ_KEY_EXACT
)))
{
if
(
error
!=
HA_ERR_KEY_NOT_FOUND
)
result
=
-
1
;
else
result
=
0
;
}
DBUG_RETURN
(
result
);
}
INDEX_MERGE
::~
INDEX_MERGE
()
{
if
(
temp_table
)
{
DBUG_PRINT
(
"info"
,
(
"Freeing temp. table"
));
free_tmp_table
(
current_thd
,
temp_table
);
}
/* rowid_item is freed automatically */
list_node
*
node
;
node
=
fields
.
first_node
();
fields
.
remove
(
&
node
);
}
int
QUICK_INDEX_MERGE_SELECT
::
get_next
()
{
int
result
;
int
put_result
;
DBUG_ENTER
(
"QUICK_INDEX_MERGE_SELECT::get_next"
);
do
{
while
((
result
=
cur_quick_select
->
get_next
())
==
HA_ERR_END_OF_FILE
)
{
cur_quick_select
=
cur_quick_it
++
;
if
(
!
cur_quick_select
)
break
;
cur_quick_select
->
init
();
cur_quick_select
->
reset
();
if
(
last_quick_select
==
cur_quick_select
)
{
if
((
result
=
index_merge
.
start_last_quick_select
()))
DBUG_RETURN
(
result
);
}
}
if
(
result
)
{
/*
table read error (including HA_ERR_END_OF_FILE on last quick select
in index_merge)
*/
DBUG_RETURN
(
result
);
}
cur_quick_select
->
file
->
position
(
cur_quick_select
->
record
);
put_result
=
index_merge
.
check_record_in
();
}
while
(
put_result
==
1
);
/* While record is processed */
DBUG_RETURN
((
put_result
!=
-
1
)
?
result
:
index_merge
.
error
);
}
/* get next possible record using quick-struct */
int
QUICK_SELECT
::
get_next
()
int
QUICK_
RANGE_
SELECT
::
get_next
()
{
DBUG_ENTER
(
"get_next"
);
DBUG_ENTER
(
"
QUICK_RANGE_SELECT::
get_next"
);
for
(;;)
{
...
...
@@ -2518,7 +3298,7 @@ int QUICK_SELECT::get_next()
Returns 0 if key <= range->max_key
*/
int
QUICK_SELECT
::
cmp_next
(
QUICK_RANGE
*
range_arg
)
int
QUICK_
RANGE_
SELECT
::
cmp_next
(
QUICK_RANGE
*
range_arg
)
{
if
(
range_arg
->
flag
&
NO_MAX_RANGE
)
return
0
;
/* key can't be to large */
...
...
@@ -2559,8 +3339,9 @@ int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg)
for now, this seems to work right at least.
*/
QUICK_SELECT_DESC
::
QUICK_SELECT_DESC
(
QUICK_SELECT
*
q
,
uint
used_key_parts
)
:
QUICK_SELECT
(
*
q
),
rev_it
(
rev_ranges
)
QUICK_SELECT_DESC
::
QUICK_SELECT_DESC
(
QUICK_RANGE_SELECT
*
q
,
uint
used_key_parts
)
:
QUICK_RANGE_SELECT
(
*
q
),
rev_it
(
rev_ranges
)
{
bool
not_read_after_key
=
file
->
table_flags
()
&
HA_NOT_READ_AFTER_KEY
;
QUICK_RANGE
*
r
;
...
...
@@ -2827,7 +3608,23 @@ print_key(KEY_PART *key_part,const char *key,uint used_length)
}
}
static
void
print_quick
(
QUICK_SELECT
*
quick
,
key_map
needed_reg
)
void
print_quick_sel_imerge
(
QUICK_INDEX_MERGE_SELECT
*
quick
,
key_map
needed_reg
)
{
DBUG_ENTER
(
"print_param"
);
if
(
!
_db_on_
||
!
quick
)
DBUG_VOID_RETURN
;
List_iterator_fast
<
QUICK_RANGE_SELECT
>
it
(
quick
->
quick_selects
);
QUICK_RANGE_SELECT
*
quick_range_sel
;
while
((
quick_range_sel
=
it
++
))
{
print_quick_sel_range
(
quick_range_sel
,
needed_reg
);
}
DBUG_VOID_RETURN
;
}
void
print_quick_sel_range
(
QUICK_RANGE_SELECT
*
quick
,
key_map
needed_reg
)
{
QUICK_RANGE
*
range
;
DBUG_ENTER
(
"print_param"
);
...
...
sql/opt_range.h
View file @
ca6c4cc3
...
...
@@ -65,41 +65,198 @@ class QUICK_RANGE :public Sql_alloc {
}
};
class
INDEX_MERGE
;
class
QUICK_SELECT
{
/*
Quick select interface.
This class is parent for all QUICK_*_SELECT and FT_SELECT classes.
*/
class
QUICK_SELECT_I
{
public:
ha_rows
records
;
/* estimate of # of records to be retrieved */
double
read_time
;
/* time to perform this retrieval */
TABLE
*
head
;
/*
the only index this quick select uses, or MAX_KEY for
QUICK_INDEX_MERGE_SELECT
*/
uint
index
;
uint
max_used_key_length
,
used_key_parts
;
QUICK_SELECT_I
();
virtual
~
QUICK_SELECT_I
(){};
virtual
int
init
()
=
0
;
virtual
void
reset
(
void
)
=
0
;
virtual
int
get_next
()
=
0
;
/* get next record to retrieve */
virtual
bool
reverse_sorted
()
=
0
;
virtual
bool
unique_key_range
()
{
return
false
;
}
enum
{
QS_TYPE_RANGE
=
0
,
QS_TYPE_INDEX_MERGE
=
1
,
QS_TYPE_RANGE_DESC
=
2
,
QS_TYPE_FULLTEXT
=
3
};
/* Get type of this quick select - one of the QS_* values */
virtual
int
get_type
()
=
0
;
};
struct
st_qsel_param
;
class
SEL_ARG
;
class
QUICK_RANGE_SELECT
:
public
QUICK_SELECT_I
{
protected:
bool
next
,
dont_free
;
public:
int
error
;
uint
index
,
max_used_key_length
,
used_key_parts
;
TABLE
*
head
;
handler
*
file
;
byte
*
record
;
protected:
friend
void
print_quick_sel_range
(
QUICK_RANGE_SELECT
*
quick
,
key_map
needed_reg
);
friend
QUICK_RANGE_SELECT
*
get_quick_select_for_ref
(
TABLE
*
table
,
struct
st_table_ref
*
ref
);
friend
bool
get_quick_keys
(
struct
st_qsel_param
*
param
,
QUICK_RANGE_SELECT
*
quick
,
KEY_PART
*
key
,
SEL_ARG
*
key_tree
,
char
*
min_key
,
uint
min_key_flag
,
char
*
max_key
,
uint
max_key_flag
);
friend
QUICK_RANGE_SELECT
*
get_quick_select
(
struct
st_qsel_param
*
,
uint
idx
,
SEL_ARG
*
key_tree
,
MEM_ROOT
*
alloc
);
friend
class
QUICK_SELECT_DESC
;
List
<
QUICK_RANGE
>
ranges
;
List_iterator
<
QUICK_RANGE
>
it
;
QUICK_RANGE
*
range
;
MEM_ROOT
alloc
;
KEY_PART
*
key_parts
;
ha_rows
records
;
double
read_time
;
int
cmp_next
(
QUICK_RANGE
*
range
);
public:
QUICK_RANGE_SELECT
(
TABLE
*
table
,
uint
index_arg
,
bool
no_alloc
=
0
,
MEM_ROOT
*
parent_alloc
=
NULL
);
~
QUICK_RANGE_SELECT
();
QUICK_SELECT
(
TABLE
*
table
,
uint
index_arg
,
bool
no_alloc
=
0
);
virtual
~
QUICK_SELECT
();
void
reset
(
void
)
{
next
=
0
;
it
.
rewind
();
}
int
init
()
{
return
error
=
file
->
index_init
(
index
);
}
virtual
int
get_next
();
virtual
bool
reverse_sorted
()
{
return
0
;
}
int
cmp_next
(
QUICK_RANGE
*
range
);
int
init
();
int
get_next
();
bool
reverse_sorted
()
{
return
0
;
}
bool
unique_key_range
();
int
get_type
()
{
return
QS_TYPE_RANGE
;
}
};
/*
Helper class for keeping track of rows that have been passed to output
in index_merge access method.
NOTES
Current implementation uses a temporary table to store ROWIDs of rows that
have been passed to output. In the future it might be changed to use more
efficient mechanisms, like Unique class.
*/
class
INDEX_MERGE
{
public:
INDEX_MERGE
(
THD
*
thd_arg
);
~
INDEX_MERGE
();
int
init
(
TABLE
*
table
);
int
check_record_in
();
int
start_last_quick_select
();
int
error
;
private:
/* The only field in temporary table */
class
Item_rowid
:
public
Item_str_func
{
TABLE
*
head
;
/* source table */
public:
Item_rowid
(
TABLE
*
table
)
:
head
(
table
)
{
max_length
=
table
->
file
->
ref_length
;
collation
.
set
(
&
my_charset_bin
);
};
const
char
*
func_name
()
const
{
return
"rowid"
;
}
bool
const_item
()
const
{
return
0
;
}
String
*
val_str
(
String
*
);
void
fix_length_and_dec
()
{}
};
/* Check if record has been processed and save it if it wasn't */
inline
int
put_record
();
/* Check if record has been processed without saving it */
inline
int
check_record
();
/* If true, check_record_in does't store ROWIDs it is passed. */
bool
dont_save
;
THD
*
thd
;
TABLE
*
head
;
/* source table */
TABLE
*
temp_table
;
/* temp. table used for values storage */
TMP_TABLE_PARAM
tmp_table_param
;
/* temp. table creation parameters */
Item_rowid
*
rowid_item
;
/* the only field in temp. table */
List
<
Item
>
fields
;
/* temp. table fields list
(the only element is rowid_item) */
ORDER
order
;
/* key for temp. table (rowid_item) */
};
/*
Index merge quick select.
It is implemented as a container for several QUICK_RANGE_SELECTs.
*/
class
QUICK_INDEX_MERGE_SELECT
:
public
QUICK_SELECT_I
{
public:
QUICK_INDEX_MERGE_SELECT
(
THD
*
thd
,
TABLE
*
table
);
~
QUICK_INDEX_MERGE_SELECT
();
int
init
();
void
reset
(
void
);
int
get_next
();
bool
reverse_sorted
()
{
return
false
;
}
bool
unique_key_range
()
{
return
false
;
}
int
get_type
()
{
return
QS_TYPE_INDEX_MERGE
;
}
bool
push_quick_back
(
QUICK_RANGE_SELECT
*
quick_sel_range
);
/* range quick selects this index_merge read consists of */
List
<
QUICK_RANGE_SELECT
>
quick_selects
;
/* quick select which is currently used for rows retrieval */
List_iterator_fast
<
QUICK_RANGE_SELECT
>
cur_quick_it
;
QUICK_RANGE_SELECT
*
cur_quick_select
;
/*
Last element in quick_selects list.
INDEX_MERGE::start_last_quick_select is called before retrieving
rows for it.
*/
QUICK_RANGE_SELECT
*
last_quick_select
;
/*
Used to keep track of what records have been already passed to output
when doing index_merge access (NULL means no index_merge)
*/
INDEX_MERGE
index_merge
;
MEM_ROOT
alloc
;
};
class
QUICK_SELECT_DESC
:
public
QUICK_SELECT
class
QUICK_SELECT_DESC
:
public
QUICK_
RANGE_
SELECT
{
public:
QUICK_SELECT_DESC
(
QUICK_SELECT
*
q
,
uint
used_key_parts
);
QUICK_SELECT_DESC
(
QUICK_
RANGE_
SELECT
*
q
,
uint
used_key_parts
);
int
get_next
();
bool
reverse_sorted
()
{
return
1
;
}
int
get_type
()
{
return
QS_TYPE_RANGE_DESC
;
}
private:
int
cmp_prev
(
QUICK_RANGE
*
range
);
bool
range_reads_after_key
(
QUICK_RANGE
*
range
);
...
...
@@ -114,7 +271,7 @@ private:
class
SQL_SELECT
:
public
Sql_alloc
{
public:
QUICK_SELECT
*
quick
;
// If quick-select used
QUICK_SELECT
_I
*
quick
;
// If quick-select used
COND
*
cond
;
// where condition
TABLE
*
head
;
IO_CACHE
file
;
// Positions to used records
...
...
@@ -134,6 +291,6 @@ class SQL_SELECT :public Sql_alloc {
bool
force_quick_range
=
0
);
};
QUICK_SELECT
*
get_quick_select_for_ref
(
TABLE
*
table
,
struct
st_table_ref
*
ref
);
QUICK_
RANGE_
SELECT
*
get_quick_select_for_ref
(
TABLE
*
table
,
struct
st_table_ref
*
ref
);
#endif
sql/sql_class.cc
View file @
ca6c4cc3
...
...
@@ -537,8 +537,8 @@ int THD::send_explain_fields(select_result *result)
item
->
maybe_null
=
1
;
field_list
.
push_back
(
item
=
new
Item_empty_string
(
"key"
,
NAME_LEN
));
item
->
maybe_null
=
1
;
field_list
.
push_back
(
item
=
new
Item_
return_int
(
"key_len"
,
3
,
MYSQL_TYPE_LONGLONG
));
field_list
.
push_back
(
item
=
new
Item_
empty_string
(
"key_len"
,
NAME_LEN
*
MAX_KEY
));
item
->
maybe_null
=
1
;
field_list
.
push_back
(
item
=
new
Item_empty_string
(
"ref"
,
NAME_LEN
*
MAX_REF_PARTS
));
...
...
sql/sql_list.h
View file @
ca6c4cc3
...
...
@@ -135,6 +135,12 @@ public:
last
=
&
first
;
return
tmp
->
info
;
}
inline
void
concat
(
base_list
*
list
)
{
*
last
=
list
->
first
;
last
=
list
->
last
;
elements
+=
list
->
elements
;
}
inline
list_node
*
last_node
()
{
return
*
last
;
}
inline
list_node
*
first_node
()
{
return
first
;}
inline
void
*
head
()
{
return
first
->
info
;
}
...
...
@@ -255,6 +261,7 @@ public:
}
empty
();
}
inline
void
concat
(
List
<
T
>
*
list
)
{
base_list
::
concat
(
list
);
}
};
...
...
sql/sql_select.cc
View file @
ca6c4cc3
...
...
@@ -32,7 +32,8 @@
const
char
*
join_type_str
[]
=
{
"UNKNOWN"
,
"system"
,
"const"
,
"eq_ref"
,
"ref"
,
"MAYBE_REF"
,
"ALL"
,
"range"
,
"index"
,
"fulltext"
,
"ref_or_null"
,
"simple_in"
,
"index_in"
"ref_or_null"
,
"simple_in"
,
"index_in"
,
"index_merge"
};
static
void
optimize_keyuse
(
JOIN
*
join
,
DYNAMIC_ARRAY
*
keyuse_array
);
...
...
@@ -114,7 +115,6 @@ static int join_read_next_same_or_null(READ_RECORD *info);
static
COND
*
make_cond_for_table
(
COND
*
cond
,
table_map
table
,
table_map
used_table
);
static
Item
*
part_of_refkey
(
TABLE
*
form
,
Field
*
field
);
static
uint
find_shortest_key
(
TABLE
*
table
,
key_map
usable_keys
);
static
bool
test_if_skip_sort_order
(
JOIN_TAB
*
tab
,
ORDER
*
order
,
ha_rows
select_limit
,
bool
no_changes
);
static
int
create_sort_index
(
THD
*
thd
,
JOIN
*
join
,
ORDER
*
order
,
...
...
@@ -6473,7 +6473,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
return
reverse
;
}
static
uint
find_shortest_key
(
TABLE
*
table
,
key_map
usable_keys
)
uint
find_shortest_key
(
TABLE
*
table
,
key_map
usable_keys
)
{
uint
min_length
=
(
uint
)
~
0
;
uint
best
=
MAX_KEY
;
...
...
@@ -6601,6 +6601,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
else
if
(
select
&&
select
->
quick
)
// Range found by opt_range
{
/* assume results are not ordered when index merge is used */
if
(
select
->
quick
->
get_type
()
==
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
)
DBUG_RETURN
(
0
);
ref_key
=
select
->
quick
->
index
;
ref_key_parts
=
select
->
quick
->
used_key_parts
;
}
...
...
@@ -6635,6 +6638,10 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
else
{
/*
We have verified above that select->quick is not
index_merge quick select.
*/
select
->
quick
->
index
=
new_ref_key
;
select
->
quick
->
init
();
}
...
...
@@ -6656,10 +6663,13 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
*/
if
(
!
select
->
quick
->
reverse_sorted
())
{
if
(
table
->
file
->
index_flags
(
ref_key
)
&
HA_NOT_READ_PREFIX_LAST
)
if
(
table
->
file
->
index_flags
(
ref_key
)
&
HA_NOT_READ_PREFIX_LAST
||
(
select
->
quick
->
get_type
()
==
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
))
DBUG_RETURN
(
0
);
// Use filesort
// ORDER BY range_key DESC
QUICK_SELECT_DESC
*
tmp
=
new
QUICK_SELECT_DESC
(
select
->
quick
,
QUICK_SELECT_DESC
*
tmp
=
new
QUICK_SELECT_DESC
(
(
QUICK_RANGE_SELECT
*
)(
select
->
quick
)
,
used_key_parts
);
if
(
!
tmp
||
tmp
->
error
)
{
...
...
@@ -6794,8 +6804,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{
select
->
quick
=
tab
->
quick
;
tab
->
quick
=
0
;
/* We can only use 'Only index' if quick key is same as ref_key */
if
(
table
->
key_read
&&
(
uint
)
tab
->
ref
.
key
!=
select
->
quick
->
index
)
/*
We can only use 'Only index' if quick key is same as ref_key
and in index_merge 'Only index' cannot be used
*/
if
(
table
->
key_read
&&
((
uint
)
tab
->
ref
.
key
!=
select
->
quick
->
index
))
{
table
->
key_read
=
0
;
table
->
file
->
extra
(
HA_EXTRA_NO_KEYREAD
);
...
...
@@ -8598,12 +8611,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
JOIN_TAB
*
tab
=
join
->
join_tab
+
i
;
TABLE
*
table
=
tab
->
table
;
char
buff
[
512
],
*
buff_ptr
=
buff
;
char
buff1
[
512
],
buff2
[
512
];
char
buff1
[
512
],
buff2
[
512
],
buff3
[
512
];
char
keylen_str_buf
[
64
];
char
derived_name
[
64
];
String
tmp1
(
buff1
,
sizeof
(
buff1
),
cs
);
String
tmp2
(
buff2
,
sizeof
(
buff2
),
cs
);
String
tmp3
(
buff3
,
sizeof
(
buff3
),
cs
);
tmp1
.
length
(
0
);
tmp2
.
length
(
0
);
tmp3
.
length
(
0
);
item_list
.
empty
();
item_list
.
push_back
(
new
Item_int
((
int32
)
...
...
@@ -8612,7 +8628,13 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
strlen
(
join
->
select_lex
->
type
),
cs
));
if
(
tab
->
type
==
JT_ALL
&&
tab
->
select
&&
tab
->
select
->
quick
)
tab
->
type
=
JT_RANGE
;
{
if
(
tab
->
select
->
quick
->
get_type
()
==
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
)
tab
->
type
=
JT_INDEX_MERGE
;
else
tab
->
type
=
JT_RANGE
;
}
if
(
table
->
derived_select_number
)
{
/* Derived table name generation */
...
...
@@ -8646,10 +8668,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if
(
tab
->
ref
.
key_parts
)
{
KEY
*
key_info
=
table
->
key_info
+
tab
->
ref
.
key
;
register
uint
length
;
item_list
.
push_back
(
new
Item_string
(
key_info
->
name
,
strlen
(
key_info
->
name
),
system_charset_info
));
item_list
.
push_back
(
new
Item_int
((
int32
)
tab
->
ref
.
key_length
));
length
=
longlong2str
(
tab
->
ref
.
key_length
,
keylen_str_buf
,
10
)
-
keylen_str_buf
;
item_list
.
push_back
(
new
Item_string
(
keylen_str_buf
,
length
,
system_charset_info
));
for
(
store_key
**
ref
=
tab
->
ref
.
key_copy
;
*
ref
;
ref
++
)
{
if
(
tmp2
.
length
())
...
...
@@ -8661,18 +8687,60 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
else
if
(
tab
->
type
==
JT_NEXT
)
{
KEY
*
key_info
=
table
->
key_info
+
tab
->
index
;
register
uint
length
;
item_list
.
push_back
(
new
Item_string
(
key_info
->
name
,
strlen
(
key_info
->
name
),
cs
));
item_list
.
push_back
(
new
Item_int
((
int32
)
key_info
->
key_length
));
length
=
longlong2str
(
key_info
->
key_length
,
keylen_str_buf
,
10
)
-
keylen_str_buf
;
item_list
.
push_back
(
new
Item_string
(
keylen_str_buf
,
length
,
system_charset_info
));
item_list
.
push_back
(
item_null
);
}
else
if
(
tab
->
select
&&
tab
->
select
->
quick
)
{
KEY
*
key_info
=
table
->
key_info
+
tab
->
select
->
quick
->
index
;
item_list
.
push_back
(
new
Item_string
(
key_info
->
name
,
strlen
(
key_info
->
name
),
cs
));
item_list
.
push_back
(
new
Item_int
((
int32
)
tab
->
select
->
quick
->
max_used_key_length
));
if
(
tab
->
select
->
quick
->
get_type
()
==
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
)
{
QUICK_INDEX_MERGE_SELECT
*
quick_imerge
=
(
QUICK_INDEX_MERGE_SELECT
*
)
tab
->
select
->
quick
;
QUICK_RANGE_SELECT
*
quick
;
List_iterator_fast
<
QUICK_RANGE_SELECT
>
it
(
quick_imerge
->
quick_selects
);
while
((
quick
=
it
++
))
{
KEY
*
key_info
=
table
->
key_info
+
quick
->
index
;
register
uint
length
;
if
(
tmp3
.
length
())
tmp3
.
append
(
','
);
tmp3
.
append
(
key_info
->
name
);
if
(
tmp2
.
length
())
tmp2
.
append
(
','
);
length
=
longlong2str
(
quick
->
max_used_key_length
,
keylen_str_buf
,
10
)
-
keylen_str_buf
;
tmp2
.
append
(
keylen_str_buf
,
length
);
}
}
else
{
KEY
*
key_info
=
table
->
key_info
+
tab
->
select
->
quick
->
index
;
register
uint
length
;
tmp3
.
append
(
key_info
->
name
);
length
=
longlong2str
(
tab
->
select
->
quick
->
max_used_key_length
,
keylen_str_buf
,
10
)
-
keylen_str_buf
;
tmp2
.
append
(
keylen_str_buf
,
length
);
}
item_list
.
push_back
(
new
Item_string
(
tmp3
.
ptr
(),
tmp3
.
length
(),
cs
));
item_list
.
push_back
(
new
Item_string
(
tmp2
.
ptr
(),
tmp2
.
length
(),
cs
));
item_list
.
push_back
(
item_null
);
}
else
...
...
sql/sql_select.h
View file @
ca6c4cc3
...
...
@@ -76,7 +76,7 @@ typedef struct st_join_cache {
enum
join_type
{
JT_UNKNOWN
,
JT_SYSTEM
,
JT_CONST
,
JT_EQ_REF
,
JT_REF
,
JT_MAYBE_REF
,
JT_ALL
,
JT_RANGE
,
JT_NEXT
,
JT_FT
,
JT_REF_OR_NULL
,
JT_SIMPLE_IN
,
JT_INDEX_IN
};
JT_SIMPLE_IN
,
JT_INDEX_IN
,
JT_INDEX_MERGE
};
class
JOIN
;
...
...
@@ -85,7 +85,7 @@ typedef struct st_join_table {
KEYUSE
*
keyuse
;
/* pointer to first used key */
SQL_SELECT
*
select
;
COND
*
select_cond
;
QUICK_SELECT
*
quick
;
QUICK_SELECT
_I
*
quick
;
Item
*
on_expr
;
const
char
*
info
;
byte
*
null_ref_key
;
...
...
@@ -307,6 +307,7 @@ void copy_fields(TMP_TABLE_PARAM *param);
void
copy_funcs
(
Item
**
func_ptr
);
bool
create_myisam_from_heap
(
THD
*
thd
,
TABLE
*
table
,
TMP_TABLE_PARAM
*
param
,
int
error
,
bool
ignore_last_dupp_error
);
uint
find_shortest_key
(
TABLE
*
table
,
key_map
usable_keys
);
/* functions from opt_sum.cc */
int
opt_sum_query
(
TABLE_LIST
*
tables
,
List
<
Item
>
&
all_fields
,
COND
*
conds
);
...
...
sql/sql_test.cc
View file @
ca6c4cc3
...
...
@@ -179,9 +179,39 @@ TEST_join(JOIN *join)
" quick select checked for each record (keys: %d)
\n
"
,
(
int
)
tab
->
select
->
quick_keys
);
else
if
(
tab
->
select
->
quick
)
fprintf
(
DBUG_FILE
,
" quick select used on key %s, length: %d
\n
"
,
{
int
quick_type
=
tab
->
select
->
quick
->
get_type
();
if
((
quick_type
==
QUICK_SELECT_I
::
QS_TYPE_RANGE
)
||
(
quick_type
==
QUICK_SELECT_I
::
QS_TYPE_RANGE_DESC
))
{
fprintf
(
DBUG_FILE
,
" quick select used on key %s, length: %d
\n
"
,
form
->
key_info
[
tab
->
select
->
quick
->
index
].
name
,
tab
->
select
->
quick
->
max_used_key_length
);
}
else
if
(
quick_type
==
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
)
{
QUICK_INDEX_MERGE_SELECT
*
quick_imerge
=
(
QUICK_INDEX_MERGE_SELECT
*
)
tab
->
select
->
quick
;
QUICK_RANGE_SELECT
*
quick
;
fprintf
(
DBUG_FILE
,
" index_merge quick select used
\n
"
);
List_iterator_fast
<
QUICK_RANGE_SELECT
>
it
(
quick_imerge
->
quick_selects
);
while
((
quick
=
it
++
))
{
fprintf
(
DBUG_FILE
,
" range quick select: key %s, length: %d
\n
"
,
form
->
key_info
[
quick
->
index
].
name
,
quick
->
max_used_key_length
);
}
}
else
{
fprintf
(
DBUG_FILE
,
" quick select of unknown nature used
\n
"
);
}
}
else
VOID
(
fputs
(
" select used
\n
"
,
DBUG_FILE
));
}
...
...
sql/sql_union.cc
View file @
ca6c4cc3
...
...
@@ -117,6 +117,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result,
{
SELECT_LEX
*
lex_select_save
=
thd
->
lex
.
current_select
;
SELECT_LEX
*
select_cursor
;
SELECT_LEX
*
sl
;
DBUG_ENTER
(
"st_select_lex_unit::prepare"
);
if
(
prepared
)
...
...
@@ -185,7 +186,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result,
union_result
->
not_describe
=
1
;
union_result
->
tmp_table_param
=
tmp_table_param
;
for
(
SELECT_LEX
*
sl
=
select_cursor
;
sl
;
sl
=
sl
->
next_select
())
for
(
sl
=
select_cursor
;
sl
;
sl
=
sl
->
next_select
())
{
JOIN
*
join
=
new
JOIN
(
thd
,
sl
->
item_list
,
sl
->
options
|
thd
->
options
|
SELECT_NO_UNLOCK
,
...
...
sql/sql_update.cc
View file @
ca6c4cc3
...
...
@@ -171,10 +171,18 @@ int mysql_update(THD *thd,
init_ftfuncs
(
thd
,
&
thd
->
lex
.
select_lex
,
1
);
/* Check if we are modifying a key that we are used to search with */
if
(
select
&&
select
->
quick
)
{
if
(
select
->
quick
->
get_type
()
!=
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
)
{
used_index
=
select
->
quick
->
index
;
used_key_is_modified
=
(
!
select
->
quick
->
unique_key_range
()
&&
check_if_key_used
(
table
,
(
used_index
=
select
->
quick
->
index
),
fields
));
check_if_key_used
(
table
,
used_index
,
fields
));
}
else
{
used_key_is_modified
=
true
;
}
}
else
if
((
used_index
=
table
->
file
->
key_used_on_scan
)
<
MAX_KEY
)
used_key_is_modified
=
check_if_key_used
(
table
,
used_index
,
fields
);
else
...
...
@@ -688,8 +696,26 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
case
JT_ALL
:
/* If range search on index */
if
(
join_tab
->
quick
)
return
!
check_if_key_used
(
table
,
join_tab
->
quick
->
index
,
*
fields
);
{
if
(
join_tab
->
quick
->
get_type
()
!=
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
)
{
return
!
check_if_key_used
(
table
,
join_tab
->
quick
->
index
,
*
fields
);
}
else
{
QUICK_INDEX_MERGE_SELECT
*
qsel_imerge
=
(
QUICK_INDEX_MERGE_SELECT
*
)(
join_tab
->
quick
);
List_iterator_fast
<
QUICK_RANGE_SELECT
>
it
(
qsel_imerge
->
quick_selects
);
QUICK_RANGE_SELECT
*
quick
;
while
((
quick
=
it
++
))
{
if
(
check_if_key_used
(
table
,
quick
->
index
,
*
fields
))
return
0
;
}
return
1
;
}
}
/* If scanning in clustered key */
if
((
table
->
file
->
table_flags
()
&
HA_PRIMARY_KEY_IN_READ_INDEX
)
&&
table
->
primary_key
<
MAX_KEY
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment