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
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
1c61a92b
Commit
1c61a92b
authored
Nov 19, 2003
by
sergefp@mysql.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
index_merge implementation using Unique class, to be merged into 5.0
parent
0466c250
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1108 additions
and
177 deletions
+1108
-177
BitKeeper/etc/logging_ok
BitKeeper/etc/logging_ok
+1
-0
sql/filesort.cc
sql/filesort.cc
+40
-15
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
+777
-104
sql/opt_range.h
sql/opt_range.h
+112
-18
sql/records.cc
sql/records.cc
+2
-1
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
+88
-20
sql/sql_select.h
sql/sql_select.h
+6
-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 @
1c61a92b
...
...
@@ -110,6 +110,7 @@ serg@serg.mylan
serg@serg.mysql.com
serg@sergbook.mylan
serg@sergbook.mysql.com
sergefp@mysql.com
sinisa@rhols221.adsl.netsonic.fi
tfr@beta.frontier86.ee
tfr@indrek.tfr.cafe.ee
...
...
sql/filesort.cc
View file @
1c61a92b
...
...
@@ -49,7 +49,8 @@ static int merge_index(SORTPARAM *param,uchar *sort_buffer,
BUFFPEK
*
buffpek
,
uint
maxbuffer
,
IO_CACHE
*
tempfile
,
IO_CACHE
*
outfile
);
static
bool
save_index
(
SORTPARAM
*
param
,
uchar
**
sort_keys
,
uint
count
);
static
bool
save_index
(
SORTPARAM
*
param
,
uchar
**
sort_keys
,
uint
count
,
FILESORT_INFO
*
table_sort
);
static
uint
sortlength
(
SORT_FIELD
*
sortorder
,
uint
s_length
,
bool
*
multi_byte_charset
);
static
SORT_ADDON_FIELD
*
get_addon_fields
(
THD
*
thd
,
Field
**
ptabfield
,
...
...
@@ -86,7 +87,9 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_PUSH
(
""
);
/* No DBUG here */
#endif
FILESORT_INFO
table_sort
;
bzero
(
&
table_sort
,
sizeof
(
FILESORT_INFO
));
outfile
=
table
->
sort
.
io_cache
;
my_b_clear
(
&
tempfile
);
my_b_clear
(
&
buffpek_pointers
);
...
...
@@ -108,14 +111,15 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
param
.
sort_length
,
&
param
.
addon_length
);
}
table
->
sort
.
addon_buf
=
0
;
table
->
sort
.
addon_length
=
param
.
addon_length
;
table
->
sort
.
addon_field
=
param
.
addon_field
;
table
->
sort
.
unpack
=
unpack_addon_fields
;
table_sort
.
addon_buf
=
0
;
table_sort
.
addon_length
=
param
.
addon_length
;
table_sort
.
addon_field
=
param
.
addon_field
;
table_sort
.
unpack
=
unpack_addon_fields
;
if
(
param
.
addon_field
)
{
param
.
res_length
=
param
.
addon_length
;
if
(
!
(
table
->
sort
.
addon_buf
=
(
byte
*
)
my_malloc
(
param
.
addon_length
,
if
(
!
(
table
_
sort
.
addon_buf
=
(
byte
*
)
my_malloc
(
param
.
addon_length
,
MYF
(
MY_WME
))))
goto
err
;
}
...
...
@@ -194,7 +198,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
if
(
maxbuffer
==
0
)
// The whole set is in memory
{
if
(
save_index
(
&
param
,
sort_keys
,(
uint
)
records
))
if
(
save_index
(
&
param
,
sort_keys
,(
uint
)
records
,
&
table_sort
))
goto
err
;
}
else
...
...
@@ -257,6 +261,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_POP
();
/* Ok to DBUG */
#endif
memcpy
(
&
table
->
sort
,
&
table_sort
,
sizeof
(
FILESORT_INFO
));
table
->
sort
.
io_cache
=
outfile
;
DBUG_PRINT
(
"exit"
,(
"records: %ld"
,
records
));
DBUG_RETURN
(
error
?
HA_POS_ERROR
:
records
);
}
/* filesort */
...
...
@@ -360,12 +366,24 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
current_thd
->
variables
.
read_buff_size
);
}
READ_RECORD
read_record_info
;
if
(
quick_select
)
{
if
(
select
->
quick
->
reset
())
DBUG_RETURN
(
HA_POS_ERROR
);
init_read_record
(
&
read_record_info
,
current_thd
,
select
->
quick
->
head
,
select
,
1
,
1
);
}
for
(;;)
{
if
(
quick_select
)
{
if
((
error
=
select
->
quick
->
get_next
()))
break
;
if
((
error
=
read_record_info
.
read_record
(
&
read_record_info
)))
{
error
=
HA_ERR_END_OF_FILE
;
break
;
}
file
->
position
(
sort_form
->
record
[
0
]);
}
else
/* Not quick-select */
...
...
@@ -393,6 +411,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
if
(
error
&&
error
!=
HA_ERR_RECORD_DELETED
)
break
;
}
if
(
*
killed
)
{
DBUG_PRINT
(
"info"
,(
"Sort killed by user"
));
...
...
@@ -426,8 +445,14 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
else
file
->
unlock_row
();
}
(
void
)
file
->
extra
(
HA_EXTRA_NO_CACHE
);
/* End cacheing of records */
file
->
rnd_end
();
if
(
quick_select
)
end_read_record
(
&
read_record_info
);
else
{
(
void
)
file
->
extra
(
HA_EXTRA_NO_CACHE
);
/* End cacheing of records */
file
->
rnd_end
();
}
DBUG_PRINT
(
"test"
,(
"error: %d indexpos: %d"
,
error
,
indexpos
));
if
(
error
!=
HA_ERR_END_OF_FILE
)
{
...
...
@@ -665,8 +690,8 @@ static void make_sortkey(register SORTPARAM *param,
return
;
}
static
bool
save_index
(
SORTPARAM
*
param
,
uchar
**
sort_keys
,
uint
coun
t
)
static
bool
save_index
(
SORTPARAM
*
param
,
uchar
**
sort_keys
,
uint
count
,
FILESORT_INFO
*
table_sor
t
)
{
uint
offset
,
res_length
;
byte
*
to
;
...
...
@@ -677,7 +702,7 @@ static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count)
offset
=
param
->
rec_length
-
res_length
;
if
((
ha_rows
)
count
>
param
->
max_rows
)
count
=
(
uint
)
param
->
max_rows
;
if
(
!
(
to
=
param
->
sort_form
->
sort
.
record_pointers
=
if
(
!
(
to
=
table_sort
->
record_pointers
=
(
byte
*
)
my_malloc
(
res_length
*
count
,
MYF
(
MY_WME
))))
DBUG_RETURN
(
1
);
/* purecov: inspected */
for
(
uchar
**
end
=
sort_keys
+
count
;
sort_keys
!=
end
;
sort_keys
++
)
...
...
sql/opt_ft.cc
View file @
1c61a92b
...
...
@@ -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 @
1c61a92b
...
...
@@ -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
();
}
int
init
()
{
return
error
=
file
->
ft_init
(
);
}
QUICK_
RANGE_
SELECT
(
table
,
tref
->
key
,
1
),
ref
(
tref
)
{
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 @
1c61a92b
...
...
@@ -267,14 +267,17 @@ class SEL_ARG :public Sql_alloc
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,47 @@ QUICK_SELECT::~QUICK_SELECT()
}
}
QUICK_INDEX_MERGE_SELECT
::
QUICK_INDEX_MERGE_SELECT
(
THD
*
thd_param
,
TABLE
*
table
)
:
cur_quick_it
(
quick_selects
),
thd
(
thd_param
),
unique
(
NULL
)
{
index
=
MAX_KEY
;
head
=
table
;
reset_called
=
false
;
init_sql_alloc
(
&
alloc
,
1024
,
0
);
}
int
QUICK_INDEX_MERGE_SELECT
::
init
()
{
cur_quick_it
.
rewind
();
cur_quick_select
=
cur_quick_it
++
;
return
cur_quick_select
->
init
();
}
int
QUICK_INDEX_MERGE_SELECT
::
reset
()
{
int
result
;
DBUG_ENTER
(
"QUICK_INDEX_MERGE_SELECT::reset"
);
if
(
reset_called
)
DBUG_RETURN
(
0
);
reset_called
=
true
;
result
=
cur_quick_select
->
reset
()
&&
prepare_unique
();
DBUG_RETURN
(
result
);
}
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 +863,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
=
NULL
;
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 +910,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
}
...
...
@@ -673,70 +957,205 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables,
read_time
=
(
double
)
HA_POS_ERROR
;
}
else
if
(
tree
->
type
==
SEL_TREE
::
KEY
||
tree
->
type
==
SEL_TREE
::
KEY_SMALLER
)
tree
->
type
==
SEL_TREE
::
KEY_SMALLER
)
{
SEL_ARG
**
key
,
**
end
,
**
best_key
=
0
;
for
(
idx
=
0
,
key
=
tree
->
keys
,
end
=
key
+
param
.
keys
;
key
!=
end
;
key
++
,
idx
++
)
{
ha_rows
found_records
;
double
found_read_time
;
if
(
*
key
)
{
uint
keynr
=
param
.
real_keynr
[
idx
];
if
((
*
key
)
->
type
==
SEL_ARG
::
MAYBE_KEY
||
(
*
key
)
->
maybe_flag
)
needed_reg
|=
(
key_map
)
1
<<
keynr
;
found_records
=
check_quick_select
(
&
param
,
idx
,
*
key
);
if
(
found_records
!=
HA_POS_ERROR
&&
found_records
>
2
&&
head
->
used_keys
&
((
table_map
)
1
<<
keynr
)
&&
(
head
->
file
->
index_flags
(
keynr
)
&
HA_KEY_READ_ONLY
))
{
/*
We can resolve this by only reading through this key.
Assume that we will read trough the whole key range
and that all key blocks are half full (normally things are
much better).
*/
uint
keys_per_block
=
(
head
->
file
->
block_size
/
2
/
(
head
->
key_info
[
keynr
].
key_length
+
head
->
file
->
ref_length
)
+
1
);
found_read_time
=
((
double
)
(
found_records
+
keys_per_block
-
1
)
/
(
double
)
keys_per_block
);
}
else
found_read_time
=
(
head
->
file
->
read_time
(
keynr
,
param
.
range_count
,
found_records
)
+
(
double
)
found_records
/
TIME_FOR_COMPARE
);
if
(
read_time
>
found_read_time
)
{
read_time
=
found_read_time
;
records
=
found_records
;
best_key
=
key
;
}
}
}
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
;
}
}
/*
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
*/
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."
));
}
goto
end
;
}
}
}
end_free:
free_root
(
&
alloc
,
MYF
(
0
));
// Return memory & allocator
my_pthread_setspecific_ptr
(
THR_MALLOC
,
old_root
);
current_thd
->
no_errors
=
0
;
end:
thd
->
no_errors
=
0
;
}
DBUG_EXECUTE
(
"info"
,
print_quick
(
quick
,
needed_reg
););
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
...
...
@@ -744,6 +1163,77 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables,
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
++
)
{
ha_rows
found_records
;
double
found_read_time
;
if
(
*
key
)
{
uint
keynr
=
param
.
real_keynr
[
idx
];
if
((
*
key
)
->
type
==
SEL_ARG
::
MAYBE_KEY
||
(
*
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
&&
usable_keys
&&
(
head
->
file
->
index_flags
(
keynr
)
&
HA_KEY_READ_ONLY
))
{
/*
We can resolve this by only reading through this key.
Assume that we will read trough the whole key range
and that all key blocks are half full (normally things are
much better).
*/
uint
keys_per_block
=
(
head
->
file
->
block_size
/
2
/
(
head
->
key_info
[
keynr
].
key_length
+
head
->
file
->
ref_length
)
+
1
);
found_read_time
=
((
double
)
(
found_records
+
keys_per_block
-
1
)
/
(
double
)
keys_per_block
);
}
else
found_read_time
=
(
head
->
file
->
read_time
(
keynr
,
param
.
range_count
,
found_records
)
+
(
double
)
found_records
/
TIME_FOR_COMPARE
);
if
(
*
read_time
>
found_read_time
)
{
*
read_time
=
found_read_time
;
*
records
=
found_records
;
*
key_to_read
=
key
;
result
=
0
;
}
}
}
return
result
;
}
/* make a select tree of all keys in condition */
static
SEL_TREE
*
get_mm_tree
(
PARAM
*
param
,
COND
*
cond
)
...
...
@@ -911,6 +1401,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 +1667,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 +1677,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 +1686,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,19 +1753,61 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if
(
tree2
->
type
==
SEL_TREE
::
MAYBE
)
DBUG_RETURN
(
tree2
);
/* 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
;
key1
!=
end
;
key1
++
,
key2
++
)
SEL_TREE
*
result
=
0
;
key_map
result_keys
=
0
;
if
(
sel_trees_can_be_ored
(
tree1
,
tree2
,
param
))
{
*
key1
=
key_or
(
*
key1
,
*
key2
);
if
(
*
key1
)
/* Join the trees key per key */
SEL_ARG
**
key1
,
**
key2
,
**
end
;
for
(
key1
=
tree1
->
keys
,
key2
=
tree2
->
keys
,
end
=
key1
+
param
->
keys
;
key1
!=
end
;
key1
++
,
key2
++
)
{
result
=
tree1
;
// Added to tree1
*
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
);
(
*
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 +2777,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,9 +2799,10 @@ 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
],
sizeof
(
KEY_PART
)
*
param
->
table
->
key_info
[
param
->
real_keynr
[
idx
]].
key_parts
);
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
);
}
}
DBUG_RETURN
(
quick
);
...
...
@@ -2232,9 +2812,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 +2922,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 +2959,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 +3012,82 @@ QUICK_SELECT *get_quick_select_for_ref(TABLE *table, TABLE_REF *ref)
return
0
;
}
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
/*
Fetch all row ids into unique.
*/
int
QUICK_INDEX_MERGE_SELECT
::
prepare_unique
()
{
int
result
;
DBUG_ENTER
(
"QUICK_INDEX_MERGE_SELECT::prepare_unique"
);
/* we're going to just read rowids */
head
->
file
->
extra
(
HA_EXTRA_KEYREAD
);
unique
=
new
Unique
(
refposcmp2
,
(
void
*
)
&
head
->
file
->
ref_length
,
head
->
file
->
ref_length
,
MEM_STRIP_BUF_SIZE
);
if
(
!
unique
)
DBUG_RETURN
(
1
);
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
();
if
(
cur_quick_select
->
reset
())
DBUG_RETURN
(
1
);
}
if
(
result
)
{
/*
table read error (including HA_ERR_END_OF_FILE on last quick select
in index_merge)
*/
if
(
result
!=
HA_ERR_END_OF_FILE
)
{
DBUG_RETURN
(
result
);
}
else
break
;
}
if
(
thd
->
killed
)
DBUG_RETURN
(
1
);
cur_quick_select
->
file
->
position
(
cur_quick_select
->
record
);
if
(
unique
->
unique_add
((
char
*
)
cur_quick_select
->
file
->
ref
))
DBUG_RETURN
(
1
);
}
while
(
true
);
/* ok, all row ids are in Unique */
result
=
unique
->
get
(
head
);
/* index_merge currently doesn't support "using index" at all */
head
->
file
->
extra
(
HA_EXTRA_NO_KEYREAD
);
DBUG_RETURN
(
result
);
}
int
QUICK_INDEX_MERGE_SELECT
::
get_next
()
{
DBUG_ENTER
(
"QUICK_INDEX_MERGE_SELECT::get_next"
);
DBUG_PRINT
(
"QUICK_INDEX_MERGE_SELECT"
,
(
"ERROR: index merge error: get_next should not be called "
));
DBUG_ASSERT
(
0
);
DBUG_RETURN
(
HA_ERR_END_OF_FILE
);
}
/* 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 +3174,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 +3215,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 +3484,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 @
1c61a92b
...
...
@@ -65,48 +65,142 @@ 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
int
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
;
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
);
public:
QUICK_RANGE_SELECT
(
TABLE
*
table
,
uint
index_arg
,
bool
no_alloc
=
0
,
MEM_ROOT
*
parent_alloc
=
NULL
);
~
QUICK_RANGE_SELECT
();
int
reset
(
void
)
{
next
=
0
;
it
.
rewind
();
return
0
;
}
int
init
();
int
get_next
();
bool
reverse_sorted
()
{
return
0
;
}
bool
unique_key_range
();
int
get_type
()
{
return
QS_TYPE_RANGE
;
}
};
/*
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
();
int
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. */
QUICK_RANGE_SELECT
*
last_quick_select
;
Unique
*
unique
;
MEM_ROOT
alloc
;
THD
*
thd
;
int
prepare_unique
();
bool
reset_called
;
};
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
);
#ifdef NOT_USED
bool
test_if_null_range
(
QUICK_RANGE
*
range
,
uint
used_key_parts
);
#endif
void
reset
(
void
)
{
next
=
0
;
rev_it
.
rewind
()
;
}
int
reset
(
void
)
{
next
=
0
;
rev_it
.
rewind
();
return
0
;
}
List
<
QUICK_RANGE
>
rev_ranges
;
List_iterator
<
QUICK_RANGE
>
rev_it
;
};
...
...
@@ -114,7 +208,7 @@ class QUICK_SELECT_DESC: public QUICK_SELECT
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 +228,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/records.cc
View file @
1c61a92b
...
...
@@ -98,7 +98,8 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
}
}
}
else
if
(
select
&&
select
->
quick
)
else
if
(
select
&&
select
->
quick
&&
(
select
->
quick
->
get_type
()
!=
QUICK_SELECT_I
::
QS_TYPE_INDEX_MERGE
))
{
DBUG_PRINT
(
"info"
,(
"using rr_quick"
));
info
->
read_record
=
rr_quick
;
...
...
sql/sql_class.cc
View file @
1c61a92b
...
...
@@ -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 @
1c61a92b
...
...
@@ -135,6 +135,12 @@ class base_list :public Sql_alloc
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 @@ template <class T> class List :public base_list
}
empty
();
}
inline
void
concat
(
List
<
T
>
*
list
)
{
base_list
::
concat
(
list
);
}
};
...
...
sql/sql_select.cc
View file @
1c61a92b
...
...
@@ -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
,
...
...
@@ -3285,7 +3285,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
with key reading */
if
(
tab
->
needed_reg
==
0
&&
tab
->
type
!=
JT_EQ_REF
&&
tab
->
type
!=
JT_FT
&&
(
tab
->
type
!=
JT_REF
||
(
uint
)
tab
->
ref
.
key
==
tab
->
quick
->
index
))
(
uint
)
tab
->
ref
.
key
==
tab
->
quick
->
index
))
{
sel
->
quick
=
tab
->
quick
;
// Use value from get_quick_...
sel
->
quick_keys
=
0
;
...
...
@@ -5725,8 +5725,8 @@ test_if_quick_select(JOIN_TAB *tab)
static
int
join_init_read_record
(
JOIN_TAB
*
tab
)
{
if
(
tab
->
select
&&
tab
->
select
->
quick
)
tab
->
select
->
quick
->
reset
()
;
if
(
tab
->
select
&&
tab
->
select
->
quick
&&
tab
->
select
->
quick
->
reset
()
)
return
1
;
init_read_record
(
&
tab
->
read_record
,
tab
->
join
->
thd
,
tab
->
table
,
tab
->
select
,
1
,
1
);
return
(
*
tab
->
read_record
.
read_record
)(
&
tab
->
read_record
);
...
...
@@ -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
,
// ORDER BY range_key DESC
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 @
1c61a92b
...
...
@@ -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,10 +307,14 @@ 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
);
/* from sql_delete.cc, used by opt_range.cc */
extern
"C"
int
refposcmp2
(
void
*
arg
,
const
void
*
a
,
const
void
*
b
);
/* class to copying an field/item to a key struct */
class
store_key
:
public
Sql_alloc
...
...
sql/sql_test.cc
View file @
1c61a92b
...
...
@@ -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 @
1c61a92b
...
...
@@ -117,7 +117,8 @@ 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
;
DBUG_ENTER
(
"st_select_lex_unit::prepare"
);
SELECT_LEX
*
sl
;
DBUG_ENTER
(
"st_select_lex_unit::prepare"
);
if
(
prepared
)
DBUG_RETURN
(
0
);
...
...
@@ -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 @
1c61a92b
...
...
@@ -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
)
used_key_is_modified
=
(
!
select
->
quick
->
unique_key_range
()
&&
check_if_key_used
(
table
,
(
used_index
=
select
->
quick
->
index
),
fields
));
{
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
,
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