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
bbbe3ac8
Commit
bbbe3ac8
authored
Nov 05, 2010
by
Igor Babaev
Browse files
Options
Browse Files
Download
Plain Diff
Merge
parents
71e48fbf
dba8cfd5
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
78 additions
and
37 deletions
+78
-37
sql/sql_select.cc
sql/sql_select.cc
+76
-36
sql/sql_select.h
sql/sql_select.h
+2
-1
No files found.
sql/sql_select.cc
View file @
bbbe3ac8
...
@@ -179,12 +179,14 @@ static int join_ft_read_next(READ_RECORD *info);
...
@@ -179,12 +179,14 @@ static int join_ft_read_next(READ_RECORD *info);
int
join_read_always_key_or_null
(
JOIN_TAB
*
tab
);
int
join_read_always_key_or_null
(
JOIN_TAB
*
tab
);
int
join_read_next_same_or_null
(
READ_RECORD
*
info
);
int
join_read_next_same_or_null
(
READ_RECORD
*
info
);
static
COND
*
make_cond_for_table
(
Item
*
cond
,
table_map
table
,
static
COND
*
make_cond_for_table
(
Item
*
cond
,
table_map
table
,
table_map
used_table
,
table_map
used_table
,
bool
exclude_expensive_cond
);
bool
exclude_expensive_cond
,
bool
retain_ref_cond
);
static
COND
*
make_cond_for_table_from_pred
(
Item
*
root_cond
,
Item
*
cond
,
static
COND
*
make_cond_for_table_from_pred
(
Item
*
root_cond
,
Item
*
cond
,
table_map
tables
,
table_map
tables
,
table_map
used_table
,
table_map
used_table
,
bool
exclude_expensive_cond
);
bool
exclude_expensive_cond
,
bool
retain_ref_cond
);
static
Item
*
part_of_refkey
(
TABLE
*
form
,
Field
*
field
);
static
Item
*
part_of_refkey
(
TABLE
*
form
,
Field
*
field
);
uint
find_shortest_key
(
TABLE
*
table
,
const
key_map
*
usable_keys
);
uint
find_shortest_key
(
TABLE
*
table
,
const
key_map
*
usable_keys
);
...
@@ -927,7 +929,7 @@ JOIN::optimize()
...
@@ -927,7 +929,7 @@ JOIN::optimize()
if
(
conds
&&
!
(
thd
->
lex
->
describe
&
DESCRIBE_EXTENDED
))
if
(
conds
&&
!
(
thd
->
lex
->
describe
&
DESCRIBE_EXTENDED
))
{
{
COND
*
table_independent_conds
=
COND
*
table_independent_conds
=
make_cond_for_table
(
conds
,
PSEUDO_TABLE_BITS
,
0
,
FALSE
);
make_cond_for_table
(
conds
,
PSEUDO_TABLE_BITS
,
0
,
FALSE
,
FALSE
);
DBUG_EXECUTE
(
"where"
,
DBUG_EXECUTE
(
"where"
,
print_where
(
table_independent_conds
,
print_where
(
table_independent_conds
,
"where after opt_sum_query()"
,
"where after opt_sum_query()"
,
...
@@ -2254,7 +2256,7 @@ JOIN::exec()
...
@@ -2254,7 +2256,7 @@ JOIN::exec()
Item
*
sort_table_cond
=
make_cond_for_table
(
curr_join
->
tmp_having
,
Item
*
sort_table_cond
=
make_cond_for_table
(
curr_join
->
tmp_having
,
used_tables
,
used_tables
,
(
table_map
)
0
,
FALSE
);
(
table_map
)
0
,
FALSE
,
FALSE
);
if
(
sort_table_cond
)
if
(
sort_table_cond
)
{
{
if
(
!
curr_table
->
select
)
if
(
!
curr_table
->
select
)
...
@@ -2277,7 +2279,7 @@ JOIN::exec()
...
@@ -2277,7 +2279,7 @@ JOIN::exec()
QT_ORDINARY
););
QT_ORDINARY
););
curr_join
->
tmp_having
=
make_cond_for_table
(
curr_join
->
tmp_having
,
curr_join
->
tmp_having
=
make_cond_for_table
(
curr_join
->
tmp_having
,
~
(
table_map
)
0
,
~
(
table_map
)
0
,
~
used_tables
,
FALSE
);
~
used_tables
,
FALSE
,
FALSE
);
DBUG_EXECUTE
(
"where"
,
print_where
(
curr_join
->
tmp_having
,
DBUG_EXECUTE
(
"where"
,
print_where
(
curr_join
->
tmp_having
,
"having after sort"
,
"having after sort"
,
QT_ORDINARY
););
QT_ORDINARY
););
...
@@ -5979,6 +5981,46 @@ bool JOIN_TAB::hash_join_is_possible()
...
@@ -5979,6 +5981,46 @@ bool JOIN_TAB::hash_join_is_possible()
}
}
/*
@brief
Extract pushdown conditions for a table scan
@details
This functions extracts pushdown conditions usable when this table is scanned.
The conditions are extracted either from WHERE or from ON expressions.
The conditions are attached to the field cache_select of this table.
@note
Currently the extracted conditions are used only by BNL and BNLH join.
algorithms.
@retval 0 on success
1 otherwise
*/
int
JOIN_TAB
::
make_scan_filter
()
{
COND
*
tmp
;
DBUG_ENTER
(
"make_join_select"
);
Item
*
cond
=
is_last_inner_table
()
?
*
get_first_inner_table
()
->
on_expr_ref
:
join
->
conds
;
if
(
cond
&&
(
tmp
=
make_cond_for_table
(
cond
,
join
->
const_table_map
|
table
->
map
,
table
->
map
,
FALSE
,
TRUE
)))
{
DBUG_EXECUTE
(
"where"
,
print_where
(
tmp
,
"cache"
,
QT_ORDINARY
););
if
(
!
(
cache_select
=
(
SQL_SELECT
*
)
join
->
thd
->
memdup
((
uchar
*
)
select
,
sizeof
(
SQL_SELECT
))))
DBUG_RETURN
(
1
);
cache_select
->
cond
=
tmp
;
cache_select
->
read_tables
=
join
->
const_table_map
;
}
DBUG_RETURN
(
0
);
}
static
uint
static
uint
cache_record_length
(
JOIN
*
join
,
uint
idx
)
cache_record_length
(
JOIN
*
join
,
uint
idx
)
{
{
...
@@ -6751,7 +6793,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
...
@@ -6751,7 +6793,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
COND
*
const_cond
=
COND
*
const_cond
=
make_cond_for_table
(
cond
,
make_cond_for_table
(
cond
,
join
->
const_table_map
,
join
->
const_table_map
,
(
table_map
)
0
,
TRUE
);
(
table_map
)
0
,
TRUE
,
FALSE
);
/* Add conditions added by add_not_null_conds(). */
/* Add conditions added by add_not_null_conds(). */
for
(
uint
i
=
0
;
i
<
join
->
const_tables
;
i
++
)
for
(
uint
i
=
0
;
i
<
join
->
const_tables
;
i
++
)
add_cond_and_fix
(
&
const_cond
,
join
->
join_tab
[
i
].
select_cond
);
add_cond_and_fix
(
&
const_cond
,
join
->
join_tab
[
i
].
select_cond
);
...
@@ -6765,7 +6807,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
...
@@ -6765,7 +6807,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
JOIN_TAB
*
cond_tab
=
tab
->
first_inner
;
JOIN_TAB
*
cond_tab
=
tab
->
first_inner
;
COND
*
tmp
=
make_cond_for_table
(
*
tab
->
on_expr_ref
,
COND
*
tmp
=
make_cond_for_table
(
*
tab
->
on_expr_ref
,
join
->
const_table_map
,
join
->
const_table_map
,
(
table_map
)
0
,
FALSE
);
(
table_map
)
0
,
FALSE
,
FALSE
);
if
(
!
tmp
)
if
(
!
tmp
)
continue
;
continue
;
tmp
=
new
Item_func_trig_cond
(
tmp
,
&
cond_tab
->
not_null_compl
);
tmp
=
new
Item_func_trig_cond
(
tmp
,
&
cond_tab
->
not_null_compl
);
...
@@ -6853,7 +6895,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
...
@@ -6853,7 +6895,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tmp
=
NULL
;
tmp
=
NULL
;
if
(
cond
)
if
(
cond
)
tmp
=
make_cond_for_table
(
cond
,
used_tables
,
current_map
,
FALSE
);
tmp
=
make_cond_for_table
(
cond
,
used_tables
,
current_map
,
FALSE
,
FALSE
);
/* Add conditions added by add_not_null_conds(). */
/* Add conditions added by add_not_null_conds(). */
if
(
tab
->
select_cond
)
if
(
tab
->
select_cond
)
add_cond_and_fix
(
&
tmp
,
tab
->
select_cond
);
add_cond_and_fix
(
&
tmp
,
tab
->
select_cond
);
...
@@ -6912,7 +6954,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
...
@@ -6912,7 +6954,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if
(
thd
->
variables
.
engine_condition_pushdown
&&
!
first_inner_tab
)
if
(
thd
->
variables
.
engine_condition_pushdown
&&
!
first_inner_tab
)
{
{
COND
*
push_cond
=
COND
*
push_cond
=
make_cond_for_table
(
tmp
,
current_map
,
current_map
,
FALSE
);
make_cond_for_table
(
tmp
,
current_map
,
current_map
,
FALSE
,
FALSE
);
if
(
push_cond
)
if
(
push_cond
)
{
{
/* Push condition to handler */
/* Push condition to handler */
...
@@ -7037,19 +7079,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
...
@@ -7037,19 +7079,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if
(
i
!=
join
->
const_tables
&&
tab
->
use_quick
!=
2
&&
if
(
i
!=
join
->
const_tables
&&
tab
->
use_quick
!=
2
&&
!
tab
->
first_inner
)
!
tab
->
first_inner
)
{
/* Read with cache */
{
/* Read with cache */
if
(
cond
&&
if
(
tab
->
make_scan_filter
())
(
tmp
=
make_cond_for_table
(
cond
,
DBUG_RETURN
(
1
);
join
->
const_table_map
|
}
current_map
,
current_map
,
FALSE
)))
{
DBUG_EXECUTE
(
"where"
,
print_where
(
tmp
,
"cache"
,
QT_ORDINARY
););
tab
->
cache_select
=
(
SQL_SELECT
*
)
thd
->
memdup
((
uchar
*
)
sel
,
sizeof
(
SQL_SELECT
));
tab
->
cache_select
->
cond
=
tmp
;
tab
->
cache_select
->
read_tables
=
join
->
const_table_map
;
}
}
}
}
}
}
...
@@ -7071,7 +7103,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
...
@@ -7071,7 +7103,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
JOIN_TAB
*
cond_tab
=
join_tab
->
first_inner
;
JOIN_TAB
*
cond_tab
=
join_tab
->
first_inner
;
COND
*
tmp
=
make_cond_for_table
(
*
join_tab
->
on_expr_ref
,
COND
*
tmp
=
make_cond_for_table
(
*
join_tab
->
on_expr_ref
,
join
->
const_table_map
,
join
->
const_table_map
,
(
table_map
)
0
,
FALSE
);
(
table_map
)
0
,
FALSE
,
FALSE
);
if
(
!
tmp
)
if
(
!
tmp
)
continue
;
continue
;
tmp
=
new
Item_func_trig_cond
(
tmp
,
&
cond_tab
->
not_null_compl
);
tmp
=
new
Item_func_trig_cond
(
tmp
,
&
cond_tab
->
not_null_compl
);
...
@@ -7106,7 +7138,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
...
@@ -7106,7 +7138,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
current_map
=
tab
->
table
->
map
;
current_map
=
tab
->
table
->
map
;
used_tables2
|=
current_map
;
used_tables2
|=
current_map
;
COND
*
tmp_cond
=
make_cond_for_table
(
on_expr
,
used_tables2
,
COND
*
tmp_cond
=
make_cond_for_table
(
on_expr
,
used_tables2
,
current_map
,
FALSE
);
current_map
,
FALSE
,
FALSE
);
add_cond_and_fix
(
&
tmp_cond
,
tab
->
on_precond
);
add_cond_and_fix
(
&
tmp_cond
,
tab
->
on_precond
);
if
(
tmp_cond
)
if
(
tmp_cond
)
{
{
...
@@ -7686,7 +7718,8 @@ uint check_join_cache_usage(JOIN_TAB *tab,
...
@@ -7686,7 +7718,8 @@ uint check_join_cache_usage(JOIN_TAB *tab,
if
((
cache_level
<=
4
&&
!
no_hashed_cache
)
||
no_bka_cache
||
if
((
cache_level
<=
4
&&
!
no_hashed_cache
)
||
no_bka_cache
||
((
flags
&
HA_MRR_NO_ASSOCIATION
)
&&
cache_level
<=
6
))
((
flags
&
HA_MRR_NO_ASSOCIATION
)
&&
cache_level
<=
6
))
{
{
if
(
!
tab
->
hash_join_is_possible
())
if
(
!
tab
->
hash_join_is_possible
()
||
tab
->
make_scan_filter
())
goto
no_join_cache
;
goto
no_join_cache
;
if
(
cache_level
==
3
)
if
(
cache_level
==
3
)
prev_cache
=
0
;
prev_cache
=
0
;
...
@@ -14874,6 +14907,7 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
...
@@ -14874,6 +14907,7 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
used_table Table that we're extracting the condition for (may
used_table Table that we're extracting the condition for (may
also include PSEUDO_TABLE_BITS
also include PSEUDO_TABLE_BITS
exclude_expensive_cond Do not push expensive conditions
exclude_expensive_cond Do not push expensive conditions
retain_ref_cond Retain ref conditions
DESCRIPTION
DESCRIPTION
Extract the condition that can be checked after reading the table
Extract the condition that can be checked after reading the table
...
@@ -14899,16 +14933,18 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
...
@@ -14899,16 +14933,18 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item)
static
Item
*
static
Item
*
make_cond_for_table
(
Item
*
cond
,
table_map
tables
,
table_map
used_table
,
make_cond_for_table
(
Item
*
cond
,
table_map
tables
,
table_map
used_table
,
bool
exclude_expensive_cond
)
bool
exclude_expensive_cond
,
bool
retain_ref_cond
)
{
{
return
make_cond_for_table_from_pred
(
cond
,
cond
,
tables
,
used_table
,
return
make_cond_for_table_from_pred
(
cond
,
cond
,
tables
,
used_table
,
exclude_expensive_cond
);
exclude_expensive_cond
,
retain_ref_cond
);
}
}
static
Item
*
static
Item
*
make_cond_for_table_from_pred
(
Item
*
root_cond
,
Item
*
cond
,
make_cond_for_table_from_pred
(
Item
*
root_cond
,
Item
*
cond
,
table_map
tables
,
table_map
used_table
,
table_map
tables
,
table_map
used_table
,
bool
exclude_expensive_cond
)
bool
exclude_expensive_cond
,
bool
retain_ref_cond
)
{
{
if
(
used_table
&&
!
(
cond
->
used_tables
()
&
used_table
)
&&
if
(
used_table
&&
!
(
cond
->
used_tables
()
&
used_table
)
&&
...
@@ -14939,7 +14975,8 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
...
@@ -14939,7 +14975,8 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
{
{
Item
*
fix
=
make_cond_for_table_from_pred
(
root_cond
,
item
,
Item
*
fix
=
make_cond_for_table_from_pred
(
root_cond
,
item
,
tables
,
used_table
,
tables
,
used_table
,
exclude_expensive_cond
);
exclude_expensive_cond
,
retain_ref_cond
);
if
(
fix
)
if
(
fix
)
new_cond
->
argument_list
()
->
push_back
(
fix
);
new_cond
->
argument_list
()
->
push_back
(
fix
);
}
}
...
@@ -14971,7 +15008,8 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
...
@@ -14971,7 +15008,8 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
{
{
Item
*
fix
=
make_cond_for_table_from_pred
(
root_cond
,
item
,
Item
*
fix
=
make_cond_for_table_from_pred
(
root_cond
,
item
,
tables
,
0L
,
tables
,
0L
,
exclude_expensive_cond
);
exclude_expensive_cond
,
retain_ref_cond
);
if
(
!
fix
)
if
(
!
fix
)
return
(
COND
*
)
0
;
// Always true
return
(
COND
*
)
0
;
// Always true
new_cond
->
argument_list
()
->
push_back
(
fix
);
new_cond
->
argument_list
()
->
push_back
(
fix
);
...
@@ -14992,7 +15030,8 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
...
@@ -14992,7 +15030,8 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
table_count times, we mark each item that we have examined with the result
table_count times, we mark each item that we have examined with the result
of the test
of the test
*/
*/
if
(
cond
->
marker
==
3
||
(
cond
->
used_tables
()
&
~
tables
)
||
if
((
cond
->
marker
==
3
&&
!
retain_ref_cond
)
||
(
cond
->
used_tables
()
&
~
tables
)
||
/*
/*
When extracting constant conditions, treat expensive conditions as
When extracting constant conditions, treat expensive conditions as
non-constant, so that they are not evaluated at optimization time.
non-constant, so that they are not evaluated at optimization time.
...
@@ -15007,13 +15046,13 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
...
@@ -15007,13 +15046,13 @@ make_cond_for_table_from_pred(Item *root_cond, Item *cond,
{
{
Item
*
left_item
=
((
Item_func
*
)
cond
)
->
arguments
()[
0
]
->
real_item
();
Item
*
left_item
=
((
Item_func
*
)
cond
)
->
arguments
()[
0
]
->
real_item
();
Item
*
right_item
=
((
Item_func
*
)
cond
)
->
arguments
()[
1
]
->
real_item
();
Item
*
right_item
=
((
Item_func
*
)
cond
)
->
arguments
()[
1
]
->
real_item
();
if
(
left_item
->
type
()
==
Item
::
FIELD_ITEM
&&
if
(
left_item
->
type
()
==
Item
::
FIELD_ITEM
&&
!
retain_ref_cond
&&
test_if_ref
(
root_cond
,
(
Item_field
*
)
left_item
,
right_item
))
test_if_ref
(
root_cond
,
(
Item_field
*
)
left_item
,
right_item
))
{
{
cond
->
marker
=
3
;
// Checked when read
cond
->
marker
=
3
;
// Checked when read
return
(
COND
*
)
0
;
return
(
COND
*
)
0
;
}
}
if
(
right_item
->
type
()
==
Item
::
FIELD_ITEM
&&
if
(
right_item
->
type
()
==
Item
::
FIELD_ITEM
&&
!
retain_ref_cond
&&
test_if_ref
(
root_cond
,
(
Item_field
*
)
right_item
,
left_item
))
test_if_ref
(
root_cond
,
(
Item_field
*
)
right_item
,
left_item
))
{
{
cond
->
marker
=
3
;
// Checked when read
cond
->
marker
=
3
;
// Checked when read
...
@@ -16198,7 +16237,7 @@ static bool fix_having(JOIN *join, Item **having)
...
@@ -16198,7 +16237,7 @@ static bool fix_having(JOIN *join, Item **having)
DBUG_EXECUTE
(
"where"
,
print_where
(
*
having
,
"having"
,
QT_ORDINARY
););
DBUG_EXECUTE
(
"where"
,
print_where
(
*
having
,
"having"
,
QT_ORDINARY
););
Item
*
sort_table_cond
=
make_cond_for_table
(
*
having
,
used_tables
,
used_tables
,
Item
*
sort_table_cond
=
make_cond_for_table
(
*
having
,
used_tables
,
used_tables
,
FALSE
);
FALSE
,
FALSE
);
if
(
sort_table_cond
)
if
(
sort_table_cond
)
{
{
if
(
!
table
->
select
)
if
(
!
table
->
select
)
...
@@ -16216,7 +16255,8 @@ static bool fix_having(JOIN *join, Item **having)
...
@@ -16216,7 +16255,8 @@ static bool fix_having(JOIN *join, Item **having)
DBUG_EXECUTE
(
"where"
,
print_where
(
table
->
select_cond
,
DBUG_EXECUTE
(
"where"
,
print_where
(
table
->
select_cond
,
"select and having"
,
"select and having"
,
QT_ORDINARY
););
QT_ORDINARY
););
*
having
=
make_cond_for_table
(
*
having
,
~
(
table_map
)
0
,
~
used_tables
,
FALSE
);
*
having
=
make_cond_for_table
(
*
having
,
~
(
table_map
)
0
,
~
used_tables
,
FALSE
,
FALSE
);
DBUG_EXECUTE
(
"where"
,
DBUG_EXECUTE
(
"where"
,
print_where
(
*
having
,
"having after make_cond"
,
QT_ORDINARY
););
print_where
(
*
having
,
"having after make_cond"
,
QT_ORDINARY
););
}
}
...
...
sql/sql_select.h
View file @
bbbe3ac8
...
@@ -394,6 +394,7 @@ typedef struct st_join_table {
...
@@ -394,6 +394,7 @@ typedef struct st_join_table {
}
}
double
get_partial_join_cardinality
()
{
return
partial_join_cardinality
;
}
double
get_partial_join_cardinality
()
{
return
partial_join_cardinality
;
}
bool
hash_join_is_possible
();
bool
hash_join_is_possible
();
int
make_scan_filter
();
}
JOIN_TAB
;
}
JOIN_TAB
;
...
@@ -1038,7 +1039,7 @@ public:
...
@@ -1038,7 +1039,7 @@ public:
}
}
JOIN_TAB
*
get_next_table
(
JOIN_TAB
*
tab
);
JOIN_TAB
*
get_next_table
(
JOIN_TAB
*
tab
);
friend
class
JOIN_CACHE_HASHED
;
friend
class
JOIN_CACHE_HASHED
;
friend
class
JOIN_CACHE_BNL
;
friend
class
JOIN_CACHE_BNL
;
friend
class
JOIN_CACHE_BKA
;
friend
class
JOIN_CACHE_BKA
;
...
...
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