77#include <access/attmap.h>
88#include <access/attnum.h>
99#include <access/detoast.h>
10+ #include <access/htup_details.h>
1011#include <access/skey.h>
1112#include <access/tupdesc.h>
1213#include <catalog/heap.h>
@@ -146,7 +147,7 @@ static void row_compressor_process_ordered_slot(RowCompressor *row_compressor, T
146147static void row_compressor_update_group (RowCompressor * row_compressor , TupleTableSlot * row );
147148static bool row_compressor_new_row_is_in_new_group (RowCompressor * row_compressor ,
148149 TupleTableSlot * row );
149- static void create_per_compressed_column (RowDecompressor * decompressor );
150+ static void create_per_compressed_column (RowDecompressor * decompressor , bool internal_error );
150151static void row_compressor_append_row (RowCompressor * row_compressor , TupleTableSlot * row );
151152static void row_compressor_flush (RowCompressor * row_compressor , BulkWriter * writer ,
152153 bool changed_groups );
@@ -2022,8 +2023,9 @@ bulk_writer_close(BulkWriter *writer)
20222023 ** decompress_chunk **
20232024 **********************/
20242025
2025- RowDecompressor
2026- build_decompressor (const TupleDesc in_desc , const TupleDesc out_desc , Oid in_oid , Oid out_oid )
2026+ static inline RowDecompressor
2027+ build_decompressor_common (const TupleDesc in_desc , const TupleDesc out_desc , Oid in_oid ,
2028+ Oid out_oid , bool internal_error )
20272029{
20282030 AttrNumber count_meta_attnum = InvalidAttrNumber ;
20292031 AttrMap * attrmap = build_decompress_attrmap (out_desc , in_desc , & count_meta_attnum );
@@ -2054,7 +2056,7 @@ build_decompressor(const TupleDesc in_desc, const TupleDesc out_desc, Oid in_oid
20542056 .attrmap = attrmap ,
20552057 };
20562058
2057- create_per_compressed_column (& decompressor );
2059+ create_per_compressed_column (& decompressor , internal_error );
20582060
20592061 /*
20602062 * We need to make sure decompressed_is_nulls is in a defined state. While this
@@ -2071,6 +2073,12 @@ build_decompressor(const TupleDesc in_desc, const TupleDesc out_desc, Oid in_oid
20712073 return decompressor ;
20722074}
20732075
2076+ RowDecompressor
2077+ build_decompressor (const TupleDesc in_desc , const TupleDesc out_desc , Oid in_oid , Oid out_oid )
2078+ {
2079+ return build_decompressor_common (in_desc , out_desc , in_oid , out_oid , true);
2080+ }
2081+
20742082void
20752083row_decompressor_init_stats (RowDecompressor * decompressor , Oid compressed_relid ,
20762084 Oid uncompressed_relid , CmdType cmd_type )
@@ -2190,7 +2198,7 @@ decompress_chunk(Oid in_table, Oid out_table)
21902198}
21912199
21922200static void
2193- create_per_compressed_column (RowDecompressor * decompressor )
2201+ create_per_compressed_column (RowDecompressor * decompressor , bool internal_error )
21942202{
21952203 Oid compressed_data_type_oid = ts_custom_type_cache_get (CUSTOM_TYPE_COMPRESSED_DATA )-> type_oid ;
21962204 Assert (OidIsValid (compressed_data_type_oid ));
@@ -2234,12 +2242,14 @@ create_per_compressed_column(RowDecompressor *decompressor)
22342242 is_compressed = compressed_attr -> atttypid == compressed_data_type_oid ;
22352243 if (!is_compressed && compressed_attr -> atttypid != decompressed_type )
22362244 {
2237- elog (ERROR ,
2238- "compressed table type '%s' does not match decompressed table type '%s' for "
2239- "segment-by column \"%s\"" ,
2240- format_type_be (compressed_attr -> atttypid ),
2241- format_type_be (decompressed_type ),
2242- col_name );
2245+ ereport (ERROR ,
2246+ (errcode (internal_error ? ERRCODE_INTERNAL_ERROR :
2247+ ERRCODE_INVALID_PARAMETER_VALUE ),
2248+ errmsg ("compressed table type '%s' does not match decompressed "
2249+ "table type '%s' for segment-by column \"%s\"" ,
2250+ format_type_be (compressed_attr -> atttypid ),
2251+ format_type_be (decompressed_type ),
2252+ col_name )));
22432253 }
22442254
22452255 * per_compressed_col = (PerCompressedColumn ){
@@ -2767,6 +2777,130 @@ tsl_compressed_data_decompress_reverse(PG_FUNCTION_ARGS)
27672777 ;
27682778}
27692779
2780+ /*
2781+ * decompress_batch(compressed_tuple record) RETURNS SETOF record
2782+ *
2783+ * Decompresses a single compressed batch (one row of a compressed chunk) into
2784+ * the individual rows it represents. The shape of the input compressed tuple
2785+ * is taken from the record's own type info (typeId/typmod in the header).
2786+ * The shape of the output rows is taken from the call site's column
2787+ * definition list (the AS t(...) clause).
2788+ */
2789+ typedef struct DecompressBatchSRFContext
2790+ {
2791+ RowDecompressor decompressor ;
2792+ int next_row ;
2793+ int total_rows ;
2794+ } DecompressBatchSRFContext ;
2795+
2796+ Datum
2797+ tsl_decompress_batch (PG_FUNCTION_ARGS )
2798+ {
2799+ FuncCallContext * funcctx ;
2800+ DecompressBatchSRFContext * decompress_ctx ;
2801+
2802+ if (SRF_IS_FIRSTCALL ())
2803+ {
2804+ funcctx = SRF_FIRSTCALL_INIT ();
2805+ MemoryContext oldcontext = MemoryContextSwitchTo (funcctx -> multi_call_memory_ctx );
2806+
2807+ TupleDesc out_desc ;
2808+ if (get_call_result_type (fcinfo , NULL , & out_desc ) != TYPEFUNC_COMPOSITE )
2809+ {
2810+ ereport (ERROR ,
2811+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
2812+ errmsg ("function returning record called in context "
2813+ "that cannot accept type record" )));
2814+ }
2815+ BlessTupleDesc (out_desc );
2816+
2817+ HeapTupleHeader td = PG_GETARG_HEAPTUPLEHEADER (0 );
2818+ TupleDesc in_desc =
2819+ lookup_rowtype_tupdesc (HeapTupleHeaderGetTypeId (td ), HeapTupleHeaderGetTypMod (td ));
2820+
2821+ /*
2822+ * Verify that the input record actually looks like a compressed-chunk
2823+ * row before handing it to the decompressor. We are looking for the
2824+ * "_ts_meta_count" metadata column which the decompressor uses to
2825+ * identify the number of rows in the batch.
2826+ */
2827+ bool has_count_column = false;
2828+ for (int i = 0 ; i < in_desc -> natts ; i ++ )
2829+ {
2830+ Form_pg_attribute attr = TupleDescAttr (in_desc , i );
2831+
2832+ if (attr -> attisdropped )
2833+ {
2834+ continue ;
2835+ }
2836+
2837+ if (strcmp (NameStr (attr -> attname ), COMPRESSION_COLUMN_METADATA_COUNT_NAME ) == 0 )
2838+ {
2839+ if (attr -> atttypid != INT4OID )
2840+ {
2841+ ereport (ERROR ,
2842+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
2843+ errmsg ("input record is not a compressed batch" ),
2844+ errdetail ("Column \"%s\" must have type integer." ,
2845+ COMPRESSION_COLUMN_METADATA_COUNT_NAME )));
2846+ }
2847+
2848+ has_count_column = true;
2849+ break ;
2850+ }
2851+ }
2852+
2853+ if (!has_count_column )
2854+ {
2855+ ReleaseTupleDesc (in_desc );
2856+ ereport (ERROR ,
2857+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
2858+ errmsg ("input record is not a compressed batch" ),
2859+ errdetail ("A compressed batch must have a \"%s\" metadata column." ,
2860+ COMPRESSION_COLUMN_METADATA_COUNT_NAME )));
2861+ }
2862+
2863+ decompress_ctx = palloc0 (sizeof (DecompressBatchSRFContext ));
2864+ decompress_ctx -> decompressor =
2865+ build_decompressor_common (in_desc , out_desc , InvalidOid , InvalidOid , false);
2866+
2867+ HeapTupleData compressed_tuple ;
2868+ compressed_tuple .t_len = HeapTupleHeaderGetDatumLength (td );
2869+ ItemPointerSetInvalid (& compressed_tuple .t_self );
2870+ compressed_tuple .t_tableOid = InvalidOid ;
2871+ compressed_tuple .t_data = td ;
2872+
2873+ heap_deform_tuple (& compressed_tuple ,
2874+ in_desc ,
2875+ decompress_ctx -> decompressor .compressed_datums ,
2876+ decompress_ctx -> decompressor .compressed_is_nulls );
2877+
2878+ ReleaseTupleDesc (in_desc );
2879+
2880+ decompress_ctx -> total_rows = decompress_batch (& decompress_ctx -> decompressor );
2881+ decompress_ctx -> next_row = 0 ;
2882+
2883+ funcctx -> user_fctx = decompress_ctx ;
2884+ funcctx -> tuple_desc = decompress_ctx -> decompressor .out_desc ;
2885+ MemoryContextSwitchTo (oldcontext );
2886+ }
2887+
2888+ funcctx = SRF_PERCALL_SETUP ();
2889+ decompress_ctx = (DecompressBatchSRFContext * ) funcctx -> user_fctx ;
2890+
2891+ if (decompress_ctx -> next_row >= decompress_ctx -> total_rows )
2892+ {
2893+ row_decompressor_close (& decompress_ctx -> decompressor );
2894+ SRF_RETURN_DONE (funcctx );
2895+ }
2896+
2897+ TupleTableSlot * slot =
2898+ decompress_ctx -> decompressor .decompressed_slots [decompress_ctx -> next_row ++ ];
2899+ bool should_free ;
2900+ HeapTuple tuple = ExecFetchSlotHeapTuple (slot , false, & should_free );
2901+ SRF_RETURN_NEXT (funcctx , HeapTupleGetDatum (tuple ));
2902+ }
2903+
27702904/*
27712905 * compressed_data_to_array(compressed_data, element_type) -> anyarray
27722906 */
0 commit comments