agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH 01/12] Disallow compressed data inside container types
51+ messages / 3 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ 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; 51+ 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] 51+ messages in thread

* [PATCH v3 2/4] docs: load stylesheet via custom.css.source
@ 2023-03-24 07:13  Andres Freund <[email protected]>
  0 siblings, 0 replies; 51+ messages in thread

From: Andres Freund @ 2023-03-24 07:13 UTC (permalink / raw)

---
 doc/src/sgml/Makefile                    |  4 +---
 doc/src/sgml/meson.build                 | 12 ++++++------
 doc/src/sgml/stylesheet-common.xsl       | 14 ++++++++++++++
 doc/src/sgml/stylesheet-hh.xsl           |  1 -
 doc/src/sgml/stylesheet-html-nochunk.xsl |  3 +++
 doc/src/sgml/stylesheet.css.xml          |  8 ++++++++
 doc/src/sgml/stylesheet.xsl              | 11 -----------
 7 files changed, 32 insertions(+), 21 deletions(-)
 create mode 100644 doc/src/sgml/stylesheet.css.xml

diff --git a/doc/src/sgml/Makefile b/doc/src/sgml/Makefile
index 1b098f983ec..9be480613b4 100644
--- a/doc/src/sgml/Makefile
+++ b/doc/src/sgml/Makefile
@@ -39,7 +39,7 @@ ifndef FOP
 FOP = $(missing) fop
 endif
 
-XMLINCLUDE = --path .
+XMLINCLUDE = --path . --path $(srcdir)
 
 ifdef XMLLINT
 XMLLINT := $(XMLLINT) --nonet
@@ -144,14 +144,12 @@ html: html-stamp
 
 html-stamp: stylesheet.xsl postgres-full.xml $(ALL_IMAGES)
 	$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(XSLTPROC_HTML_FLAGS) $(wordlist 1,2,$^)
-	cp $(srcdir)/stylesheet.css html/
 	touch $@
 
 htmlhelp: htmlhelp-stamp
 
 htmlhelp-stamp: stylesheet-hh.xsl postgres-full.xml $(ALL_IMAGES)
 	$(XSLTPROC) $(XMLINCLUDE) $(XSLTPROCFLAGS) $(wordlist 1,2,$^)
-	cp $(srcdir)/stylesheet.css htmlhelp/
 	touch $@
 
 # single-page HTML
diff --git a/doc/src/sgml/meson.build b/doc/src/sgml/meson.build
index 157b8589e55..d9c46224dc9 100644
--- a/doc/src/sgml/meson.build
+++ b/doc/src/sgml/meson.build
@@ -104,7 +104,7 @@ if xsltproc_bin.found()
   xsltproc_flags = [
     '--nonet',
     '--stringparam', 'pg.version', pg_version,
-    '--param', 'website.stylesheet', '1'
+    '--path', '@CURRENT_SOURCE_DIR@', '--path', '@OUTDIR@',
   ]
 
   xsltproc = xmltools_wrapper + [
@@ -140,7 +140,7 @@ if xsltproc_bin.found()
     output: 'htmlhelp',
     depfile: 'htmlhelp.d',
     depends: doc_generated,
-    command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
+    command: [xsltproc, '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
     build_by_default: false,
   )
   alldocs += htmlhelp
@@ -152,7 +152,7 @@ if xsltproc_bin.found()
     output: 'postgres.html',
     depfile: 'postgres.html.d',
     depends: doc_generated,
-    command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTPUT@', xsltproc_flags, '@INPUT@'],
+    command: [xsltproc, '-o', '@OUTPUT@', xsltproc_flags, '@INPUT@'],
     build_by_default: false,
   )
   alldocs += postgres_html
@@ -180,14 +180,14 @@ if xsltproc_bin.found()
     output: 'INSTALL.xml',
     depfile: 'INSTALL.xml.d',
     depends: doc_generated + [postgres_full_xml],
-    command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTPUT@', xsltproc_flags, '--xinclude', '@INPUT@'],
+    command: [xsltproc, '-o', '@OUTPUT@', xsltproc_flags, '--xinclude', '@INPUT@'],
     build_by_default: false,
   )
   install_html = custom_target('INSTALL.html',
     input: ['stylesheet-text.xsl', install_xml],
     output: 'INSTALL.html',
     depfile: 'INSTALL.html.d',
-    command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTPUT@', xsltproc_flags, '@INPUT@'],
+    command: [xsltproc, '-o', '@OUTPUT@', xsltproc_flags, '@INPUT@'],
     build_by_default: false,
   )
   alldocs += install_html
@@ -216,7 +216,7 @@ if xsltproc_bin.found()
     output: ['man1', 'man3', 'man7'],
     depfile: 'man.d',
     depends: doc_generated,
-    command: [xsltproc, '--path', '@OUTDIR@', '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
+    command: [xsltproc, '-o', '@OUTDIR@/', xsltproc_flags, '@INPUT@'],
     build_by_default: false,
   )
   alldocs += man
diff --git a/doc/src/sgml/stylesheet-common.xsl b/doc/src/sgml/stylesheet-common.xsl
index d2928f86eb7..c1a7d46bcc6 100644
--- a/doc/src/sgml/stylesheet-common.xsl
+++ b/doc/src/sgml/stylesheet-common.xsl
@@ -42,6 +42,20 @@
 <xsl:param name="variablelist.term.separator"></xsl:param>
 <xsl:param name="xref.with.number.and.title" select="0"></xsl:param>
 
+<!--
+    currently htmlhelp and "normal" html have no common stylesheet, so this is
+    implemented here
+-->
+<xsl:param name="website.stylesheet" select="0"/>
+
+<xsl:param name="custom.css.source">
+  <xsl:if test="$website.stylesheet = 0">stylesheet.css.xml</xsl:if>
+</xsl:param>
+
+<xsl:param name="html.stylesheet">
+  <xsl:if test="not($website.stylesheet = 0)">https://www.postgresql.org/media/css/docs-complete.css.xml</xsl:if;
+</xsl:param>
+
 
 <!-- Change display of some elements -->
 
diff --git a/doc/src/sgml/stylesheet-hh.xsl b/doc/src/sgml/stylesheet-hh.xsl
index 568ccf36d2a..fb659d2ec38 100644
--- a/doc/src/sgml/stylesheet-hh.xsl
+++ b/doc/src/sgml/stylesheet-hh.xsl
@@ -9,7 +9,6 @@
 <xsl:param name="htmlhelp.use.hhk" select="'1'"/>
 
 <xsl:param name="base.dir" select="'htmlhelp/'"></xsl:param>
-<xsl:param name="html.stylesheet" select="'stylesheet.css'"></xsl:param>
 <xsl:param name="use.id.as.filename" select="'1'"></xsl:param>
 <xsl:param name="manifest.in.base.dir" select="1"/>
 <xsl:param name="make.valid.html" select="1"></xsl:param>
diff --git a/doc/src/sgml/stylesheet-html-nochunk.xsl b/doc/src/sgml/stylesheet-html-nochunk.xsl
index 8167127b93a..fae8d5fbd96 100644
--- a/doc/src/sgml/stylesheet-html-nochunk.xsl
+++ b/doc/src/sgml/stylesheet-html-nochunk.xsl
@@ -7,6 +7,9 @@
 <xsl:include href="stylesheet-html-common.xsl" />
 <xsl:include href="stylesheet-speedup-xhtml.xsl" />
 
+<!-- embed stylesheet.css if using that -->
+<xsl:param name="generate.css.header" select="$website.stylesheet = 0"/>
+
 <!-- embed SVG images into output file -->
 <xsl:template match="imagedata[@format='SVG']">
   <xsl:variable name="filename">
diff --git a/doc/src/sgml/stylesheet.css.xml b/doc/src/sgml/stylesheet.css.xml
new file mode 100644
index 00000000000..a21fcca576f
--- /dev/null
+++ b/doc/src/sgml/stylesheet.css.xml
@@ -0,0 +1,8 @@
+<!--
+    wrapper around stylesheet.css.xml to allow it to be loaded via docbook-xsl's
+    generate.css.header parameter.
+-->
+<!DOCTYPE style [
+<!ENTITY css SYSTEM "stylesheet.css">
+]>
+<style>&css;</style>
diff --git a/doc/src/sgml/stylesheet.xsl b/doc/src/sgml/stylesheet.xsl
index f9163e7d946..65606ca3dbe 100644
--- a/doc/src/sgml/stylesheet.xsl
+++ b/doc/src/sgml/stylesheet.xsl
@@ -17,17 +17,6 @@
 <xsl:param name="chunk.quietly" select="1"></xsl:param>
 <xsl:param name="admon.style"></xsl:param>  <!-- handled by CSS stylesheet -->
 
-<xsl:param name="website.stylesheet" select="0"/>
-
-<xsl:param name="html.stylesheet">
-  <xsl:choose>
-    <xsl:when test="$website.stylesheet = 0">stylesheet.css</xsl:when>
-    <xsl:otherwise>
-      https://www.postgresql.org/media/css/docs-complete.css
-    </xsl:otherwise>
-  </xsl:choose>
-</xsl:param>
-
 
 <xsl:template match="imageobject">
   <xsl:call-template name="write-image"/>
-- 
2.38.0


--l5hiy5hty64mvsca
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v3-0003-docs-speed-up-docs-build-by-special-casing-the-ge.patch"



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

*  回复: [PATCH] Expose checkpoint reason to completion log messages.
@ 2026-01-08 13:31  li carol <[email protected]>
  0 siblings, 0 replies; 51+ messages in thread

From: li carol @ 2026-01-08 13:31 UTC (permalink / raw)
  To: Soumya S Murali <[email protected]>; Michael Banck <[email protected]>; +Cc: Fujii Masao <[email protected]>; VASUKI M <[email protected]>; Andres Freund <[email protected]>; PostgreSQL Hackers <[email protected]>; Álvaro Herrera <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>

> 
> Hi all,
> 
> On Wed, Jan 7, 2026 at 1:37 PM Michael Banck <[email protected]> wrote:
> >
> > Hi,
> >
> > On Wed, Jan 07, 2026 at 12:35:08PM +0530, Soumya S Murali wrote:
> > > I agree with your point that the checkpoint completion message
> > > should use the same terminology as the starting message ( “fast”
> > > rather than
> > > “immediate”) and I have updated the patch accordingly and have
> > > attached herewith for review. This patch aligns the checkpoint
> > > completion log wording with the existing checkpoint starting message
> > > (using “fast” instead of “immediate”) for consistency.
> >
> > First off, please send complete and not incremental patches.
> >
> > From what I can tell, you currently also do not have "wal" as reason?
> > Also, you have "timed" vs "time" etc.
> >
> > I think it would make most sense to factor out the logic for the
> > starting checkpoint reason from xlog.c ("(errmsg("checkpoint
> > starting:%s%s%s%s%s%s%s%s")") and use that both for starting and
> > stopping.
> >
> >
> > Michael
> 
> Thank you for the review and feedback.
> I have reworked the patch to factor out the checkpoint reason logic to use the
> same wording for both checkpoint start and completion log (including wal, time,
> fast, force, and wait). The format has been aligned with existing conventions
> and has been verified by running make check and recovery TAP tests including
> t/019_replslot_limit.pl successfully.
> Kindly review my patch and let me know the feedback.
> 
> Regards,
> Soumya

Hi Soumya,

Thanks for the updated patch. I agree that having the checkpoint reason in the completion log is very helpful for performance analysis, as it avoids the need to manually correlate "starting" and "complete" messages in busy logs.
I’ve reviewed the latest version and have a few suggestions to make the refactoring more complete:
1. Consistency in LogCheckpointStart
Currently, the checkpoint branch uses the new CheckpointReasonString helper, but the restartpoint branch still uses the old inline triple-operator logic. It would be better to use the helper function in both places to ensure they stay in sync.

2. Missing Reason for Restartpoints 
In LogCheckpointEnd, the checkpoint reason is added to the checkpoint complete message, but the restartpoint complete message remains unchanged. I think users would find the reason for restartpoints just as useful for debugging.

3. Global Variable vs. Passing Flags: Instead of using a file-level global checkpoint_reason_str to pass data between the start and end functions, would it make sense to simply pass the flags as a new argument to LogCheckpointEnd(bool restartpoint, int flags)? 
This would allow you to call the helper function directly inside the ereport calls and avoid maintaining global state.

4. Whitespace Errors: I noticed a few whitespace warnings (space before tabs) when applying the patch. 
/home/carol/0001-Expose-checkpoint-reason-in-checkpoint-completion-lo.patch:69: space before tab in indent.
        	CheckpointReasonString(flags),
/home/carol/0001-Expose-checkpoint-reason-in-checkpoint-completion-lo.patch:70: space before tab in indent.
        	sizeof(checkpoint_reason_str));
/home/carol/0001-Expose-checkpoint-reason-in-checkpoint-completion-lo.patch:88: space before tab in indent.
        		(errmsg("checkpoint starting:%s", checkpoint_reason_str)));
warning: 3 lines add whitespace errors.

The patch can be compiled successfully and can pass make check -C src/test/recovery/

Regards,
Yuan Li(carol)


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


end of thread, other threads:[~2026-01-08 13:31 UTC | newest]

Thread overview: 51+ 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-03-24 07:13 [PATCH v3 2/4] docs: load stylesheet via custom.css.source Andres Freund <[email protected]>
2026-01-08 13:31  回复: [PATCH] Expose checkpoint reason to completion log messages. li carol <[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