public inbox for [email protected]help / color / mirror / Atom feed
[PATCH 01/12] Disallow compressed data inside container types 54+ 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; 54+ messages in thread From: Dilip Kumar @ 2021-02-16 13:54 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the comprassed field but while contructing the row in some cases we don't decompress the compressed data whereas in the other cases we onlle decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. --- src/backend/access/common/heaptuple.c | 28 ++++++--------------------- src/backend/access/heap/heaptoast.c | 16 ++++++++------- src/backend/executor/execTuples.c | 11 ----------- src/include/access/heaptoast.h | 4 ++-- 4 files changed, 17 insertions(+), 42 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 24a27e387d..5c3194f96d 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -983,30 +983,14 @@ heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc) Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { - HeapTupleHeader td; - - /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. - */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, - tuple->t_len, - tupleDesc); - /* - * Fast path for easy case: just make a palloc'd copy and insert the - * correct composite-Datum header fields (since those may not be set if - * the given tuple came from disk, rather than from heap_form_tuple). + * The tuple contains compressed/external TOAST pointers, so we have + * to inline those fields to meet the conventions for composite-type + * Datums. */ - td = (HeapTupleHeader) palloc(tuple->t_len); - memcpy((char *) td, (char *) tuple->t_data, tuple->t_len); - - HeapTupleHeaderSetDatumLength(td, tuple->t_len); - HeapTupleHeaderSetTypeId(td, tupleDesc->tdtypeid); - HeapTupleHeaderSetTypMod(td, tupleDesc->tdtypmod); - - return PointerGetDatum(td); + return toast_flatten_tuple_to_datum(tuple->t_data, + tuple->t_len, + tupleDesc); } /* diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..dd162daab9 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -549,14 +549,15 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup, /* ---------- * toast_build_flattened_tuple - * - * Build a tuple containing no out-of-line toasted fields. - * (This does not eliminate compressed or short-header datums.) + * Build a tuple containing no compressed/out-of-line toasted fields. + * (This does not eliminate short-header datums.) * * This is essentially just like heap_form_tuple, except that it will - * expand any external-data pointers beforehand. + * expand any compressed/external-data pointers beforehand. * - * It's not very clear whether it would be preferable to decompress - * in-line compressed datums while at it. For now, we don't. + * It is not necessary to decompress the compressed data for the + * correctness, but reflects an expectation that compression will be more + * effective if applied to the whole tuple not individual fields. * ---------- */ HeapTuple @@ -589,9 +590,10 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || + VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..ca7fbed576 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2189,13 +2189,6 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) * memory context. Beware of code that changes context between the initial * heap_form_tuple/etc call and calling HeapTuple(Header)GetDatum. * - * For performance-critical callers, it could be worthwhile to take extra - * steps to ensure that there aren't TOAST pointers in the output of - * heap_form_tuple to begin with. It's likely however that the costs of the - * typcache lookup and tuple disassembly/reassembly are swamped by TOAST - * dereference costs, so that the benefits of such extra effort would be - * minimal. - * * XXX it would likely be better to create wrapper functions that produce * a composite Datum from the field values in one step. However, there's * enough code using the existing APIs that we couldn't get rid of this @@ -2207,10 +2200,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h index 8b29f1a986..c80af45012 100644 --- a/src/include/access/heaptoast.h +++ b/src/include/access/heaptoast.h @@ -128,8 +128,8 @@ extern Datum toast_flatten_tuple_to_datum(HeapTupleHeader tup, /* ---------- * toast_build_flattened_tuple - * - * Build a tuple containing no out-of-line toasted fields. - * (This does not eliminate compressed or short-header datums.) + * Build a tuple containing no compressed/out-of-line toasted fields. + * (This does not eliminate short-header datums.) * ---------- */ extern HeapTuple toast_build_flattened_tuple(TupleDesc tupleDesc, -- 2.17.0 --9Ek0hoCL9XbhcSqy Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0002-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --cvVnyQ+4j833TQvp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* [PATCH 3/8] Disallow compressed data inside container types @ 2021-03-04 11:03 Dilip Kumar <[email protected]> 0 siblings, 0 replies; 54+ messages in thread From: Dilip Kumar @ 2021-03-04 11:03 UTC (permalink / raw) Currently, we have a general rule that Datums of container types (rows, arrays, ranges, etc) must not contain any external TOAST pointers. But the rule for the compressed data is not defined and no specific rule is followed e.g. while constructing the array we decompress the compressed field but while constructing the row in some cases we don't decompress the compressed data whereas in the other cases we only decompress while flattening the external toast pointers. This patch make a general rule for the compressed data i.e. we don't allow the compressed data in the container type. Dilip Kumar based on idea from Robert Haas --- src/backend/access/common/heaptuple.c | 9 +-- src/backend/access/heap/heaptoast.c | 4 +- src/backend/executor/execExprInterp.c | 6 +- src/backend/executor/execTuples.c | 4 -- src/backend/utils/adt/expandedrecord.c | 76 +++++++++----------------- src/backend/utils/adt/jsonfuncs.c | 3 +- src/include/funcapi.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 3 +- 8 files changed, 38 insertions(+), 71 deletions(-) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index c36c283253..eb9f016dfa 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -984,15 +984,12 @@ Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) { /* - * If the tuple contains any external TOAST pointers, we have to inline - * those fields to meet the conventions for composite-type Datums. + * We have to inline any external/compressed data to meet the conventions + * for composite-type Datums. */ - if (HeapTupleHasExternal(tuple)) - return toast_flatten_tuple_to_datum(tuple->t_data, + return toast_flatten_tuple_to_datum(tuple->t_data, tuple->t_len, tupleDesc); - else - return heap_copy_tuple_as_raw_datum(tuple, tupleDesc); } /* ---------------- diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c index 55bbe1d584..b09462348b 100644 --- a/src/backend/access/heap/heaptoast.c +++ b/src/backend/access/heap/heaptoast.c @@ -589,9 +589,9 @@ toast_build_flattened_tuple(TupleDesc tupleDesc, struct varlena *new_value; new_value = (struct varlena *) DatumGetPointer(new_values[i]); - if (VARATT_IS_EXTERNAL(new_value)) + if (VARATT_IS_EXTERNAL(new_value) || VARATT_IS_COMPRESSED(new_value)) { - new_value = detoast_external_attr(new_value); + new_value = detoast_attr(new_value); new_values[i] = PointerGetDatum(new_value); freeable_values[num_to_free++] = (Pointer) new_value; } diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index c3754acca4..71e6f41fee 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2845,8 +2845,7 @@ ExecEvalRow(ExprState *state, ExprEvalStep *op) { Form_pg_attribute attr = TupleDescAttr(op->d.row.tupdesc, i); - if (op->d.row.elemnulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.row.elemvalues[i]))) + if (op->d.row.elemnulls[i] || attr->attlen != -1) continue; op->d.row.elemvalues[i] = @@ -3103,8 +3102,7 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext { Form_pg_attribute attr = TupleDescAttr(*op->d.fieldstore.argdesc, i); - if (op->d.fieldstore.nulls[i] || attr->attlen != -1 || - !VARATT_IS_EXTERNAL(DatumGetPointer(op->d.fieldstore.values[i]))) + if (op->d.fieldstore.nulls[i] || attr->attlen != -1) continue; op->d.fieldstore.values[i] = PointerGetDatum( PG_DETOAST_DATUM_PACKED(op->d.fieldstore.values[i])); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 73c35df9c9..f11546468e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -2207,10 +2207,6 @@ HeapTupleHeaderGetDatum(HeapTupleHeader tuple) Datum result; TupleDesc tupDesc; - /* No work if there are no external TOAST pointers in the tuple */ - if (!HeapTupleHeaderHasExternal(tuple)) - return PointerGetDatum(tuple); - /* Use the type data saved by heap_form_tuple to look up the rowtype */ tupDesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(tuple), HeapTupleHeaderGetTypMod(tuple)); diff --git a/src/backend/utils/adt/expandedrecord.c b/src/backend/utils/adt/expandedrecord.c index e19491ecf7..3cbc256671 100644 --- a/src/backend/utils/adt/expandedrecord.c +++ b/src/backend/utils/adt/expandedrecord.c @@ -673,14 +673,6 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) erh->er_typmod = tupdesc->tdtypmod; } - /* - * If we have a valid flattened value without out-of-line fields, we can - * just use it as-is. - */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - return erh->fvalue->t_len; - /* If we have a cached size value, believe that */ if (erh->flat_size) return erh->flat_size; @@ -693,38 +685,36 @@ ER_get_flat_size(ExpandedObjectHeader *eohptr) tupdesc = erh->er_tupdesc; /* - * Composite datums mustn't contain any out-of-line values. + * Composite datums mustn't contain any out-of-line/compressed values. */ - if (erh->flags & ER_FLAG_HAVE_EXTERNAL) + for (i = 0; i < erh->nfields; i++) { - for (i = 0; i < erh->nfields; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); + Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - if (!erh->dnulls[i] && - !attr->attbyval && attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i]))) - { - /* - * expanded_record_set_field_internal can do the actual work - * of detoasting. It needn't recheck domain constraints. - */ - expanded_record_set_field_internal(erh, i + 1, - erh->dvalues[i], false, - true, - false); - } + if (!erh->dnulls[i] && + !attr->attbyval && attr->attlen == -1 && + (VARATT_IS_EXTERNAL(DatumGetPointer(erh->dvalues[i])) || + VARATT_IS_COMPRESSED(DatumGetPointer(erh->dvalues[i])))) + { + /* + * expanded_record_set_field_internal can do the actual work + * of detoasting. It needn't recheck domain constraints. + */ + expanded_record_set_field_internal(erh, i + 1, + erh->dvalues[i], false, + true, + false); } - - /* - * We have now removed all external field values, so we can clear the - * flag about them. This won't cause ER_flatten_into() to mistakenly - * take the fast path, since expanded_record_set_field() will have - * cleared ER_FLAG_FVALUE_VALID. - */ - erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; } + /* + * We have now removed all external field values, so we can clear the + * flag about them. This won't cause ER_flatten_into() to mistakenly + * take the fast path, since expanded_record_set_field() will have + * cleared ER_FLAG_FVALUE_VALID. + */ + erh->flags &= ~ER_FLAG_HAVE_EXTERNAL; + /* Test if we currently have any null values */ hasnull = false; for (i = 0; i < erh->nfields; i++) @@ -770,19 +760,6 @@ ER_flatten_into(ExpandedObjectHeader *eohptr, Assert(erh->er_magic == ER_MAGIC); - /* Easy if we have a valid flattened value without out-of-line fields */ - if (erh->flags & ER_FLAG_FVALUE_VALID && - !(erh->flags & ER_FLAG_HAVE_EXTERNAL)) - { - Assert(allocated_size == erh->fvalue->t_len); - memcpy(tuphdr, erh->fvalue->t_data, allocated_size); - /* The original flattened value might not have datum header fields */ - HeapTupleHeaderSetDatumLength(tuphdr, allocated_size); - HeapTupleHeaderSetTypeId(tuphdr, erh->er_typeid); - HeapTupleHeaderSetTypMod(tuphdr, erh->er_typmod); - return; - } - /* Else allocation should match previous get_flat_size result */ Assert(allocated_size == erh->flat_size); @@ -1155,11 +1132,12 @@ expanded_record_set_field_internal(ExpandedRecordHeader *erh, int fnumber, if (expand_external) { if (attr->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(newValue))) + (VARATT_IS_EXTERNAL(DatumGetPointer(newValue)) || + VARATT_IS_COMPRESSED(DatumGetPointer(newValue)))) { /* Detoasting should be done in short-lived context. */ oldcxt = MemoryContextSwitchTo(get_short_term_cxt(erh)); - newValue = PointerGetDatum(detoast_external_attr((struct varlena *) DatumGetPointer(newValue))); + newValue = PointerGetDatum(detoast_attr((struct varlena *) DatumGetPointer(newValue))); MemoryContextSwitchTo(oldcxt); } else diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index c3d464f42b..821aa8fbdb 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3388,8 +3388,7 @@ populate_record(TupleDesc tupdesc, &field, &nulls[i]); - if (!nulls[i] && att->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(values[i]))) + if (!nulls[i] && att->attlen == -1) values[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(values[i])); } diff --git a/src/include/funcapi.h b/src/include/funcapi.h index 8ba7ae211f..c869012873 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -208,10 +208,10 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); * Macro declarations/inline functions: * HeapTupleHeaderGetRawDatum(HeapTupleHeader tuple) - same as * HeapTupleHeaderGetDatum but the input tuple should not contain - * external varlena + * external/compressed varlena * HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum. * HeapTupleGetRawDatum(HeapTuple tuple) - same as HeapTupleGetDatum - * but the input tuple should not contain external varlena + * but the input tuple should not contain external/compressed varlena * * Obsolete routines and macros: * TupleDesc RelationNameGetTupleDesc(const char *relname) - Use to get a diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd073767bc..0519253cbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -7300,8 +7300,7 @@ make_tuple_from_row(PLpgSQL_execstate *estate, &dvalues[i], &nulls[i]); if (fieldtypeid != TupleDescAttr(tupdesc, i)->atttypid) return NULL; - if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1 && - VARATT_IS_EXTERNAL(DatumGetPointer(dvalues[i]))) + if (!nulls[i] && TupleDescAttr(tupdesc, i)->attlen == -1) dvalues[i] = PointerGetDatum(PG_DETOAST_DATUM_PACKED(dvalues[i])); /* XXX should we insist on typmod match, too? */ } -- 2.17.0 --C94crkcyjafcjHxo Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0004-Built-in-compression-method.patch" ^ permalink raw reply [nested|flat] 54+ messages in thread
* Re: Patch: Improve Boolean Predicate JSON Path Docs @ 2023-10-15 23:04 David E. Wheeler <[email protected]> 2023-10-16 03:03 ` Re: Patch: Improve Boolean Predicate JSON Path Docs Erik Wienhold <[email protected]> 0 siblings, 1 reply; 54+ messages in thread From: David E. Wheeler @ 2023-10-15 23:04 UTC (permalink / raw) To: Erik Wienhold <[email protected]>; +Cc: [email protected] On Oct 14, 2023, at 19:51, Erik Wienhold <[email protected]> wrote: > Thanks for putting this together. See my review at the end. Appreciate the speedy review! > Nice. This really does help to make some sense of it. I checked all > queries and they do work out except for two queries where the path > expression string is not properly quoted (but the intended output is > still correct). 🤦🏻♂️ >> Follow-ups I’d like to make: >> >> 1. Expand the modes section to show how the types of results can vary >> depending on the mode, thanks to the flattening. Examples: >> >> david=# select jsonb_path_query('{"a":[1,2,3,4,5]}', '$.a ?(@[*] > 2)'); >> jsonb_path_query >> ------------------ >> 3 >> 4 >> 5 >> (3 rows) >> >> david=# select jsonb_path_query('{"a":[1,2,3,4,5]}', 'strict $.a ?(@[*] > 2)'); >> jsonb_path_query >> ------------------ >> [1, 2, 3, 4, 5] >> >> 2. Improve the descriptions and examples for @?/jsonb_path_exists() >> and @@/jsonb_path_match(). > > +1 I planned to submit these changes in a separate patch, based on Tom Lane’s suggestion[1]. Would it be preferred to add them to this patch? > Perhaps make it explicit that the reader must run this in psql in order > to use \set and :'json' in the ensuing samples? Some of the existing > examples already use psql output but they do not rely on any psql > features. Good call, done. > This should use <screen>, <userinput>, and <computeroutput> if it shows > a psql session, e.g.: > > <screen> > <userinput>select jsonb_path_query(:'json', '$.track.segments');</userinput> > <computeroutput> > jsonb_path_query > ------------------------------------------------------------------------------------------------------------------------------------------------------------------- > [{"HR": 73, "location": [47.763, 13.4034], "start time": "2018-10-14 10:05:14"}, {"HR": 135, "location": [47.706, 13.2635], "start time": "2018-10-14 10:39:21"}] > </computeroutput> > </screen> I pokwds around, and it appears the computeroutput bit is used for function output. So I followed the precedent in queries.sgml[2] and omitted the computeroutput tags but added prompt, e.g., <screen> <prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'strict $.**.HR');</userinput> jsonb_path_query ------------------ 73 135 </screen> > Also the cast to jsonb is not necessary and only adds clutter IMO. Right, removed them all in function calls. >> + <para> >> + Predicate-only path expressions are necessary for implementation of the >> + <literal>@@</literal> operator (and the >> + <function>jsonb_path_match</function> function), and should not be used >> + with the <literal>@?</literal> operator (or >> + <function>jsonb_path_exists</function> function). >> + </para> >> + >> + <para> >> + Conversely, non-predicate <type>jsonpath</type> expressions should not be >> + used with the <literal>@@</literal> operator (or the >> + <function>jsonb_path_match</function> function). >> + </para> >> + </sect4> > > Both paras should be wrapped in a single <note> so that they stand out > from the rest of the text. Maybe even <warning>, but <note> is already > used on this page for things that I'd consider warnings. Agreed. Would be good if we could teach these functions and operators to reject path expressions they don’t support. >> + <sect4 id="jsonpath-regular-expression-deviation"> >> + <title>Regular Expression Interpretation</title> >> + <para> >> + There are minor differences in the interpretation of regular >> + expression patterns used in <literal>like_regex</literal> filters, as >> + described in <xref linkend="jsonpath-regular-expressions"/>. >> + </para> >> + </sect4> > > <sect3 id="devations-from-the-standard"> should be closed here, > otherwise the docs won't build. This can be checked with > `make -C doc/src/sgml check`. Thanks. That produces a bunch of warnings for postgres.sgml and legal.sgml (and a failure to load the docbook DTD), but func.sgml is clean now. > `git diff --check` shows a couple of lines with trailing whitespace > (mostly psql output). I must’ve cleaned those after I sent the patch, good now. Updated patch attached, this time created by `git format-patch -v2`. Best, David [1] https://www.postgresql.org/message-id/1229727.1680535592%40sss.pgh.pa.us [2] https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-JOIN Attachments: [application/octet-stream] v2-0001-Improve-boolean-predicate-JSON-Path-docs.patch (13.3K, ../../[email protected]/2-v2-0001-Improve-boolean-predicate-JSON-Path-docs.patch) download | inline diff: From d0ededc16eee7f879eefe4f726921bee8644b51b Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" <[email protected]> Date: Sat, 14 Oct 2023 16:42:58 -0400 Subject: [PATCH v2] Improve boolean predicate JSON Path docs Following up from a suggestion from Tom Lane[1] to improve the documentation of boolean predicate JSON path expressions, please find enclosed a draft patch to do so. It does three things: 1. Converts all of the example path queries to use `jsonb_path_query()` and show the results, to make it clearer what the behaviors are. 2. Replaces the list of deviations from the standards with a new subsection, with each deviation in its own sub-subsection. The regex section is unchanged, but I've greatly expanded the boolean expression JSON path section with examples comparing standard filter expressions and nonstandard boolean predicates. I've also added an exhortation not use boolean expressions with @? or standard path expressions with @@. 3. While converting the modes section to use `jsonb_path_query()` and show the results, I also added an example of strict mode returning an error. --- doc/src/sgml/func.sgml | 224 ++++++++++++++++++++++++++++------------- 1 file changed, 154 insertions(+), 70 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index affd1254bb..a2bfc12312 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17203,9 +17203,12 @@ array w/o UK? | t <para> For example, suppose you have some JSON data from a GPS tracker that you - would like to parse, such as: + would like to parse, such as this JSON, set up as a + <link linkend="app-psql-meta-command-set"><application>psql</application> + <command>\set</command> variable</link> for use as <literal>:'json'</literal> + in the examples below: <programlisting> -{ + \set json '{ "track": { "segments": [ { @@ -17220,7 +17223,7 @@ array w/o UK? | t } ] } -} +}' </programlisting> </para> @@ -17228,9 +17231,13 @@ array w/o UK? | t To retrieve the available track segments, you need to use the <literal>.<replaceable>key</replaceable></literal> accessor operator to descend through surrounding JSON objects: -<programlisting> -$.track.segments -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments');</userinput> +select jsonb_path_query(:'json', '$.track.segments'); + jsonb_path_query +------------------------------------------------------------------------------------------------------------------------------------------------------------------- + [{"HR": 73, "location": [47.763, 13.4034], "start time": "2018-10-14 10:05:14"}, {"HR": 135, "location": [47.706, 13.2635], "start time": "2018-10-14 10:39:21"}] +</screen> </para> <para> @@ -17238,18 +17245,25 @@ $.track.segments <literal>[*]</literal> operator. For example, the following path will return the location coordinates for all the available track segments: -<programlisting> -$.track.segments[*].location -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments[*].location');</userinput> + jsonb_path_query +------------------- + [47.763, 13.4034] + [47.706, 13.2635] +</screen> </para> <para> To return the coordinates of the first segment only, you can specify the corresponding subscript in the <literal>[]</literal> accessor operator. Recall that JSON array indexes are 0-relative: -<programlisting> -$.track.segments[0].location -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'strict $.track.segments[0].location');</userinput> + jsonb_path_query +------------------- + [47.763, 13.4034] +</screen> </para> <para> @@ -17258,9 +17272,12 @@ $.track.segments[0].location listed in <xref linkend="functions-sqljson-path-operators"/>. Each method name must be preceded by a dot. For example, you can get the size of an array: -<programlisting> -$.track.segments.size() -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'strict $.track.segments.size()');</userinput> + jsonb_path_query +------------------ + 2 +</screen> More examples of using <type>jsonpath</type> operators and methods within path expressions appear below in <xref linkend="functions-sqljson-path-operators"/>. @@ -17301,9 +17318,12 @@ $.track.segments.size() <para> For example, suppose you would like to retrieve all heart rate values higher than 130. You can achieve this using the following expression: -<programlisting> -$.track.segments[*].HR ? (@ > 130) -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments[*].HR ? (@ > 130)');</userinput> + jsonb_path_query +------------------ + 135 +</screen> </para> <para> @@ -17311,65 +17331,108 @@ $.track.segments[*].HR ? (@ > 130) filter out irrelevant segments before returning the start times, so the filter expression is applied to the previous step, and the path used in the condition is different: -<programlisting> -$.track.segments[*] ? (@.HR > 130)."start time" -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments[*] ? (@.HR > 130)."start time"');</userinput> + jsonb_path_query +----------------------- + "2018-10-14 10:39:21" +</screen> </para> <para> You can use several filter expressions in sequence, if required. For example, the following expression selects start times of all segments that contain locations with relevant coordinates and high heart rate values: -<programlisting> -$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time" -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"');</userinput> + jsonb_path_query +----------------------- + "2018-10-14 10:39:21" +</screen> </para> <para> Using filter expressions at different nesting levels is also allowed. The following example first filters all segments by location, and then returns high heart rate values for these segments, if available: -<programlisting> -$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130) -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)');</userinput> + jsonb_path_query +------------------ + 135 +</screen> </para> <para> You can also nest filter expressions within each other: -<programlisting> -$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size() -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()');</userinput> + jsonb_path_query +------------------ + 2 +</screen> This expression returns the size of the track if it contains any segments with high heart rate values, or an empty sequence otherwise. </para> - <para> - <productname>PostgreSQL</productname>'s implementation of the SQL/JSON path - language has the following deviations from the SQL/JSON standard: - </para> + <sect3 id="deviations-from-the-standard"> + <title>Deviations from the SQL Standard</title> + <para> + <productname>PostgreSQL</productname>'s implementation of the SQL/JSON path + language has the following deviations from the SQL/JSON standard. + </para> - <itemizedlist> - <listitem> + <sect4 id="boolean-predicate-path-expressions"> + <title>Boolean Predicate Path Expressions</title> <para> - A path expression can be a Boolean predicate, although the SQL/JSON - standard allows predicates only in filters. This is necessary for - implementation of the <literal>@@</literal> operator. For example, - the following <type>jsonpath</type> expression is valid in - <productname>PostgreSQL</productname>: -<programlisting> -$.track.segments[*].HR < 70 -</programlisting> - </para> - </listitem> + As an extension to the SQL standard, a <productname>PostgreSQL</productname> + path expression can be a Boolean predicate, whereas the SQL standard allows + predicates only in filters. Where SQL standard path expressions return the + relevant contents of the queried JSON value, predicate path expressions + return the three-valued result of the predicate: <literal>true</literal>, + <literal>false</literal>, or <literal>unknown</literal>. Compare this + filter <type>jsonpath</type> expression: +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments ?(@[*].HR > 130)');</userinput> + jsonb_path_query +--------------------------------------------------------------------------------- + {"HR": 135, "location": [47.706, 13.2635], "start time": "2018-10-14 10:39:21"} +</screen> + To a predicate expression, which returns <literal>true</literal> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments[*].HR > 130');</userinput> + jsonb_path_query +------------------ + true +</screen> + </para> - <listitem> - <para> - There are minor differences in the interpretation of regular - expression patterns used in <literal>like_regex</literal> filters, as - described in <xref linkend="jsonpath-regular-expressions"/>. - </para> - </listitem> - </itemizedlist> + <note> + <para> + Predicate-only path expressions are necessary for implementation of the + <literal>@@</literal> operator (and the + <function>jsonb_path_match</function> function), and should not be used + with the <literal>@?</literal> operator (or + <function>jsonb_path_exists</function> function). + </para> + + <para> + Conversely, non-predicate <type>jsonpath</type> expressions should not be + used with the <literal>@@</literal> operator (or the + <function>jsonb_path_match</function> function). + </para> + </note> + </sect4> + + <sect4 id="jsonpath-regular-expression-deviation"> + <title>Regular Expression Interpretation</title> + <para> + There are minor differences in the interpretation of regular + expression patterns used in <literal>like_regex</literal> filters, as + described in <xref linkend="jsonpath-regular-expressions"/>. + </para> + </sect4> + </sect3> <sect3 id="strict-and-lax-modes"> <title>Strict and Lax Modes</title> @@ -17430,40 +17493,61 @@ $.track.segments[*].HR < 70 For example, when querying the GPS data listed above, you can abstract from the fact that it stores an array of segments when using the lax mode: -<programlisting> -lax $.track.segments.location -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'lax $.track.segments.location');</userinput> + jsonb_path_query +------------------- + [47.763, 13.4034] + [47.706, 13.2635] +</screen> </para> <para> - In the strict mode, the specified path must exactly match the structure of + In strict mode, the specified path must exactly match the structure of the queried JSON document to return an SQL/JSON item, so using this - path expression will cause an error. To get the same result as in - the lax mode, you have to explicitly unwrap the + path expression will cause an error: +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'strict $.track.segments.location');</userinput> +ERROR: jsonpath member accessor can only be applied to an object +</screen> + To get the same result as in the lax mode, you have to explicitly unwrap the <literal>segments</literal> array: -<programlisting> -strict $.track.segments[*].location -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'strict $.track.segments[*].location');</userinput> + jsonb_path_query +------------------- + [47.763, 13.4034] + [47.706, 13.2635] +</screen> </para> <para> The <literal>.**</literal> accessor can lead to surprising results when using the lax mode. For instance, the following query selects every <literal>HR</literal> value twice: -<programlisting> -lax $.**.HR -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'lax $.**.HR');</userinput> + jsonb_path_query +------------------ + 73 + 135 + 73 + 135 +</screen> This happens because the <literal>.**</literal> accessor selects both the <literal>segments</literal> array and each of its elements, while the <literal>.HR</literal> accessor automatically unwraps arrays when using the lax mode. To avoid surprising results, we recommend using the <literal>.**</literal> accessor only in the strict mode. The following query selects each <literal>HR</literal> value just once: -<programlisting> -strict $.**.HR -</programlisting> +<screen> +<prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'strict $.**.HR');</userinput> + jsonb_path_query +------------------ + 73 + 135 +</screen> </para> - </sect3> <sect3 id="functions-sqljson-path-operators"> -- 2.42.0 ^ permalink raw reply [nested|flat] 54+ messages in thread
* Re: Patch: Improve Boolean Predicate JSON Path Docs 2023-10-15 23:04 Re: Patch: Improve Boolean Predicate JSON Path Docs David E. Wheeler <[email protected]> @ 2023-10-16 03:03 ` Erik Wienhold <[email protected]> 2023-10-16 19:59 ` Re: Patch: Improve Boolean Predicate JSON Path Docs David E. Wheeler <[email protected]> 0 siblings, 1 reply; 54+ messages in thread From: Erik Wienhold @ 2023-10-16 03:03 UTC (permalink / raw) To: David E. Wheeler <[email protected]>; +Cc: [email protected] On 2023-10-16 01:04 +0200, David E. Wheeler write: > On Oct 14, 2023, at 19:51, Erik Wienhold <[email protected]> wrote: > > > Thanks for putting this together. See my review at the end. > > Appreciate the speedy review! You're welcome. > >> Follow-ups I’d like to make: > >> > >> 1. Expand the modes section to show how the types of results can vary > >> depending on the mode, thanks to the flattening. Examples: > >> > >> david=# select jsonb_path_query('{"a":[1,2,3,4,5]}', '$.a ?(@[*] > 2)'); > >> jsonb_path_query > >> ------------------ > >> 3 > >> 4 > >> 5 > >> (3 rows) > >> > >> david=# select jsonb_path_query('{"a":[1,2,3,4,5]}', 'strict $.a ?(@[*] > 2)'); > >> jsonb_path_query > >> ------------------ > >> [1, 2, 3, 4, 5] > >> > >> 2. Improve the descriptions and examples for @?/jsonb_path_exists() > >> and @@/jsonb_path_match(). > > > > +1 > > I planned to submit these changes in a separate patch, based on Tom > Lane’s suggestion[1]. Would it be preferred to add them to this patch? Your call but I'm not against including it in this patch because it already touches the modes section. > I pokwds around, and it appears the computeroutput bit is used for > function output. So I followed the precedent in queries.sgml[2] and > omitted the computeroutput tags but added prompt, e.g., > <screen> > <prompt>=></prompt> <userinput>select jsonb_path_query(:'json', 'strict $.**.HR');</userinput> > jsonb_path_query > ------------------ > 73 > 135 > </screen> Okay, Not sure what the preferred style is but I saw <userinput> and <computeroutput> used together in doc/src/sgml/ref/createuser.sgml. But it's not applied consistently in the rest of the docs. > >> + <para> > >> + Predicate-only path expressions are necessary for implementation of the > >> + <literal>@@</literal> operator (and the > >> + <function>jsonb_path_match</function> function), and should not be used > >> + with the <literal>@?</literal> operator (or > >> + <function>jsonb_path_exists</function> function). > >> + </para> > >> + > >> + <para> > >> + Conversely, non-predicate <type>jsonpath</type> expressions should not be > >> + used with the <literal>@@</literal> operator (or the > >> + <function>jsonb_path_match</function> function). > >> + </para> > >> + </sect4> > > > > Both paras should be wrapped in a single <note> so that they stand out > > from the rest of the text. Maybe even <warning>, but <note> is already > > used on this page for things that I'd consider warnings. > > Agreed. Would be good if we could teach these functions and operators > to reject path expressions they don’t support. Right, you mentioned that idea in [1] (separate types). Not sure what the best strategy here is but it's likely to break existing queries. Maybe deprecating unsupported path expressions in the next major release and changing that to an error in the major release after that. > > This can be checked with `make -C doc/src/sgml check`. > > Thanks. That produces a bunch of warnings for postgres.sgml and > legal.sgml (and a failure to load the docbook DTD), but func.sgml is > clean now. Hmm... I get no warnings on 1f89b73c4e. Did you install all tools as described in [2]? The DTD needs to be installed as well. [1] https://www.postgresql.org/message-id/BAF11F2D-5EDD-4DBB-87FA-4F35845029AE%40justatheory.com [2] https://www.postgresql.org/docs/current/docguide-toolsets.html -- Erik ^ permalink raw reply [nested|flat] 54+ messages in thread
* Re: Patch: Improve Boolean Predicate JSON Path Docs 2023-10-15 23:04 Re: Patch: Improve Boolean Predicate JSON Path Docs David E. Wheeler <[email protected]> 2023-10-16 03:03 ` Re: Patch: Improve Boolean Predicate JSON Path Docs Erik Wienhold <[email protected]> @ 2023-10-16 19:59 ` David E. Wheeler <[email protected]> 2023-10-16 22:07 ` Re: Patch: Improve Boolean Predicate JSON Path Docs Erik Wienhold <[email protected]> 0 siblings, 1 reply; 54+ messages in thread From: David E. Wheeler @ 2023-10-16 19:59 UTC (permalink / raw) To: Erik Wienhold <[email protected]>; +Cc: [email protected] On Oct 15, 2023, at 23:03, Erik Wienhold <[email protected]> wrote: > Your call but I'm not against including it in this patch because it > already touches the modes section. Okay, added, let’s just put all our cards on the table. :-) >> Agreed. Would be good if we could teach these functions and operators >> to reject path expressions they don’t support. > > Right, you mentioned that idea in [1] (separate types). Not sure what > the best strategy here is but it's likely to break existing queries. > Maybe deprecating unsupported path expressions in the next major release > and changing that to an error in the major release after that. Well if the functions have a JsonPathItem struct, they can check its type attribute and reject those with a root type that’s a predicate in @? and reject it if it’s not a predicate in @@. Example of checking type here: https://github.com/postgres/postgres/blob/54b208f90963cb8b48b9794a5392b2fae4b40a98/src/backend/utils... >>> This can be checked with `make -C doc/src/sgml check`. >> >> Thanks. That produces a bunch of warnings for postgres.sgml and >> legal.sgml (and a failure to load the docbook DTD), but func.sgml is >> clean now. > > Hmm... I get no warnings on 1f89b73c4e. Did you install all tools as > described in [2]? The DTD needs to be installed as well. Thanks, got it down to one: postgres.sgml:112: element sect4: validity error : Element sect4 content does not follow the DTD, expecting (sect4info? , (title , subtitle? , titleabbrev?) , (toc | lot | index | glossary | bibliography)* , (((calloutlist | glosslist | bibliolist | itemizedlist | orderedlist | segmentedlist | simplelist | variablelist | caution | important | note | tip | warning | literallayout | programlisting | programlistingco | screen | screenco | screenshot | synopsis | cmdsynopsis | funcsynopsis | classsynopsis | fieldsynopsis | constructorsynopsis | destructorsynopsis | methodsynopsis | formalpara | para | simpara | address | blockquote | graphic | graphicco | mediaobject | mediaobjectco | informalequation | informalexample | informalfigure | informaltable | equation | example | figure | table | msgset | procedure | sidebar | qandaset | task | anchor | bridgehead | remark | highlights | abstract | authorblurb | epigraph | indexterm | beginpage)+ , (refentry* | sect5* | simplesect*)) | refentry+ | sect5+ | simplesect+) , (toc | lot | index | glossary | bibliography)*), got (para para ) &func; David Attachments: [application/applefile] v3-0001-Improve-boolean-predicate-JSON-Path-docs.patch (114B, ../../[email protected]/2-v3-0001-Improve-boolean-predicate-JSON-Path-docs.patch) download | inline diff: 2 <