pg.ddx.io  pgsql-bugs@postgresql.org mailing list archive  
help / color / mirror / Atom feed
Fallout from 'Make type "name" collation-aware' and tg_table_name
3+ messages / 2 participants
[nested] [flat]

* Fallout from 'Make type "name" collation-aware' and tg_table_name
@ 2022-06-08 13:07  Christoph Berg <christoph.berg@credativ.de>
  0 siblings, 1 reply; 3+ messages in thread

From: Christoph Berg @ 2022-06-08 13:07 UTC (permalink / raw)
  To: pgsql-bugs

Hi,

this is more a heads-up than a bug report, but I'm still reporting it
here because I think that variant of the problem has not been
addressed in the releases notes.

I was just debugging a client problem where a trigger was running fine
with PG 11, but very slow with PG 13. The symptom is that some COLLATE "C"
is appearing in the auto_explain output:

Good trigger plan on PG 11:

     ->  Index Scan using cache_revisions_zone_table_rev on cache_revisions (cost=0.15..8.17 rows=1 width=218) (actual time=0.009..0.009 rows=0 loops=1)
           Index Cond: (((zone_id)::text = 'insert-test'::text) AND ((table_name)::text = 'provider'::text) AND (revision < 1))
           Buffers: shared hit=1

Bad trigger plan on PG 13 (and PG 12):

     ->  Seq Scan on cache_revisions (cost=0.00..15.60 rows=1 width=218) (actual time=0.007..0.007 rows=0 loops=1)
           Filter: ((revision < 1) AND ((table_name)::text = 'provider'::text COLLATE "C") AND ((zone_id)::text = 'insert-test'::text COLLATE "C"))
           Rows Removed by Filter: 1
           Buffers: shared hit=1

Git-bisecting leads to this commit introducing the change:

commit 586b98fdf1aaef4a27744f8b988479aad4bd9a01
Author: Tom Lane <tgl@sss.pgh.pa.us>
Date:   Wed Dec 19 17:35:12 2018 -0500

    Make type "name" collation-aware.

    The "name" comparison operators now all support collations, making them
    functionally equivalent to "text" comparisons, except for the different
    physical representation of the datatype.  They do, in fact, mostly share
    the varstr_cmp and varstr_sortsupport infrastructure, which has been
    slightly enlarged to handle the case.

    To avoid changes in the default behavior of the datatype, set name's
    typcollation to C_COLLATION_OID not DEFAULT_COLLATION_OID, so that
    by default comparisons to a name value will continue to use strcmp
    semantics.  (This would have been the case for system catalog columns
    anyway, because of commit 6b0faf723, but doing this makes it true for
    user-created name columns as well.  In particular, this avoids
    locale-dependent changes in our regression test results.)

    In consequence, tweak a couple of places that made assumptions about
    collatable base types always having typcollation DEFAULT_COLLATION_OID.
    I have not, however, attempted to relax the restriction that user-
    defined collatable types must have that.  Hence, "name" doesn't
    behave quite like a user-defined type; it acts more like a domain
    with COLLATE "C".  (Conceivably, if we ever get rid of the need for
    catalog name columns to be fixed-length, "name" could actually become
    such a domain over text.  But that'd be a pretty massive undertaking,
    and I'm not volunteering.)

    Discussion: https://postgr.es/m/15938.1544377821@sss.pgh.pa.us


And indeed the "name" type is used in the trigger code, via
tg_table_name:

CREATE FUNCTION public.update_cache_revision() RETURNS trigger
    LANGUAGE plpgsql SECURITY DEFINER
    AS $$
DECLARE
    v_zone_id    TEXT;
BEGIN
        v_zone_id := NEW.zone_id;

        PERFORM increase_revision(tg_table_name::TEXT, v_zone_id);

        RETURN NEW;
END;
$$;

The "C" collation of name on tg_table_name is preserved by the ::text
cast, and the code in increase_revision() then fails to use the btree
index on cache_revisions (where the column is of type "text") because
the collation does not match.

The intent of the change seems to have been not to change the
user-visible behavior, but here it's clearly changed when leaving the
collation at "default" would have meant no change.

Of course the fix is to attach 'collate "default"' to tg_table_name:

        PERFORM increase_revision(tg_table_name::TEXT collate "default", v_zone_id);

The PG 12 release notes have this note:

     <para>
      Mark table columns of type <link
      linkend="datatype-character-special-table">name</link> as having
      <quote>C</quote> collation by default (Tom Lane, Daniel Vérité)
     </para>

     <para>
      The comparison operators for data type <type>name</type> can now use
      any collation, rather than always using <quote>C</quote> collation.
      To preserve the previous semantics of queries, columns of
      type <type>name</type> are now explicitly marked as
      having <quote>C</quote> collation.  A side effect of this is that
      regular-expression operators on <type>name</type> columns will now
      use the <quote>C</quote> collation by default, not the database
      collation, to determine the behavior of locale-dependent regular
      expression patterns (such as <literal>\w</literal>).  If you want
      non-C behavior for a regular expression on a <type>name</type>
      column, attach an explicit <literal>COLLATE</literal> clause.  (For
      user-defined <type>name</type> columns, another possibility is to
      specify a different collation at table creation time; but that just
      moves the non-backwards-compatibility to the comparison operators.)
     </para>

Of course it's a bit too late now to change that, but the impact is
clearly wider than just regular expression operations.

Christoph
-- 
Senior Consultant, Tel.: +49 2166 9901 187
credativ GmbH, HRB Mönchengladbach 12080, USt-ID-Nummer: DE204566209
Trompeterallee 108, 41189 Mönchengladbach
Geschäftsführung: Dr. Michael Meskes, Geoff Richardson, Peter Lilley
Unser Umgang mit personenbezogenen Daten unterliegt folgenden
Bestimmungen: https://www.credativ.de/datenschutz





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

* Re: Fallout from 'Make type "name" collation-aware' and tg_table_name
@ 2022-06-08 14:05  Tom Lane <tgl@sss.pgh.pa.us>
  parent: Christoph Berg <christoph.berg@credativ.de>
  0 siblings, 1 reply; 3+ messages in thread

From: Tom Lane @ 2022-06-08 14:05 UTC (permalink / raw)
  To: Christoph Berg <christoph.berg@credativ.de>; +Cc: pgsql-bugs

Christoph Berg <christoph.berg@credativ.de> writes:
> I was just debugging a client problem where a trigger was running fine
> with PG 11, but very slow with PG 13.

Hmmm ... I do not see how the collation within the PERFORM statement
would affect whatever it's calling?

			regards, tom lane





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

* Re: Fallout from 'Make type "name" collation-aware' and tg_table_name
@ 2022-06-08 14:12  Christoph Berg <christoph.berg@credativ.de>
  parent: Tom Lane <tgl@sss.pgh.pa.us>
  0 siblings, 0 replies; 3+ messages in thread

From: Christoph Berg @ 2022-06-08 14:12 UTC (permalink / raw)
  To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: pgsql-bugs

Re: Tom Lane
> Christoph Berg <christoph.berg@credativ.de> writes:
> > I was just debugging a client problem where a trigger was running fine
> > with PG 11, but very slow with PG 13.
> 
> Hmmm ... I do not see how the collation within the PERFORM statement
> would affect whatever it's calling?

Here's the actual reproducer I used:

$ cat run.sql
drop database if exists test;
create database test;
\c test

\i pgdump
reset all;

alter system set log_line_prefix = '%m [%p] %q%u@%d %a ';
select pg_reload_conf();

set jit=off;
LOAD 'auto_explain';
set auto_explain.log_analyze=on;
set auto_explain.log_buffers=on;
set auto_explain.log_min_duration=0;
set auto_explain.log_nested_statements=on;
set auto_explain.log_triggers=on;

set application_name = 'collate_bug';
INSERT INTO public.identity_provider (id, created, lastmodified, "version", identity_zone_id, "name", origin_key, "type", config, active)
VALUES('24e9dd6d-5a63-9ea8-b425-ae89aa419a33', CURRENT_TIMESTAMP,CURRENT_TIMESTAMP, 0, 'insert-test', 'test', 'test', 'test', 'none', true);


Mit freundlichen Grüßen,
Christoph Berg
-- 
Senior Consultant, Tel.: +49 2166 9901 187
credativ GmbH, HRB Mönchengladbach 12080, USt-ID-Nummer: DE204566209
Trompeterallee 108, 41189 Mönchengladbach
Geschäftsführung: Dr. Michael Meskes, Geoff Richardson, Peter Lilley
Unser Umgang mit personenbezogenen Daten unterliegt folgenden
Bestimmungen: https://www.credativ.de/datenschutz

Attachments:

pgdump (text/plain, 5.8K) download -- -- PostgreSQL database dump -- -- Dumped from database version 13.6 (Debian 13.6-1.pgdg110+1) -- Dumped by pg_dump version 13.7 (Ubuntu 13.7-1.pgdg20.04+1) SET statement_timeout = 0; SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; SELECT pg_catalog.set_config('search_path', '', false); SET check_function_bodies = false; SET xmloption = content; SET client_min_messages = warning; SET row_security = off; -- -- Name: increase_revision(text, text); Type: FUNCTION; Schema: public; Owner: root -- CREATE FUNCTION public.increase_revision(v_table_name text, v_identity_zone_id text) RETURNS integer LANGUAGE plpgsql AS $$ DECLARE rev INTEGER; updated_rows INTEGER; BEGIN SELECT nextval('xs_cache_revisions_seq') INTO rev; UPDATE xs_cache_revisions SET revision = rev WHERE table_name = v_table_name AND identity_zone_id = v_identity_zone_id AND revision = (SELECT revision FROM xs_cache_revisions WHERE table_name = v_table_name AND identity_zone_id = v_identity_zone_id FOR UPDATE SKIP LOCKED LIMIT 1); GET DIAGNOSTICS updated_rows = ROW_COUNT; IF (updated_rows = 0) THEN INSERT INTO xs_cache_revisions(table_name, identity_zone_id, revision) values (v_table_name, v_identity_zone_id, rev); END IF; delete from xs_cache_revisions where (table_name,identity_zone_id,revision) in (select table_name,identity_zone_id,revision from xs_cache_revisions where (table_name = v_table_name and identity_zone_id = v_identity_zone_id and revision < rev) for update SKIP LOCKED); RETURN rev; END; $$; --ALTER FUNCTION public.increase_revision(v_table_name text, v_identity_zone_id text) OWNER TO root; -- -- Name: update_cache_revision(); Type: FUNCTION; Schema: public; Owner: root -- CREATE FUNCTION public.update_cache_revision() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_zone_id TEXT; BEGIN IF (TG_OP = 'UPDATE') or (TG_OP = 'INSERT') THEN v_zone_id := NEW.identity_zone_id; PERFORM increase_revision(tg_table_name::TEXT, v_zone_id); RETURN NEW; ELSIF (TG_OP = 'DELETE') THEN v_zone_id := OLD.identity_zone_id; PERFORM increase_revision(tg_table_name::TEXT, v_zone_id); RETURN OLD; END IF; END; $$; --ALTER FUNCTION public.update_cache_revision() OWNER TO root; SET default_tablespace = ''; --SET default_table_access_method = heap; -- -- Name: identity_provider; Type: TABLE; Schema: public; Owner: root -- CREATE TABLE public.identity_provider ( id character varying(36) NOT NULL, created timestamp without time zone DEFAULT CURRENT_TIMESTAMP, lastmodified timestamp without time zone DEFAULT CURRENT_TIMESTAMP, version bigint DEFAULT 0, identity_zone_id character varying(36) NOT NULL, name character varying(255) NOT NULL, origin_key character varying(36) NOT NULL, type character varying(255) NOT NULL, config text, active boolean DEFAULT true NOT NULL ); --ALTER TABLE public.identity_provider OWNER TO root; -- -- Name: xs_cache_revisions; Type: TABLE; Schema: public; Owner: root -- CREATE TABLE public.xs_cache_revisions ( table_name character varying(48) NOT NULL, identity_zone_id character varying(36) NOT NULL, revision bigint NOT NULL, created timestamp without time zone DEFAULT now() ); --ALTER TABLE public.xs_cache_revisions OWNER TO root; -- -- Name: xs_cache_revisions_revision_seq; Type: SEQUENCE; Schema: public; Owner: root -- CREATE SEQUENCE public.xs_cache_revisions_revision_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; --ALTER TABLE public.xs_cache_revisions_revision_seq OWNER TO root; -- -- Name: xs_cache_revisions_revision_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: root -- ALTER SEQUENCE public.xs_cache_revisions_revision_seq OWNED BY public.xs_cache_revisions.revision; -- -- Name: xs_cache_revisions_seq; Type: SEQUENCE; Schema: public; Owner: root -- CREATE SEQUENCE public.xs_cache_revisions_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; --ALTER TABLE public.xs_cache_revisions_seq OWNER TO root; -- -- Name: xs_cache_revisions revision; Type: DEFAULT; Schema: public; Owner: root -- ALTER TABLE ONLY public.xs_cache_revisions ALTER COLUMN revision SET DEFAULT nextval('public.xs_cache_revisions_revision_seq'::regclass); -- -- Name: identity_provider identity_provider_pkey; Type: CONSTRAINT; Schema: public; Owner: root -- ALTER TABLE ONLY public.identity_provider ADD CONSTRAINT identity_provider_pkey PRIMARY KEY (id); -- -- Name: xs_cache_revisions xs_cache_revisions_constr; Type: CONSTRAINT; Schema: public; Owner: root -- ALTER TABLE ONLY public.xs_cache_revisions ADD CONSTRAINT xs_cache_revisions_constr UNIQUE (table_name, identity_zone_id, revision); -- -- Name: active_in_zone; Type: INDEX; Schema: public; Owner: root -- CREATE INDEX active_in_zone ON public.identity_provider USING btree (identity_zone_id, active); -- -- Name: key_in_zone; Type: INDEX; Schema: public; Owner: root -- CREATE UNIQUE INDEX key_in_zone ON public.identity_provider USING btree (identity_zone_id, origin_key); -- -- Name: xs_cache_revisions_zone_table_rev; Type: INDEX; Schema: public; Owner: root -- CREATE INDEX xs_cache_revisions_zone_table_rev ON public.xs_cache_revisions USING btree (identity_zone_id, table_name, revision); -- -- Name: identity_provider t_identity_provider_change_trigger; Type: TRIGGER; Schema: public; Owner: root -- CREATE TRIGGER t_identity_provider_change_trigger AFTER INSERT OR DELETE OR UPDATE ON public.identity_provider FOR EACH ROW EXECUTE PROCEDURE public.update_cache_revision('false'); -- -- PostgreSQL database dump complete --
^ permalink raw reply [nested|flat] 3+ messages in thread


end of thread, other threads:[~2022-06-08 14:12 UTC | newest]

Thread overview: 3+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2022-06-08 13:07 Fallout from 'Make type "name" collation-aware' and tg_table_name Christoph Berg <christoph.berg@credativ.de>
2022-06-08 14:05 ` Tom Lane <tgl@sss.pgh.pa.us>
2022-06-08 14:12   ` Christoph Berg <christoph.berg@credativ.de>

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox