mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Standardize LSN formatting by zero padding
This commit standardizes the output format for LSNs to ensure consistent representation across various tools and messages. Previously, LSNs were inconsistently printed as `%X/%X` in some contexts, while others used zero-padding. This often led to confusion when comparing. To address this, the LSN format is now uniformly set to `%X/%08X`, ensuring the lower 32-bit part is always zero-padded to eight hexadecimal digits. Author: Japin Li <japinli@hotmail.com> Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com> Reviewed-by: Álvaro Herrera <alvherre@kurilemu.de> Discussion: https://postgr.es/m/ME0P300MB0445CA53CA0E4B8C1879AF84B641A@ME0P300MB0445.AUSP300.PROD.OUTLOOK.COM
This commit is contained in:
@ -913,7 +913,7 @@ bt_report_duplicate(BtreeCheckState *state,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("index uniqueness is violated for index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail("Index %s%s and%s%s (point to heap %s and %s) page lsn=%X/%X.",
|
||||
errdetail("Index %s%s and%s%s (point to heap %s and %s) page lsn=%X/%08X.",
|
||||
itid, pposting, nitid, pnposting, htid, nhtid,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
}
|
||||
@ -1058,7 +1058,7 @@ bt_leftmost_ignoring_half_dead(BtreeCheckState *state,
|
||||
(errcode(ERRCODE_NO_DATA),
|
||||
errmsg_internal("harmless interrupted page deletion detected in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Block=%u right block=%u page lsn=%X/%X.",
|
||||
errdetail_internal("Block=%u right block=%u page lsn=%X/%08X.",
|
||||
reached, reached_from,
|
||||
LSN_FORMAT_ARGS(pagelsn))));
|
||||
|
||||
@ -1283,7 +1283,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("wrong number of high key index tuple attributes in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Index block=%u natts=%u block type=%s page lsn=%X/%X.",
|
||||
errdetail_internal("Index block=%u natts=%u block type=%s page lsn=%X/%08X.",
|
||||
state->targetblock,
|
||||
BTreeTupleGetNAtts(itup, state->rel),
|
||||
P_ISLEAF(topaque) ? "heap" : "index",
|
||||
@ -1332,7 +1332,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("index tuple size does not equal lp_len in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Index tid=(%u,%u) tuple size=%zu lp_len=%u page lsn=%X/%X.",
|
||||
errdetail_internal("Index tid=(%u,%u) tuple size=%zu lp_len=%u page lsn=%X/%08X.",
|
||||
state->targetblock, offset,
|
||||
tupsize, ItemIdGetLength(itemid),
|
||||
LSN_FORMAT_ARGS(state->targetlsn)),
|
||||
@ -1356,7 +1356,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("wrong number of index tuple attributes in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Index tid=%s natts=%u points to %s tid=%s page lsn=%X/%X.",
|
||||
errdetail_internal("Index tid=%s natts=%u points to %s tid=%s page lsn=%X/%08X.",
|
||||
itid,
|
||||
BTreeTupleGetNAtts(itup, state->rel),
|
||||
P_ISLEAF(topaque) ? "heap" : "index",
|
||||
@ -1406,7 +1406,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("could not find tuple using search from root page in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Index tid=%s points to heap tid=%s page lsn=%X/%X.",
|
||||
errdetail_internal("Index tid=%s points to heap tid=%s page lsn=%X/%08X.",
|
||||
itid, htid,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
}
|
||||
@ -1435,7 +1435,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg_internal("posting list contains misplaced TID in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Index tid=%s posting list offset=%d page lsn=%X/%X.",
|
||||
errdetail_internal("Index tid=%s posting list offset=%d page lsn=%X/%08X.",
|
||||
itid, i,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
}
|
||||
@ -1488,7 +1488,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("index row size %zu exceeds maximum for index \"%s\"",
|
||||
tupsize, RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Index tid=%s points to %s tid=%s page lsn=%X/%X.",
|
||||
errdetail_internal("Index tid=%s points to %s tid=%s page lsn=%X/%08X.",
|
||||
itid,
|
||||
P_ISLEAF(topaque) ? "heap" : "index",
|
||||
htid,
|
||||
@ -1595,7 +1595,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("high key invariant violated for index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Index tid=%s points to %s tid=%s page lsn=%X/%X.",
|
||||
errdetail_internal("Index tid=%s points to %s tid=%s page lsn=%X/%08X.",
|
||||
itid,
|
||||
P_ISLEAF(topaque) ? "heap" : "index",
|
||||
htid,
|
||||
@ -1641,9 +1641,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("item order invariant violated for index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Lower index tid=%s (points to %s tid=%s) "
|
||||
"higher index tid=%s (points to %s tid=%s) "
|
||||
"page lsn=%X/%X.",
|
||||
errdetail_internal("Lower index tid=%s (points to %s tid=%s) higher index tid=%s (points to %s tid=%s) page lsn=%X/%08X.",
|
||||
itid,
|
||||
P_ISLEAF(topaque) ? "heap" : "index",
|
||||
htid,
|
||||
@ -1760,7 +1758,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("cross page item order invariant violated for index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Last item on page tid=(%u,%u) page lsn=%X/%X.",
|
||||
errdetail_internal("Last item on page tid=(%u,%u) page lsn=%X/%08X.",
|
||||
state->targetblock, offset,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
}
|
||||
@ -1813,7 +1811,7 @@ bt_target_page_check(BtreeCheckState *state)
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("right block of leaf block is non-leaf for index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Block=%u page lsn=%X/%X.",
|
||||
errdetail_internal("Block=%u page lsn=%X/%08X.",
|
||||
state->targetblock,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
|
||||
@ -2237,7 +2235,7 @@ bt_child_highkey_check(BtreeCheckState *state,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("the first child of leftmost target page is not leftmost of its level in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
|
||||
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%08X.",
|
||||
state->targetblock, blkno,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
|
||||
@ -2323,7 +2321,7 @@ bt_child_highkey_check(BtreeCheckState *state,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("child high key is greater than rightmost pivot key on target level in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
|
||||
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%08X.",
|
||||
state->targetblock, blkno,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
pivotkey_offset = P_HIKEY;
|
||||
@ -2353,7 +2351,7 @@ bt_child_highkey_check(BtreeCheckState *state,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("can't find left sibling high key in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
|
||||
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%08X.",
|
||||
state->targetblock, blkno,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
itup = state->lowkey;
|
||||
@ -2365,7 +2363,7 @@ bt_child_highkey_check(BtreeCheckState *state,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("mismatch between parent key and child high key in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%X.",
|
||||
errdetail_internal("Target block=%u child block=%u target page lsn=%X/%08X.",
|
||||
state->targetblock, blkno,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
}
|
||||
@ -2505,7 +2503,7 @@ bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("downlink to deleted page found in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Parent block=%u child block=%u parent page lsn=%X/%X.",
|
||||
errdetail_internal("Parent block=%u child block=%u parent page lsn=%X/%08X.",
|
||||
state->targetblock, childblock,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
|
||||
@ -2546,7 +2544,7 @@ bt_child_check(BtreeCheckState *state, BTScanInsert targetkey,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("down-link lower bound invariant violated for index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Parent block=%u child index tid=(%u,%u) parent page lsn=%X/%X.",
|
||||
errdetail_internal("Parent block=%u child index tid=(%u,%u) parent page lsn=%X/%08X.",
|
||||
state->targetblock, childblock, offset,
|
||||
LSN_FORMAT_ARGS(state->targetlsn))));
|
||||
}
|
||||
@ -2616,7 +2614,7 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
|
||||
(errcode(ERRCODE_NO_DATA),
|
||||
errmsg_internal("harmless interrupted page split detected in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Block=%u level=%u left sibling=%u page lsn=%X/%X.",
|
||||
errdetail_internal("Block=%u level=%u left sibling=%u page lsn=%X/%08X.",
|
||||
blkno, opaque->btpo_level,
|
||||
opaque->btpo_prev,
|
||||
LSN_FORMAT_ARGS(pagelsn))));
|
||||
@ -2638,7 +2636,7 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("leaf index block lacks downlink in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Block=%u page lsn=%X/%X.",
|
||||
errdetail_internal("Block=%u page lsn=%X/%08X.",
|
||||
blkno,
|
||||
LSN_FORMAT_ARGS(pagelsn))));
|
||||
|
||||
@ -2704,7 +2702,7 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg_internal("downlink to deleted leaf page found in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Top parent/target block=%u leaf block=%u top parent/under check lsn=%X/%X.",
|
||||
errdetail_internal("Top parent/target block=%u leaf block=%u top parent/under check lsn=%X/%08X.",
|
||||
blkno, childblk,
|
||||
LSN_FORMAT_ARGS(pagelsn))));
|
||||
|
||||
@ -2730,7 +2728,7 @@ bt_downlink_missing_check(BtreeCheckState *state, bool rightsplit,
|
||||
(errcode(ERRCODE_INDEX_CORRUPTED),
|
||||
errmsg("internal index block lacks downlink in index \"%s\"",
|
||||
RelationGetRelationName(state->rel)),
|
||||
errdetail_internal("Block=%u level=%u page lsn=%X/%X.",
|
||||
errdetail_internal("Block=%u level=%u page lsn=%X/%08X.",
|
||||
blkno, opaque->btpo_level,
|
||||
LSN_FORMAT_ARGS(pagelsn))));
|
||||
}
|
||||
|
@ -5,21 +5,21 @@ CREATE UNLOGGED TABLE test_gist AS SELECT point(i,i) p, i::text t FROM
|
||||
CREATE INDEX test_gist_idx ON test_gist USING gist (p);
|
||||
-- Page 0 is the root, the rest are leaf pages
|
||||
SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 0));
|
||||
lsn | nsn | rightlink | flags
|
||||
-----+-----+------------+-------
|
||||
0/1 | 0/0 | 4294967295 | {}
|
||||
lsn | nsn | rightlink | flags
|
||||
------------+------------+------------+-------
|
||||
0/00000001 | 0/00000000 | 4294967295 | {}
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 1));
|
||||
lsn | nsn | rightlink | flags
|
||||
-----+-----+------------+--------
|
||||
0/1 | 0/0 | 4294967295 | {leaf}
|
||||
lsn | nsn | rightlink | flags
|
||||
------------+------------+------------+--------
|
||||
0/00000001 | 0/00000000 | 4294967295 | {leaf}
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2));
|
||||
lsn | nsn | rightlink | flags
|
||||
-----+-----+-----------+--------
|
||||
0/1 | 0/0 | 1 | {leaf}
|
||||
lsn | nsn | rightlink | flags
|
||||
------------+------------+-----------+--------
|
||||
0/00000001 | 0/00000000 | 1 | {leaf}
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0), 'test_gist_idx');
|
||||
|
@ -265,9 +265,9 @@ SELECT fsm_page_contents(decode(repeat('00', :block_size), 'hex'));
|
||||
(1 row)
|
||||
|
||||
SELECT page_header(decode(repeat('00', :block_size), 'hex'));
|
||||
page_header
|
||||
-----------------------
|
||||
(0/0,0,0,0,0,0,0,0,0)
|
||||
page_header
|
||||
------------------------------
|
||||
(0/00000000,0,0,0,0,0,0,0,0)
|
||||
(1 row)
|
||||
|
||||
SELECT page_checksum(decode(repeat('00', :block_size), 'hex'), 1);
|
||||
|
@ -282,7 +282,7 @@ page_header(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char lsnchar[64];
|
||||
|
||||
snprintf(lsnchar, sizeof(lsnchar), "%X/%X", LSN_FORMAT_ARGS(lsn));
|
||||
snprintf(lsnchar, sizeof(lsnchar), "%X/%08X", LSN_FORMAT_ARGS(lsn));
|
||||
values[0] = CStringGetTextDatum(lsnchar);
|
||||
}
|
||||
else
|
||||
|
@ -19,14 +19,14 @@ INSERT INTO sample_tbl SELECT * FROM generate_series(3, 4);
|
||||
-- ===================================================================
|
||||
-- Invalid input LSN.
|
||||
SELECT * FROM pg_get_wal_record_info('0/0');
|
||||
ERROR: could not read WAL at LSN 0/0
|
||||
ERROR: could not read WAL at LSN 0/00000000
|
||||
-- Invalid start LSN.
|
||||
SELECT * FROM pg_get_wal_records_info('0/0', :'wal_lsn1');
|
||||
ERROR: could not read WAL at LSN 0/0
|
||||
ERROR: could not read WAL at LSN 0/00000000
|
||||
SELECT * FROM pg_get_wal_stats('0/0', :'wal_lsn1');
|
||||
ERROR: could not read WAL at LSN 0/0
|
||||
ERROR: could not read WAL at LSN 0/00000000
|
||||
SELECT * FROM pg_get_wal_block_info('0/0', :'wal_lsn1');
|
||||
ERROR: could not read WAL at LSN 0/0
|
||||
ERROR: could not read WAL at LSN 0/00000000
|
||||
-- Start LSN > End LSN.
|
||||
SELECT * FROM pg_get_wal_records_info(:'wal_lsn2', :'wal_lsn1');
|
||||
ERROR: WAL start LSN must be less than end LSN
|
||||
|
@ -105,7 +105,7 @@ InitXLogReaderState(XLogRecPtr lsn)
|
||||
if (lsn < XLOG_BLCKSZ)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("could not read WAL at LSN %X/%X",
|
||||
errmsg("could not read WAL at LSN %X/%08X",
|
||||
LSN_FORMAT_ARGS(lsn))));
|
||||
|
||||
private_data = (ReadLocalXLogPageNoWaitPrivate *)
|
||||
@ -128,8 +128,8 @@ InitXLogReaderState(XLogRecPtr lsn)
|
||||
|
||||
if (XLogRecPtrIsInvalid(first_valid_record))
|
||||
ereport(ERROR,
|
||||
(errmsg("could not find a valid record after %X/%X",
|
||||
LSN_FORMAT_ARGS(lsn))));
|
||||
errmsg("could not find a valid record after %X/%08X",
|
||||
LSN_FORMAT_ARGS(lsn)));
|
||||
|
||||
return xlogreader;
|
||||
}
|
||||
@ -168,12 +168,12 @@ ReadNextXLogRecord(XLogReaderState *xlogreader)
|
||||
if (errormsg)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read WAL at %X/%X: %s",
|
||||
errmsg("could not read WAL at %X/%08X: %s",
|
||||
LSN_FORMAT_ARGS(xlogreader->EndRecPtr), errormsg)));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read WAL at %X/%X",
|
||||
errmsg("could not read WAL at %X/%08X",
|
||||
LSN_FORMAT_ARGS(xlogreader->EndRecPtr))));
|
||||
}
|
||||
|
||||
@ -479,7 +479,7 @@ pg_get_wal_record_info(PG_FUNCTION_ARGS)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("WAL input LSN must be less than current LSN"),
|
||||
errdetail("Current WAL LSN on the database system is at %X/%X.",
|
||||
errdetail("Current WAL LSN on the database system is at %X/%08X.",
|
||||
LSN_FORMAT_ARGS(curr_lsn))));
|
||||
|
||||
/* Build a tuple descriptor for our result type. */
|
||||
@ -491,7 +491,7 @@ pg_get_wal_record_info(PG_FUNCTION_ARGS)
|
||||
if (!ReadNextXLogRecord(xlogreader))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("could not read WAL at %X/%X",
|
||||
errmsg("could not read WAL at %X/%08X",
|
||||
LSN_FORMAT_ARGS(xlogreader->EndRecPtr))));
|
||||
|
||||
GetWALRecordInfo(xlogreader, values, nulls, PG_GET_WAL_RECORD_INFO_COLS);
|
||||
@ -521,7 +521,7 @@ ValidateInputLSNs(XLogRecPtr start_lsn, XLogRecPtr *end_lsn)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("WAL start LSN must be less than current LSN"),
|
||||
errdetail("Current WAL LSN on the database system is at %X/%X.",
|
||||
errdetail("Current WAL LSN on the database system is at %X/%08X.",
|
||||
LSN_FORMAT_ARGS(curr_lsn))));
|
||||
|
||||
if (start_lsn > *end_lsn)
|
||||
@ -827,7 +827,7 @@ pg_get_wal_records_info_till_end_of_wal(PG_FUNCTION_ARGS)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("WAL start LSN must be less than current LSN"),
|
||||
errdetail("Current WAL LSN on the database system is at %X/%X.",
|
||||
errdetail("Current WAL LSN on the database system is at %X/%08X.",
|
||||
LSN_FORMAT_ARGS(end_lsn))));
|
||||
|
||||
GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
|
||||
@ -846,7 +846,7 @@ pg_get_wal_stats_till_end_of_wal(PG_FUNCTION_ARGS)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("WAL start LSN must be less than current LSN"),
|
||||
errdetail("Current WAL LSN on the database system is at %X/%X.",
|
||||
errdetail("Current WAL LSN on the database system is at %X/%08X.",
|
||||
LSN_FORMAT_ARGS(end_lsn))));
|
||||
|
||||
GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
|
||||
|
Reference in New Issue
Block a user