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
9ceb829c
Commit
9ceb829c
authored
Jul 30, 2009
by
Matthias Leich
Browse files
Options
Browse Files
Download
Plain Diff
Merge latest changes into GCA tree, no conflicts
parents
097e735b
846f0913
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
212 additions
and
58 deletions
+212
-58
libmysqld/emb_qcache.h
libmysqld/emb_qcache.h
+1
-1
libmysqld/lib_sql.cc
libmysqld/lib_sql.cc
+21
-13
mysql-test/r/partition.result
mysql-test/r/partition.result
+7
-1
mysql-test/r/sp_notembedded.result
mysql-test/r/sp_notembedded.result
+22
-0
mysql-test/t/partition.test
mysql-test/t/partition.test
+11
-0
mysql-test/t/sp_notembedded.test
mysql-test/t/sp_notembedded.test
+36
-0
sql/protocol.cc
sql/protocol.cc
+90
-40
sql/protocol.h
sql/protocol.h
+1
-1
sql/sp_head.cc
sql/sp_head.cc
+2
-2
sql/sql_yacc.yy
sql/sql_yacc.yy
+21
-0
No files found.
libmysqld/emb_qcache.h
View file @
9ceb829c
...
...
@@ -79,4 +79,4 @@ public:
uint
emb_count_querycache_size
(
THD
*
thd
);
int
emb_load_querycache_result
(
THD
*
thd
,
Querycache_stream
*
src
);
void
emb_store_querycache_result
(
Querycache_stream
*
dst
,
THD
*
thd
);
void
net_send_eof
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
);
bool
net_send_eof
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
);
libmysqld/lib_sql.cc
View file @
9ceb829c
...
...
@@ -803,11 +803,11 @@ MYSQL_DATA *THD::alloc_new_dataset()
*/
static
void
bool
write_eof_packet
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
)
{
if
(
!
thd
->
mysql
)
// bootstrap file handling
return
;
return
FALSE
;
/*
The following test should never be true, but it's better to do it
because if 'is_fatal_error' is set the server is not going to execute
...
...
@@ -822,6 +822,7 @@ write_eof_packet(THD *thd, uint server_status, uint total_warn_count)
*/
thd
->
cur_data
->
embedded_info
->
warning_count
=
(
thd
->
spcont
?
0
:
min
(
total_warn_count
,
65535
));
return
FALSE
;
}
...
...
@@ -1032,31 +1033,34 @@ bool Protocol_binary::write()
@sa Server implementation of net_send_ok in protocol.cc for
description of the arguments.
@return The function does not return errors.
@return
@retval TRUE An error occurred
@retval FALSE Success
*/
void
bool
net_send_ok
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
,
ha_rows
affected_rows
,
ulonglong
id
,
const
char
*
message
)
{
DBUG_ENTER
(
"emb_net_send_ok"
);
MYSQL_DATA
*
data
;
bool
error
;
MYSQL
*
mysql
=
thd
->
mysql
;
if
(
!
mysql
)
// bootstrap file handling
DBUG_
VOID_RETURN
;
DBUG_
RETURN
(
FALSE
)
;
if
(
!
(
data
=
thd
->
alloc_new_dataset
()))
return
;
return
TRUE
;
data
->
embedded_info
->
affected_rows
=
affected_rows
;
data
->
embedded_info
->
insert_id
=
id
;
if
(
message
)
strmake
(
data
->
embedded_info
->
info
,
message
,
sizeof
(
data
->
embedded_info
->
info
)
-
1
);
write_eof_packet
(
thd
,
server_status
,
total_warn_count
);
error
=
write_eof_packet
(
thd
,
server_status
,
total_warn_count
);
thd
->
cur_data
=
0
;
DBUG_
VOID_RETURN
;
DBUG_
RETURN
(
error
)
;
}
...
...
@@ -1065,18 +1069,21 @@ net_send_ok(THD *thd,
@sa net_send_ok
@return This function does not return errors.
@return
@retval TRUE An error occurred
@retval FALSE Success
*/
void
bool
net_send_eof
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
)
{
write_eof_packet
(
thd
,
server_status
,
total_warn_count
);
bool
error
=
write_eof_packet
(
thd
,
server_status
,
total_warn_count
);
thd
->
cur_data
=
0
;
return
error
;
}
void
net_send_error_packet
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
)
bool
net_send_error_packet
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
)
{
MYSQL_DATA
*
data
=
thd
->
cur_data
;
struct
embedded_query_result
*
ei
;
...
...
@@ -1084,7 +1091,7 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
if
(
!
thd
->
mysql
)
// bootstrap file handling
{
fprintf
(
stderr
,
"ERROR: %d %s
\n
"
,
sql_errno
,
err
);
return
;
return
TRUE
;
}
if
(
!
data
)
...
...
@@ -1096,6 +1103,7 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
strmov
(
ei
->
sqlstate
,
mysql_errno_to_sqlstate
(
sql_errno
));
ei
->
server_status
=
thd
->
server_status
;
thd
->
cur_data
=
0
;
return
FALSE
;
}
...
...
mysql-test/r/partition.result
View file @
9ceb829c
...
...
@@ -1068,7 +1068,13 @@ partition by range (a)
subpartition by hash(a)
(partition p0 values less than (0),
partition p1 values less than (1) (subpartition sp0));
ERROR 42000: Wrong number of subpartitions defined, mismatch with previous setting near '))' at line 5
ERROR 42000: Wrong number of subpartitions defined, mismatch with previous setting near 'subpartition sp0))' at line 5
create table t1 (a int, b int)
partition by list (a)
subpartition by hash(a)
(partition p0 values in (0),
partition p1 values in (1) (subpartition sp0));
ERROR 42000: Wrong number of subpartitions defined, mismatch with previous setting near 'subpartition sp0))' at line 5
create table t1 (a int)
partition by hash (a)
(partition p0 (subpartition sp0));
...
...
mysql-test/r/sp_notembedded.result
View file @
9ceb829c
...
...
@@ -249,3 +249,25 @@ DROP PROCEDURE p1;
DELETE FROM mysql.user WHERE User='mysqltest_1';
FLUSH PRIVILEGES;
set @@global.concurrent_insert= @old_concurrent_insert;
#
# Bug#44521 Prepared Statement: CALL p() - crashes: `! thd->main_da.is_sent' failed et.al.
#
SELECT GET_LOCK('Bug44521', 0);
GET_LOCK('Bug44521', 0)
1
** Connection con1
CREATE PROCEDURE p()
BEGIN
SELECT 1;
SELECT GET_LOCK('Bug44521', 100);
SELECT 2;
END$
CALL p();;
** Default connection
SELECT RELEASE_LOCK('Bug44521');
RELEASE_LOCK('Bug44521')
1
DROP PROCEDURE p;
# ------------------------------------------------------------------
# -- End of 5.1 tests
# ------------------------------------------------------------------
mysql-test/t/partition.test
View file @
9ceb829c
...
...
@@ -1019,6 +1019,17 @@ subpartition by hash(a)
(
partition
p0
values
less
than
(
0
),
partition
p1
values
less
than
(
1
)
(
subpartition
sp0
));
#
# Bug 46354 Crash with subpartition
#
--
error
ER_PARSE_ERROR
create
table
t1
(
a
int
,
b
int
)
partition
by
list
(
a
)
subpartition
by
hash
(
a
)
(
partition
p0
values
in
(
0
),
partition
p1
values
in
(
1
)
(
subpartition
sp0
));
#
# BUG 15961 No error when subpartition defined without subpartition by clause
#
...
...
mysql-test/t/sp_notembedded.test
View file @
9ceb829c
...
...
@@ -380,3 +380,39 @@ set @@global.concurrent_insert= @old_concurrent_insert;
# Wait till all disconnects are completed
--
source
include
/
wait_until_count_sessions
.
inc
--
echo
#
--
echo
# Bug#44521 Prepared Statement: CALL p() - crashes: `! thd->main_da.is_sent' failed et.al.
--
echo
#
SELECT
GET_LOCK
(
'Bug44521'
,
0
);
--
connect
(
con1
,
localhost
,
root
,,)
--
echo
**
Connection
con1
delimiter
$
;
CREATE
PROCEDURE
p
()
BEGIN
SELECT
1
;
SELECT
GET_LOCK
(
'Bug44521'
,
100
);
SELECT
2
;
END
$
delimiter
;
$
--
send
CALL
p
();
--
connection
default
--
echo
**
Default
connection
let
$wait_condition
=
SELECT
count
(
*
)
=
1
FROM
information_schema
.
processlist
WHERE
state
=
"User lock"
AND
info
=
"SELECT GET_LOCK('Bug44521', 100)"
;
--
source
include
/
wait_condition
.
inc
let
$conid
=
`SELECT id FROM information_schema.processlist
WHERE state = "User lock" AND info = "SELECT GET_LOCK('Bug44521', 100)"`
;
dirty_close
con1
;
SELECT
RELEASE_LOCK
(
'Bug44521'
);
let
$wait_condition
=
SELECT
count
(
*
)
=
0
FROM
information_schema
.
processlist
WHERE
id
=
$conid
;
--
source
include
/
wait_condition
.
inc
DROP
PROCEDURE
p
;
--
echo
# ------------------------------------------------------------------
--
echo
# -- End of 5.1 tests
--
echo
# ------------------------------------------------------------------
sql/protocol.cc
View file @
9ceb829c
...
...
@@ -29,11 +29,11 @@
static
const
unsigned
int
PACKET_BUFFER_EXTRA_ALLOC
=
1024
;
/* Declared non-static only because of the embedded library. */
void
net_send_error_packet
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
);
void
net_send_ok
(
THD
*
,
uint
,
uint
,
ha_rows
,
ulonglong
,
const
char
*
);
void
net_send_eof
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
);
bool
net_send_error_packet
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
);
bool
net_send_ok
(
THD
*
,
uint
,
uint
,
ha_rows
,
ulonglong
,
const
char
*
);
bool
net_send_eof
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
);
#ifndef EMBEDDED_LIBRARY
static
void
write_eof_packet
(
THD
*
thd
,
NET
*
net
,
static
bool
write_eof_packet
(
THD
*
thd
,
NET
*
net
,
uint
server_status
,
uint
total_warn_count
);
#endif
...
...
@@ -70,8 +70,17 @@ bool Protocol_binary::net_store_data(const uchar *from, size_t length)
For SIGNAL/RESIGNAL and GET DIAGNOSTICS functionality it's
critical that every error that can be intercepted is issued in one
place only, my_message_sql.
@param thd Thread handler
@param sql_errno The error code to send
@param err A pointer to the error message
@return
@retval FALSE The message was sent to the client
@retval TRUE An error occurred and the message wasn't sent properly
*/
void
net_send_error
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
)
bool
net_send_error
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
)
{
DBUG_ENTER
(
"net_send_error"
);
...
...
@@ -80,6 +89,7 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
DBUG_ASSERT
(
err
&&
err
[
0
]);
DBUG_PRINT
(
"enter"
,(
"sql_errno: %d err: %s"
,
sql_errno
,
err
));
bool
error
;
/*
It's one case when we can push an error even though there
...
...
@@ -90,11 +100,11 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
/* Abort multi-result sets */
thd
->
server_status
&=
~
SERVER_MORE_RESULTS_EXISTS
;
net_send_error_packet
(
thd
,
sql_errno
,
err
);
error
=
net_send_error_packet
(
thd
,
sql_errno
,
err
);
thd
->
main_da
.
can_overwrite_status
=
FALSE
;
DBUG_
VOID_RETURN
;
DBUG_
RETURN
(
error
)
;
}
/**
...
...
@@ -113,25 +123,33 @@ void net_send_error(THD *thd, uint sql_errno, const char *err)
Is not stored if no message.
@param thd Thread handler
@param server_status The server status
@param total_warn_count Total number of warnings
@param affected_rows Number of rows changed by statement
@param id Auto_increment id for first row (if used)
@param message Message to send to the client (Used by mysql_status)
@return
@retval FALSE The message was successfully sent
@retval TRUE An error occurred and the messages wasn't sent properly
*/
#ifndef EMBEDDED_LIBRARY
void
bool
net_send_ok
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
,
ha_rows
affected_rows
,
ulonglong
id
,
const
char
*
message
)
{
NET
*
net
=
&
thd
->
net
;
uchar
buff
[
MYSQL_ERRMSG_SIZE
+
10
],
*
pos
;
bool
error
=
FALSE
;
DBUG_ENTER
(
"my_ok"
);
if
(
!
net
->
vio
)
// hack for re-parsing queries
{
DBUG_PRINT
(
"info"
,
(
"vio present: NO"
));
DBUG_
VOID_RETURN
;
DBUG_
RETURN
(
FALSE
)
;
}
buff
[
0
]
=
0
;
// No fields
...
...
@@ -162,13 +180,14 @@ net_send_ok(THD *thd,
if
(
message
&&
message
[
0
])
pos
=
net_store_data
(
pos
,
(
uchar
*
)
message
,
strlen
(
message
));
VOID
(
my_net_write
(
net
,
buff
,
(
size_t
)
(
pos
-
buff
)));
VOID
(
net_flush
(
net
));
error
=
my_net_write
(
net
,
buff
,
(
size_t
)
(
pos
-
buff
));
if
(
!
error
)
error
=
net_flush
(
net
);
thd
->
main_da
.
can_overwrite_status
=
FALSE
;
DBUG_PRINT
(
"info"
,
(
"OK sent, so no more error sending allowed"
));
DBUG_
VOID_RETURN
;
DBUG_
RETURN
(
error
)
;
}
static
uchar
eof_buff
[
1
]
=
{
(
uchar
)
254
};
/* Marker for end of fields */
...
...
@@ -188,37 +207,54 @@ static uchar eof_buff[1]= { (uchar) 254 }; /* Marker for end of fields */
client.
@param thd Thread handler
@param no_flush Set to 1 if there will be more data to the client,
like in send_fields().
@param server_status The server status
@param total_warn_count Total number of warnings
@return
@retval FALSE The message was successfully sent
@retval TRUE An error occurred and the message wasn't sent properly
*/
void
bool
net_send_eof
(
THD
*
thd
,
uint
server_status
,
uint
total_warn_count
)
{
NET
*
net
=
&
thd
->
net
;
bool
error
=
FALSE
;
DBUG_ENTER
(
"net_send_eof"
);
/* Set to TRUE if no active vio, to work well in case of --init-file */
if
(
net
->
vio
!=
0
)
{
thd
->
main_da
.
can_overwrite_status
=
TRUE
;
write_eof_packet
(
thd
,
net
,
server_status
,
total_warn_count
);
VOID
(
net_flush
(
net
));
error
=
write_eof_packet
(
thd
,
net
,
server_status
,
total_warn_count
);
if
(
!
error
)
error
=
net_flush
(
net
);
thd
->
main_da
.
can_overwrite_status
=
FALSE
;
DBUG_PRINT
(
"info"
,
(
"EOF sent, so no more error sending allowed"
));
}
DBUG_
VOID_RETURN
;
DBUG_
RETURN
(
error
)
;
}
/**
Format EOF packet according to the current protocol and
write it to the network output buffer.
@param thd The thread handler
@param net The network handler
@param server_status The server status
@param total_warn_count The number of warnings
@return
@retval FALSE The message was sent successfully
@retval TRUE An error occurred and the messages wasn't sent properly
*/
static
void
write_eof_packet
(
THD
*
thd
,
NET
*
net
,
static
bool
write_eof_packet
(
THD
*
thd
,
NET
*
net
,
uint
server_status
,
uint
total_warn_count
)
{
bool
error
;
if
(
thd
->
client_capabilities
&
CLIENT_PROTOCOL_41
)
{
uchar
buff
[
5
];
...
...
@@ -237,10 +273,12 @@ static void write_eof_packet(THD *thd, NET *net,
if
(
thd
->
is_fatal_error
)
server_status
&=
~
SERVER_MORE_RESULTS_EXISTS
;
int2store
(
buff
+
3
,
server_status
);
VOID
(
my_net_write
(
net
,
buff
,
5
)
);
error
=
my_net_write
(
net
,
buff
,
5
);
}
else
VOID
(
my_net_write
(
net
,
eof_buff
,
1
));
error
=
my_net_write
(
net
,
eof_buff
,
1
);
return
error
;
}
/**
...
...
@@ -261,7 +299,17 @@ bool send_old_password_request(THD *thd)
}
void
net_send_error_packet
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
)
/**
@param thd Thread handler
@param sql_errno The error code to send
@param err A pointer to the error message
@return
@retval FALSE The message was successfully sent
@retval TRUE An error occurred and the messages wasn't sent properly
*/
bool
net_send_error_packet
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
)
{
NET
*
net
=
&
thd
->
net
;
uint
length
;
...
...
@@ -279,7 +327,7 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
/* In bootstrap it's ok to print on stderr */
fprintf
(
stderr
,
"ERROR: %d %s
\n
"
,
sql_errno
,
err
);
}
DBUG_
VOID_RETURN
;
DBUG_
RETURN
(
FALSE
)
;
}
if
(
net
->
return_errno
)
...
...
@@ -301,9 +349,8 @@ void net_send_error_packet(THD *thd, uint sql_errno, const char *err)
length
=
(
uint
)
strlen
(
err
);
set_if_smaller
(
length
,
MYSQL_ERRMSG_SIZE
-
1
);
}
VOID
(
net_write_command
(
net
,(
uchar
)
255
,
(
uchar
*
)
""
,
0
,
(
uchar
*
)
err
,
DBUG_RETURN
(
net_write_command
(
net
,(
uchar
)
255
,
(
uchar
*
)
""
,
0
,
(
uchar
*
)
err
,
length
));
DBUG_VOID_RETURN
;
}
#endif
/* EMBEDDED_LIBRARY */
...
...
@@ -389,36 +436,39 @@ void net_end_statement(THD *thd)
if
(
thd
->
main_da
.
is_sent
)
return
;
bool
error
=
FALSE
;
switch
(
thd
->
main_da
.
status
())
{
case
Diagnostics_area
:
:
DA_ERROR
:
/* The query failed, send error to log and abort bootstrap. */
net_send_error
(
thd
,
thd
->
main_da
.
sql_errno
(),
thd
->
main_da
.
message
());
error
=
net_send_error
(
thd
,
thd
->
main_da
.
sql_errno
(),
thd
->
main_da
.
message
());
break
;
case
Diagnostics_area
:
:
DA_EOF
:
net_send_eof
(
thd
,
thd
->
main_da
.
server_status
(),
thd
->
main_da
.
total_warn_count
());
error
=
net_send_eof
(
thd
,
thd
->
main_da
.
server_status
(),
thd
->
main_da
.
total_warn_count
());
break
;
case
Diagnostics_area
:
:
DA_OK
:
net_send_ok
(
thd
,
thd
->
main_da
.
server_status
(),
thd
->
main_da
.
total_warn_count
(),
thd
->
main_da
.
affected_rows
(),
thd
->
main_da
.
last_insert_id
(),
thd
->
main_da
.
message
());
error
=
net_send_ok
(
thd
,
thd
->
main_da
.
server_status
(),
thd
->
main_da
.
total_warn_count
(),
thd
->
main_da
.
affected_rows
(),
thd
->
main_da
.
last_insert_id
(),
thd
->
main_da
.
message
());
break
;
case
Diagnostics_area
:
:
DA_DISABLED
:
break
;
case
Diagnostics_area
:
:
DA_EMPTY
:
default:
DBUG_ASSERT
(
0
);
net_send_ok
(
thd
,
thd
->
server_status
,
thd
->
total_warn_count
,
0
,
0
,
NULL
);
error
=
net_send_ok
(
thd
,
thd
->
server_status
,
thd
->
total_warn_count
,
0
,
0
,
NULL
);
break
;
}
thd
->
main_da
.
is_sent
=
TRUE
;
if
(
!
error
)
thd
->
main_da
.
is_sent
=
TRUE
;
}
...
...
sql/protocol.h
View file @
9ceb829c
...
...
@@ -173,7 +173,7 @@ public:
};
void
send_warning
(
THD
*
thd
,
uint
sql_errno
,
const
char
*
err
=
0
);
void
net_send_error
(
THD
*
thd
,
uint
sql_errno
=
0
,
const
char
*
err
=
0
);
bool
net_send_error
(
THD
*
thd
,
uint
sql_errno
=
0
,
const
char
*
err
=
0
);
void
net_end_statement
(
THD
*
thd
);
bool
send_old_password_request
(
THD
*
thd
);
uchar
*
net_store_data
(
uchar
*
to
,
const
uchar
*
from
,
size_t
length
);
...
...
sql/sp_head.cc
View file @
9ceb829c
...
...
@@ -1248,7 +1248,7 @@ sp_head::execute(THD *thd)
*/
if
(
thd
->
prelocked_mode
==
NON_PRELOCKED
)
thd
->
user_var_events_alloc
=
thd
->
mem_root
;
err_status
=
i
->
execute
(
thd
,
&
ip
);
if
(
i
->
free_list
)
...
...
@@ -2863,7 +2863,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
if
(
!
thd
->
is_error
())
thd
->
main_da
.
reset_diagnostics_area
();
}
DBUG_RETURN
(
res
);
DBUG_RETURN
(
res
||
thd
->
is_error
()
);
}
...
...
sql/sql_yacc.yy
View file @
9ceb829c
...
...
@@ -4206,6 +4206,10 @@ opt_sub_partition:
if (Lex->part_info->no_subparts != 0 &&
!Lex->part_info->use_default_subpartitions)
{
/*
We come here when we have defined subpartitions on the first
partition but not on all the subsequent partitions.
*/
my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
MYSQL_YYABORT;
}
...
...
@@ -4248,6 +4252,23 @@ sub_part_definition:
partition_info *part_info= lex->part_info;
partition_element *curr_part= part_info->current_partition;
partition_element *sub_p_elem= new partition_element(curr_part);
if (part_info->use_default_subpartitions &&
part_info->partitions.elements >= 2)
{
/*
create table t1 (a int)
partition by list (a) subpartition by hash (a)
(partition p0 values in (1),
partition p1 values in (2) subpartition sp11);
causes use to arrive since we are on the second
partition, but still use_default_subpartitions
is set. When we come here we're processing at least
the second partition (the current partition processed
have already been put into the partitions list.
*/
my_parse_error(ER(ER_PARTITION_WRONG_NO_SUBPART_ERROR));
MYSQL_YYABORT;
}
if (!sub_p_elem ||
curr_part->subpartitions.push_back(sub_p_elem))
{
...
...
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