Add pg_column_toast_chunk_id().
authorNathan Bossart <[email protected]>
Thu, 14 Mar 2024 15:58:00 +0000 (10:58 -0500)
committerNathan Bossart <[email protected]>
Thu, 14 Mar 2024 15:58:00 +0000 (10:58 -0500)
This function returns the chunk_id of an on-disk TOASTed value.  If
the value is un-TOASTed or not on-disk, it returns NULL.  This is
useful for identifying which values are actually TOASTed and for
investigating "unexpected chunk number" errors.

Bumps catversion.

Author: Yugo Nagata
Reviewed-by: Jian He
Discussion: https://postgr.es/m/20230329105507.d764497456eeac1ca491b5bd%40sraoss.co.jp

doc/src/sgml/func.sgml
src/backend/utils/adt/varlena.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/test/regress/expected/misc_functions.out
src/test/regress/sql/misc_functions.sql

index 91f1e69359495d0f7d65d7db95a41583540f7138..72c5175e3b3680f646bda05f45722a4d89734d6b 100644 (file)
@@ -28552,6 +28552,23 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_column_toast_chunk_id</primary>
+        </indexterm>
+        <function>pg_column_toast_chunk_id</function> ( <type>"any"</type> )
+        <returnvalue>oid</returnvalue>
+       </para>
+       <para>
+        Shows the <structfield>chunk_id</structfield> of an on-disk
+        <acronym>TOAST</acronym>ed value.  Returns <literal>NULL</literal>
+        if the value is un-<acronym>TOAST</acronym>ed or not on-disk.  See
+        <xref linkend="storage-toast"/> for more information about
+        <acronym>TOAST</acronym>.
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
index 543afb66e581d1d9c91c0d5c0bda0f73d1e710db..8d28dd42ce1bbe3ac04bf4b2bbfdf6af04241352 100644 (file)
@@ -5105,6 +5105,47 @@ pg_column_compression(PG_FUNCTION_ARGS)
    PG_RETURN_TEXT_P(cstring_to_text(result));
 }
 
+/*
+ * Return the chunk_id of the on-disk TOASTed value.  Return NULL if the value
+ * is un-TOASTed or not on-disk.
+ */
+Datum
+pg_column_toast_chunk_id(PG_FUNCTION_ARGS)
+{
+   int         typlen;
+   struct varlena *attr;
+   struct varatt_external toast_pointer;
+
+   /* On first call, get the input type's typlen, and save at *fn_extra */
+   if (fcinfo->flinfo->fn_extra == NULL)
+   {
+       /* Lookup the datatype of the supplied argument */
+       Oid         argtypeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
+
+       typlen = get_typlen(argtypeid);
+       if (typlen == 0)        /* should not happen */
+           elog(ERROR, "cache lookup failed for type %u", argtypeid);
+
+       fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                     sizeof(int));
+       *((int *) fcinfo->flinfo->fn_extra) = typlen;
+   }
+   else
+       typlen = *((int *) fcinfo->flinfo->fn_extra);
+
+   if (typlen != -1)
+       PG_RETURN_NULL();
+
+   attr = (struct varlena *) DatumGetPointer(PG_GETARG_DATUM(0));
+
+   if (!VARATT_IS_EXTERNAL_ONDISK(attr))
+       PG_RETURN_NULL();
+
+   VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
+
+   PG_RETURN_OID(toast_pointer.va_valueid);
+}
+
 /*
  * string_agg - Concatenates values and returns string.
  *
index 429989efd911be8aa100db987016f086ce140641..9cf6dae3d90c94b793c349185f1482555be01b52 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202403132
+#define CATALOG_VERSION_NO 202403141
 
 #endif
index 4af5c2e847018a5917fe805a9f40f117c468666e..700f7daf7b29c3d2e8b2f52f666a849d494cd569 100644 (file)
 { oid => '2121', descr => 'compression method for the compressed datum',
   proname => 'pg_column_compression', provolatile => 's', prorettype => 'text',
   proargtypes => 'any', prosrc => 'pg_column_compression' },
+{ oid => '8393', descr => 'chunk ID of on-disk TOASTed value',
+  proname => 'pg_column_toast_chunk_id', provolatile => 's', prorettype => 'oid',
+  proargtypes => 'any', prosrc => 'pg_column_toast_chunk_id' },
 { oid => '2322',
   descr => 'total disk space usage for the specified tablespace',
   proname => 'pg_tablespace_size', provolatile => 'v', prorettype => 'int8',
index d5f61dfad933c35ad30387bfa53e9e2b4a3c3b29..e0ba9fdafaecaa7d264e75b1db17a47f7381df03 100644 (file)
@@ -703,3 +703,19 @@ SELECT has_function_privilege('regress_current_logfile',
 (1 row)
 
 DROP ROLE regress_current_logfile;
+-- pg_column_toast_chunk_id
+CREATE TABLE test_chunk_id (a TEXT, b TEXT STORAGE EXTERNAL);
+INSERT INTO test_chunk_id VALUES ('x', repeat('x', 8192));
+SELECT t.relname AS toastrel FROM pg_class c
+  LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
+  WHERE c.relname = 'test_chunk_id'
+\gset
+SELECT pg_column_toast_chunk_id(a) IS NULL,
+  pg_column_toast_chunk_id(b) IN (SELECT chunk_id FROM pg_toast.:toastrel)
+  FROM test_chunk_id;
+ ?column? | ?column? 
+----------+----------
+ t        | t
+(1 row)
+
+DROP TABLE test_chunk_id;
index 928b04db7ffb0dc7a21bcae116faf4c7207774df..ff5ed5eddeb245a1996477bc53f8cb26ac34f883 100644 (file)
@@ -265,3 +265,15 @@ GRANT pg_monitor TO regress_current_logfile;
 SELECT has_function_privilege('regress_current_logfile',
   'pg_current_logfile()', 'EXECUTE');
 DROP ROLE regress_current_logfile;
+
+-- pg_column_toast_chunk_id
+CREATE TABLE test_chunk_id (a TEXT, b TEXT STORAGE EXTERNAL);
+INSERT INTO test_chunk_id VALUES ('x', repeat('x', 8192));
+SELECT t.relname AS toastrel FROM pg_class c
+  LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
+  WHERE c.relname = 'test_chunk_id'
+\gset
+SELECT pg_column_toast_chunk_id(a) IS NULL,
+  pg_column_toast_chunk_id(b) IN (SELECT chunk_id FROM pg_toast.:toastrel)
+  FROM test_chunk_id;
+DROP TABLE test_chunk_id;