Re: PostgreSQL 8.1.0 catalog corruption

From: Bob Ippolito <bob(at)redivi(dot)com>
To: Alvaro Herrera <alvherre(at)commandprompt(dot)com>
Cc: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, pgsql-hackers(at)postgresql(dot)org
Subject: Re: PostgreSQL 8.1.0 catalog corruption
Date: 2005-11-22 01:07:26
Message-ID: 8CCA2953-6099-4948-87FF-3C3C5B118C11@redivi.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers


On Nov 21, 2005, at 4:33 PM, Alvaro Herrera wrote:

> Tom Lane wrote:
>> Bob Ippolito <bob(at)redivi(dot)com> writes:
>>> On Nov 21, 2005, at 3:56 PM, Tom Lane wrote:
>>>> Well, I count at least a couple hundred deleted versions of that
>>>> table
>>>> row :-(. What the heck were you doing with it?
>>
>>> The ETL process keeps trying until it succeeds or someone stops it,
>>> so I guess that's why there's so much churn in there for that table.
>>> Kept trying to create it, and ran into the issue. I'd estimate
>>> around 1700 to 1800 dead versions of that table, because it ran for
>>> some time before I noticed and stopped it... this is just a test box
>>> after all, I don't have 8.1 in production yet (thankfully!).
>>
>> Um, no, that theory doesn't seem to explain the evidence. A failed
>> insertion would result in a row with an uncommitted XMIN and no XMAX.
>> All of the entries I'm seeing have both XMIN and XMAX set. A good-
>> size
>> fraction have the same XMIN and XMAX (but different CMIN and
>> CMAX), but
>> I see some that have different XMIN and XMAX. It looks to me like
>> the
>> table was definitely created successfully, and it survived across
>> multiple transactions ... but something was doing a lot of DDL
>> changes
>> on it. If we could find out what, maybe we could reproduce the
>> problem.
>
> Maybe the UPDATE pg_class SET relhastriggers='f' that people is so
> fond
> of doing to deactivate triggers? Or something similar?

I don't touch pg_class at all... this is what I'm doing (over and
over again).

-- clone_table is almost always a no-op, but once a day it creates a
new table
SELECT clone_table('ping', 'ping_%s', '')
SELECT drop_ping_constraints('ping_%s')
-- stuff that doesn't effect DDL
SELECT add_ping_constraints('ping_%s')

and the referenced UDFs are as follows:

CREATE OR REPLACE FUNCTION
clone_table(parent text, child text, extra text) RETURNS boolean
AS $$
DECLARE
tmprec record;
user_index record;
parent_constraint record;
user_index_column record;
indexname text;
i integer;
columns text[];
BEGIN
-- are we done?
FOR tmprec IN
SELECT 1 FROM pg_sysviews.pg_user_tables WHERE
table_name=child
LOOP

RETURN FALSE;
END LOOP;

-- inherit the table
EXECUTE 'CREATE TABLE '
|| quote_ident(child)
|| '('
|| extra
|| ') INHERITS ('
|| quote_ident(parent)
|| ')';

FOR parent_constraint IN
SELECT *
FROM pg_sysviews.pg_user_table_constraints A
WHERE A.table_name = parent
LOOP
EXECUTE 'ALTER TABLE '
|| quote_ident(child)
|| ' ADD '
|| parent_constraint.definition;
END LOOP;

i := 0;
FOR user_index IN
SELECT *
FROM pg_sysviews.pg_user_indexes A
WHERE
A.table_name = parent
AND A.index_name != (parent || '_pkey')
LOOP

i := i + 1;
indexname := child;
columns := '{}'::text[];
FOR user_index_column IN
SELECT B.column_name, quote_ident(B.column_name) AS col
FROM pg_sysviews.pg_user_index_columns B
WHERE
B.table_name = user_index.table_name
AND B.index_name = user_index.index_name
ORDER BY B.column_position
LOOP

indexname := indexname || '_' ||
user_index_column.column_name;
columns := array_append(columns, user_index_column.col);
END LOOP;

IF user_index.predicate IS NOT NULL THEN
indexname := indexname || '_p' || i::text;
END IF;

-- this is not complete, but works
-- missing tablespace, index_method, is_clustered,
EXECUTE ('CREATE '
|| (CASE WHEN user_index.is_unique THEN 'UNIQUE ' ELSE
'' END)
|| 'INDEX '
|| quote_ident(indexname)
|| ' ON '
|| quote_ident(child)
|| ' USING '
|| quote_ident(user_index.index_method)
|| ' ('
|| array_to_string(columns, ',')
|| ')'
|| (CASE WHEN user_index.predicate IS NOT NULL
THEN ' WHERE ' || user_index.predicate
ELSE '' END)
);

END LOOP;

RETURN TRUE;

END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION
drop_ping_constraints(ping_table text) RETURNS void
AS $drop_ping_constraints$
DECLARE
next_sql text;
constraint_rec record;
BEGIN

next_sql := $sql$
SELECT
"constraint_name"
FROM pg_sysviews.pg_user_table_constraints
WHERE "constraint_name" IN ($sql$
|| quote_literal(ping_table || '_timestamp_check')
|| ', '
|| quote_literal(ping_table || '_id_check')
|| ')';

-- RAISE NOTICE 'SQL: %', next_sql;
FOR constraint_rec IN EXECUTE next_sql LOOP
next_sql := 'ALTER TABLE '
|| quote_ident(ping_table)
|| ' DROP CONSTRAINT '
|| quote_ident(constraint_rec.constraint_name);
-- RAISE NOTICE 'SQL: %', next_sql;
EXECUTE next_sql;
END LOOP;

RETURN;
END
$drop_ping_constraints$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION
add_ping_constraints(ping_table text) RETURNS void
AS $add_ping_constraints$
DECLARE
next_sql text;
extents_rec RECORD;
BEGIN

next_sql := $sql$
SELECT
MIN("id") AS "min_id",
MAX("id") AS "max_id",
MIN("timestamp") AS "min_timestamp",
MAX("timestamp") AS "max_timestamp"
FROM $sql$
|| quote_ident(ping_table);

-- RAISE NOTICE 'SQL: %', next_sql;
FOR extents_rec IN EXECUTE next_sql LOOP
END LOOP;

next_sql := 'ALTER TABLE '
|| quote_ident(ping_table)
|| ' ADD CHECK("id" BETWEEN '
|| quote_literal(extents_rec.min_id) || '::bigint'
|| ' AND '
|| quote_literal(extents_rec.max_id) || '::bigint'
|| ')';
-- RAISE NOTICE 'SQL: %', next_sql;
EXECUTE next_sql;

next_sql := 'ALTER TABLE '
|| quote_ident(ping_table)
|| ' ADD CHECK("timestamp" BETWEEN '
|| quote_literal(extents_rec.min_timestamp) || '::timestamptz'
|| ' AND '
|| quote_literal(extents_rec.max_timestamp) || '::timestamptz'
|| ')';

-- RAISE NOTICE 'SQL: %', next_sql;
EXECUTE next_sql;

RETURN;
END
$add_ping_constraints$ LANGUAGE plpgsql;

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Tom Lane 2005-11-22 01:09:16 Re: [HACKERS] Should libedit be preferred to libreadline?
Previous Message Jim C. Nasby 2005-11-22 01:05:07 Re: Bug in predicate indexes?