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
4523a7b1
Commit
4523a7b1
authored
May 23, 2003
by
pem@mysql.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adopt SP stuff to the new lex pointer.
parent
384b10e2
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
121 additions
and
68 deletions
+121
-68
mysql-test/r/sp.result
mysql-test/r/sp.result
+17
-0
mysql-test/t/sp.test
mysql-test/t/sp.test
+18
-0
sql/mysql_priv.h
sql/mysql_priv.h
+1
-1
sql/sp.cc
sql/sp.cc
+22
-9
sql/sp_head.cc
sql/sp_head.cc
+31
-40
sql/sp_head.h
sql/sp_head.h
+4
-4
sql/sql_lex.h
sql/sql_lex.h
+2
-2
sql/sql_parse.cc
sql/sql_parse.cc
+23
-12
sql/sql_prepare.cc
sql/sql_prepare.cc
+3
-0
No files found.
mysql-test/r/sp.result
View file @
4523a7b1
...
@@ -95,6 +95,23 @@ delete from t1;
...
@@ -95,6 +95,23 @@ delete from t1;
drop procedure zip;
drop procedure zip;
drop procedure zap;
drop procedure zap;
drop procedure bar;
drop procedure bar;
create procedure c1(x int)
call c2("c", x);
create procedure c2(s char(16), x int)
call c3(x, s);
create procedure c3(x int, s char(16))
call c4("level", x, s);
create procedure c4(l char(8), x int, s char(16))
insert into t1 values (concat(l,s), x);
call c1(42);
select * from t1;
id data
levelc 42
delete from t1;
drop procedure c1;
drop procedure c2;
drop procedure c3;
drop procedure c4;
create procedure iotest(x1 char(16), x2 char(16), y int)
create procedure iotest(x1 char(16), x2 char(16), y int)
begin
begin
call inc2(x2, y);
call inc2(x2, y);
...
...
mysql-test/t/sp.test
View file @
4523a7b1
...
@@ -126,6 +126,24 @@ drop procedure zap|
...
@@ -126,6 +126,24 @@ drop procedure zap|
drop
procedure
bar
|
drop
procedure
bar
|
# "Deep" calls...
create
procedure
c1
(
x
int
)
call
c2
(
"c"
,
x
)
|
create
procedure
c2
(
s
char
(
16
),
x
int
)
call
c3
(
x
,
s
)
|
create
procedure
c3
(
x
int
,
s
char
(
16
))
call
c4
(
"level"
,
x
,
s
)
|
create
procedure
c4
(
l
char
(
8
),
x
int
,
s
char
(
16
))
insert
into
t1
values
(
concat
(
l
,
s
),
x
)
|
call
c1
(
42
)
|
select
*
from
t1
|
delete
from
t1
|
drop
procedure
c1
|
drop
procedure
c2
|
drop
procedure
c3
|
drop
procedure
c4
|
# INOUT test
# INOUT test
create
procedure
iotest
(
x1
char
(
16
),
x2
char
(
16
),
y
int
)
create
procedure
iotest
(
x1
char
(
16
),
x2
char
(
16
),
y
int
)
begin
begin
...
...
sql/mysql_priv.h
View file @
4523a7b1
...
@@ -353,7 +353,7 @@ bool is_update_query(enum enum_sql_command command);
...
@@ -353,7 +353,7 @@ bool is_update_query(enum enum_sql_command command);
void
free_items
(
Item
*
item
);
void
free_items
(
Item
*
item
);
bool
alloc_query
(
THD
*
thd
,
char
*
packet
,
ulong
packet_length
);
bool
alloc_query
(
THD
*
thd
,
char
*
packet
,
ulong
packet_length
);
void
mysql_init_select
(
LEX
*
lex
);
void
mysql_init_select
(
LEX
*
lex
);
void
mysql_init_query
(
THD
*
thd
);
void
mysql_init_query
(
THD
*
thd
,
bool
lexonly
=
0
);
bool
mysql_new_select
(
LEX
*
lex
,
bool
move_down
);
bool
mysql_new_select
(
LEX
*
lex
,
bool
move_down
);
void
create_select_for_variable
(
const
char
*
var_name
);
void
create_select_for_variable
(
const
char
*
var_name
);
void
mysql_init_multi_delete
(
LEX
*
lex
);
void
mysql_init_multi_delete
(
LEX
*
lex
);
...
...
sql/sp.cc
View file @
4523a7b1
...
@@ -88,7 +88,6 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
...
@@ -88,7 +88,6 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
DBUG_ENTER
(
"db_find_routine"
);
DBUG_ENTER
(
"db_find_routine"
);
DBUG_PRINT
(
"enter"
,
(
"type: %d name: %*s"
,
type
,
namelen
,
name
));
DBUG_PRINT
(
"enter"
,
(
"type: %d name: %*s"
,
type
,
namelen
,
name
));
extern
int
yyparse
(
void
*
thd
);
extern
int
yyparse
(
void
*
thd
);
LEX
*
tmplex
;
TABLE
*
table
;
TABLE
*
table
;
const
char
*
defstr
;
const
char
*
defstr
;
int
ret
;
int
ret
;
...
@@ -146,15 +145,29 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
...
@@ -146,15 +145,29 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
table
=
NULL
;
table
=
NULL
;
}
}
tmplex
=
lex_start
(
thd
,
(
uchar
*
)
defstr
,
strlen
(
defstr
));
if
(
yyparse
(
thd
)
||
thd
->
is_fatal_error
||
tmplex
->
sphead
==
NULL
)
ret
=
SP_PARSE_ERROR
;
else
{
{
*
sphp
=
tmplex
->
sphead
;
LEX
*
oldlex
=
thd
->
lex
;
(
*
sphp
)
->
sp_set_info
((
char
*
)
creator
,
(
uint
)
strlen
(
creator
),
enum
enum_sql_command
oldcmd
=
thd
->
lex
->
sql_command
;
created
,
modified
,
suid
,
ptr
,
length
);
lex_start
(
thd
,
(
uchar
*
)
defstr
,
strlen
(
defstr
));
if
(
yyparse
(
thd
)
||
thd
->
is_fatal_error
||
thd
->
lex
->
sphead
==
NULL
)
{
if
(
thd
->
lex
->
sphead
)
{
if
(
oldlex
!=
thd
->
lex
)
thd
->
lex
->
sphead
->
restore_lex
(
thd
);
thd
->
lex
->
sphead
->
destroy
();
}
ret
=
SP_PARSE_ERROR
;
}
else
{
*
sphp
=
thd
->
lex
->
sphead
;
(
*
sphp
)
->
sp_set_info
((
char
*
)
creator
,
(
uint
)
strlen
(
creator
),
created
,
modified
,
suid
,
ptr
,
length
);
}
thd
->
lex
->
sql_command
=
oldcmd
;
}
}
done:
done:
...
...
sql/sp_head.cc
View file @
4523a7b1
...
@@ -365,54 +365,44 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
...
@@ -365,54 +365,44 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
void
void
sp_head
::
reset_lex
(
THD
*
thd
)
sp_head
::
reset_lex
(
THD
*
thd
)
{
{
memcpy
(
&
m_lex
,
thd
->
lex
,
sizeof
(
LEX
));
// Save old one
DBUG_ENTER
(
"sp_head::reset_lex"
);
LEX
*
sublex
;
m_lex
=
thd
->
lex
;
thd
->
lex
=
sublex
=
new
st_lex
;
sublex
->
yylineno
=
m_lex
->
yylineno
;
/* Reset most stuff. The length arguments doesn't matter here. */
/* Reset most stuff. The length arguments doesn't matter here. */
lex_start
(
thd
,
m_lex
.
buf
,
m_lex
.
end_of_query
-
m_lex
.
ptr
);
lex_start
(
thd
,
m_lex
->
buf
,
m_lex
->
end_of_query
-
m_lex
->
ptr
);
/* We must reset ptr and end_of_query again */
/* We must reset ptr and end_of_query again */
thd
->
lex
->
ptr
=
m_lex
.
ptr
;
sublex
->
ptr
=
m_lex
->
ptr
;
thd
->
lex
->
end_of_query
=
m_lex
.
end_of_query
;
sublex
->
end_of_query
=
m_lex
->
end_of_query
;
/* And keep the SP stuff too */
/* And keep the SP stuff too */
thd
->
lex
->
sphead
=
m_lex
.
sphead
;
sublex
->
sphead
=
m_lex
->
sphead
;
thd
->
lex
->
spcont
=
m_lex
.
spcont
;
sublex
->
spcont
=
m_lex
->
spcont
;
/* Clear all lists. (QQ Why isn't this reset by lex_start()?).
mysql_init_query
(
thd
,
true
);
// Only init lex
We may be overdoing this, but we know for sure that value_list must
DBUG_VOID_RETURN
;
be cleared at least. */
thd
->
lex
->
col_list
.
empty
();
thd
->
lex
->
ref_list
.
empty
();
thd
->
lex
->
drop_list
.
empty
();
thd
->
lex
->
alter_list
.
empty
();
thd
->
lex
->
interval_list
.
empty
();
thd
->
lex
->
users_list
.
empty
();
thd
->
lex
->
columns
.
empty
();
thd
->
lex
->
key_list
.
empty
();
thd
->
lex
->
create_list
.
empty
();
thd
->
lex
->
insert_list
=
NULL
;
thd
->
lex
->
field_list
.
empty
();
thd
->
lex
->
value_list
.
empty
();
thd
->
lex
->
many_values
.
empty
();
thd
->
lex
->
var_list
.
empty
();
thd
->
lex
->
param_list
.
empty
();
thd
->
lex
->
proc_list
.
empty
();
thd
->
lex
->
auxilliary_table_list
.
empty
();
}
}
// Restore lex during parsing, after we have parsed a sub statement.
// Restore lex during parsing, after we have parsed a sub statement.
void
void
sp_head
::
restore_lex
(
THD
*
thd
)
sp_head
::
restore_lex
(
THD
*
thd
)
{
{
DBUG_ENTER
(
"sp_head::restore_lex"
);
LEX
*
sublex
=
thd
->
lex
;
// Update some state in the old one first
// Update some state in the old one first
m_lex
.
ptr
=
thd
->
lex
->
ptr
;
m_lex
->
ptr
=
sub
lex
->
ptr
;
m_lex
.
next_state
=
thd
->
lex
->
next_state
;
m_lex
->
next_state
=
sub
lex
->
next_state
;
// Collect some data from the sub statement lex.
// Collect some data from the sub statement lex.
sp_merge_funs
(
&
m_lex
,
thd
->
lex
);
sp_merge_funs
(
m_lex
,
sub
lex
);
#if 0
#if 0
// QQ We're not using this at the moment.
// QQ We're not using this at the moment.
if (
thd->
lex.sql_command == SQLCOM_CALL)
if (
sub
lex.sql_command == SQLCOM_CALL)
{
{
// It would be slightly faster to keep the list sorted, but we need
// It would be slightly faster to keep the list sorted, but we need
// an "insert before" method to do that.
// an "insert before" method to do that.
char *proc=
thd->
lex.udf.name.str;
char *proc=
sub
lex.udf.name.str;
List_iterator_fast<char *> li(m_calls);
List_iterator_fast<char *> li(m_calls);
char **it;
char **it;
...
@@ -428,7 +418,7 @@ sp_head::restore_lex(THD *thd)
...
@@ -428,7 +418,7 @@ sp_head::restore_lex(THD *thd)
// QQ ...or just open tables in thd->open_tables?
// QQ ...or just open tables in thd->open_tables?
// This is not entirerly clear at the moment, but for now, we collect
// This is not entirerly clear at the moment, but for now, we collect
// tables here.
// tables here.
for (SELECT_LEX *sl=
thd->
lex.all_selects_list ;
for (SELECT_LEX *sl=
sub
lex.all_selects_list ;
sl ;
sl ;
sl= sl->next_select())
sl= sl->next_select())
{
{
...
@@ -448,7 +438,8 @@ sp_head::restore_lex(THD *thd)
...
@@ -448,7 +438,8 @@ sp_head::restore_lex(THD *thd)
}
}
#endif
#endif
memcpy
(
thd
->
lex
,
&
m_lex
,
sizeof
(
LEX
));
// Restore lex
thd
->
lex
=
m_lex
;
DBUG_VOID_RETURN
;
}
}
void
void
...
@@ -490,14 +481,14 @@ int
...
@@ -490,14 +481,14 @@ int
sp_instr_stmt
::
execute
(
THD
*
thd
,
uint
*
nextp
)
sp_instr_stmt
::
execute
(
THD
*
thd
,
uint
*
nextp
)
{
{
DBUG_ENTER
(
"sp_instr_stmt::execute"
);
DBUG_ENTER
(
"sp_instr_stmt::execute"
);
DBUG_PRINT
(
"info"
,
(
"command: %d"
,
m_lex
.
sql_command
));
DBUG_PRINT
(
"info"
,
(
"command: %d"
,
m_lex
->
sql_command
));
LEX
olex
;
// The other lex
LEX
*
olex
;
// The other lex
int
res
;
int
res
;
memcpy
(
&
olex
,
thd
->
lex
,
sizeof
(
LEX
));
// Save the other lex
olex
=
thd
->
lex
;
// Save the other lex
thd
->
lex
=
m_lex
;
// Use my own lex
memcpy
(
thd
->
lex
,
&
m_lex
,
sizeof
(
LEX
));
// Use my own lex
thd
->
lex
->
thd
=
thd
;
// QQ Not reentrant!
thd
->
lex
->
thd
=
thd
;
thd
->
lex
->
unit
.
thd
=
thd
;
// QQ Not reentrant
res
=
mysql_execute_command
(
thd
);
res
=
mysql_execute_command
(
thd
);
if
(
thd
->
lock
||
thd
->
open_tables
||
thd
->
derived_tables
)
if
(
thd
->
lock
||
thd
->
open_tables
||
thd
->
derived_tables
)
...
@@ -506,7 +497,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
...
@@ -506,7 +497,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
close_thread_tables
(
thd
);
/* Free tables */
close_thread_tables
(
thd
);
/* Free tables */
}
}
memcpy
(
thd
->
lex
,
&
olex
,
sizeof
(
LEX
));
// Restore the other lex
thd
->
lex
=
olex
;
// Restore the other lex
*
nextp
=
m_ip
+
1
;
*
nextp
=
m_ip
+
1
;
DBUG_RETURN
(
res
);
DBUG_RETURN
(
res
);
...
...
sql/sp_head.h
View file @
4523a7b1
...
@@ -136,7 +136,7 @@ private:
...
@@ -136,7 +136,7 @@ private:
bool
m_suid
;
bool
m_suid
;
sp_pcontext
*
m_pcont
;
// Parse context
sp_pcontext
*
m_pcont
;
// Parse context
LEX
m_lex
;
// Temp. store for the other lex
LEX
*
m_lex
;
// Temp. store for the other lex
DYNAMIC_ARRAY
m_instr
;
// The "instructions"
DYNAMIC_ARRAY
m_instr
;
// The "instructions"
typedef
struct
typedef
struct
{
{
...
@@ -222,18 +222,18 @@ public:
...
@@ -222,18 +222,18 @@ public:
inline
void
inline
void
set_lex
(
LEX
*
lex
)
set_lex
(
LEX
*
lex
)
{
{
m
emcpy
(
&
m_lex
,
lex
,
sizeof
(
LEX
))
;
m
_lex
=
lex
;
}
}
inline
LEX
*
inline
LEX
*
get_lex
()
get_lex
()
{
{
return
&
m_lex
;
return
m_lex
;
}
}
private:
private:
LEX
m_lex
;
// My own lex
LEX
*
m_lex
;
// My own lex
};
// class sp_instr_stmt : public sp_instr
};
// class sp_instr_stmt : public sp_instr
...
...
sql/sql_lex.h
View file @
4523a7b1
...
@@ -320,7 +320,7 @@ public:
...
@@ -320,7 +320,7 @@ public:
int
exec
();
int
exec
();
int
cleanup
();
int
cleanup
();
friend
void
mysql_init_query
(
THD
*
thd
);
friend
void
mysql_init_query
(
THD
*
thd
,
bool
lexonly
);
friend
int
subselect_union_engine
::
exec
();
friend
int
subselect_union_engine
::
exec
();
private:
private:
bool
create_total_list_n_last_return
(
THD
*
thd
,
st_lex
*
lex
,
bool
create_total_list_n_last_return
(
THD
*
thd
,
st_lex
*
lex
,
...
@@ -408,7 +408,7 @@ public:
...
@@ -408,7 +408,7 @@ public:
order_list
.
next
=
(
byte
**
)
&
order_list
.
first
;
order_list
.
next
=
(
byte
**
)
&
order_list
.
first
;
}
}
friend
void
mysql_init_query
(
THD
*
thd
);
friend
void
mysql_init_query
(
THD
*
thd
,
bool
lexonly
);
st_select_lex
(
struct
st_lex
*
lex
);
st_select_lex
(
struct
st_lex
*
lex
);
st_select_lex
()
{}
st_select_lex
()
{}
void
make_empty_select
(
st_select_lex
*
last_select
)
void
make_empty_select
(
st_select_lex
*
last_select
)
...
...
sql/sql_parse.cc
View file @
4523a7b1
...
@@ -3433,7 +3433,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize)
...
@@ -3433,7 +3433,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize)
****************************************************************************/
****************************************************************************/
void
void
mysql_init_query
(
THD
*
thd
)
mysql_init_query
(
THD
*
thd
,
bool
lexonly
)
{
{
DBUG_ENTER
(
"mysql_init_query"
);
DBUG_ENTER
(
"mysql_init_query"
);
LEX
*
lex
=
thd
->
lex
;
LEX
*
lex
=
thd
->
lex
;
...
@@ -3457,17 +3457,20 @@ mysql_init_query(THD *thd)
...
@@ -3457,17 +3457,20 @@ mysql_init_query(THD *thd)
lex
->
lock_option
=
TL_READ
;
lex
->
lock_option
=
TL_READ
;
lex
->
found_colon
=
0
;
lex
->
found_colon
=
0
;
lex
->
safe_to_cache_query
=
1
;
lex
->
safe_to_cache_query
=
1
;
thd
->
select_number
=
lex
->
select_lex
.
select_number
=
1
;
if
(
!
lexonly
)
thd
->
free_list
=
0
;
{
thd
->
total_warn_count
=
0
;
// Warnings for this query
thd
->
select_number
=
lex
->
select_lex
.
select_number
=
1
;
thd
->
last_insert_id_used
=
thd
->
query_start_used
=
thd
->
insert_id_used
=
0
;
thd
->
free_list
=
0
;
thd
->
sent_row_count
=
thd
->
examined_row_count
=
0
;
thd
->
total_warn_count
=
0
;
// Warnings for this query
thd
->
is_fatal_error
=
thd
->
rand_used
=
0
;
thd
->
last_insert_id_used
=
thd
->
query_start_used
=
thd
->
insert_id_used
=
0
;
thd
->
server_status
&=
~
SERVER_MORE_RESULTS_EXISTS
;
thd
->
sent_row_count
=
thd
->
examined_row_count
=
0
;
thd
->
tmp_table_used
=
0
;
thd
->
is_fatal_error
=
thd
->
rand_used
=
0
;
if
(
opt_bin_log
)
thd
->
server_status
&=
~
SERVER_MORE_RESULTS_EXISTS
;
reset_dynamic
(
&
thd
->
user_var_events
);
thd
->
tmp_table_used
=
0
;
thd
->
clear_error
();
if
(
opt_bin_log
)
reset_dynamic
(
&
thd
->
user_var_events
);
thd
->
clear_error
();
}
DBUG_VOID_RETURN
;
DBUG_VOID_RETURN
;
}
}
...
@@ -3582,7 +3585,11 @@ mysql_parse(THD *thd, char *inBuf, uint length)
...
@@ -3582,7 +3585,11 @@ mysql_parse(THD *thd, char *inBuf, uint length)
else
else
{
{
if
(
thd
->
net
.
report_error
)
if
(
thd
->
net
.
report_error
)
{
send_error
(
thd
,
0
,
NullS
);
send_error
(
thd
,
0
,
NullS
);
if
(
thd
->
lex
->
sphead
)
thd
->
lex
->
sphead
->
destroy
();
}
else
else
{
{
mysql_execute_command
(
thd
);
mysql_execute_command
(
thd
);
...
@@ -3598,8 +3605,12 @@ mysql_parse(THD *thd, char *inBuf, uint length)
...
@@ -3598,8 +3605,12 @@ mysql_parse(THD *thd, char *inBuf, uint length)
thd
->
is_fatal_error
));
thd
->
is_fatal_error
));
#ifndef EMBEDDED_LIBRARY
/* TODO query cache in embedded library*/
#ifndef EMBEDDED_LIBRARY
/* TODO query cache in embedded library*/
query_cache_abort
(
&
thd
->
net
);
query_cache_abort
(
&
thd
->
net
);
if
(
thd
->
lex
->
sphead
)
thd
->
lex
->
sphead
->
destroy
();
#endif
#endif
}
}
if
(
thd
->
lex
->
sphead
&&
lex
!=
thd
->
lex
)
thd
->
lex
->
sphead
->
restore_lex
(
thd
);
thd
->
proc_info
=
"freeing items"
;
thd
->
proc_info
=
"freeing items"
;
free_items
(
thd
->
free_list
);
/* Free strings used by items */
free_items
(
thd
->
free_list
);
/* Free strings used by items */
lex_end
(
lex
);
lex_end
(
lex
);
...
...
sql/sql_prepare.cc
View file @
4523a7b1
...
@@ -71,6 +71,7 @@ Long data handling:
...
@@ -71,6 +71,7 @@ Long data handling:
#include "sql_acl.h"
#include "sql_acl.h"
#include "sql_select.h" // for JOIN
#include "sql_select.h" // for JOIN
#include <m_ctype.h> // for isspace()
#include <m_ctype.h> // for isspace()
#include "sp_head.h"
#define IS_PARAM_NULL(pos, param_no) pos[param_no/8] & (1 << param_no & 7)
#define IS_PARAM_NULL(pos, param_no) pos[param_no/8] & (1 << param_no & 7)
...
@@ -761,6 +762,8 @@ static bool parse_prepare_query(PREP_STMT *stmt,
...
@@ -761,6 +762,8 @@ static bool parse_prepare_query(PREP_STMT *stmt,
thd
->
lex
->
param_count
=
0
;
thd
->
lex
->
param_count
=
0
;
if
(
!
yyparse
((
void
*
)
thd
)
&&
!
thd
->
is_fatal_error
)
if
(
!
yyparse
((
void
*
)
thd
)
&&
!
thd
->
is_fatal_error
)
error
=
send_prepare_results
(
stmt
);
error
=
send_prepare_results
(
stmt
);
if
(
thd
->
lex
->
sphead
&&
lex
!=
thd
->
lex
)
thd
->
lex
->
sphead
->
restore_lex
(
thd
);
lex_end
(
lex
);
lex_end
(
lex
);
DBUG_RETURN
(
error
);
DBUG_RETURN
(
error
);
}
}
...
...
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