Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
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
Abuhujair Javed
Postgres FD Implementation
Commits
bbbd8070
Commit
bbbd8070
authored
Dec 18, 2015
by
Teodor Sigaev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert
9246af67
because
I miss too much. Patch is returned to commitfest process.
parent
3c7042a7
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
28 additions
and
184 deletions
+28
-184
doc/src/sgml/array.sgml
doc/src/sgml/array.sgml
+0
-20
src/backend/executor/execQual.c
src/backend/executor/execQual.c
+9
-35
src/backend/nodes/copyfuncs.c
src/backend/nodes/copyfuncs.c
+0
-2
src/backend/nodes/equalfuncs.c
src/backend/nodes/equalfuncs.c
+0
-2
src/backend/nodes/outfuncs.c
src/backend/nodes/outfuncs.c
+0
-2
src/backend/parser/gram.y
src/backend/parser/gram.y
+0
-31
src/backend/parser/parse_node.c
src/backend/parser/parse_node.c
+17
-32
src/backend/parser/parse_target.c
src/backend/parser/parse_target.c
+1
-1
src/include/nodes/parsenodes.h
src/include/nodes/parsenodes.h
+0
-2
src/test/regress/expected/arrays.out
src/test/regress/expected/arrays.out
+0
-40
src/test/regress/output/misc.source
src/test/regress/output/misc.source
+1
-2
src/test/regress/sql/arrays.sql
src/test/regress/sql/arrays.sql
+0
-15
No files found.
doc/src/sgml/array.sgml
View file @
bbbd8070
...
@@ -255,26 +255,6 @@ SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';
...
@@ -255,26 +255,6 @@ SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';
------------------------
------------------------
{{meeting},{training}}
{{meeting},{training}}
(1 row)
(1 row)
</programlisting>
Possible to skip the <literal><replaceable>lower-bound</replaceable></literal> or
<literal><replaceable>upper-bound</replaceable></literal>
for get first or last element in slice.
<programlisting>
SELECT schedule[:][:] FROM sal_emp WHERE name = 'Bill';
schedule
------------------------
{{meeting,lunch},{training,presentation}}
(1 row)
SELECT schedule[:2][2:] FROM sal_emp WHERE name = 'Bill';
schedule
------------------------
{{lunch},{presentation}}
(1 row)
</programlisting>
</programlisting>
If any dimension is written as a slice, i.e., contains a colon, then all
If any dimension is written as a slice, i.e., contains a colon, then all
...
...
src/backend/executor/execQual.c
View file @
bbbd8070
...
@@ -268,12 +268,10 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
...
@@ -268,12 +268,10 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
bool
eisnull
;
bool
eisnull
;
ListCell
*
l
;
ListCell
*
l
;
int
i
=
0
,
int
i
=
0
,
j
=
0
,
j
=
0
;
indexexpr
;
IntArray
upper
,
IntArray
upper
,
lower
;
lower
;
int
*
lIndex
;
int
*
lIndex
;
AnyArrayType
*
arrays
;
array_source
=
ExecEvalExpr
(
astate
->
refexpr
,
array_source
=
ExecEvalExpr
(
astate
->
refexpr
,
econtext
,
econtext
,
...
@@ -295,7 +293,6 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
...
@@ -295,7 +293,6 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
foreach
(
l
,
astate
->
refupperindexpr
)
foreach
(
l
,
astate
->
refupperindexpr
)
{
{
ExprState
*
eltstate
=
(
ExprState
*
)
lfirst
(
l
);
ExprState
*
eltstate
=
(
ExprState
*
)
lfirst
(
l
);
eisnull
=
false
;
if
(
i
>=
MAXDIM
)
if
(
i
>=
MAXDIM
)
ereport
(
ERROR
,
ereport
(
ERROR
,
...
@@ -303,23 +300,10 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
...
@@ -303,23 +300,10 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
errmsg
(
"number of array dimensions (%d) exceeds the maximum allowed (%d)"
,
errmsg
(
"number of array dimensions (%d) exceeds the maximum allowed (%d)"
,
i
+
1
,
MAXDIM
)));
i
+
1
,
MAXDIM
)));
if
(
eltstate
==
NULL
&&
astate
->
refattrlength
<=
0
)
upper
.
indx
[
i
++
]
=
DatumGetInt32
(
ExecEvalExpr
(
eltstate
,
{
econtext
,
if
(
isAssignment
)
&
eisnull
,
ereport
(
ERROR
,
NULL
));
(
errcode
(
ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
errmsg
(
"cannot determine upper index for empty array"
)));
arrays
=
(
AnyArrayType
*
)
DatumGetArrayTypeP
(
array_source
);
indexexpr
=
AARR_LBOUND
(
arrays
)[
i
]
+
AARR_DIMS
(
arrays
)[
i
]
-
1
;
}
else
indexexpr
=
DatumGetInt32
(
ExecEvalExpr
(
eltstate
,
econtext
,
&
eisnull
,
NULL
));
upper
.
indx
[
i
++
]
=
indexexpr
;
/* If any index expr yields NULL, result is NULL or error */
/* If any index expr yields NULL, result is NULL or error */
if
(
eisnull
)
if
(
eisnull
)
{
{
...
@@ -337,7 +321,6 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
...
@@ -337,7 +321,6 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
foreach
(
l
,
astate
->
reflowerindexpr
)
foreach
(
l
,
astate
->
reflowerindexpr
)
{
{
ExprState
*
eltstate
=
(
ExprState
*
)
lfirst
(
l
);
ExprState
*
eltstate
=
(
ExprState
*
)
lfirst
(
l
);
eisnull
=
false
;
if
(
j
>=
MAXDIM
)
if
(
j
>=
MAXDIM
)
ereport
(
ERROR
,
ereport
(
ERROR
,
...
@@ -345,19 +328,10 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
...
@@ -345,19 +328,10 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
errmsg
(
"number of array dimensions (%d) exceeds the maximum allowed (%d)"
,
errmsg
(
"number of array dimensions (%d) exceeds the maximum allowed (%d)"
,
j
+
1
,
MAXDIM
)));
j
+
1
,
MAXDIM
)));
if
(
eltstate
==
NULL
)
lower
.
indx
[
j
++
]
=
DatumGetInt32
(
ExecEvalExpr
(
eltstate
,
{
econtext
,
arrays
=
(
AnyArrayType
*
)
DatumGetArrayTypeP
(
array_source
);
&
eisnull
,
indexexpr
=
AARR_LBOUND
(
arrays
)[
j
];
NULL
));
}
else
indexexpr
=
DatumGetInt32
(
ExecEvalExpr
(
eltstate
,
econtext
,
&
eisnull
,
NULL
));
lower
.
indx
[
j
++
]
=
indexexpr
;
/* If any index expr yields NULL, result is NULL or error */
/* If any index expr yields NULL, result is NULL or error */
if
(
eisnull
)
if
(
eisnull
)
{
{
...
...
src/backend/nodes/copyfuncs.c
View file @
bbbd8070
...
@@ -2403,8 +2403,6 @@ _copyAIndices(const A_Indices *from)
...
@@ -2403,8 +2403,6 @@ _copyAIndices(const A_Indices *from)
COPY_NODE_FIELD
(
lidx
);
COPY_NODE_FIELD
(
lidx
);
COPY_NODE_FIELD
(
uidx
);
COPY_NODE_FIELD
(
uidx
);
COPY_SCALAR_FIELD
(
lidx_default
);
COPY_SCALAR_FIELD
(
uidx_default
);
return
newnode
;
return
newnode
;
}
}
...
...
src/backend/nodes/equalfuncs.c
View file @
bbbd8070
...
@@ -2153,8 +2153,6 @@ _equalAIndices(const A_Indices *a, const A_Indices *b)
...
@@ -2153,8 +2153,6 @@ _equalAIndices(const A_Indices *a, const A_Indices *b)
{
{
COMPARE_NODE_FIELD
(
lidx
);
COMPARE_NODE_FIELD
(
lidx
);
COMPARE_NODE_FIELD
(
uidx
);
COMPARE_NODE_FIELD
(
uidx
);
COMPARE_SCALAR_FIELD
(
lidx_default
);
COMPARE_SCALAR_FIELD
(
uidx_default
);
return
true
;
return
true
;
}
}
...
...
src/backend/nodes/outfuncs.c
View file @
bbbd8070
...
@@ -2765,8 +2765,6 @@ _outA_Indices(StringInfo str, const A_Indices *node)
...
@@ -2765,8 +2765,6 @@ _outA_Indices(StringInfo str, const A_Indices *node)
WRITE_NODE_FIELD
(
lidx
);
WRITE_NODE_FIELD
(
lidx
);
WRITE_NODE_FIELD
(
uidx
);
WRITE_NODE_FIELD
(
uidx
);
WRITE_BOOL_FIELD
(
lidx_default
);
WRITE_BOOL_FIELD
(
uidx_default
);
}
}
static
void
static
void
...
...
src/backend/parser/gram.y
View file @
bbbd8070
...
@@ -13193,35 +13193,6 @@ indirection_el:
...
@@ -13193,35 +13193,6 @@ indirection_el:
A_Indices *ai = makeNode(A_Indices);
A_Indices *ai = makeNode(A_Indices);
ai->lidx = NULL;
ai->lidx = NULL;
ai->uidx = $2;
ai->uidx = $2;
ai->lidx_default = false;
ai->uidx_default = false;
$$ = (Node *) ai;
}
| '[' ':' ']'
{
A_Indices *ai = makeNode(A_Indices);
ai->lidx = NULL;
ai->uidx = NULL;
ai->lidx_default = true;
ai->uidx_default = true;
$$ = (Node *) ai;
}
| '[' ':' a_expr ']'
{
A_Indices *ai = makeNode(A_Indices);
ai->lidx = NULL;
ai->uidx = $3;
ai->lidx_default = true;
ai->uidx_default = false;
$$ = (Node *) ai;
}
| '[' a_expr ':' ']'
{
A_Indices *ai = makeNode(A_Indices);
ai->lidx = $2;
ai->uidx = NULL;
ai->lidx_default = false;
ai->uidx_default = true;
$$ = (Node *) ai;
$$ = (Node *) ai;
}
}
| '[' a_expr ':' a_expr ']'
| '[' a_expr ':' a_expr ']'
...
@@ -13229,8 +13200,6 @@ indirection_el:
...
@@ -13229,8 +13200,6 @@ indirection_el:
A_Indices *ai = makeNode(A_Indices);
A_Indices *ai = makeNode(A_Indices);
ai->lidx = $2;
ai->lidx = $2;
ai->uidx = $4;
ai->uidx = $4;
ai->lidx_default = false;
ai->uidx_default = false;
$$ = (Node *) ai;
$$ = (Node *) ai;
}
}
;
;
...
...
src/backend/parser/parse_node.c
View file @
bbbd8070
...
@@ -311,7 +311,7 @@ transformArraySubscripts(ParseState *pstate,
...
@@ -311,7 +311,7 @@ transformArraySubscripts(ParseState *pstate,
elementType
=
transformArrayType
(
&
arrayType
,
&
arrayTypMod
);
elementType
=
transformArrayType
(
&
arrayType
,
&
arrayTypMod
);
/*
/*
* A list containing only single subscripts
(uidx)
refers to a single array
* A list containing only single subscripts refers to a single array
* element. If any of the items are double subscripts (lower:upper), then
* element. If any of the items are double subscripts (lower:upper), then
* the subscript expression means an array slice operation. In this case,
* the subscript expression means an array slice operation. In this case,
* we supply a default lower bound of 1 for any items that contain only a
* we supply a default lower bound of 1 for any items that contain only a
...
@@ -322,7 +322,7 @@ transformArraySubscripts(ParseState *pstate,
...
@@ -322,7 +322,7 @@ transformArraySubscripts(ParseState *pstate,
{
{
A_Indices
*
ai
=
(
A_Indices
*
)
lfirst
(
idx
);
A_Indices
*
ai
=
(
A_Indices
*
)
lfirst
(
idx
);
if
(
ai
->
lidx
!=
NULL
||
ai
->
lidx_default
)
if
(
ai
->
lidx
!=
NULL
)
{
{
isSlice
=
true
;
isSlice
=
true
;
break
;
break
;
...
@@ -335,17 +335,9 @@ transformArraySubscripts(ParseState *pstate,
...
@@ -335,17 +335,9 @@ transformArraySubscripts(ParseState *pstate,
foreach
(
idx
,
indirection
)
foreach
(
idx
,
indirection
)
{
{
A_Indices
*
ai
=
(
A_Indices
*
)
lfirst
(
idx
);
A_Indices
*
ai
=
(
A_Indices
*
)
lfirst
(
idx
);
Node
*
subexpr
=
NULL
;
Node
*
subexpr
;
Assert
(
IsA
(
ai
,
A_Indices
));
Assert
(
IsA
(
ai
,
A_Indices
));
if
((
ai
->
uidx_default
||
ai
->
lidx_default
)
&&
assignFrom
!=
NULL
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
errmsg
(
"array subscript must have both boundaries"
),
errhint
(
"You can't omit the upper or lower"
" boundaries when updating or inserting"
),
parser_errposition
(
pstate
,
exprLocation
(
arrayBase
))));
if
(
isSlice
)
if
(
isSlice
)
{
{
if
(
ai
->
lidx
)
if
(
ai
->
lidx
)
...
@@ -364,7 +356,7 @@ transformArraySubscripts(ParseState *pstate,
...
@@ -364,7 +356,7 @@ transformArraySubscripts(ParseState *pstate,
errmsg
(
"array subscript must have type integer"
),
errmsg
(
"array subscript must have type integer"
),
parser_errposition
(
pstate
,
exprLocation
(
ai
->
lidx
))));
parser_errposition
(
pstate
,
exprLocation
(
ai
->
lidx
))));
}
}
else
if
(
ai
->
lidx_default
==
false
)
else
{
{
/* Make a constant 1 */
/* Make a constant 1 */
subexpr
=
(
Node
*
)
makeConst
(
INT4OID
,
subexpr
=
(
Node
*
)
makeConst
(
INT4OID
,
...
@@ -377,26 +369,19 @@ transformArraySubscripts(ParseState *pstate,
...
@@ -377,26 +369,19 @@ transformArraySubscripts(ParseState *pstate,
}
}
lowerIndexpr
=
lappend
(
lowerIndexpr
,
subexpr
);
lowerIndexpr
=
lappend
(
lowerIndexpr
,
subexpr
);
}
}
subexpr
=
transformExpr
(
pstate
,
ai
->
uidx
,
pstate
->
p_expr_kind
);
if
(
ai
->
uidx_default
==
false
)
/* If it's not int4 already, try to coerce */
{
subexpr
=
coerce_to_target_type
(
pstate
,
subexpr
=
transformExpr
(
pstate
,
ai
->
uidx
,
pstate
->
p_expr_kind
);
subexpr
,
exprType
(
subexpr
),
/* If it's not int4 already, try to coerce */
INT4OID
,
-
1
,
subexpr
=
coerce_to_target_type
(
pstate
,
COERCION_ASSIGNMENT
,
subexpr
,
exprType
(
subexpr
),
COERCE_IMPLICIT_CAST
,
INT4OID
,
-
1
,
-
1
);
COERCION_ASSIGNMENT
,
if
(
subexpr
==
NULL
)
COERCE_IMPLICIT_CAST
,
ereport
(
ERROR
,
-
1
);
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
if
(
subexpr
==
NULL
)
errmsg
(
"array subscript must have type integer"
),
ereport
(
ERROR
,
parser_errposition
(
pstate
,
exprLocation
(
ai
->
uidx
))));
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"array subscript must have type integer"
),
parser_errposition
(
pstate
,
exprLocation
(
ai
->
uidx
))));
}
else
subexpr
=
NULL
;
upperIndexpr
=
lappend
(
upperIndexpr
,
subexpr
);
upperIndexpr
=
lappend
(
upperIndexpr
,
subexpr
);
}
}
...
...
src/backend/parser/parse_target.c
View file @
bbbd8070
...
@@ -650,7 +650,7 @@ transformAssignmentIndirection(ParseState *pstate,
...
@@ -650,7 +650,7 @@ transformAssignmentIndirection(ParseState *pstate,
if
(
IsA
(
n
,
A_Indices
))
if
(
IsA
(
n
,
A_Indices
))
{
{
subscripts
=
lappend
(
subscripts
,
n
);
subscripts
=
lappend
(
subscripts
,
n
);
if
(((
A_Indices
*
)
n
)
->
lidx
!=
NULL
||
((
A_Indices
*
)
n
)
->
lidx_default
)
if
(((
A_Indices
*
)
n
)
->
lidx
!=
NULL
)
isSlice
=
true
;
isSlice
=
true
;
}
}
else
if
(
IsA
(
n
,
A_Star
))
else
if
(
IsA
(
n
,
A_Star
))
...
...
src/include/nodes/parsenodes.h
View file @
bbbd8070
...
@@ -358,8 +358,6 @@ typedef struct A_Indices
...
@@ -358,8 +358,6 @@ typedef struct A_Indices
NodeTag
type
;
NodeTag
type
;
Node
*
lidx
;
/* NULL if it's a single subscript */
Node
*
lidx
;
/* NULL if it's a single subscript */
Node
*
uidx
;
Node
*
uidx
;
bool
lidx_default
;
bool
uidx_default
;
}
A_Indices
;
}
A_Indices
;
/*
/*
...
...
src/test/regress/expected/arrays.out
View file @
bbbd8070
...
@@ -2031,43 +2031,3 @@ SELECT width_bucket(5, ARRAY[3, 4, NULL]);
...
@@ -2031,43 +2031,3 @@ SELECT width_bucket(5, ARRAY[3, 4, NULL]);
ERROR: thresholds array must not contain NULLs
ERROR: thresholds array must not contain NULLs
SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
ERROR: thresholds must be one-dimensional array
ERROR: thresholds must be one-dimensional array
-- slices with empty lower and/or upper index
CREATE TABLE arrtest_s (
a int2[],
b int2[][]
);
INSERT INTO arrtest_s VALUES ('{1,2,3,4,5}', '{{1,2,3}, {4,5,6}, {7,8,9}}');
SELECT a[:3], b[:2][:2] FROM arrtest_s;
a | b
---------+---------------
{1,2,3} | {{1,2},{4,5}}
(1 row)
SELECT a[2:], b[2:][2:] FROM arrtest_s;
a | b
-----------+---------------
{2,3,4,5} | {{5,6},{8,9}}
(1 row)
SELECT a[:], b[:] FROM arrtest_s;
a | b
-------------+---------------------------
{1,2,3,4,5} | {{1,2,3},{4,5,6},{7,8,9}}
(1 row)
-- errors
UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{11,12}, {14, 15}}';
ERROR: array subscript must have both boundaries
LINE 1: UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{...
^
HINT: You can't omit the upper or lower boundaries when updating or inserting
UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{25,26}, {28, 29}}';
ERROR: array subscript must have both boundaries
LINE 1: UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{...
^
HINT: You can't omit the upper or lower boundaries when updating or inserting
UPDATE arrtest_s SET a[:] = '{23, 24, 25}';
ERROR: array subscript must have both boundaries
LINE 1: UPDATE arrtest_s SET a[:] = '{23, 24, 25}';
^
HINT: You can't omit the upper or lower boundaries when updating or inserting
src/test/regress/output/misc.source
View file @
bbbd8070
...
@@ -586,7 +586,6 @@ SELECT user_relns() AS user_relns
...
@@ -586,7 +586,6 @@ SELECT user_relns() AS user_relns
array_index_op_test
array_index_op_test
array_op_test
array_op_test
arrtest
arrtest
arrtest_s
b
b
b_star
b_star
bb
bb
...
@@ -711,7 +710,7 @@ SELECT user_relns() AS user_relns
...
@@ -711,7 +710,7 @@ SELECT user_relns() AS user_relns
tvvmv
tvvmv
varchar_tbl
varchar_tbl
xacttest
xacttest
(13
3
rows)
(13
2
rows)
SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
name
name
...
...
src/test/regress/sql/arrays.sql
View file @
bbbd8070
...
@@ -609,18 +609,3 @@ SELECT width_bucket(5, '{}');
...
@@ -609,18 +609,3 @@ SELECT width_bucket(5, '{}');
SELECT
width_bucket
(
'5'
::
text
,
ARRAY
[
3
,
4
]::
integer
[]);
SELECT
width_bucket
(
'5'
::
text
,
ARRAY
[
3
,
4
]::
integer
[]);
SELECT
width_bucket
(
5
,
ARRAY
[
3
,
4
,
NULL
]);
SELECT
width_bucket
(
5
,
ARRAY
[
3
,
4
,
NULL
]);
SELECT
width_bucket
(
5
,
ARRAY
[
ARRAY
[
1
,
2
],
ARRAY
[
3
,
4
]]);
SELECT
width_bucket
(
5
,
ARRAY
[
ARRAY
[
1
,
2
],
ARRAY
[
3
,
4
]]);
-- slices with empty lower and/or upper index
CREATE
TABLE
arrtest_s
(
a
int2
[],
b
int2
[][]
);
INSERT
INTO
arrtest_s
VALUES
(
'{1,2,3,4,5}'
,
'{{1,2,3}, {4,5,6}, {7,8,9}}'
);
SELECT
a
[:
3
],
b
[:
2
][:
2
]
FROM
arrtest_s
;
SELECT
a
[
2
:],
b
[
2
:][
2
:]
FROM
arrtest_s
;
SELECT
a
[:],
b
[:]
FROM
arrtest_s
;
-- errors
UPDATE
arrtest_s
SET
a
[:
3
]
=
'{11, 12, 13}'
,
b
[:
2
][:
2
]
=
'{{11,12}, {14, 15}}'
;
UPDATE
arrtest_s
SET
a
[
3
:]
=
'{23, 24, 25}'
,
b
[
2
:][
2
:]
=
'{{25,26}, {28, 29}}'
;
UPDATE
arrtest_s
SET
a
[:]
=
'{23, 24, 25}'
;
\ No newline at end of file
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