Commit a89b4b1b authored by Robert Haas's avatar Robert Haas

Update citext extension for parallel query.

All citext functions are PARALLEL SAFE, and a couple of them can
benefit from having aggregate combine functions.

Andreas Karlsson
parent 40fc4575
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
MODULES = citext MODULES = citext
EXTENSION = citext EXTENSION = citext
DATA = citext--1.1.sql citext--1.0--1.1.sql citext--unpackaged--1.0.sql DATA = citext--1.2.sql citext--1.1--1.2.sql citext--1.0--1.1.sql \
citext--unpackaged--1.0.sql
PGFILEDESC = "citext - case-insensitive character string data type" PGFILEDESC = "citext - case-insensitive character string data type"
REGRESS = citext REGRESS = citext
......
/* contrib/citext/citext--1.1--1.2.sql */
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION citext UPDATE TO '1.2'" to load this file. \quit
ALTER FUNCTION citextin(cstring) PARALLEL SAFE;
ALTER FUNCTION citextout(citext) PARALLEL SAFE;
ALTER FUNCTION citextrecv(internal) PARALLEL SAFE;
ALTER FUNCTION citextsend(citext) PARALLEL SAFE;
ALTER FUNCTION citext(bpchar) PARALLEL SAFE;
ALTER FUNCTION citext(boolean) PARALLEL SAFE;
ALTER FUNCTION citext(inet) PARALLEL SAFE;
ALTER FUNCTION citext_eq(citext, citext) PARALLEL SAFE;
ALTER FUNCTION citext_ne(citext, citext) PARALLEL SAFE;
ALTER FUNCTION citext_lt(citext, citext) PARALLEL SAFE;
ALTER FUNCTION citext_le(citext, citext) PARALLEL SAFE;
ALTER FUNCTION citext_gt(citext, citext) PARALLEL SAFE;
ALTER FUNCTION citext_ge(citext, citext) PARALLEL SAFE;
ALTER FUNCTION citext_cmp(citext, citext) PARALLEL SAFE;
ALTER FUNCTION citext_hash(citext) PARALLEL SAFE;
ALTER FUNCTION citext_smaller(citext, citext) PARALLEL SAFE;
ALTER FUNCTION citext_larger(citext, citext) PARALLEL SAFE;
ALTER FUNCTION texticlike(citext, citext) PARALLEL SAFE;
ALTER FUNCTION texticnlike(citext, citext) PARALLEL SAFE;
ALTER FUNCTION texticregexeq(citext, citext) PARALLEL SAFE;
ALTER FUNCTION texticregexne(citext, citext) PARALLEL SAFE;
ALTER FUNCTION texticlike(citext, text) PARALLEL SAFE;
ALTER FUNCTION texticnlike(citext, text) PARALLEL SAFE;
ALTER FUNCTION texticregexeq(citext, text) PARALLEL SAFE;
ALTER FUNCTION texticregexne(citext, text) PARALLEL SAFE;
ALTER FUNCTION regexp_matches(citext, citext) PARALLEL SAFE;
ALTER FUNCTION regexp_matches(citext, citext, text) PARALLEL SAFE;
ALTER FUNCTION regexp_replace(citext, citext, text) PARALLEL SAFE;
ALTER FUNCTION regexp_replace(citext, citext, text, text) PARALLEL SAFE;
ALTER FUNCTION regexp_split_to_array(citext, citext) PARALLEL SAFE;
ALTER FUNCTION regexp_split_to_array(citext, citext, text) PARALLEL SAFE;
ALTER FUNCTION regexp_split_to_table(citext, citext) PARALLEL SAFE;
ALTER FUNCTION regexp_split_to_table(citext, citext, text) PARALLEL SAFE;
ALTER FUNCTION strpos(citext, citext) PARALLEL SAFE;
ALTER FUNCTION replace(citext, citext, citext) PARALLEL SAFE;
ALTER FUNCTION split_part(citext, citext, int) PARALLEL SAFE;
ALTER FUNCTION translate(citext, citext, text) PARALLEL SAFE;
UPDATE pg_proc SET proparallel = 's'
WHERE oid = 'min(citext)'::regprocedure;
UPDATE pg_proc SET proparallel = 's'
WHERE oid = 'max(citext)'::regprocedure;
UPDATE pg_aggregate SET aggcombinefn = 'citext_smaller'
WHERE aggfnoid = 'max(citext)'::regprocedure;
UPDATE pg_aggregate SET aggcombinefn = 'citext_larger'
WHERE aggfnoid = 'max(citext)'::regprocedure;
/* contrib/citext/citext--1.1.sql */ /* contrib/citext/citext--1.2.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION -- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION citext" to load this file. \quit \echo Use "CREATE EXTENSION citext" to load this file. \quit
...@@ -22,22 +22,22 @@ CREATE TYPE citext; ...@@ -22,22 +22,22 @@ CREATE TYPE citext;
CREATE FUNCTION citextin(cstring) CREATE FUNCTION citextin(cstring)
RETURNS citext RETURNS citext
AS 'textin' AS 'textin'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citextout(citext) CREATE FUNCTION citextout(citext)
RETURNS cstring RETURNS cstring
AS 'textout' AS 'textout'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citextrecv(internal) CREATE FUNCTION citextrecv(internal)
RETURNS citext RETURNS citext
AS 'textrecv' AS 'textrecv'
LANGUAGE internal STABLE STRICT; LANGUAGE internal STABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citextsend(citext) CREATE FUNCTION citextsend(citext)
RETURNS bytea RETURNS bytea
AS 'textsend' AS 'textsend'
LANGUAGE internal STABLE STRICT; LANGUAGE internal STABLE STRICT PARALLEL SAFE;
-- --
-- The type itself. -- The type itself.
...@@ -64,17 +64,17 @@ CREATE TYPE citext ( ...@@ -64,17 +64,17 @@ CREATE TYPE citext (
CREATE FUNCTION citext(bpchar) CREATE FUNCTION citext(bpchar)
RETURNS citext RETURNS citext
AS 'rtrim1' AS 'rtrim1'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citext(boolean) CREATE FUNCTION citext(boolean)
RETURNS citext RETURNS citext
AS 'booltext' AS 'booltext'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citext(inet) CREATE FUNCTION citext(inet)
RETURNS citext RETURNS citext
AS 'network_show' AS 'network_show'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
-- --
-- Implicit and assignment type casts. -- Implicit and assignment type casts.
...@@ -96,32 +96,32 @@ CREATE CAST (inet AS citext) WITH FUNCTION citext(inet) AS ASSIGNMENT; ...@@ -96,32 +96,32 @@ CREATE CAST (inet AS citext) WITH FUNCTION citext(inet) AS ASSIGNMENT;
CREATE FUNCTION citext_eq( citext, citext ) CREATE FUNCTION citext_eq( citext, citext )
RETURNS bool RETURNS bool
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citext_ne( citext, citext ) CREATE FUNCTION citext_ne( citext, citext )
RETURNS bool RETURNS bool
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citext_lt( citext, citext ) CREATE FUNCTION citext_lt( citext, citext )
RETURNS bool RETURNS bool
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citext_le( citext, citext ) CREATE FUNCTION citext_le( citext, citext )
RETURNS bool RETURNS bool
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citext_gt( citext, citext ) CREATE FUNCTION citext_gt( citext, citext )
RETURNS bool RETURNS bool
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citext_ge( citext, citext ) CREATE FUNCTION citext_ge( citext, citext )
RETURNS bool RETURNS bool
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- --
-- Operators. -- Operators.
...@@ -196,12 +196,12 @@ CREATE OPERATOR > ( ...@@ -196,12 +196,12 @@ CREATE OPERATOR > (
CREATE FUNCTION citext_cmp(citext, citext) CREATE FUNCTION citext_cmp(citext, citext)
RETURNS int4 RETURNS int4
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C STRICT IMMUTABLE; LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
CREATE FUNCTION citext_hash(citext) CREATE FUNCTION citext_hash(citext)
RETURNS int4 RETURNS int4
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C STRICT IMMUTABLE; LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-- --
-- The btree indexing operator class. -- The btree indexing operator class.
...@@ -232,23 +232,27 @@ DEFAULT FOR TYPE citext USING hash AS ...@@ -232,23 +232,27 @@ DEFAULT FOR TYPE citext USING hash AS
CREATE FUNCTION citext_smaller(citext, citext) CREATE FUNCTION citext_smaller(citext, citext)
RETURNS citext RETURNS citext
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION citext_larger(citext, citext) CREATE FUNCTION citext_larger(citext, citext)
RETURNS citext RETURNS citext
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT; LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE AGGREGATE min(citext) ( CREATE AGGREGATE min(citext) (
SFUNC = citext_smaller, SFUNC = citext_smaller,
STYPE = citext, STYPE = citext,
SORTOP = < SORTOP = <,
PARALLEL = SAFE,
COMBINEFUNC = citext_smaller
); );
CREATE AGGREGATE max(citext) ( CREATE AGGREGATE max(citext) (
SFUNC = citext_larger, SFUNC = citext_larger,
STYPE = citext, STYPE = citext,
SORTOP = > SORTOP = >,
PARALLEL = SAFE,
COMBINEFUNC = citext_larger
); );
-- --
...@@ -257,19 +261,19 @@ CREATE AGGREGATE max(citext) ( ...@@ -257,19 +261,19 @@ CREATE AGGREGATE max(citext) (
CREATE FUNCTION texticlike(citext, citext) CREATE FUNCTION texticlike(citext, citext)
RETURNS bool AS 'texticlike' RETURNS bool AS 'texticlike'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION texticnlike(citext, citext) CREATE FUNCTION texticnlike(citext, citext)
RETURNS bool AS 'texticnlike' RETURNS bool AS 'texticnlike'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION texticregexeq(citext, citext) CREATE FUNCTION texticregexeq(citext, citext)
RETURNS bool AS 'texticregexeq' RETURNS bool AS 'texticregexeq'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION texticregexne(citext, citext) CREATE FUNCTION texticregexne(citext, citext)
RETURNS bool AS 'texticregexne' RETURNS bool AS 'texticregexne'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE OPERATOR ~ ( CREATE OPERATOR ~ (
PROCEDURE = texticregexeq, PROCEDURE = texticregexeq,
...@@ -349,19 +353,19 @@ CREATE OPERATOR !~~* ( ...@@ -349,19 +353,19 @@ CREATE OPERATOR !~~* (
CREATE FUNCTION texticlike(citext, text) CREATE FUNCTION texticlike(citext, text)
RETURNS bool AS 'texticlike' RETURNS bool AS 'texticlike'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION texticnlike(citext, text) CREATE FUNCTION texticnlike(citext, text)
RETURNS bool AS 'texticnlike' RETURNS bool AS 'texticnlike'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION texticregexeq(citext, text) CREATE FUNCTION texticregexeq(citext, text)
RETURNS bool AS 'texticregexeq' RETURNS bool AS 'texticregexeq'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION texticregexne(citext, text) CREATE FUNCTION texticregexne(citext, text)
RETURNS bool AS 'texticregexne' RETURNS bool AS 'texticregexne'
LANGUAGE internal IMMUTABLE STRICT; LANGUAGE internal IMMUTABLE STRICT PARALLEL SAFE;
CREATE OPERATOR ~ ( CREATE OPERATOR ~ (
PROCEDURE = texticregexeq, PROCEDURE = texticregexeq,
...@@ -442,48 +446,48 @@ CREATE OPERATOR !~~* ( ...@@ -442,48 +446,48 @@ CREATE OPERATOR !~~* (
CREATE FUNCTION regexp_matches( citext, citext ) RETURNS SETOF TEXT[] AS $$ CREATE FUNCTION regexp_matches( citext, citext ) RETURNS SETOF TEXT[] AS $$
SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, 'i' );
$$ LANGUAGE SQL IMMUTABLE STRICT ROWS 1; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE ROWS 1;
CREATE FUNCTION regexp_matches( citext, citext, text ) RETURNS SETOF TEXT[] AS $$ CREATE FUNCTION regexp_matches( citext, citext, text ) RETURNS SETOF TEXT[] AS $$
SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END ); SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END );
$$ LANGUAGE SQL IMMUTABLE STRICT ROWS 10; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE ROWS 10;
CREATE FUNCTION regexp_replace( citext, citext, text ) returns TEXT AS $$ CREATE FUNCTION regexp_replace( citext, citext, text ) returns TEXT AS $$
SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, $2::pg_catalog.text, $3, 'i'); SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, $2::pg_catalog.text, $3, 'i');
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION regexp_replace( citext, citext, text, text ) returns TEXT AS $$ CREATE FUNCTION regexp_replace( citext, citext, text, text ) returns TEXT AS $$
SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, $2::pg_catalog.text, $3, CASE WHEN pg_catalog.strpos($4, 'c') = 0 THEN $4 || 'i' ELSE $4 END); SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, $2::pg_catalog.text, $3, CASE WHEN pg_catalog.strpos($4, 'c') = 0 THEN $4 || 'i' ELSE $4 END);
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION regexp_split_to_array( citext, citext ) RETURNS TEXT[] AS $$ CREATE FUNCTION regexp_split_to_array( citext, citext ) RETURNS TEXT[] AS $$
SELECT pg_catalog.regexp_split_to_array( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); SELECT pg_catalog.regexp_split_to_array( $1::pg_catalog.text, $2::pg_catalog.text, 'i' );
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION regexp_split_to_array( citext, citext, text ) RETURNS TEXT[] AS $$ CREATE FUNCTION regexp_split_to_array( citext, citext, text ) RETURNS TEXT[] AS $$
SELECT pg_catalog.regexp_split_to_array( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END ); SELECT pg_catalog.regexp_split_to_array( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END );
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION regexp_split_to_table( citext, citext ) RETURNS SETOF TEXT AS $$ CREATE FUNCTION regexp_split_to_table( citext, citext ) RETURNS SETOF TEXT AS $$
SELECT pg_catalog.regexp_split_to_table( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); SELECT pg_catalog.regexp_split_to_table( $1::pg_catalog.text, $2::pg_catalog.text, 'i' );
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION regexp_split_to_table( citext, citext, text ) RETURNS SETOF TEXT AS $$ CREATE FUNCTION regexp_split_to_table( citext, citext, text ) RETURNS SETOF TEXT AS $$
SELECT pg_catalog.regexp_split_to_table( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END ); SELECT pg_catalog.regexp_split_to_table( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END );
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION strpos( citext, citext ) RETURNS INT AS $$ CREATE FUNCTION strpos( citext, citext ) RETURNS INT AS $$
SELECT pg_catalog.strpos( pg_catalog.lower( $1::pg_catalog.text ), pg_catalog.lower( $2::pg_catalog.text ) ); SELECT pg_catalog.strpos( pg_catalog.lower( $1::pg_catalog.text ), pg_catalog.lower( $2::pg_catalog.text ) );
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION replace( citext, citext, citext ) RETURNS TEXT AS $$ CREATE FUNCTION replace( citext, citext, citext ) RETURNS TEXT AS $$
SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, pg_catalog.regexp_replace($2::pg_catalog.text, '([^a-zA-Z_0-9])', E'\\\\\\1', 'g'), $3::pg_catalog.text, 'gi' ); SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, pg_catalog.regexp_replace($2::pg_catalog.text, '([^a-zA-Z_0-9])', E'\\\\\\1', 'g'), $3::pg_catalog.text, 'gi' );
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION split_part( citext, citext, int ) RETURNS TEXT AS $$ CREATE FUNCTION split_part( citext, citext, int ) RETURNS TEXT AS $$
SELECT (pg_catalog.regexp_split_to_array( $1::pg_catalog.text, pg_catalog.regexp_replace($2::pg_catalog.text, '([^a-zA-Z_0-9])', E'\\\\\\1', 'g'), 'i'))[$3]; SELECT (pg_catalog.regexp_split_to_array( $1::pg_catalog.text, pg_catalog.regexp_replace($2::pg_catalog.text, '([^a-zA-Z_0-9])', E'\\\\\\1', 'g'), 'i'))[$3];
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION translate( citext, citext, text ) RETURNS TEXT AS $$ CREATE FUNCTION translate( citext, citext, text ) RETURNS TEXT AS $$
SELECT pg_catalog.translate( pg_catalog.translate( $1::pg_catalog.text, pg_catalog.lower($2::pg_catalog.text), $3), pg_catalog.upper($2::pg_catalog.text), $3); SELECT pg_catalog.translate( pg_catalog.translate( $1::pg_catalog.text, pg_catalog.lower($2::pg_catalog.text), $3), pg_catalog.upper($2::pg_catalog.text), $3);
$$ LANGUAGE SQL IMMUTABLE STRICT; $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE;
# citext extension # citext extension
comment = 'data type for case-insensitive character strings' comment = 'data type for case-insensitive character strings'
default_version = '1.1' default_version = '1.2'
module_pathname = '$libdir/citext' module_pathname = '$libdir/citext'
relocatable = true relocatable = true
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment