($INBOX_DIR/description missing)  
help / color / mirror / Atom feed
[PATCH 01/12] Disallow compressed data inside container types
54+ messages / 4 participants
[nested] [flat]

* [PATCH 01/12] Disallow compressed data inside container types
@ 2021-02-16 13:54 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-02-16 13:54 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the comprassed field but while contructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we onlle decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.
---
 src/backend/access/common/heaptuple.c | 28 ++++++---------------------
 src/backend/access/heap/heaptoast.c   | 16 ++++++++-------
 src/backend/executor/execTuples.c     | 11 -----------
 src/include/access/heaptoast.h        |  4 ++--
 4 files changed, 17 insertions(+), 42 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 24a27e387d..5c3194f96d 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -983,30 +983,14 @@ heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc)
 Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
-	HeapTupleHeader td;
-
-	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
-	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
-											tuple->t_len,
-											tupleDesc);
-
 	/*
-	 * Fast path for easy case: just make a palloc'd copy and insert the
-	 * correct composite-Datum header fields (since those may not be set if
-	 * the given tuple came from disk, rather than from heap_form_tuple).
+	 * The tuple contains compressed/external TOAST pointers, so we have
+	 * to inline those fields to meet the conventions for composite-type
+	 * Datums.
 	 */
-	td = (HeapTupleHeader) palloc(tuple->t_len);
-	memcpy((char *) td, (char *) tuple->t_data, tuple->t_len);
-
-	HeapTupleHeaderSetDatumLength(td, tuple->t_len);
-	HeapTupleHeaderSetTypeId(td, tupleDesc->tdtypeid);
-	HeapTupleHeaderSetTypMod(td, tupleDesc->tdtypmod);
-
-	return PointerGetDatum(td);
+	return toast_flatten_tuple_to_datum(tuple->t_data,
+										tuple->t_len,
+										tupleDesc);
 }
 
 /*
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..dd162daab9 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -549,14 +549,15 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
 /* ----------
  * toast_build_flattened_tuple -
  *
- *	Build a tuple containing no out-of-line toasted fields.
- *	(This does not eliminate compressed or short-header datums.)
+ *	Build a tuple containing no compressed/out-of-line toasted fields.
+ *	(This does not eliminate short-header datums.)
  *
  *	This is essentially just like heap_form_tuple, except that it will
- *	expand any external-data pointers beforehand.
+ *	expand any compressed/external-data pointers beforehand.
  *
- *	It's not very clear whether it would be preferable to decompress
- *	in-line compressed datums while at it.  For now, we don't.
+ *	It is not necessary to decompress the compressed data for the
+ *	correctness, but reflects an expectation that compression will be more
+ *	effective if applied to the whole tuple not individual fields.
  * ----------
  */
 HeapTuple
@@ -589,9 +590,10 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) ||
+				VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..ca7fbed576 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2189,13 +2189,6 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
  * memory context.  Beware of code that changes context between the initial
  * heap_form_tuple/etc call and calling HeapTuple(Header)GetDatum.
  *
- * For performance-critical callers, it could be worthwhile to take extra
- * steps to ensure that there aren't TOAST pointers in the output of
- * heap_form_tuple to begin with.  It's likely however that the costs of the
- * typcache lookup and tuple disassembly/reassembly are swamped by TOAST
- * dereference costs, so that the benefits of such extra effort would be
- * minimal.
- *
  * XXX it would likely be better to create wrapper functions that produce
  * a composite Datum from the field values in one step.  However, there's
  * enough code using the existing APIs that we couldn't get rid of this
@@ -2207,10 +2200,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h
index 8b29f1a986..c80af45012 100644
--- a/src/include/access/heaptoast.h
+++ b/src/include/access/heaptoast.h
@@ -128,8 +128,8 @@ extern Datum toast_flatten_tuple_to_datum(HeapTupleHeader tup,
 /* ----------
  * toast_build_flattened_tuple -
  *
- *	Build a tuple containing no out-of-line toasted fields.
- *	(This does not eliminate compressed or short-header datums.)
+ *	Build a tuple containing no compressed/out-of-line toasted fields.
+ *	(This does not eliminate short-header datums.)
  * ----------
  */
 extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc,
-- 
2.17.0


--9Ek0hoCL9XbhcSqy
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0002-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--cvVnyQ+4j833TQvp
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* [PATCH 3/8] Disallow compressed data inside container types
@ 2021-03-04 11:03 Dilip Kumar <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw)

Currently, we have a general rule that Datums of container types
(rows, arrays, ranges, etc) must not contain any external TOAST
pointers.  But the rule for the compressed data is not defined
and no specific rule is followed e.g. while constructing the array
we decompress the compressed field but while constructing the row
in some cases we don't decompress the compressed data whereas in
the other cases we only decompress while flattening the external
toast pointers.  This patch make a general rule for the compressed
data i.e. we don't allow the compressed data in the container type.

Dilip Kumar based on idea from Robert Haas
---
 src/backend/access/common/heaptuple.c  |  9 +--
 src/backend/access/heap/heaptoast.c    |  4 +-
 src/backend/executor/execExprInterp.c  |  6 +-
 src/backend/executor/execTuples.c      |  4 --
 src/backend/utils/adt/expandedrecord.c | 76 +++++++++-----------------
 src/backend/utils/adt/jsonfuncs.c      |  3 +-
 src/include/funcapi.h                  |  4 +-
 src/pl/plpgsql/src/pl_exec.c           |  3 +-
 8 files changed, 38 insertions(+), 71 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index c36c283253..eb9f016dfa 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -984,15 +984,12 @@ Datum
 heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc)
 {
 	/*
-	 * If the tuple contains any external TOAST pointers, we have to inline
-	 * those fields to meet the conventions for composite-type Datums.
+	 * We have to inline any external/compressed data to meet the conventions
+	 * for composite-type Datums.
 	 */
-	if (HeapTupleHasExternal(tuple))
-		return toast_flatten_tuple_to_datum(tuple->t_data,
+	return toast_flatten_tuple_to_datum(tuple->t_data,
 											tuple->t_len,
 											tupleDesc);
-	else
-		return heap_copy_tuple_as_raw_datum(tuple, tupleDesc);
 }
 
 /* ----------------
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 55bbe1d584..b09462348b 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc,
 			struct varlena *new_value;
 
 			new_value = (struct varlena *) DatumGetPointer(new_values[i]);
-			if (VARATT_IS_EXTERNAL(new_value))
+			if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value))
 			{
-				new_value = detoast_external_attr(new_value);
+				new_value = detoast_attr(new_value);
 				new_values[i] = PointerGetDatum(new_value);
 				freeable_values[num_to_free++] = (Pointer) new_value;
 			}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c3754acca4..71e6f41fee 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op)
 	{
 		Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i);
 
-		if (op->d.row.elemnulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i])))
+		if (op->d.row.elemnulls[i] || attr->attlen != -1)
 			continue;
 
 		op->d.row.elemvalues[i] =
@@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	{
 		Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i);
 
-		if (op->d.fieldstore.nulls[i] || attr->attlen != -1 ||
-			!VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i])))
+		if (op->d.fieldstore.nulls[i] || attr->attlen != -1)
 			continue;
 		op->d.fieldstore.values[i] = PointerGetDatum(
 						PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i]));
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 73c35df9c9..f11546468e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple)
 	Datum		result;
 	TupleDesc	tupDesc;
 
-	/* No work if there are no external TOAST pointers in the tuple */
-	if (!HeapTupleHeaderHasExternal(tuple))
-		return PointerGetDatum(tuple);
-
 	/* Use the type data saved by heap_form_tuple to look up the rowtype */
 	tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple),
 									 HeapTupleHeaderGetTypMod(tuple));
diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c
index e19491ecf7..3cbc256671 100644
--- a/src/backend/utils/adt/expandedrecord.c
+++ b/src/backend/utils/adt/expandedrecord.c
@@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 		erh->er_typmod = tupdesc->tdtypmod;
 	}
 
-	/*
-	 * If we have a valid flattened value without out-of-line fields, we can
-	 * just use it as-is.
-	 */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-		return erh->fvalue->t_len;
-
 	/* If we have a cached size value, believe that */
 	if (erh->flat_size)
 		return erh->flat_size;
@@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr)
 	tupdesc = erh->er_tupdesc;
 
 	/*
-	 * Composite datums mustn't contain any out-of-line values.
+	 * Composite datums mustn't contain any out-of-line/compressed values.
 	 */
-	if (erh->flags & ER_FLAG_HAVE_EXTERNAL)
+	for (i = 0; i < erh->nfields; i++)
 	{
-		for (i = 0; i < erh->nfields; i++)
-		{
-			Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
+		Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
 
-			if (!erh->dnulls[i] &&
-				!attr->attbyval && attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])))
-			{
-				/*
-				 * expanded_record_set_field_internal can do the actual work
-				 * of detoasting.  It needn't recheck domain constraints.
-				 */
-				expanded_record_set_field_internal(erh, i + 1,
-												   erh->dvalues[i], false,
-												   true,
-												   false);
-			}
+		if (!erh->dnulls[i] &&
+			!attr->attbyval && attr->attlen == -1 &&
+			(VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) ||
+			 VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i]))))
+		{
+			/*
+			 * expanded_record_set_field_internal can do the actual work
+			 * of detoasting.  It needn't recheck domain constraints.
+			 */
+			expanded_record_set_field_internal(erh, i + 1,
+												erh->dvalues[i], false,
+												true,
+												false);
 		}
-
-		/*
-		 * We have now removed all external field values, so we can clear the
-		 * flag about them.  This won't cause ER_flatten_into() to mistakenly
-		 * take the fast path, since expanded_record_set_field() will have
-		 * cleared ER_FLAG_FVALUE_VALID.
-		 */
-		erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
 	}
 
+	/*
+	 * We have now removed all external field values, so we can clear the
+	 * flag about them.  This won't cause ER_flatten_into() to mistakenly
+	 * take the fast path, since expanded_record_set_field() will have
+	 * cleared ER_FLAG_FVALUE_VALID.
+	 */
+	erh->flags &= ~ER_FLAG_HAVE_EXTERNAL;
+
 	/* Test if we currently have any null values */
 	hasnull = false;
 	for (i = 0; i < erh->nfields; i++)
@@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr,
 
 	Assert(erh->er_magic == ER_MAGIC);
 
-	/* Easy if we have a valid flattened value without out-of-line fields */
-	if (erh->flags & ER_FLAG_FVALUE_VALID &&
-		!(erh->flags & ER_FLAG_HAVE_EXTERNAL))
-	{
-		Assert(allocated_size == erh->fvalue->t_len);
-		memcpy(tuphdr, erh->fvalue->t_data, allocated_size);
-		/* The original flattened value might not have datum header fields */
-		HeapTupleHeaderSetDatumLength(tuphdr, allocated_size);
-		HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid);
-		HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod);
-		return;
-	}
-
 	/* Else allocation should match previous get_flat_size result */
 	Assert(allocated_size == erh->flat_size);
 
@@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber,
 		if (expand_external)
 		{
 			if (attr->attlen == -1 &&
-				VARATT_IS_EXTERNAL(DatumGetPointer(newValue)))
+				(VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) ||
+				 VARATT_IS_COMPRESSED(DatumGetPointer(newValue))))
 			{
 				/* Detoasting should be done in short-lived context. */
 				oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh));
-				newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue)));
+				newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue)));
 				MemoryContextSwitchTo(oldcxt);
 			}
 			else
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index c3d464f42b..821aa8fbdb 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc,
 										  &field,
 										  &nulls[i]);
 
-		if (!nulls[i] && att->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(values[i])))
+		if (!nulls[i] && att->attlen == -1)
 			values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i]));
 	}
 
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 8ba7ae211f..c869012873 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
  * Macro declarations/inline functions:
  * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as
  * 		HeapTupleHeaderGetDatum but the input tuple should not contain
- * 		external varlena
+ * 		external/compressed varlena
  * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
  * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum
- * 		but the input tuple should not contain external varlena
+ * 		but the input tuple should not contain external/compressed varlena
  *
  * Obsolete routines and macros:
  * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fd073767bc..0519253cbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate,
 						&dvalues[i], &nulls[i]);
 		if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid)
 			return NULL;
-		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 &&
-			VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i])))
+		if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1)
 			dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i]));
 		/* XXX should we insist on typmod match, too? */
 	}
-- 
2.17.0


--C94crkcyjafcjHxo
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-Built-in-compression-method.patch"



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* Re: Add shared buffer hits to pg_stat_io
@ 2023-02-28 12:36 Drouvot, Bertrand <[email protected]>
  2023-03-06 15:38 ` Re: Add shared buffer hits to pg_stat_io Melanie Plageman <[email protected]>
  0 siblings, 1 reply; 54+ messages in thread

From: Drouvot, Bertrand @ 2023-02-28 12:36 UTC (permalink / raw)
  To: Melanie Plageman <[email protected]>; pgsql-hackers; +Cc: Maciek Sakrejda <[email protected]>; Justin Pryzby <[email protected]>; [email protected]; Kyotaro Horiguchi <[email protected]>; Lukas Fittl <[email protected]>; Magnus Hagander <[email protected]>; Alvaro Herrera <[email protected]>

Hi,

On 2/25/23 9:16 PM, Melanie Plageman wrote:
> Hi,
> 
> As suggested in [1], the attached patch adds shared buffer hits to
> pg_stat_io.
> 

Thanks for the patch!

  BufferDesc *
  LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
-                                bool *foundPtr, IOContext *io_context)
+                                bool *foundPtr, IOContext io_context)
  {
         BufferTag       newTag;                 /* identity of requested block */
         LocalBufferLookupEnt *hresult;
@@ -128,14 +128,6 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
         hresult = (LocalBufferLookupEnt *)
                 hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);

-       /*
-        * IO Operations on local buffers are only done in IOCONTEXT_NORMAL. Set
-        * io_context here (instead of after a buffer hit would have returned) for
-        * convenience since we don't have to worry about the overhead of calling
-        * IOContextForStrategy().
-        */
-       *io_context = IOCONTEXT_NORMAL;


It looks like that io_context is not used in LocalBufferAlloc() anymore and then can be removed as an argument.

> 
> I am looking for input as to the order of this column in the view. I
> think it should go after op_bytes since it is not relevant for
> non-block-oriented IO. 

Agree.

> However, I'm not sure what the order of hits,
> evictions, and reuses should be (all have to do with buffers).
> 

I'm not sure there is a strong "correct" ordering but the proposed one looks natural to me.

> While adding this, I noticed that I had made all of the IOOP columns
> int8 in the view, and I was wondering if this is sufficient for hits (I
> imagine you could end up with quite a lot of those).
> 

I think that's ok and bigint is what is already used for pg_statio_user_tables.heap_blks_hit for example.

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com






^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* Re: Add shared buffer hits to pg_stat_io
  2023-02-28 12:36 Re: Add shared buffer hits to pg_stat_io Drouvot, Bertrand <[email protected]>
@ 2023-03-06 15:38 ` Melanie Plageman <[email protected]>
  2023-03-07 15:10   ` Re: Add shared buffer hits to pg_stat_io Drouvot, Bertrand <[email protected]>
  2023-03-07 19:47   ` Re: Add shared buffer hits to pg_stat_io Andres Freund <[email protected]>
  0 siblings, 2 replies; 54+ messages in thread

From: Melanie Plageman @ 2023-03-06 15:38 UTC (permalink / raw)
  To: Drouvot, Bertrand <[email protected]>; +Cc: pgsql-hackers; Maciek Sakrejda <[email protected]>; Justin Pryzby <[email protected]>; [email protected]; Kyotaro Horiguchi <[email protected]>; Lukas Fittl <[email protected]>; Magnus Hagander <[email protected]>; Alvaro Herrera <[email protected]>

Thanks for the review!

On Tue, Feb 28, 2023 at 7:36 AM Drouvot, Bertrand
<[email protected]> wrote:
>   BufferDesc *
>   LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
> -                                bool *foundPtr, IOContext *io_context)
> +                                bool *foundPtr, IOContext io_context)
>   {
>          BufferTag       newTag;                 /* identity of requested block */
>          LocalBufferLookupEnt *hresult;
> @@ -128,14 +128,6 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
>          hresult = (LocalBufferLookupEnt *)
>                  hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
>
> -       /*
> -        * IO Operations on local buffers are only done in IOCONTEXT_NORMAL. Set
> -        * io_context here (instead of after a buffer hit would have returned) for
> -        * convenience since we don't have to worry about the overhead of calling
> -        * IOContextForStrategy().
> -        */
> -       *io_context = IOCONTEXT_NORMAL;
>
>
> It looks like that io_context is not used in LocalBufferAlloc() anymore and then can be removed as an argument.

Good catch. Updated patchset attached.

> > While adding this, I noticed that I had made all of the IOOP columns
> > int8 in the view, and I was wondering if this is sufficient for hits (I
> > imagine you could end up with quite a lot of those).
> >
>
> I think that's ok and bigint is what is already used for pg_statio_user_tables.heap_blks_hit for example.

Ah, I was silly and didn't understand that the SQL type int8 is eight
bytes and not 1. That makes a lot of things make more sense :)

https://www.postgresql.org/docs/current/xfunc-c.html#XFUNC-C-TYPE-TABLE

- Melanie


Attachments:

  [text/x-patch] v2-0002-Track-shared-buffer-hits-in-pg_stat_io.patch (12.0K, ../../CAAKRu_ae5R5PL2XhOkB-ckyv3s7JMojK8YG9xt-JE6ey3YogJA@mail.gmail.com/2-v2-0002-Track-shared-buffer-hits-in-pg_stat_io.patch)
  download | inline diff:
From 17c7104033b027b6fdd5d81b8052858c86412604 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Sat, 25 Feb 2023 14:36:06 -0500
Subject: [PATCH v2 2/2] Track shared buffer hits in pg_stat_io

Count new IOOP_HITs and add "hits" column to pg_stat_io.

Reviewed-by: Bertrand Drouvot <[email protected]>
Discussion: https://www.postgresql.org/message-id/flat/CAAKRu_beMa9Hzih40%3DXPYqhDVz6tsgUGTrhZXRo%3Dunp%2Bszb%3DUA%40mail.gmail.com
---
 doc/src/sgml/monitoring.sgml           | 11 ++++++++
 src/backend/catalog/system_views.sql   |  1 +
 src/backend/storage/buffer/bufmgr.c    | 38 ++++++++++----------------
 src/backend/storage/buffer/localbuf.c  | 11 ++------
 src/backend/utils/activity/pgstat_io.c |  2 +-
 src/backend/utils/adt/pgstatfuncs.c    |  3 ++
 src/include/catalog/pg_proc.dat        |  6 ++--
 src/include/pgstat.h                   |  1 +
 src/include/storage/buf_internals.h    |  2 +-
 src/test/regress/expected/rules.out    |  3 +-
 10 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 6249bb50d0..8b34ca60bc 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3855,6 +3855,17 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
       </entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry">
+       <para role="column_definition">
+        <structfield>hits</structfield> <type>bigint</type>
+       </para>
+       <para>
+        The number of times a desired block was found in a shared buffer.
+       </para>
+      </entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry">
        <para role="column_definition">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 34ca0e739f..87bbbdfcb3 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1126,6 +1126,7 @@ SELECT
        b.writes,
        b.extends,
        b.op_bytes,
+       b.hits,
        b.evictions,
        b.reuses,
        b.fsyncs,
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 0a05577b68..05fd3c9a2a 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -472,7 +472,7 @@ static BufferDesc *BufferAlloc(SMgrRelation smgr,
 							   ForkNumber forkNum,
 							   BlockNumber blockNum,
 							   BufferAccessStrategy strategy,
-							   bool *foundPtr, IOContext *io_context);
+							   bool *foundPtr, IOContext io_context);
 static void FlushBuffer(BufferDesc *buf, SMgrRelation reln,
 						IOObject io_object, IOContext io_context);
 static void FindAndDropRelationBuffers(RelFileLocator rlocator,
@@ -850,13 +850,14 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	if (isLocalBuf)
 	{
 		/*
-		 * LocalBufferAlloc() will set the io_context to IOCONTEXT_NORMAL. We
-		 * do not use a BufferAccessStrategy for I/O of temporary tables.
+		 * We do not use a BufferAccessStrategy for I/O of temporary tables.
 		 * However, in some cases, the "strategy" may not be NULL, so we can't
 		 * rely on IOContextForStrategy() to set the right IOContext for us.
 		 * This may happen in cases like CREATE TEMPORARY TABLE AS...
 		 */
-		bufHdr = LocalBufferAlloc(smgr, forkNum, blockNum, &found, &io_context);
+		io_context = IOCONTEXT_NORMAL;
+		io_object = IOOBJECT_TEMP_RELATION;
+		bufHdr = LocalBufferAlloc(smgr, forkNum, blockNum, &found);
 		if (found)
 			pgBufferUsage.local_blks_hit++;
 		else if (isExtend)
@@ -871,8 +872,10 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 		 * lookup the buffer.  IO_IN_PROGRESS is set if the requested block is
 		 * not currently in memory.
 		 */
+		io_context = IOContextForStrategy(strategy);
+		io_object = IOOBJECT_RELATION;
 		bufHdr = BufferAlloc(smgr, relpersistence, forkNum, blockNum,
-							 strategy, &found, &io_context);
+							 strategy, &found, io_context);
 		if (found)
 			pgBufferUsage.shared_blks_hit++;
 		else if (isExtend)
@@ -892,6 +895,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 			/* Just need to update stats before we exit */
 			*hit = true;
 			VacuumPageHit++;
+			pgstat_count_io_op(io_object, io_context, IOOP_HIT);
 
 			if (VacuumCostActive)
 				VacuumCostBalance += VacuumCostPageHit;
@@ -987,16 +991,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	 */
 	Assert(!(pg_atomic_read_u32(&bufHdr->state) & BM_VALID));	/* spinlock not needed */
 
-	if (isLocalBuf)
-	{
-		bufBlock = LocalBufHdrGetBlock(bufHdr);
-		io_object = IOOBJECT_TEMP_RELATION;
-	}
-	else
-	{
-		bufBlock = BufHdrGetBlock(bufHdr);
-		io_object = IOOBJECT_RELATION;
-	}
+	bufBlock = isLocalBuf ? LocalBufHdrGetBlock(bufHdr) : BufHdrGetBlock(bufHdr);
 
 	if (isExtend)
 	{
@@ -1139,7 +1134,7 @@ static BufferDesc *
 BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 			BlockNumber blockNum,
 			BufferAccessStrategy strategy,
-			bool *foundPtr, IOContext *io_context)
+			bool *foundPtr, IOContext io_context)
 {
 	bool		from_ring;
 	BufferTag	newTag;			/* identity of requested block */
@@ -1193,11 +1188,8 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 			{
 				/*
 				 * If we get here, previous attempts to read the buffer must
-				 * have failed ... but we shall bravely try again. Set
-				 * io_context since we will in fact need to count an IO
-				 * Operation.
+				 * have failed ... but we shall bravely try again.
 				 */
-				*io_context = IOContextForStrategy(strategy);
 				*foundPtr = false;
 			}
 		}
@@ -1211,8 +1203,6 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	 */
 	LWLockRelease(newPartitionLock);
 
-	*io_context = IOContextForStrategy(strategy);
-
 	/* Loop here in case we have to try another victim buffer */
 	for (;;)
 	{
@@ -1295,7 +1285,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 														  smgr->smgr_rlocator.locator.dbOid,
 														  smgr->smgr_rlocator.locator.relNumber);
 
-				FlushBuffer(buf, NULL, IOOBJECT_RELATION, *io_context);
+				FlushBuffer(buf, NULL, IOOBJECT_RELATION, io_context);
 				LWLockRelease(BufferDescriptorGetContentLock(buf));
 
 				ScheduleBufferTagForWriteback(&BackendWritebackContext,
@@ -1494,7 +1484,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 		 * we may have been forced to release the buffer due to concurrent
 		 * pinners or erroring out.
 		 */
-		pgstat_count_io_op(IOOBJECT_RELATION, *io_context,
+		pgstat_count_io_op(IOOBJECT_RELATION, io_context,
 						   from_ring ? IOOP_REUSE : IOOP_EVICT);
 	}
 
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 5325ddb663..880c9909b9 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -108,7 +108,7 @@ PrefetchLocalBuffer(SMgrRelation smgr, ForkNumber forkNum,
  */
 BufferDesc *
 LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
-				 bool *foundPtr, IOContext *io_context)
+				 bool *foundPtr)
 {
 	BufferTag	newTag;			/* identity of requested block */
 	LocalBufferLookupEnt *hresult;
@@ -128,14 +128,6 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 	hresult = (LocalBufferLookupEnt *)
 		hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
 
-	/*
-	 * IO Operations on local buffers are only done in IOCONTEXT_NORMAL. Set
-	 * io_context here (instead of after a buffer hit would have returned) for
-	 * convenience since we don't have to worry about the overhead of calling
-	 * IOContextForStrategy().
-	 */
-	*io_context = IOCONTEXT_NORMAL;
-
 	if (hresult)
 	{
 		b = hresult->id;
@@ -239,6 +231,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 		buf_state &= ~BM_DIRTY;
 		pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
 
+		/* Temporary table I/O does not use Buffer Access Strategies */
 		pgstat_count_io_op(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL, IOOP_WRITE);
 		pgBufferUsage.local_blks_written++;
 	}
diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.c
index af5d554610..ae8bb34f78 100644
--- a/src/backend/utils/activity/pgstat_io.c
+++ b/src/backend/utils/activity/pgstat_io.c
@@ -344,7 +344,7 @@ pgstat_tracks_io_op(BackendType bktype, IOObject io_object,
 	 * Some BackendTypes will not do certain IOOps.
 	 */
 	if ((bktype == B_BG_WRITER || bktype == B_CHECKPOINTER) &&
-		(io_op == IOOP_READ || io_op == IOOP_EVICT))
+		(io_op == IOOP_READ || io_op == IOOP_EVICT || io_op == IOOP_HIT))
 		return false;
 
 	if ((bktype == B_AUTOVAC_LAUNCHER || bktype == B_BG_WRITER ||
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 1112bc2904..e2e79eca99 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1258,6 +1258,7 @@ typedef enum io_stat_col
 	IO_COL_WRITES,
 	IO_COL_EXTENDS,
 	IO_COL_CONVERSION,
+	IO_COL_HITS,
 	IO_COL_EVICTIONS,
 	IO_COL_REUSES,
 	IO_COL_FSYNCS,
@@ -1280,6 +1281,8 @@ pgstat_get_io_op_index(IOOp io_op)
 			return IO_COL_EXTENDS;
 		case IOOP_FSYNC:
 			return IO_COL_FSYNCS;
+		case IOOP_HIT:
+			return IO_COL_HITS;
 		case IOOP_READ:
 			return IO_COL_READS;
 		case IOOP_REUSE:
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 505595620e..615139c2f9 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5721,9 +5721,9 @@
   proname => 'pg_stat_get_io', provolatile => 'v',
   prorows => '30', proretset => 't',
   proparallel => 'r', prorettype => 'record', proargtypes => '',
-  proallargtypes => '{text,text,text,int8,int8,int8,int8,int8,int8,int8,timestamptz}',
-  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{backend_type,io_object,io_context,reads,writes,extends,op_bytes,evictions,reuses,fsyncs,stats_reset}',
+  proallargtypes => '{text,text,text,int8,int8,int8,int8,int8,int8,int8,int8,timestamptz}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{backend_type,io_object,io_context,reads,writes,extends,op_bytes,hits,evictions,reuses,fsyncs,stats_reset}',
   prosrc => 'pg_stat_get_io' },
 
 { oid => '1136', descr => 'statistics: information about WAL activity',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index f43fac09ed..a7f77c17b3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -304,6 +304,7 @@ typedef enum IOOp
 	IOOP_EVICT,
 	IOOP_EXTEND,
 	IOOP_FSYNC,
+	IOOP_HIT,
 	IOOP_READ,
 	IOOP_REUSE,
 	IOOP_WRITE,
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 0b44814740..2afb9bb309 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -419,7 +419,7 @@ extern PrefetchBufferResult PrefetchLocalBuffer(SMgrRelation smgr,
 												ForkNumber forkNum,
 												BlockNumber blockNum);
 extern BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum,
-									BlockNumber blockNum, bool *foundPtr, IOContext *io_context);
+									BlockNumber blockNum, bool *foundPtr);
 extern void MarkLocalBufferDirty(Buffer buffer);
 extern void DropRelationLocalBuffers(RelFileLocator rlocator,
 									 ForkNumber forkNum,
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e953d1f515..c994da9f97 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1883,11 +1883,12 @@ pg_stat_io| SELECT backend_type,
     writes,
     extends,
     op_bytes,
+    hits,
     evictions,
     reuses,
     fsyncs,
     stats_reset
-   FROM pg_stat_get_io() b(backend_type, io_object, io_context, reads, writes, extends, op_bytes, evictions, reuses, fsyncs, stats_reset);
+   FROM pg_stat_get_io() b(backend_type, io_object, io_context, reads, writes, extends, op_bytes, hits, evictions, reuses, fsyncs, stats_reset);
 pg_stat_progress_analyze| SELECT s.pid,
     s.datid,
     d.datname,
-- 
2.37.2



  [text/x-patch] v2-0001-Reorder-pgstatfuncs-local-enum.patch (1.0K, ../../CAAKRu_ae5R5PL2XhOkB-ckyv3s7JMojK8YG9xt-JE6ey3YogJA@mail.gmail.com/3-v2-0001-Reorder-pgstatfuncs-local-enum.patch)
  download | inline diff:
From f552046b5fdc100adf6b80adabc236bfac73e21a Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Sat, 25 Feb 2023 14:37:25 -0500
Subject: [PATCH v2 1/2] Reorder pgstatfuncs-local enum

---
 src/backend/utils/adt/pgstatfuncs.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index b61a12382b..1112bc2904 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1276,16 +1276,16 @@ pgstat_get_io_op_index(IOOp io_op)
 	{
 		case IOOP_EVICT:
 			return IO_COL_EVICTIONS;
+		case IOOP_EXTEND:
+			return IO_COL_EXTENDS;
+		case IOOP_FSYNC:
+			return IO_COL_FSYNCS;
 		case IOOP_READ:
 			return IO_COL_READS;
 		case IOOP_REUSE:
 			return IO_COL_REUSES;
 		case IOOP_WRITE:
 			return IO_COL_WRITES;
-		case IOOP_EXTEND:
-			return IO_COL_EXTENDS;
-		case IOOP_FSYNC:
-			return IO_COL_FSYNCS;
 	}
 
 	elog(ERROR, "unrecognized IOOp value: %d", io_op);
-- 
2.37.2



^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* Re: Add shared buffer hits to pg_stat_io
  2023-02-28 12:36 Re: Add shared buffer hits to pg_stat_io Drouvot, Bertrand <[email protected]>
  2023-03-06 15:38 ` Re: Add shared buffer hits to pg_stat_io Melanie Plageman <[email protected]>
@ 2023-03-07 15:10   ` Drouvot, Bertrand <[email protected]>
  1 sibling, 0 replies; 54+ messages in thread

From: Drouvot, Bertrand @ 2023-03-07 15:10 UTC (permalink / raw)
  To: Melanie Plageman <[email protected]>; +Cc: pgsql-hackers; Maciek Sakrejda <[email protected]>; Justin Pryzby <[email protected]>; [email protected]; Kyotaro Horiguchi <[email protected]>; Lukas Fittl <[email protected]>; Magnus Hagander <[email protected]>; Alvaro Herrera <[email protected]>

Hi,

On 3/6/23 4:38 PM, Melanie Plageman wrote:
> Thanks for the review!
> 
> On Tue, Feb 28, 2023 at 7:36 AM Drouvot, Bertrand
> <[email protected]> wrote:
>>    BufferDesc *
>>    LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
>> -                                bool *foundPtr, IOContext *io_context)
>> +                                bool *foundPtr, IOContext io_context)
>>    {
>>           BufferTag       newTag;                 /* identity of requested block */
>>           LocalBufferLookupEnt *hresult;
>> @@ -128,14 +128,6 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
>>           hresult = (LocalBufferLookupEnt *)
>>                   hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
>>
>> -       /*
>> -        * IO Operations on local buffers are only done in IOCONTEXT_NORMAL. Set
>> -        * io_context here (instead of after a buffer hit would have returned) for
>> -        * convenience since we don't have to worry about the overhead of calling
>> -        * IOContextForStrategy().
>> -        */
>> -       *io_context = IOCONTEXT_NORMAL;
>>
>>
>> It looks like that io_context is not used in LocalBufferAlloc() anymore and then can be removed as an argument.
> 
> Good catch. Updated patchset attached.

Thanks for the update!

> 
>>> While adding this, I noticed that I had made all of the IOOP columns
>>> int8 in the view, and I was wondering if this is sufficient for hits (I
>>> imagine you could end up with quite a lot of those).
>>>
>>
>> I think that's ok and bigint is what is already used for pg_statio_user_tables.heap_blks_hit for example.
> 
> Ah, I was silly and didn't understand that the SQL type int8 is eight
> bytes and not 1. That makes a lot of things make more sense :)

Oh, I see ;-)

I may give it another review but currently V2 looks good to me.

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com






^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* Re: Add shared buffer hits to pg_stat_io
  2023-02-28 12:36 Re: Add shared buffer hits to pg_stat_io Drouvot, Bertrand <[email protected]>
  2023-03-06 15:38 ` Re: Add shared buffer hits to pg_stat_io Melanie Plageman <[email protected]>
@ 2023-03-07 19:47   ` Andres Freund <[email protected]>
  2023-03-08 18:44     ` Re: Add shared buffer hits to pg_stat_io Melanie Plageman <[email protected]>
  1 sibling, 1 reply; 54+ messages in thread

From: Andres Freund @ 2023-03-07 19:47 UTC (permalink / raw)
  To: Melanie Plageman <[email protected]>; +Cc: Drouvot, Bertrand <[email protected]>; pgsql-hackers; Maciek Sakrejda <[email protected]>; Justin Pryzby <[email protected]>; [email protected]; Kyotaro Horiguchi <[email protected]>; Lukas Fittl <[email protected]>; Magnus Hagander <[email protected]>; Alvaro Herrera <[email protected]>

Hi,

LGTM. The only comment I have is that a small test wouldn't hurt... Compared
to the other things it should be fairly easy...

Greetings,

Andres Freund






^ permalink  raw  reply  [nested|flat] 54+ messages in thread

* Re: Add shared buffer hits to pg_stat_io
  2023-02-28 12:36 Re: Add shared buffer hits to pg_stat_io Drouvot, Bertrand <[email protected]>
  2023-03-06 15:38 ` Re: Add shared buffer hits to pg_stat_io Melanie Plageman <[email protected]>
  2023-03-07 19:47   ` Re: Add shared buffer hits to pg_stat_io Andres Freund <[email protected]>
@ 2023-03-08 18:44     ` Melanie Plageman <[email protected]>
  0 siblings, 0 replies; 54+ messages in thread

From: Melanie Plageman @ 2023-03-08 18:44 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Drouvot, Bertrand <[email protected]>; pgsql-hackers; Maciek Sakrejda <[email protected]>; Justin Pryzby <[email protected]>; [email protected]; Kyotaro Horiguchi <[email protected]>; Lukas Fittl <[email protected]>; Magnus Hagander <[email protected]>; Alvaro Herrera <[email protected]>

On Tue, Mar 7, 2023 at 2:47 PM Andres Freund <[email protected]> wrote:
>
> Hi,
>
> LGTM. The only comment I have is that a small test wouldn't hurt... Compared
> to the other things it should be fairly easy...

So, I have attached an updated patchset which adds a test for hits. Since
there is only one call site where we count hits, I think this single
test is sufficient to protect against regressions.

However, I am concerned that, while unlikely, this could be flakey.
Something could happen to force all of those blocks out of shared
buffers (even though they were just read in) before we hit them.

We could simply check if hits are greater at the end of all of the
pg_stat_io tests than at the beginning and rely on the fact that it is
highly unlikely that every single buffer access will be a miss for all
of the tests. However, is it not technically also possible to have zero
hits?

- Melanie


Attachments:

  [text/x-patch] v3-0002-Track-shared-buffer-hits-in-pg_stat_io.patch (16.4K, ../../CAAKRu_ZBauWVzOGkjsM3dwLv5WQLfQA=Ht5=_raaBZ9kaaSPJw@mail.gmail.com/2-v3-0002-Track-shared-buffer-hits-in-pg_stat_io.patch)
  download | inline diff:
From cab8e0435b2a8390887f99091b2e9d336dd353c0 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Sat, 25 Feb 2023 14:36:06 -0500
Subject: [PATCH v3 2/2] Track shared buffer hits in pg_stat_io

Count new IOOP_HITs and add "hits" column to pg_stat_io.

Reviewed-by: Bertrand Drouvot <[email protected]>
Discussion: https://www.postgresql.org/message-id/flat/CAAKRu_beMa9Hzih40%3DXPYqhDVz6tsgUGTrhZXRo%3Dunp%2Bszb%3DUA%40mail.gmail.com
---
 doc/src/sgml/monitoring.sgml           | 11 ++++++++
 src/backend/catalog/system_views.sql   |  1 +
 src/backend/storage/buffer/bufmgr.c    | 38 ++++++++++----------------
 src/backend/storage/buffer/localbuf.c  | 11 ++------
 src/backend/utils/activity/pgstat_io.c |  2 +-
 src/backend/utils/adt/pgstatfuncs.c    |  3 ++
 src/include/catalog/pg_proc.dat        |  6 ++--
 src/include/pgstat.h                   |  1 +
 src/include/storage/buf_internals.h    |  2 +-
 src/test/regress/expected/rules.out    |  3 +-
 src/test/regress/expected/stats.out    | 27 ++++++++++++++++--
 src/test/regress/sql/stats.sql         | 14 ++++++++--
 12 files changed, 76 insertions(+), 43 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 6249bb50d0..8b34ca60bc 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3855,6 +3855,17 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
       </entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry">
+       <para role="column_definition">
+        <structfield>hits</structfield> <type>bigint</type>
+       </para>
+       <para>
+        The number of times a desired block was found in a shared buffer.
+       </para>
+      </entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry">
        <para role="column_definition">
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 34ca0e739f..87bbbdfcb3 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1126,6 +1126,7 @@ SELECT
        b.writes,
        b.extends,
        b.op_bytes,
+       b.hits,
        b.evictions,
        b.reuses,
        b.fsyncs,
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 0a05577b68..05fd3c9a2a 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -472,7 +472,7 @@ static BufferDesc *BufferAlloc(SMgrRelation smgr,
 							   ForkNumber forkNum,
 							   BlockNumber blockNum,
 							   BufferAccessStrategy strategy,
-							   bool *foundPtr, IOContext *io_context);
+							   bool *foundPtr, IOContext io_context);
 static void FlushBuffer(BufferDesc *buf, SMgrRelation reln,
 						IOObject io_object, IOContext io_context);
 static void FindAndDropRelationBuffers(RelFileLocator rlocator,
@@ -850,13 +850,14 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	if (isLocalBuf)
 	{
 		/*
-		 * LocalBufferAlloc() will set the io_context to IOCONTEXT_NORMAL. We
-		 * do not use a BufferAccessStrategy for I/O of temporary tables.
+		 * We do not use a BufferAccessStrategy for I/O of temporary tables.
 		 * However, in some cases, the "strategy" may not be NULL, so we can't
 		 * rely on IOContextForStrategy() to set the right IOContext for us.
 		 * This may happen in cases like CREATE TEMPORARY TABLE AS...
 		 */
-		bufHdr = LocalBufferAlloc(smgr, forkNum, blockNum, &found, &io_context);
+		io_context = IOCONTEXT_NORMAL;
+		io_object = IOOBJECT_TEMP_RELATION;
+		bufHdr = LocalBufferAlloc(smgr, forkNum, blockNum, &found);
 		if (found)
 			pgBufferUsage.local_blks_hit++;
 		else if (isExtend)
@@ -871,8 +872,10 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 		 * lookup the buffer.  IO_IN_PROGRESS is set if the requested block is
 		 * not currently in memory.
 		 */
+		io_context = IOContextForStrategy(strategy);
+		io_object = IOOBJECT_RELATION;
 		bufHdr = BufferAlloc(smgr, relpersistence, forkNum, blockNum,
-							 strategy, &found, &io_context);
+							 strategy, &found, io_context);
 		if (found)
 			pgBufferUsage.shared_blks_hit++;
 		else if (isExtend)
@@ -892,6 +895,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 			/* Just need to update stats before we exit */
 			*hit = true;
 			VacuumPageHit++;
+			pgstat_count_io_op(io_object, io_context, IOOP_HIT);
 
 			if (VacuumCostActive)
 				VacuumCostBalance += VacuumCostPageHit;
@@ -987,16 +991,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	 */
 	Assert(!(pg_atomic_read_u32(&bufHdr->state) & BM_VALID));	/* spinlock not needed */
 
-	if (isLocalBuf)
-	{
-		bufBlock = LocalBufHdrGetBlock(bufHdr);
-		io_object = IOOBJECT_TEMP_RELATION;
-	}
-	else
-	{
-		bufBlock = BufHdrGetBlock(bufHdr);
-		io_object = IOOBJECT_RELATION;
-	}
+	bufBlock = isLocalBuf ? LocalBufHdrGetBlock(bufHdr) : BufHdrGetBlock(bufHdr);
 
 	if (isExtend)
 	{
@@ -1139,7 +1134,7 @@ static BufferDesc *
 BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 			BlockNumber blockNum,
 			BufferAccessStrategy strategy,
-			bool *foundPtr, IOContext *io_context)
+			bool *foundPtr, IOContext io_context)
 {
 	bool		from_ring;
 	BufferTag	newTag;			/* identity of requested block */
@@ -1193,11 +1188,8 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 			{
 				/*
 				 * If we get here, previous attempts to read the buffer must
-				 * have failed ... but we shall bravely try again. Set
-				 * io_context since we will in fact need to count an IO
-				 * Operation.
+				 * have failed ... but we shall bravely try again.
 				 */
-				*io_context = IOContextForStrategy(strategy);
 				*foundPtr = false;
 			}
 		}
@@ -1211,8 +1203,6 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 	 */
 	LWLockRelease(newPartitionLock);
 
-	*io_context = IOContextForStrategy(strategy);
-
 	/* Loop here in case we have to try another victim buffer */
 	for (;;)
 	{
@@ -1295,7 +1285,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 														  smgr->smgr_rlocator.locator.dbOid,
 														  smgr->smgr_rlocator.locator.relNumber);
 
-				FlushBuffer(buf, NULL, IOOBJECT_RELATION, *io_context);
+				FlushBuffer(buf, NULL, IOOBJECT_RELATION, io_context);
 				LWLockRelease(BufferDescriptorGetContentLock(buf));
 
 				ScheduleBufferTagForWriteback(&BackendWritebackContext,
@@ -1494,7 +1484,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
 		 * we may have been forced to release the buffer due to concurrent
 		 * pinners or erroring out.
 		 */
-		pgstat_count_io_op(IOOBJECT_RELATION, *io_context,
+		pgstat_count_io_op(IOOBJECT_RELATION, io_context,
 						   from_ring ? IOOP_REUSE : IOOP_EVICT);
 	}
 
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 5325ddb663..880c9909b9 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -108,7 +108,7 @@ PrefetchLocalBuffer(SMgrRelation smgr, ForkNumber forkNum,
  */
 BufferDesc *
 LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
-				 bool *foundPtr, IOContext *io_context)
+				 bool *foundPtr)
 {
 	BufferTag	newTag;			/* identity of requested block */
 	LocalBufferLookupEnt *hresult;
@@ -128,14 +128,6 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 	hresult = (LocalBufferLookupEnt *)
 		hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
 
-	/*
-	 * IO Operations on local buffers are only done in IOCONTEXT_NORMAL. Set
-	 * io_context here (instead of after a buffer hit would have returned) for
-	 * convenience since we don't have to worry about the overhead of calling
-	 * IOContextForStrategy().
-	 */
-	*io_context = IOCONTEXT_NORMAL;
-
 	if (hresult)
 	{
 		b = hresult->id;
@@ -239,6 +231,7 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
 		buf_state &= ~BM_DIRTY;
 		pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
 
+		/* Temporary table I/O does not use Buffer Access Strategies */
 		pgstat_count_io_op(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL, IOOP_WRITE);
 		pgBufferUsage.local_blks_written++;
 	}
diff --git a/src/backend/utils/activity/pgstat_io.c b/src/backend/utils/activity/pgstat_io.c
index af5d554610..ae8bb34f78 100644
--- a/src/backend/utils/activity/pgstat_io.c
+++ b/src/backend/utils/activity/pgstat_io.c
@@ -344,7 +344,7 @@ pgstat_tracks_io_op(BackendType bktype, IOObject io_object,
 	 * Some BackendTypes will not do certain IOOps.
 	 */
 	if ((bktype == B_BG_WRITER || bktype == B_CHECKPOINTER) &&
-		(io_op == IOOP_READ || io_op == IOOP_EVICT))
+		(io_op == IOOP_READ || io_op == IOOP_EVICT || io_op == IOOP_HIT))
 		return false;
 
 	if ((bktype == B_AUTOVAC_LAUNCHER || bktype == B_BG_WRITER ||
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 1112bc2904..e2e79eca99 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1258,6 +1258,7 @@ typedef enum io_stat_col
 	IO_COL_WRITES,
 	IO_COL_EXTENDS,
 	IO_COL_CONVERSION,
+	IO_COL_HITS,
 	IO_COL_EVICTIONS,
 	IO_COL_REUSES,
 	IO_COL_FSYNCS,
@@ -1280,6 +1281,8 @@ pgstat_get_io_op_index(IOOp io_op)
 			return IO_COL_EXTENDS;
 		case IOOP_FSYNC:
 			return IO_COL_FSYNCS;
+		case IOOP_HIT:
+			return IO_COL_HITS;
 		case IOOP_READ:
 			return IO_COL_READS;
 		case IOOP_REUSE:
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 505595620e..615139c2f9 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5721,9 +5721,9 @@
   proname => 'pg_stat_get_io', provolatile => 'v',
   prorows => '30', proretset => 't',
   proparallel => 'r', prorettype => 'record', proargtypes => '',
-  proallargtypes => '{text,text,text,int8,int8,int8,int8,int8,int8,int8,timestamptz}',
-  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{backend_type,io_object,io_context,reads,writes,extends,op_bytes,evictions,reuses,fsyncs,stats_reset}',
+  proallargtypes => '{text,text,text,int8,int8,int8,int8,int8,int8,int8,int8,timestamptz}',
+  proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{backend_type,io_object,io_context,reads,writes,extends,op_bytes,hits,evictions,reuses,fsyncs,stats_reset}',
   prosrc => 'pg_stat_get_io' },
 
 { oid => '1136', descr => 'statistics: information about WAL activity',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index f43fac09ed..a7f77c17b3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -304,6 +304,7 @@ typedef enum IOOp
 	IOOP_EVICT,
 	IOOP_EXTEND,
 	IOOP_FSYNC,
+	IOOP_HIT,
 	IOOP_READ,
 	IOOP_REUSE,
 	IOOP_WRITE,
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 0b44814740..2afb9bb309 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -419,7 +419,7 @@ extern PrefetchBufferResult PrefetchLocalBuffer(SMgrRelation smgr,
 												ForkNumber forkNum,
 												BlockNumber blockNum);
 extern BufferDesc *LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum,
-									BlockNumber blockNum, bool *foundPtr, IOContext *io_context);
+									BlockNumber blockNum, bool *foundPtr);
 extern void MarkLocalBufferDirty(Buffer buffer);
 extern void DropRelationLocalBuffers(RelFileLocator rlocator,
 									 ForkNumber forkNum,
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index e953d1f515..c994da9f97 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1883,11 +1883,12 @@ pg_stat_io| SELECT backend_type,
     writes,
     extends,
     op_bytes,
+    hits,
     evictions,
     reuses,
     fsyncs,
     stats_reset
-   FROM pg_stat_get_io() b(backend_type, io_object, io_context, reads, writes, extends, op_bytes, evictions, reuses, fsyncs, stats_reset);
+   FROM pg_stat_get_io() b(backend_type, io_object, io_context, reads, writes, extends, op_bytes, hits, evictions, reuses, fsyncs, stats_reset);
 pg_stat_progress_analyze| SELECT s.pid,
     s.datid,
     d.datname,
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 186c296299..9dc66ef027 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -1131,6 +1131,7 @@ SELECT pg_stat_get_subscription_stats(NULL);
 -- - writes of shared buffers to permanent storage
 -- - extends of relations using shared buffers
 -- - fsyncs done to ensure the durability of data dirtying shared buffers
+-- - shared buffer hits
 -- There is no test for blocks evicted from shared buffers, because we cannot
 -- be sure of the state of shared buffers at the point the test is run.
 -- Create a regular table and insert some data to generate IOCONTEXT_NORMAL
@@ -1208,6 +1209,28 @@ SELECT :io_sum_shared_after_reads > :io_sum_shared_before_reads;
  t
 (1 row)
 
+SELECT sum(hits) AS io_sum_shared_before_hits
+  FROM pg_stat_io WHERE io_context = 'normal' AND io_object = 'relation' \gset
+SELECT COUNT(*) FROM test_io_shared;
+ count 
+-------
+   100
+(1 row)
+
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+SELECT sum(hits) AS io_sum_shared_after_hits
+  FROM pg_stat_io WHERE io_context = 'normal' AND io_object = 'relation' \gset
+SELECT :io_sum_shared_after_hits > :io_sum_shared_before_hits;
+ ?column? 
+----------
+ t
+(1 row)
+
 DROP TABLE test_io_shared;
 -- Test that the follow IOCONTEXT_LOCAL IOOps are tracked in pg_stat_io:
 -- - eviction of local buffers in order to reuse them
@@ -1342,7 +1365,7 @@ SELECT pg_stat_have_stats('io', 0, 0);
  t
 (1 row)
 
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) AS io_stats_pre_reset
+SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(hits) AS io_stats_pre_reset
   FROM pg_stat_io \gset
 SELECT pg_stat_reset_shared('io');
  pg_stat_reset_shared 
@@ -1350,7 +1373,7 @@ SELECT pg_stat_reset_shared('io');
  
 (1 row)
 
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) AS io_stats_post_reset
+SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(hits) AS io_stats_post_reset
   FROM pg_stat_io \gset
 SELECT :io_stats_post_reset < :io_stats_pre_reset;
  ?column? 
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index d7f873cfc9..b7f0aafe4f 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -541,6 +541,7 @@ SELECT pg_stat_get_subscription_stats(NULL);
 -- - writes of shared buffers to permanent storage
 -- - extends of relations using shared buffers
 -- - fsyncs done to ensure the durability of data dirtying shared buffers
+-- - shared buffer hits
 
 -- There is no test for blocks evicted from shared buffers, because we cannot
 -- be sure of the state of shared buffers at the point the test is run.
@@ -588,6 +589,15 @@ SELECT pg_stat_force_next_flush();
 SELECT sum(reads) AS io_sum_shared_after_reads
   FROM pg_stat_io WHERE io_context = 'normal' AND io_object = 'relation'  \gset
 SELECT :io_sum_shared_after_reads > :io_sum_shared_before_reads;
+
+SELECT sum(hits) AS io_sum_shared_before_hits
+  FROM pg_stat_io WHERE io_context = 'normal' AND io_object = 'relation' \gset
+SELECT COUNT(*) FROM test_io_shared;
+SELECT pg_stat_force_next_flush();
+SELECT sum(hits) AS io_sum_shared_after_hits
+  FROM pg_stat_io WHERE io_context = 'normal' AND io_object = 'relation' \gset
+SELECT :io_sum_shared_after_hits > :io_sum_shared_before_hits;
+
 DROP TABLE test_io_shared;
 
 -- Test that the follow IOCONTEXT_LOCAL IOOps are tracked in pg_stat_io:
@@ -675,10 +685,10 @@ SELECT :io_sum_bulkwrite_strategy_extends_after > :io_sum_bulkwrite_strategy_ext
 
 -- Test IO stats reset
 SELECT pg_stat_have_stats('io', 0, 0);
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) AS io_stats_pre_reset
+SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(hits) AS io_stats_pre_reset
   FROM pg_stat_io \gset
 SELECT pg_stat_reset_shared('io');
-SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) AS io_stats_post_reset
+SELECT sum(evictions) + sum(reuses) + sum(extends) + sum(fsyncs) + sum(reads) + sum(writes) + sum(hits) AS io_stats_post_reset
   FROM pg_stat_io \gset
 SELECT :io_stats_post_reset < :io_stats_pre_reset;
 
-- 
2.37.2



  [text/x-patch] v3-0001-Reorder-pgstatfuncs-local-enum.patch (1.0K, ../../CAAKRu_ZBauWVzOGkjsM3dwLv5WQLfQA=Ht5=_raaBZ9kaaSPJw@mail.gmail.com/3-v3-0001-Reorder-pgstatfuncs-local-enum.patch)
  download | inline diff:
From 23bb19c6cbdbf4dcfba53a01395d55b151d5a1f3 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Sat, 25 Feb 2023 14:37:25 -0500
Subject: [PATCH v3 1/2] Reorder pgstatfuncs-local enum

---
 src/backend/utils/adt/pgstatfuncs.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index b61a12382b..1112bc2904 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1276,16 +1276,16 @@ pgstat_get_io_op_index(IOOp io_op)
 	{
 		case IOOP_EVICT:
 			return IO_COL_EVICTIONS;
+		case IOOP_EXTEND:
+			return IO_COL_EXTENDS;
+		case IOOP_FSYNC:
+			return IO_COL_FSYNCS;
 		case IOOP_READ:
 			return IO_COL_READS;
 		case IOOP_REUSE:
 			return IO_COL_REUSES;
 		case IOOP_WRITE:
 			return IO_COL_WRITES;
-		case IOOP_EXTEND:
-			return IO_COL_EXTENDS;
-		case IOOP_FSYNC:
-			return IO_COL_FSYNCS;
 	}
 
 	elog(ERROR, "unrecognized IOOp value: %d", io_op);
-- 
2.37.2



^ permalink  raw  reply  [nested|flat] 54+ messages in thread


end of thread, other threads:[~2023-03-08 18:44 UTC | newest]

Thread overview: 54+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-02-16 13:54 [PATCH 01/12] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2021-03-04 11:03 [PATCH 3/8] Disallow compressed data inside container types Dilip Kumar <[email protected]>
2023-02-28 12:36 Re: Add shared buffer hits to pg_stat_io Drouvot, Bertrand <[email protected]>
2023-03-06 15:38 ` Re: Add shared buffer hits to pg_stat_io Melanie Plageman <[email protected]>
2023-03-07 15:10   ` Re: Add shared buffer hits to pg_stat_io Drouvot, Bertrand <[email protected]>
2023-03-07 19:47   ` Re: Add shared buffer hits to pg_stat_io Andres Freund <[email protected]>
2023-03-08 18:44     ` Re: Add shared buffer hits to pg_stat_io Melanie Plageman <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox