Re: Assert in pageinspect with NULL pages

2022-02-22 Thread Daria Lepikhova

First of all, thanks for the review and remarks!

18.02.2022 08:02, Michael Paquier пишет:

On Thu, Feb 17, 2022 at 05:40:41PM +0800, Julien Rouhaud wrote:

About the patch, it's incorrectly using a hardcoded 8192 block-size rather than
using the computed :block_size (or computing one when it's not already the
case).

Well, the tests of pageinspect fail would already fail when using a
different page size than 8k, like the checksum ones :)

Anywa, I agree with your point that if this is not a reason to not
make the tests more portable if we can do easily.


Fixed.


I'm also wondering if it wouldn't be better to return NULL rather than throwing
an error for uninitialized pages.

Agreed that this is a sensible choice.  NULL would be helpful for the
case where one compiles all the checksums of a relation with a full
scan based on the relation size, for example.  All these behaviors
ought to be documented properly, as well.  For SRFs, this should just
return no rows rather than generating an error.


So, I tried to implement this remark. However, further getting rid of 
throwing an error and replacing it with a NULL return led to reproduce 
the problem that Alexander Lakhin mentioned And here I need little more 
time to figure it out.



And one more addition. In the previous version of the patch, I forgot to 
add tests for the gist index, but the described problem is also relevant 
for it.


--
Daria Lepikhova
Postgres Professional
From 7dbfa7f01bf485d2812b24af012721b4a1e1e785 Mon Sep 17 00:00:00 2001
From: "d.lepikhova" 
Date: Tue, 22 Feb 2022 16:25:31 +0500
Subject: [PATCH] Add checks for null pages into pageinspect

---
 contrib/pageinspect/brinfuncs.c   | 10 ++
 contrib/pageinspect/btreefuncs.c  | 10 ++
 contrib/pageinspect/expected/brin.out | 13 +
 contrib/pageinspect/expected/btree.out|  5 +
 contrib/pageinspect/expected/checksum.out |  7 +++
 contrib/pageinspect/expected/gin.out  |  7 +++
 contrib/pageinspect/expected/gist.out |  5 +
 contrib/pageinspect/expected/hash.out |  9 +
 contrib/pageinspect/rawpage.c | 10 ++
 contrib/pageinspect/sql/brin.sql  |  6 ++
 contrib/pageinspect/sql/btree.sql |  3 +++
 contrib/pageinspect/sql/checksum.sql  |  3 +++
 contrib/pageinspect/sql/gin.sql   |  5 +
 contrib/pageinspect/sql/gist.sql  |  5 +
 contrib/pageinspect/sql/hash.sql  |  6 ++
 15 files changed, 104 insertions(+)

diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index 50892b5cc20..a2fee13e2d1 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -63,6 +63,10 @@ brin_page_type(PG_FUNCTION_ARGS)
  errdetail("Expected size %d, got %d",
 		   BLCKSZ, raw_page_size)));
 
+	/* check that the page is initialized before accessing any fields */
+	if (PageIsNew(page))
+		PG_RETURN_NULL();
+
 	switch (BrinPageType(page))
 	{
 		case BRIN_PAGETYPE_META:
@@ -103,6 +107,12 @@ verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
 
 	page = VARDATA(raw_page);
 
+	/* check that the page is initialized before accessing any fields */
+	if (PageIsNew(page))
+		ereport(ERROR,
+(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("input page is empty")));
+
 	/* verify the special space says this page is what we want */
 	if (BrinPageType(page) != type)
 		ereport(ERROR,
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 03debe336ba..5ea7af5b998 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -519,6 +519,12 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 		UnlockReleaseBuffer(buffer);
 		relation_close(rel, AccessShareLock);
 
+		/* check that the page is initialized before accessing any fields */
+		if (PageIsNew(uargs->page))
+			ereport(ERROR,
+	(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+	errmsg("input page is empty")));
+
 		uargs->offset = FirstOffsetNumber;
 
 		opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
@@ -615,6 +621,10 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
 
 		uargs->page = VARDATA(raw_page);
 
+		/* check that the page is initialized before accessing any fields */
+		if (PageIsNew(uargs->page))
+			PG_RETURN_NULL();
+
 		uargs->offset = FirstOffsetNumber;
 
 		opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
diff --git a/contrib/pageinspect/expected/brin.out b/contrib/pageinspect/expected/brin.out
index 71eb190380c..2839ee9435b 100644
--- a/contrib/pageinspect/expected/brin.out
+++ b/contrib/pageinspect/expected/brin.out
@@ -48,4 +48,17 @@ SELECT * FROM brin_page_items(get_raw_page('test1_a_idx', 2), 'test1_a_idx')
   1 |  0 |  1 | f| f| f   | {1 .. 1}
 (1 row)
 
+-- Ch

Re: Assert in pageinspect with NULL pages

2022-02-22 Thread Daria Lepikhova



18.02.2022 08:00, Justin Pryzby пишет:

BRIN can also crash if passed a non-brin index.

I've been sitting on this one for awhile.  Feel free to include it in your
patchset.

commit 08010a6037fc4e24a9ba05e5386e766f4310d35e
Author: Justin Pryzby 
Date:   Tue Jan 19 00:25:15 2021 -0600

 pageinspect: brin_page_items(): check that given relation is not only an 
index, but a brin one

diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index f1e64a39ef2..3de6dd943ef 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -16,6 +16,7 @@
  #include "access/brin_tuple.h"
  #include "access/htup_details.h"
  #include "catalog/index.h"
+#include "catalog/pg_am.h"
  #include "catalog/pg_type.h"
  #include "funcapi.h"
  #include "lib/stringinfo.h"
@@ -59,7 +60,7 @@ brin_page_type(PG_FUNCTION_ARGS)
if (raw_page_size != BLCKSZ)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-errmsg("input page too small"),
+errmsg("input page wrong size"),
 errdetail("Expected size %d, got %d",
   BLCKSZ, raw_page_size)));
  
@@ -97,7 +98,7 @@ verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)

if (raw_page_size != BLCKSZ)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-errmsg("input page too small"),
+errmsg("input page wrong size"),
 errdetail("Expected size %d, got %d",
   BLCKSZ, raw_page_size)));
  
@@ -169,7 +170,14 @@ brin_page_items(PG_FUNCTION_ARGS)

MemoryContextSwitchTo(oldcontext);
  
  	indexRel = index_open(indexRelid, AccessShareLock);

-   bdesc = brin_build_desc(indexRel);
+
+   /* Must be a BRIN index */
+   if (indexRel->rd_rel->relkind != RELKIND_INDEX ||
+   indexRel->rd_rel->relam != BRIN_AM_OID)
+   ereport(ERROR,
+   (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+errmsg("\"%s\" is not a BRIN index",
+RelationGetRelationName(indexRel;
  
  	/* minimally verify the page we got */

page = verify_brin_page(raw_page, BRIN_PAGETYPE_REGULAR, "regular");
@@ -178,6 +186,7 @@ brin_page_items(PG_FUNCTION_ARGS)
 * Initialize output functions for all indexed datatypes; simplifies
 * calling them later.
 */
+   bdesc = brin_build_desc(indexRel);
columns = palloc(sizeof(brin_column_state *) * 
RelationGetDescr(indexRel)->natts);
for (attno = 1; attno <= bdesc->bd_tupdesc->natts; attno++)
{



Thanks! This is a very valuable note. I think I will definitely add it 
to the next version of the patch, as soon as I deal with the error 
exception.


--

Daria Lepikhova
Postgres Professional





Assert in pageinspect with NULL pages

2022-02-17 Thread Daria Lepikhova

Hi, hackers!

If we trying to call pageinspect functions for pages which are filled 
with nulls, we will get core dump. It happens with null pages for all 
indexes in pageinspect and for page_checksum. This problem was founded 
firstly by Anastasia Lubennikova, and now I continue this task.


For example, next script leads to fail.
CREATE TABLE test1 (
    x bigserial,
    y bigint DEFAULT 0
);
INSERT INTO test1(y) SELECT 0 FROM generate_series(1,1E6) AS x;
SELECT page_checksum(repeat(E'\\000', 8192)::bytea, 1);

server closed the connection unexpectedly
    This probably means the server terminated abnormally
    before or while processing the request.
fatal: connection to server was lost
LOG:  server process (PID 16465) was terminated by signal 6
DETAIL:  Failed process was running: select 
page_checksum(repeat(E'\\000', 8192)::bytea, 1);

LOG:  terminating any other active server processes
LOG:  all server processes terminated; reinitializing
LOG:  database system was interrupted; last known up at 2022-02-16 
14:03:16 +05
LOG:  database system was not properly shut down; automatic recovery in 
progress

LOG:  redo starts at 0/14F1B20
LOG:  invalid record length at 0/5D40CD8: wanted 24, got 0
LOG:  redo done at 0/5D40BC0 system usage: CPU: user: 0.98 s, system: 
0.02 s, elapsed: 1.01 s

LOG:  checkpoint starting: end-of-recovery immediate wait
LOG:  checkpoint complete: wrote 5500 buffers (33.6%); 0 WAL file(s) 
added, 0 removed, 4 recycled; write=0.064 s, sync=0.007 s, total=0.080 
s; sync files=45, longest=0.004 s, average=0.001 s; distance=74044 kB, 
estimate=74044 kB

LOG:  database system is ready to accept connections

Also it is possible to use
    select brin_metapage_info(repeat(E'\\000', 8192)::bytea);
or
    select bt_page_items(repeat(E'\\000', 8192)::bytea);
for getting fail.

I tried to make some additional checks for null pages into pageinspect' 
functions. The attached patch also contains tests for this case.

What do you think?

--
Daria Lepikhova
Postgres Professional
From 92d71d9b5c583f5f2733646a42c36e7e437ed0a1 Mon Sep 17 00:00:00 2001
From: "d.lepikhova" 
Date: Tue, 15 Feb 2022 14:13:05 +0500
Subject: [PATCH] Add checks for null pages to pageinspect functions

---
 contrib/pageinspect/brinfuncs.c   | 12 
 contrib/pageinspect/btreefuncs.c  | 12 
 contrib/pageinspect/expected/brin.out |  9 +
 contrib/pageinspect/expected/btree.out|  3 +++
 contrib/pageinspect/expected/checksum.out |  3 +++
 contrib/pageinspect/expected/gin.out  |  7 +++
 contrib/pageinspect/expected/hash.out |  9 +
 contrib/pageinspect/rawpage.c | 12 
 contrib/pageinspect/sql/brin.sql  |  6 ++
 contrib/pageinspect/sql/btree.sql |  3 +++
 contrib/pageinspect/sql/checksum.sql  |  4 
 contrib/pageinspect/sql/gin.sql   |  5 +
 contrib/pageinspect/sql/hash.sql  |  6 ++
 13 files changed, 91 insertions(+)

diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index f1e64a39ef2..56129a40abb 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -63,6 +63,12 @@ brin_page_type(PG_FUNCTION_ARGS)
  errdetail("Expected size %d, got %d",
 		   BLCKSZ, raw_page_size)));
 
+	/* check that the page is initialized before accessing any fields */
+	if (PageIsNew(page))
+		ereport(ERROR,
+(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("input page is empty")));
+
 	switch (BrinPageType(page))
 	{
 		case BRIN_PAGETYPE_META:
@@ -103,6 +109,12 @@ verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
 
 	page = VARDATA(raw_page);
 
+	/* check that the page is initialized before accessing any fields */
+	if (PageIsNew(page))
+		ereport(ERROR,
+(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("input page is empty")));
+
 	/* verify the special space says this page is what we want */
 	if (BrinPageType(page) != type)
 		ereport(ERROR,
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 03debe336ba..7af35da19f3 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -519,6 +519,12 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
 		UnlockReleaseBuffer(buffer);
 		relation_close(rel, AccessShareLock);
 
+		/* check that the page is initialized before accessing any fields */
+		if (PageIsNew(uargs->page))
+			ereport(ERROR,
+	(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+	errmsg("input page is empty")));
+
 		uargs->offset = FirstOffsetNumber;
 
 		opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page);
@@ -615,6 +621,12 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
 
 		uargs->page = VARDATA(raw_page);
 
+		/* check that the page is initialized before accessing any fields */
+		if (Page