Converting built-in SQL functions to new style - Mailing list pgsql-hackers

From Tom Lane
Subject Converting built-in SQL functions to new style
Date
Msg-id 3956760.1618529139@sss.pgh.pa.us
Whole thread Raw
Responses Re: Converting built-in SQL functions to new style  (Noah Misch <noah@leadboat.com>)
List pgsql-hackers
[ moving this to a new thread so as not to confuse the cfbot ]

I wrote:
> Andrew Dunstan <andrew@dunslane.net> writes:
>> Is there anything else we should be doing along the eat your own dogfood
>> line that don't have these security implications?

> We can still convert the initdb-created SQL functions to new style,
> since there's no security threat during initdb.  I'll make a patch
> for that soon.

Here's a draft patch that converts all the built-in and information_schema
SQL functions to new style, except for half a dozen that cannot be
converted because they use polymorphic arguments.

Leaving that remaining half-a-dozen as old style seems okay from a
security standpoint, because they are few enough and simple enough
that it's no big notational headache to make their source text 100%
search-path-proof.  I've inserted OPERATOR() notation where necessary
to make them bulletproof.

Also worth a comment perhaps is that for the functions that are being
converted, I replaced the prosrc text in pg_proc.dat with "see
system_views.sql".  I think this might reduce confusion by making
it clear that these are not the operative definitions.

One thing this patch does that's not strictly within the charter
is to give the two forms of ts_debug() pg_proc.dat entries, just
so they are more like their new neighbors.  This means they'll be
pinned where before they were not, but that seems desirable to me.

I'm pretty confident the conversion is accurate, because I used \sf
to generate the text for the replacement definitions.  So I think
this is committable, though review is welcome.

One thing I was wondering about, but did not pull the trigger on
here, is whether to split off the function-related stuff in
system_views.sql into a new file "system_functions.sql", as has
long been speculated about by the comments in system_views.sql.
I think it is time to do this because

(a) The function stuff now amounts to a full third of the file.

(b) While the views made by system_views.sql are intentionally
not pinned, the function-related commands are messing with
pre-existing objects that *are* pinned.  This seems quite
confusing to me, and it might interfere with the intention that
you could reload the system view definitions using this file.

Thoughts?

            regards, tom lane

diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 941a9f664c..0f2c1833e9 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -43,7 +43,8 @@ SET search_path TO information_schema;
 CREATE FUNCTION _pg_expandarray(IN anyarray, OUT x anyelement, OUT n int)
     RETURNS SETOF RECORD
     LANGUAGE sql STRICT IMMUTABLE PARALLEL SAFE
-    AS 'select $1[s], s - pg_catalog.array_lower($1,1) + 1
+    AS 'select $1[s],
+        s operator(pg_catalog.-) pg_catalog.array_lower($1,1) operator(pg_catalog.+) 1
         from pg_catalog.generate_series(pg_catalog.array_lower($1,1),
                                         pg_catalog.array_upper($1,1),
                                         1) as g(s)';
@@ -52,28 +53,26 @@ CREATE FUNCTION _pg_expandarray(IN anyarray, OUT x anyelement, OUT n int)
  * column's position in the index (NULL if not there) */
 CREATE FUNCTION _pg_index_position(oid, smallint) RETURNS int
     LANGUAGE sql STRICT STABLE
-    AS $$
+BEGIN ATOMIC
 SELECT (ss.a).n FROM
   (SELECT information_schema._pg_expandarray(indkey) AS a
    FROM pg_catalog.pg_index WHERE indexrelid = $1) ss
   WHERE (ss.a).x = $2;
-$$;
+END;

 CREATE FUNCTION _pg_truetypid(pg_attribute, pg_type) RETURNS oid
     LANGUAGE sql
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT CASE WHEN $2.typtype = 'd' THEN $2.typbasetype ELSE $1.atttypid END$$;
+RETURN CASE WHEN $2.typtype = 'd' THEN $2.typbasetype ELSE $1.atttypid END;

 CREATE FUNCTION _pg_truetypmod(pg_attribute, pg_type) RETURNS int4
     LANGUAGE sql
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT CASE WHEN $2.typtype = 'd' THEN $2.typtypmod ELSE $1.atttypmod END$$;
+RETURN CASE WHEN $2.typtype = 'd' THEN $2.typtypmod ELSE $1.atttypmod END;

 -- these functions encapsulate knowledge about the encoding of typmod:

@@ -82,8 +81,7 @@ CREATE FUNCTION _pg_char_max_length(typid oid, typmod int4) RETURNS integer
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT
+RETURN
   CASE WHEN $2 = -1 /* default typmod */
        THEN null
        WHEN $1 IN (1042, 1043) /* char, varchar */
@@ -91,15 +89,14 @@ $$SELECT
        WHEN $1 IN (1560, 1562) /* bit, varbit */
        THEN $2
        ELSE null
-  END$$;
+  END;

 CREATE FUNCTION _pg_char_octet_length(typid oid, typmod int4) RETURNS integer
     LANGUAGE sql
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT
+RETURN
   CASE WHEN $1 IN (25, 1042, 1043) /* text, char, varchar */
        THEN CASE WHEN $2 = -1 /* default typmod */
                  THEN CAST(2^30 AS integer)
@@ -107,15 +104,14 @@ $$SELECT
                       pg_catalog.pg_encoding_max_length((SELECT encoding FROM pg_catalog.pg_database WHERE datname =
pg_catalog.current_database()))
             END
        ELSE null
-  END$$;
+  END;

 CREATE FUNCTION _pg_numeric_precision(typid oid, typmod int4) RETURNS integer
     LANGUAGE sql
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT
+RETURN
   CASE $1
          WHEN 21 /*int2*/ THEN 16
          WHEN 23 /*int4*/ THEN 32
@@ -128,27 +124,25 @@ $$SELECT
          WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/
          WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
          ELSE null
-  END$$;
+  END;

 CREATE FUNCTION _pg_numeric_precision_radix(typid oid, typmod int4) RETURNS integer
     LANGUAGE sql
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT
+RETURN
   CASE WHEN $1 IN (21, 23, 20, 700, 701) THEN 2
        WHEN $1 IN (1700) THEN 10
        ELSE null
-  END$$;
+  END;

 CREATE FUNCTION _pg_numeric_scale(typid oid, typmod int4) RETURNS integer
     LANGUAGE sql
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT
+RETURN
   CASE WHEN $1 IN (21, 23, 20) THEN 0
        WHEN $1 IN (1700) THEN
             CASE WHEN $2 = -1
@@ -156,15 +150,14 @@ $$SELECT
                  ELSE ($2 - 4) & 65535
                  END
        ELSE null
-  END$$;
+  END;

 CREATE FUNCTION _pg_datetime_precision(typid oid, typmod int4) RETURNS integer
     LANGUAGE sql
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT
+RETURN
   CASE WHEN $1 IN (1082) /* date */
            THEN 0
        WHEN $1 IN (1083, 1114, 1184, 1266) /* time, timestamp, same + tz */
@@ -172,19 +165,18 @@ $$SELECT
        WHEN $1 IN (1186) /* interval */
            THEN CASE WHEN $2 < 0 OR $2 & 65535 = 65535 THEN 6 ELSE $2 & 65535 END
        ELSE null
-  END$$;
+  END;

 CREATE FUNCTION _pg_interval_type(typid oid, mod int4) RETURNS text
     LANGUAGE sql
     IMMUTABLE
     PARALLEL SAFE
     RETURNS NULL ON NULL INPUT
-    AS
-$$SELECT
+RETURN
   CASE WHEN $1 IN (1186) /* interval */
            THEN pg_catalog.upper(substring(pg_catalog.format_type($1, $2) similar 'interval[()0-9]* #"%#"' escape
'#'))
        ELSE null
-  END$$;
+  END;


 -- 5.2 INFORMATION_SCHEMA_CATALOG_NAME view appears later.
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 451db2ee0a..7e1c1718b7 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1273,68 +1273,357 @@ GRANT SELECT (subdbid, subname, subowner, subenabled, subbinary, substream, subs
 -- At some point there might be enough to justify breaking them out into
 -- a separate "system_functions.sql" file.
 --
+-- These are placed here because they require SQL function bodies, default
+-- expressions, or other things that are too complicated for a handwritten
+-- definition in pg_proc.dat.  Note that there *is* a definition in pg_proc.dat
+-- for each of these, to reserve an OID for it.  We're just overwriting it.
+--

--- Tsearch debug function.  Defined here because it'd be pretty unwieldy
--- to put it into pg_proc.h
-
-CREATE FUNCTION ts_debug(IN config regconfig, IN document text,
+CREATE OR REPLACE FUNCTION lpad(text, integer)
+ RETURNS text
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN lpad($1, $2, ' ');
+
+CREATE OR REPLACE FUNCTION rpad(text, integer)
+ RETURNS text
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN rpad($1, $2, ' ');
+
+CREATE OR REPLACE FUNCTION "substring"(text, text, text)
+ RETURNS text
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN substring($1, similar_to_escape($2, $3));
+
+CREATE OR REPLACE FUNCTION bit_length(bit)
+ RETURNS integer
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN length($1);
+
+CREATE OR REPLACE FUNCTION bit_length(bytea)
+ RETURNS integer
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN octet_length($1) * 8;
+
+CREATE OR REPLACE FUNCTION bit_length(text)
+ RETURNS integer
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN octet_length($1) * 8;
+
+CREATE OR REPLACE FUNCTION log(numeric)
+ RETURNS numeric
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN log(10, $1);
+
+CREATE OR REPLACE FUNCTION log10(numeric)
+ RETURNS numeric
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN log(10, $1);
+
+CREATE OR REPLACE FUNCTION round(numeric)
+ RETURNS numeric
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN round($1, 0);
+
+CREATE OR REPLACE FUNCTION trunc(numeric)
+ RETURNS numeric
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN trunc($1, 0);
+
+CREATE OR REPLACE FUNCTION numeric_pl_pg_lsn(numeric, pg_lsn)
+ RETURNS pg_lsn
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION path_contain_pt(path, point)
+ RETURNS boolean
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN on_ppath($2, $1);
+
+CREATE OR REPLACE FUNCTION polygon(circle)
+ RETURNS polygon
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN polygon(12, $1);
+
+CREATE OR REPLACE FUNCTION age(timestamptz)
+ RETURNS interval
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT COST 1
+RETURN age(cast(current_date as timestamptz), $1);
+
+CREATE OR REPLACE FUNCTION age(timestamp)
+ RETURNS interval
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT COST 1
+RETURN age(cast(current_date as timestamp), $1);
+
+CREATE OR REPLACE FUNCTION date_part(text, date)
+ RETURNS double precision
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN date_part($1, cast($2 as timestamp));
+
+CREATE OR REPLACE FUNCTION timestamptz(date, time)
+ RETURNS timestamptz
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT COST 1
+RETURN cast(($1 + $2) as timestamptz);
+
+CREATE OR REPLACE FUNCTION timedate_pl(time, date)
+ RETURNS timestamp
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION timetzdate_pl(timetz, date)
+ RETURNS timestamptz
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION interval_pl_time(interval, time)
+ RETURNS time
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION interval_pl_date(interval, date)
+ RETURNS timestamp
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION interval_pl_timetz(interval, timetz)
+ RETURNS timetz
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION interval_pl_timestamp(interval, timestamp)
+ RETURNS timestamp
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION interval_pl_timestamptz(interval, timestamptz)
+ RETURNS timestamptz
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION integer_pl_date(integer, date)
+ RETURNS date
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION "overlaps"(timestamptz, timestamptz,
+  timestamptz, interval)
+ RETURNS boolean
+ LANGUAGE sql
+ STABLE PARALLEL SAFE COST 1
+RETURN ($1, $2) overlaps ($3, ($3 + $4));
+
+CREATE OR REPLACE FUNCTION "overlaps"(timestamptz, interval,
+  timestamptz, interval)
+ RETURNS boolean
+ LANGUAGE sql
+ STABLE PARALLEL SAFE COST 1
+RETURN ($1, ($1 + $2)) overlaps ($3, ($3 + $4));
+
+CREATE OR REPLACE FUNCTION "overlaps"(timestamptz, interval,
+  timestamptz, timestamptz)
+ RETURNS boolean
+ LANGUAGE sql
+ STABLE PARALLEL SAFE COST 1
+RETURN ($1, ($1 + $2)) overlaps ($3, $4);
+
+CREATE OR REPLACE FUNCTION "overlaps"(timestamp, timestamp,
+  timestamp, interval)
+ RETURNS boolean
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE COST 1
+RETURN ($1, $2) overlaps ($3, ($3 + $4));
+
+CREATE OR REPLACE FUNCTION "overlaps"(timestamp, interval,
+  timestamp, timestamp)
+ RETURNS boolean
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE COST 1
+RETURN ($1, ($1 + $2)) overlaps ($3, $4);
+
+CREATE OR REPLACE FUNCTION "overlaps"(timestamp, interval,
+  timestamp, interval)
+ RETURNS boolean
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE COST 1
+RETURN ($1, ($1 + $2)) overlaps ($3, ($3 + $4));
+
+CREATE OR REPLACE FUNCTION "overlaps"(time, interval,
+  time, interval)
+ RETURNS boolean
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE COST 1
+RETURN ($1, ($1 + $2)) overlaps ($3, ($3 + $4));
+
+CREATE OR REPLACE FUNCTION "overlaps"(time, time,
+  time, interval)
+ RETURNS boolean
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE COST 1
+RETURN ($1, $2) overlaps ($3, ($3 + $4));
+
+CREATE OR REPLACE FUNCTION "overlaps"(time, interval,
+  time, time)
+ RETURNS boolean
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE COST 1
+RETURN ($1, ($1 + $2)) overlaps ($3, $4);
+
+CREATE OR REPLACE FUNCTION int8pl_inet(bigint, inet)
+ RETURNS inet
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN $2 + $1;
+
+CREATE OR REPLACE FUNCTION xpath(text, xml)
+ RETURNS xml[]
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN xpath($1, $2, '{}'::text[]);
+
+CREATE OR REPLACE FUNCTION xpath_exists(text, xml)
+ RETURNS boolean
+ LANGUAGE sql
+ IMMUTABLE PARALLEL SAFE STRICT COST 1
+RETURN xpath_exists($1, $2, '{}'::text[]);
+
+CREATE OR REPLACE FUNCTION pg_sleep_for(interval)
+ RETURNS void
+ LANGUAGE sql
+ PARALLEL SAFE STRICT COST 1
+RETURN pg_sleep(extract(epoch from clock_timestamp() + $1) -
+                extract(epoch from clock_timestamp()));
+
+CREATE OR REPLACE FUNCTION pg_sleep_until(timestamptz)
+ RETURNS void
+ LANGUAGE sql
+ PARALLEL SAFE STRICT COST 1
+RETURN pg_sleep(extract(epoch from $1) -
+                extract(epoch from clock_timestamp()));
+
+CREATE OR REPLACE FUNCTION pg_relation_size(regclass)
+ RETURNS bigint
+ LANGUAGE sql
+ PARALLEL SAFE STRICT COST 1
+RETURN pg_relation_size($1, 'main');
+
+CREATE OR REPLACE FUNCTION obj_description(oid, name)
+ RETURNS text
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT
+BEGIN ATOMIC
+select description from pg_description
+  where objoid = $1 and
+    classoid = (select oid from pg_class where relname = $2 and
+                relnamespace = 'pg_catalog'::regnamespace) and
+    objsubid = 0;
+END;
+
+CREATE OR REPLACE FUNCTION shobj_description(oid, name)
+ RETURNS text
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT
+BEGIN ATOMIC
+select description from pg_shdescription
+  where objoid = $1 and
+    classoid = (select oid from pg_class where relname = $2 and
+                relnamespace = 'pg_catalog'::regnamespace);
+END;
+
+CREATE OR REPLACE FUNCTION obj_description(oid)
+ RETURNS text
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT
+BEGIN ATOMIC
+select description from pg_description where objoid = $1 and objsubid = 0;
+END;
+
+CREATE OR REPLACE FUNCTION col_description(oid, integer)
+ RETURNS text
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT
+BEGIN ATOMIC
+select description from pg_description
+  where objoid = $1 and classoid = 'pg_class'::regclass and objsubid = $2;
+END;
+
+CREATE OR REPLACE FUNCTION ts_debug(config regconfig, document text,
     OUT alias text,
     OUT description text,
     OUT token text,
     OUT dictionaries regdictionary[],
     OUT dictionary regdictionary,
     OUT lexemes text[])
-RETURNS SETOF record AS
-$$
-SELECT
+ RETURNS SETOF record
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT
+BEGIN ATOMIC
+select
     tt.alias AS alias,
     tt.description AS description,
     parse.token AS token,
-    ARRAY ( SELECT m.mapdict::pg_catalog.regdictionary
-            FROM pg_catalog.pg_ts_config_map AS m
+    ARRAY ( SELECT m.mapdict::regdictionary
+            FROM pg_ts_config_map AS m
             WHERE m.mapcfg = $1 AND m.maptokentype = parse.tokid
             ORDER BY m.mapseqno )
     AS dictionaries,
-    ( SELECT mapdict::pg_catalog.regdictionary
-      FROM pg_catalog.pg_ts_config_map AS m
+    ( SELECT mapdict::regdictionary
+      FROM pg_ts_config_map AS m
       WHERE m.mapcfg = $1 AND m.maptokentype = parse.tokid
-      ORDER BY pg_catalog.ts_lexize(mapdict, parse.token) IS NULL, m.mapseqno
+      ORDER BY ts_lexize(mapdict, parse.token) IS NULL, m.mapseqno
       LIMIT 1
     ) AS dictionary,
-    ( SELECT pg_catalog.ts_lexize(mapdict, parse.token)
-      FROM pg_catalog.pg_ts_config_map AS m
+    ( SELECT ts_lexize(mapdict, parse.token)
+      FROM pg_ts_config_map AS m
       WHERE m.mapcfg = $1 AND m.maptokentype = parse.tokid
-      ORDER BY pg_catalog.ts_lexize(mapdict, parse.token) IS NULL, m.mapseqno
+      ORDER BY ts_lexize(mapdict, parse.token) IS NULL, m.mapseqno
       LIMIT 1
     ) AS lexemes
-FROM pg_catalog.ts_parse(
-        (SELECT cfgparser FROM pg_catalog.pg_ts_config WHERE oid = $1 ), $2
+FROM ts_parse(
+        (SELECT cfgparser FROM pg_ts_config WHERE oid = $1 ), $2
     ) AS parse,
-     pg_catalog.ts_token_type(
-        (SELECT cfgparser FROM pg_catalog.pg_ts_config WHERE oid = $1 )
+     ts_token_type(
+        (SELECT cfgparser FROM pg_ts_config WHERE oid = $1 )
     ) AS tt
-WHERE tt.tokid = parse.tokid
-$$
-LANGUAGE SQL STRICT STABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION ts_debug(regconfig,text) IS
-    'debug function for text search configuration';
+WHERE tt.tokid = parse.tokid;
+END;

-CREATE FUNCTION ts_debug(IN document text,
+CREATE OR REPLACE FUNCTION ts_debug(document text,
     OUT alias text,
     OUT description text,
     OUT token text,
     OUT dictionaries regdictionary[],
     OUT dictionary regdictionary,
     OUT lexemes text[])
-RETURNS SETOF record AS
-$$
-    SELECT * FROM pg_catalog.ts_debug( pg_catalog.get_current_ts_config(), $1);
-$$
-LANGUAGE SQL STRICT STABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION ts_debug(text) IS
-    'debug function for current text search configuration';
+ RETURNS SETOF record
+ LANGUAGE sql
+ STABLE PARALLEL SAFE STRICT
+BEGIN ATOMIC
+    SELECT * FROM ts_debug(get_current_ts_config(), $1);
+END;

 --
 -- Redeclare built-in functions that need default values attached to their
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index f4957653ae..211ac4be58 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -2364,7 +2364,7 @@
 { oid => '1176', descr => 'convert date and time to timestamp with time zone',
   proname => 'timestamptz', prolang => 'sql', provolatile => 's',
   prorettype => 'timestamptz', proargtypes => 'date time',
-  prosrc => 'select cast(($1 + $2) as timestamp with time zone)' },
+  prosrc => 'see system_views.sql' },
 { oid => '1178', descr => 'convert timestamp with time zone to date',
   proname => 'date', provolatile => 's', prorettype => 'date',
   proargtypes => 'timestamptz', prosrc => 'timestamptz_date' },
@@ -2417,16 +2417,16 @@
 { oid => '1215', descr => 'get description for object id and catalog name',
   proname => 'obj_description', prolang => 'sql', procost => '100',
   provolatile => 's', prorettype => 'text', proargtypes => 'oid name',
-  prosrc => 'select description from pg_catalog.pg_description where objoid = $1 and classoid = (select oid from
pg_catalog.pg_classwhere relname = $2 and relnamespace = \'pg_catalog\'::pg_catalog.regnamespace) and objsubid = 0' }, 
+  prosrc => 'see system_views.sql' },
 { oid => '1216', descr => 'get description for table column',
   proname => 'col_description', prolang => 'sql', procost => '100',
   provolatile => 's', prorettype => 'text', proargtypes => 'oid int4',
-  prosrc => 'select description from pg_catalog.pg_description where objoid = $1 and classoid =
\'pg_catalog.pg_class\'::pg_catalog.regclassand objsubid = $2' }, 
+  prosrc => 'see system_views.sql' },
 { oid => '1993',
   descr => 'get description for object id and shared catalog name',
   proname => 'shobj_description', prolang => 'sql', procost => '100',
   provolatile => 's', prorettype => 'text', proargtypes => 'oid name',
-  prosrc => 'select description from pg_catalog.pg_shdescription where objoid = $1 and classoid = (select oid from
pg_catalog.pg_classwhere relname = $2 and relnamespace = \'pg_catalog\'::pg_catalog.regnamespace)' }, 
+  prosrc => 'see system_views.sql' },

 { oid => '1217',
   descr => 'truncate timestamp with time zone to specified units',
@@ -2601,13 +2601,13 @@

 { oid => '1296',
   proname => 'timedate_pl', prolang => 'sql', prorettype => 'timestamp',
-  proargtypes => 'time date', prosrc => 'select ($2 + $1)' },
+  proargtypes => 'time date', prosrc => 'see system_views.sql' },
 { oid => '1297',
   proname => 'datetimetz_pl', prorettype => 'timestamptz',
   proargtypes => 'date timetz', prosrc => 'datetimetz_timestamptz' },
 { oid => '1298',
   proname => 'timetzdate_pl', prolang => 'sql', prorettype => 'timestamptz',
-  proargtypes => 'timetz date', prosrc => 'select ($2 + $1)' },
+  proargtypes => 'timetz date', prosrc => 'see system_views.sql' },
 { oid => '1299', descr => 'current transaction time',
   proname => 'now', provolatile => 's', prorettype => 'timestamptz',
   proargtypes => '', prosrc => 'now' },
@@ -2651,17 +2651,17 @@
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   provolatile => 's', prorettype => 'bool',
   proargtypes => 'timestamptz interval timestamptz interval',
-  prosrc => 'select ($1, ($1 + $2)) overlaps ($3, ($3 + $4))' },
+  prosrc => 'see system_views.sql' },
 { oid => '1306', descr => 'intervals overlap?',
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   provolatile => 's', prorettype => 'bool',
   proargtypes => 'timestamptz timestamptz timestamptz interval',
-  prosrc => 'select ($1, $2) overlaps ($3, ($3 + $4))' },
+  prosrc => 'see system_views.sql' },
 { oid => '1307', descr => 'intervals overlap?',
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   provolatile => 's', prorettype => 'bool',
   proargtypes => 'timestamptz interval timestamptz timestamptz',
-  prosrc => 'select ($1, ($1 + $2)) overlaps ($3, $4)' },
+  prosrc => 'see system_views.sql' },

 { oid => '1308', descr => 'intervals overlap?',
   proname => 'overlaps', proisstrict => 'f', prorettype => 'bool',
@@ -2669,15 +2669,15 @@
 { oid => '1309', descr => 'intervals overlap?',
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   prorettype => 'bool', proargtypes => 'time interval time interval',
-  prosrc => 'select ($1, ($1 + $2)) overlaps ($3, ($3 + $4))' },
+  prosrc => 'see system_views.sql' },
 { oid => '1310', descr => 'intervals overlap?',
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   prorettype => 'bool', proargtypes => 'time time time interval',
-  prosrc => 'select ($1, $2) overlaps ($3, ($3 + $4))' },
+  prosrc => 'see system_views.sql' },
 { oid => '1311', descr => 'intervals overlap?',
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   prorettype => 'bool', proargtypes => 'time interval time time',
-  prosrc => 'select ($1, ($1 + $2)) overlaps ($3, $4)' },
+  prosrc => 'see system_views.sql' },

 { oid => '1312', descr => 'I/O',
   proname => 'timestamp_in', provolatile => 's', prorettype => 'timestamp',
@@ -2758,7 +2758,7 @@
 { oid => '1348', descr => 'deprecated, use two-argument form instead',
   proname => 'obj_description', prolang => 'sql', procost => '100',
   provolatile => 's', prorettype => 'text', proargtypes => 'oid',
-  prosrc => 'select description from pg_catalog.pg_description where objoid = $1 and objsubid = 0' },
+  prosrc => 'see system_views.sql' },

 { oid => '1349', descr => 'print type names of oidvector field',
   proname => 'oidvectortypes', provolatile => 's', prorettype => 'text',
@@ -2841,8 +2841,7 @@

 { oid => '1384', descr => 'extract field from date',
   proname => 'date_part', prolang => 'sql', prorettype => 'float8',
-  proargtypes => 'text date',
-  prosrc => 'select pg_catalog.date_part($1, cast($2 as timestamp without time zone))' },
+  proargtypes => 'text date', prosrc => 'see system_views.sql' },
 { oid => '9979', descr => 'extract field from date',
   proname => 'extract', prorettype => 'numeric', proargtypes => 'text date',
   prosrc => 'extract_date' },
@@ -2856,7 +2855,7 @@
   descr => 'date difference from today preserving months and years',
   proname => 'age', prolang => 'sql', provolatile => 's',
   prorettype => 'interval', proargtypes => 'timestamptz',
-  prosrc => 'select pg_catalog.age(cast(current_date as timestamp with time zone), $1)' },
+  prosrc => 'see system_views.sql' },

 { oid => '1388',
   descr => 'convert timestamp with time zone to time with time zone',
@@ -2970,7 +2969,7 @@
   prosrc => 'box_div' },
 { oid => '1426',
   proname => 'path_contain_pt', prolang => 'sql', prorettype => 'bool',
-  proargtypes => 'path point', prosrc => 'select pg_catalog.on_ppath($2, $1)' },
+  proargtypes => 'path point', prosrc => 'see system_views.sql' },
 { oid => '1428',
   proname => 'poly_contain_pt', prorettype => 'bool',
   proargtypes => 'polygon point', prosrc => 'poly_contain_pt' },
@@ -3230,7 +3229,7 @@
   prosrc => 'circle_center' },
 { oid => '1544', descr => 'convert circle to 12-vertex polygon',
   proname => 'polygon', prolang => 'sql', prorettype => 'polygon',
-  proargtypes => 'circle', prosrc => 'select pg_catalog.polygon(12, $1)' },
+  proargtypes => 'circle', prosrc => 'see system_views.sql' },
 { oid => '1545', descr => 'number of points',
   proname => 'npoints', prorettype => 'int4', proargtypes => 'path',
   prosrc => 'path_npoints' },
@@ -3526,12 +3525,10 @@
   prosrc => 'translate' },
 { oid => '879', descr => 'left-pad string to length',
   proname => 'lpad', prolang => 'sql', prorettype => 'text',
-  proargtypes => 'text int4',
-  prosrc => 'select pg_catalog.lpad($1, $2, \' \')' },
+  proargtypes => 'text int4', prosrc => 'see system_views.sql' },
 { oid => '880', descr => 'right-pad string to length',
   proname => 'rpad', prolang => 'sql', prorettype => 'text',
-  proargtypes => 'text int4',
-  prosrc => 'select pg_catalog.rpad($1, $2, \' \')' },
+  proargtypes => 'text int4', prosrc => 'see system_views.sql' },
 { oid => '881', descr => 'trim spaces from left end of string',
   proname => 'ltrim', prorettype => 'text', proargtypes => 'text',
   prosrc => 'ltrim1' },
@@ -4147,7 +4144,7 @@
   prosrc => 'inetpl' },
 { oid => '2631',
   proname => 'int8pl_inet', prolang => 'sql', prorettype => 'inet',
-  proargtypes => 'int8 inet', prosrc => 'select $2 + $1' },
+  proargtypes => 'int8 inet', prosrc => 'see system_views.sql' },
 { oid => '2632',
   proname => 'inetmi_int8', prorettype => 'inet', proargtypes => 'inet int8',
   prosrc => 'inetmi_int8' },
@@ -4279,13 +4276,13 @@
   prosrc => 'numeric_round' },
 { oid => '1708', descr => 'value rounded to \'scale\' of zero',
   proname => 'round', prolang => 'sql', prorettype => 'numeric',
-  proargtypes => 'numeric', prosrc => 'select pg_catalog.round($1,0)' },
+  proargtypes => 'numeric', prosrc => 'see system_views.sql' },
 { oid => '1709', descr => 'value truncated to \'scale\'',
   proname => 'trunc', prorettype => 'numeric', proargtypes => 'numeric int4',
   prosrc => 'numeric_trunc' },
 { oid => '1710', descr => 'value truncated to \'scale\' of zero',
   proname => 'trunc', prolang => 'sql', prorettype => 'numeric',
-  proargtypes => 'numeric', prosrc => 'select pg_catalog.trunc($1,0)' },
+  proargtypes => 'numeric', prosrc => 'see system_views.sql' },
 { oid => '1711', descr => 'nearest integer >= value',
   proname => 'ceil', prorettype => 'numeric', proargtypes => 'numeric',
   prosrc => 'numeric_ceil' },
@@ -4385,10 +4382,10 @@
   proargtypes => 'int4', prosrc => 'int4_numeric' },
 { oid => '1741', descr => 'base 10 logarithm',
   proname => 'log', prolang => 'sql', prorettype => 'numeric',
-  proargtypes => 'numeric', prosrc => 'select pg_catalog.log(10, $1)' },
+  proargtypes => 'numeric', prosrc => 'see system_views.sql' },
 { oid => '1481', descr => 'base 10 logarithm',
   proname => 'log10', prolang => 'sql', prorettype => 'numeric',
-  proargtypes => 'numeric', prosrc => 'select pg_catalog.log(10, $1)' },
+  proargtypes => 'numeric', prosrc => 'see system_views.sql' },
 { oid => '1742', descr => 'convert float4 to numeric',
   proname => 'numeric', proleakproof => 't', prorettype => 'numeric',
   proargtypes => 'float4', prosrc => 'float4_numeric' },
@@ -4572,13 +4569,13 @@

 { oid => '1810', descr => 'length in bits',
   proname => 'bit_length', prolang => 'sql', prorettype => 'int4',
-  proargtypes => 'bytea', prosrc => 'select pg_catalog.octet_length($1) * 8' },
+  proargtypes => 'bytea', prosrc => 'see system_views.sql' },
 { oid => '1811', descr => 'length in bits',
   proname => 'bit_length', prolang => 'sql', prorettype => 'int4',
-  proargtypes => 'text', prosrc => 'select pg_catalog.octet_length($1) * 8' },
+  proargtypes => 'text', prosrc => 'see system_views.sql' },
 { oid => '1812', descr => 'length in bits',
   proname => 'bit_length', prolang => 'sql', prorettype => 'int4',
-  proargtypes => 'bit', prosrc => 'select pg_catalog.length($1)' },
+  proargtypes => 'bit', prosrc => 'see system_views.sql' },

 # Selectivity estimators for LIKE and related operators
 { oid => '1814', descr => 'restriction selectivity of ILIKE',
@@ -4897,7 +4894,7 @@

 { oid => '1848',
   proname => 'interval_pl_time', prolang => 'sql', prorettype => 'time',
-  proargtypes => 'interval time', prosrc => 'select $2 + $1' },
+  proargtypes => 'interval time', prosrc => 'see system_views.sql' },

 { oid => '1850',
   proname => 'int28eq', proleakproof => 't', prorettype => 'bool',
@@ -5782,11 +5779,11 @@
 { oid => '2003',
   proname => 'textanycat', prolang => 'sql', provolatile => 's',
   prorettype => 'text', proargtypes => 'text anynonarray',
-  prosrc => 'select $1 || $2::pg_catalog.text' },
+  prosrc => 'select $1 operator(pg_catalog.||) $2::pg_catalog.text' },
 { oid => '2004',
   proname => 'anytextcat', prolang => 'sql', provolatile => 's',
   prorettype => 'text', proargtypes => 'anynonarray text',
-  prosrc => 'select $1::pg_catalog.text || $2' },
+  prosrc => 'select $1::pg_catalog.text operator(pg_catalog.||) $2' },

 { oid => '2005',
   proname => 'bytealike', prosupport => 'textlike_support',
@@ -5906,15 +5903,15 @@
 { oid => '2042', descr => 'intervals overlap?',
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   prorettype => 'bool', proargtypes => 'timestamp interval timestamp interval',
-  prosrc => 'select ($1, ($1 + $2)) overlaps ($3, ($3 + $4))' },
+  prosrc => 'see system_views.sql' },
 { oid => '2043', descr => 'intervals overlap?',
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   prorettype => 'bool', proargtypes => 'timestamp timestamp timestamp interval',
-  prosrc => 'select ($1, $2) overlaps ($3, ($3 + $4))' },
+  prosrc => 'see system_views.sql' },
 { oid => '2044', descr => 'intervals overlap?',
   proname => 'overlaps', prolang => 'sql', proisstrict => 'f',
   prorettype => 'bool', proargtypes => 'timestamp interval timestamp timestamp',
-  prosrc => 'select ($1, ($1 + $2)) overlaps ($3, $4)' },
+  prosrc => 'see system_views.sql' },
 { oid => '2045', descr => 'less-equal-greater',
   proname => 'timestamp_cmp', proleakproof => 't', prorettype => 'int4',
   proargtypes => 'timestamp timestamp', prosrc => 'timestamp_cmp' },
@@ -5980,7 +5977,7 @@
   descr => 'date difference from today preserving months and years',
   proname => 'age', prolang => 'sql', provolatile => 's',
   prorettype => 'interval', proargtypes => 'timestamp',
-  prosrc => 'select pg_catalog.age(cast(current_date as timestamp without time zone), $1)' },
+  prosrc => 'see system_views.sql' },

 { oid => '2069', descr => 'adjust timestamp to new time zone',
   proname => 'timezone', prorettype => 'timestamptz',
@@ -6000,8 +5997,7 @@
   prosrc => 'textregexsubstr' },
 { oid => '2074', descr => 'extract text matching SQL regular expression',
   proname => 'substring', prolang => 'sql', prorettype => 'text',
-  proargtypes => 'text text text',
-  prosrc => 'select pg_catalog.substring($1, pg_catalog.similar_to_escape($2, $3))' },
+  proargtypes => 'text text text', prosrc => 'see system_views.sql' },

 { oid => '2075', descr => 'convert int8 to bitstring',
   proname => 'bit', prorettype => 'bit', proargtypes => 'int8 int4',
@@ -6366,11 +6362,11 @@
 { oid => '3935', descr => 'sleep for the specified interval',
   proname => 'pg_sleep_for', prolang => 'sql', provolatile => 'v',
   prorettype => 'void', proargtypes => 'interval',
-  prosrc => 'select pg_catalog.pg_sleep(extract(epoch from pg_catalog.clock_timestamp() operator(pg_catalog.+) $1)
operator(pg_catalog.-)extract(epoch from pg_catalog.clock_timestamp()))' }, 
+  prosrc => 'see system_views.sql' },
 { oid => '3936', descr => 'sleep until the specified time',
   proname => 'pg_sleep_until', prolang => 'sql', provolatile => 'v',
   prorettype => 'void', proargtypes => 'timestamptz',
-  prosrc => 'select pg_catalog.pg_sleep(extract(epoch from $1) operator(pg_catalog.-) extract(epoch from
pg_catalog.clock_timestamp()))'}, 
+  prosrc => 'see system_views.sql' },
 { oid => '315', descr => 'Is JIT compilation available in this session?',
   proname => 'pg_jit_available', provolatile => 'v', prorettype => 'bool',
   proargtypes => '', prosrc => 'pg_jit_available' },
@@ -7185,7 +7181,7 @@
   descr => 'disk space usage for the main fork of the specified table or index',
   proname => 'pg_relation_size', prolang => 'sql', provolatile => 'v',
   prorettype => 'int8', proargtypes => 'regclass',
-  prosrc => 'select pg_catalog.pg_relation_size($1, \'main\')' },
+  prosrc => 'see system_views.sql' },
 { oid => '2332',
   descr => 'disk space usage for the specified fork of a table or index',
   proname => 'pg_relation_size', provolatile => 'v', prorettype => 'int8',
@@ -8104,21 +8100,21 @@
 # formerly-missing interval + datetime operators
 { oid => '2546',
   proname => 'interval_pl_date', prolang => 'sql', prorettype => 'timestamp',
-  proargtypes => 'interval date', prosrc => 'select $2 + $1' },
+  proargtypes => 'interval date', prosrc => 'see system_views.sql' },
 { oid => '2547',
   proname => 'interval_pl_timetz', prolang => 'sql', prorettype => 'timetz',
-  proargtypes => 'interval timetz', prosrc => 'select $2 + $1' },
+  proargtypes => 'interval timetz', prosrc => 'see system_views.sql' },
 { oid => '2548',
   proname => 'interval_pl_timestamp', prolang => 'sql',
   prorettype => 'timestamp', proargtypes => 'interval timestamp',
-  prosrc => 'select $2 + $1' },
+  prosrc => 'see system_views.sql' },
 { oid => '2549',
   proname => 'interval_pl_timestamptz', prolang => 'sql', provolatile => 's',
   prorettype => 'timestamptz', proargtypes => 'interval timestamptz',
-  prosrc => 'select $2 + $1' },
+  prosrc => 'see system_views.sql' },
 { oid => '2550',
   proname => 'integer_pl_date', prolang => 'sql', prorettype => 'date',
-  proargtypes => 'int4 date', prosrc => 'select $2 + $1' },
+  proargtypes => 'int4 date', prosrc => 'see system_views.sql' },

 { oid => '2556', descr => 'get OIDs of databases in a tablespace',
   proname => 'pg_tablespace_databases', prorows => '1000', proretset => 't',
@@ -8601,8 +8597,7 @@
   prosrc => 'xpath' },
 { oid => '2932', descr => 'evaluate XPath expression',
   proname => 'xpath', prolang => 'sql', prorettype => '_xml',
-  proargtypes => 'text xml',
-  prosrc => 'select pg_catalog.xpath($1, $2, \'{}\'::pg_catalog.text[])' },
+  proargtypes => 'text xml', prosrc => 'see system_views.sql' },

 { oid => '2614', descr => 'test XML value against XPath expression',
   proname => 'xmlexists', prorettype => 'bool', proargtypes => 'text xml',
@@ -8614,8 +8609,7 @@
   proargtypes => 'text xml _text', prosrc => 'xpath_exists' },
 { oid => '3050', descr => 'test XML value against XPath expression',
   proname => 'xpath_exists', prolang => 'sql', prorettype => 'bool',
-  proargtypes => 'text xml',
-  prosrc => 'select pg_catalog.xpath_exists($1, $2, \'{}\'::pg_catalog.text[])' },
+  proargtypes => 'text xml', prosrc => 'see system_views.sql' },
 { oid => '3051', descr => 'determine if a string is well formed XML',
   proname => 'xml_is_well_formed', provolatile => 's', prorettype => 'bool',
   proargtypes => 'text', prosrc => 'xml_is_well_formed' },
@@ -8880,7 +8874,7 @@
   proargtypes => 'pg_lsn numeric', prosrc => 'pg_lsn_pli' },
 { oid => '5023',
   proname => 'numeric_pl_pg_lsn', prolang => 'sql', prorettype => 'pg_lsn',
-  proargtypes => 'numeric pg_lsn', prosrc => 'select $2 + $1' },
+  proargtypes => 'numeric pg_lsn', prosrc => 'see system_views.sql' },
 { oid => '5024',
   proname => 'pg_lsn_mii', prorettype => 'pg_lsn',
   proargtypes => 'pg_lsn numeric', prosrc => 'pg_lsn_mii' },
@@ -9319,6 +9313,23 @@
   proname => 'ts_lexize', prorettype => '_text',
   proargtypes => 'regdictionary text', prosrc => 'ts_lexize' },

+{ oid => '9531', descr => 'debug function for text search configuration',
+  proname => 'ts_debug', prolang => 'sql', prorows => '1000', proretset => 't',
+  provolatile => 's', prorettype => 'record', proargtypes => 'regconfig text',
+  proallargtypes => '{regconfig,text,text,text,text,_regdictionary,regdictionary,_text}',
+  proargmodes => '{i,i,o,o,o,o,o,o}',
+  proargnames => '{config,document,alias,description,token,dictionaries,dictionary,lexemes}',
+  prosrc => 'see system_views.sql' },
+
+{ oid => '9532',
+  descr => 'debug function for current text search configuration',
+  proname => 'ts_debug', prolang => 'sql', prorows => '1000', proretset => 't',
+  provolatile => 's', prorettype => 'record', proargtypes => 'text',
+  proallargtypes => '{text,text,text,text,_regdictionary,regdictionary,_text}',
+  proargmodes => '{i,o,o,o,o,o,o}',
+  proargnames => '{document,alias,description,token,dictionaries,dictionary,lexemes}',
+  prosrc => 'see system_views.sql' },
+
 { oid => '3725', descr => '(internal)',
   proname => 'dsimple_init', prorettype => 'internal',
   proargtypes => 'internal', prosrc => 'dsimple_init' },

pgsql-hackers by date:

Previous
From: Thomas Munro
Date:
Subject: Re: Collation versioning
Next
From: Andres Freund
Date:
Subject: Re: Replacing pg_depend PIN entries with a fixed range check