Обсуждение: In processing DDL, when does pg_catalog get updated?
I'm running a DDL script that does the following (in this order):
1. Creates a table containing a BIGSERIAL primary key column declaration,
which apparently automatically creates a sequence to populate this column.
2. Runs a "gen_sequences" function that I wrote, which executes CREATE
SEQUENCE statements for all columns in the table that have defaults like
'nextval%' but that don't already have sequences. The part of the function
that checks that the sequence doesn't already exist consults the pg_catalog,
as follows:
IF NOT EXISTS (SELECT 1
FROM pg_catalog.pg_class AS t,
pg_catalog.pg_namespace AS s
WHERE t.relname = sequence_name
AND s.nspname = schema_name
AND t.relnamespace = s.oid
AND t.relkind = 'S')
THEN
<execute the CREATE SEQUENCE statement>
END IF;
This script aborts with a message like this:
ERROR: relation "my_table_id_seq" already exists
...which implies that the code above is not finding "my_table_id_seq" in the
catalog. I know that the code works OK in detecting sequences that
pre-existed the execution of this script. So the only explanation that I
can come up with is that, at step 2, the pg_catalog has not yet been updated
to reflect the results of step 1 - namely, that the new sequence has been
created.
Is it possible that the pg_catalog is not updated with the results of a DDL
script until the whole script has executed?
If this is so, is there any way to force the pg_catalog to be updated along
the way?
~ TIA
~ Ken
"Ken Winter" <ken@sunward.org> writes:
> Is it possible that the pg_catalog is not updated with the results of a DDL
> script until the whole script has executed?
No, the serial sequence should exist as soon as the CREATE TABLE is done
... unless you are doing something weird like wrapping the whole thing
in a transaction and expecting uncommitted transaction results to be
visible from another session. Could you show us a complete test case
instead of an extract?
regards, tom lane
Tom ~
Good idea. The grisly details are as follows.
****************************
Here is the DDL script (generated from PowerDesigner 10.1.0.1134):
/*==============================================================*/
/* DBMS name: PostgreSQL 7.3 */
/* Created on: 12/30/2005 11:08:02 AM */
/*==============================================================*/
SET search_path TO public;
/*==============================================================*/
/* Table: e_mail_address */
/*==============================================================*/
create table e_mail_address (
pop_id INT8 not null default
nextval('pop_seq'),
effective_date_and_time TIMESTAMP WITH TIME ZONE not null default
CURRENT_TIMESTAMP,
invisible_id BIGSERIAL not null,
e_mail_type VARCHAR(255) null,
expiration_date_and_time TIMESTAMP WITH TIME ZONE null default 'infinity',
user_name VARCHAR(255) not null,
domain_name VARCHAR(255) not null,
use_this_e_mail_for_administrative_matters BOOL null,
use_this_e_mail_for_teaching_and_practice BOOL null,
use_this_e_mail_for_personal_messages BOOL null,
omit_this_e_mail_from_school_directory BOOL null,
comments VARCHAR(4000) null,
constraint PK_e_mail_address_priority_pk primary key (pop_id,
effective_date_and_time, invisible_id),
constraint fk_contact___e_mail_address foreign key (pop_id)
references pop (pop_id)
on delete cascade on update cascade,
constraint fk_e_mail_type___e_mail_address foreign key (e_mail_type)
references e_mail_type (e_mail_type)
on delete cascade on update cascade
)
INHERITS (when_and_who)
WITH OIDS;
SELECT gen_sequences('e_mail_address', 'public');
****************************
Here is the function "gen_sequences" that is evoking the error:
CREATE OR REPLACE FUNCTION gen_sequences ( VARCHAR, VARCHAR ) RETURNS
VARCHAR AS
'
DECLARE
table_name ALIAS FOR $1;
schema_name ALIAS FOR $2;
this_table RECORD;
dummy RECORD;
sequence_name VARCHAR;
cre_seq_arr VARCHAR [] := ''{}'';
cre_seq_code VARCHAR := '''';
BEGIN
EXECUTE ''SET search_path TO '' || schema_name;
/* Create a CREATE SEQUENCE statement for the sequence
of each sequence-assigned column,
if the sequence doesnt exist already. */
FOR this_table IN
SELECT c.column_name AS sub_idcol,
c.column_default AS default_expr
FROM information_schema.columns c
WHERE c.table_name = table_name
AND c.table_schema = schema_name
AND c.column_default LIKE ''nextval%''
LOOP
sequence_name := split_part(this_table.default_expr, '''''''',
2);
IF NOT EXISTS (SELECT 1
FROM pg_catalog.pg_class AS t,
pg_catalog.pg_namespace AS s
WHERE t.relname = sequence_name
AND s.nspname = schema_name
AND t.relnamespace = s.oid
AND t.relkind = ''S'')
THEN
IF array_upper(cre_seq_arr, 1) IS NULL THEN
cre_seq_arr[1] := ''CREATE SEQUENCE '' || sequence_name
|| '';'' ;
ELSE
cre_seq_arr[array_upper(cre_seq_arr, 1) + 1] := ''CREATE
SEQUENCE '' || sequence_name || '';'' ;
END IF;
cre_seq_code := cre_seq_code ||
cre_seq_arr[array_upper(cre_seq_arr, 1)] || ''
'';
END IF;
END LOOP;
/* Execute the CREATE SEQUENCE statements, if any. */
IF array_upper(cre_seq_arr, 1) IS NOT NULL THEN
FOR n IN 1..array_upper(cre_seq_arr, 1) LOOP
EXECUTE cre_seq_arr[n];
END LOOP;
END IF;
RETURN cre_seq_code;
END;
'
LANGUAGE plpgsql
;
****************************
And here is the error message from phpPgAdmin:
SQL error:
ERROR: relation "e_mail_address_invisible_id_seq" already exists
CONTEXT: PL/pgSQL function "gen_sequences" line 45 at execute statement
****************************
Line 45 is the line that contains the execute statement.
Those are the raw facts.
My question is: Why didn't the chunk of "gen_sequences" code that consults
pg_catalog find a record of "e_mail_address_invisible_id_seq", and thereby
refrain from trying to create it again?
~ Thanks again
~ Ken
"Ken Winter" <ken@sunward.org> writes:
> My question is: Why didn't the chunk of "gen_sequences" code that consults
> pg_catalog find a record of "e_mail_address_invisible_id_seq", and thereby
> refrain from trying to create it again?
I added a few "raise notice" commands to your function, and got this:
NOTICE: sub_idcol = invisible_id
NOTICE: default_exp = nextval('public.e_mail_address_invisible_id_seq'::text)
NOTICE: sequence_name = public.e_mail_address_invisible_id_seq
NOTICE: not found
NOTICE: sub_idcol = pop_id
NOTICE: default_exp = nextval('pop_seq'::text)
NOTICE: sequence_name = pop_seq
NOTICE: found
ERROR: relation "e_mail_address_invisible_id_seq" already exists
CONTEXT: SQL statement "CREATE SEQUENCE public.e_mail_address_invisible_id_seq;"
PL/pgSQL function "gen_sequences" line 51 at execute statement
The problem seems to be that you're not accounting for a schema name
possibly appearing in nextval's argument.
regards, tom lane
Tom ~ Right you are! I added some code to trim off the schema name, and it works fine. You also alerted me to the NOTICE facility. (I'm new to PostgreSQL, and have been learning it in "wade right in" mode rather than properly studying the whole environment.) I have a "PostgreSQL for Dummies" question about RAISE NOTICE: Where do I find its output? According to the documentation (http://www.postgresql.org/docs/7.4/static/plpgsql-errors-and-messages.htmlh ttp://www.postgresql.org/docs/7.4/static/plpgsql-errors-and-messages.html), the messages are either "reported to the client, written to the server log, or both". My database is on a web host (zettai.net), and I'm working on it via phpPgAdmin. I don't know where to find messages "reported to the client", and I don't know how to access the system log. Can anyone help? ~ Thanks! ~ Ken > -----Original Message----- > From: Tom Lane [mailto:tgl@sss.pgh.pa.us] > Sent: Friday, December 30, 2005 12:10 PM > To: Ken Winter > Cc: 'PostgreSQL pg-general List' > Subject: Re: [GENERAL] In processing DDL, when does pg_catalog get > updated? > > "Ken Winter" <ken@sunward.org> writes: > > My question is: Why didn't the chunk of "gen_sequences" code that > consults > > pg_catalog find a record of "e_mail_address_invisible_id_seq", and > thereby > > refrain from trying to create it again? > > I added a few "raise notice" commands to your function, and got this: > > NOTICE: sub_idcol = invisible_id > NOTICE: default_exp = > nextval('public.e_mail_address_invisible_id_seq'::text) > NOTICE: sequence_name = public.e_mail_address_invisible_id_seq > NOTICE: not found > NOTICE: sub_idcol = pop_id > NOTICE: default_exp = nextval('pop_seq'::text) > NOTICE: sequence_name = pop_seq > NOTICE: found > ERROR: relation "e_mail_address_invisible_id_seq" already exists > CONTEXT: SQL statement "CREATE SEQUENCE > public.e_mail_address_invisible_id_seq;" > PL/pgSQL function "gen_sequences" line 51 at execute statement > > The problem seems to be that you're not accounting for a schema name > possibly appearing in nextval's argument. > > regards, tom lane