Thread: Converting to identity columns with domains on PK columns

Converting to identity columns with domains on PK columns

From
PegoraroF10
Date:
Domains on Postgres are really strange to me. Am I creating a domain which is
exactly equal to integer, right ?

create domain i32 as integer;
create domain T50 as varchar(50);

Create table MyTable(
ID I32 not null primary key,
Description T50);

Then, after inserts and updates done to that table, I want to convert that
primary key to a identity column.

alter table MyTable alter ID add generated always as identity;

ERROR: identity column type must be smallint, integer, or bigint

So, What do I need do to create this identity column ?
Why Postgres consider different I32 and integer ?



--
Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/4/19 12:41 PM, PegoraroF10 wrote:
> Domains on Postgres are really strange to me. Am I creating a domain which is
> exactly equal to integer, right ?
> 
> create domain i32 as integer;
> create domain T50 as varchar(50);
> 
> Create table MyTable(
> ID I32 not null primary key,
> Description T50);
> 
> Then, after inserts and updates done to that table, I want to convert that
> primary key to a identity column.
> 
> alter table MyTable alter ID add generated always as identity;
> 
> ERROR: identity column type must be smallint, integer, or bigint
> 
> So, What do I need do to create this identity column ?
> Why Postgres consider different I32 and integer ?

Because one(integer) is a base type and the other is a domain over a 
base type(I32). Domains can have restrictions over what is accepted so I 
can see why they would not be good candidates for a sequence(identity).

Solutions:

1) Create a new integer column for the identity.

2) alter table MyTable alter ID type integer;

    alter table MyTable alter ID add generated always as identity;

\d 'MyTable' 
 

                            Table "public.mytable" 
 

    Column    |  Type   | Collation | Nullable |           Default 
 

-------------+---------+-----------+----------+------------------------------ 
 

  id          | integer |           | not null | generated always as 
identity 

  description | t50     |           |          | 
 



> 
> 
> 
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
> 
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Converting to identity columns with domains on PK columns

From
PegoraroF10
Date:
ok, thanks for the explanation but ... I cannot add a field and move data,
constraints, triggers, identity to it because my pk field will be
repositioned to the last field on that table and I have lots of other codes
which point to pk as the first field on every table.

So, there is a way to convert that field to a identity field ?




--
Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/4/19 1:03 PM, PegoraroF10 wrote:
> ok, thanks for the explanation but ... I cannot add a field and move data,
> constraints, triggers, identity to it because my pk field will be
> repositioned to the last field on that table and I have lots of other codes
> which point to pk as the first field on every table.

I stay away from using the index position of field for this reason.

> 
> So, there is a way to convert that field to a identity field ?

See my second option in previous post.

Or, old school identity column:):

create sequence id_seq owned by mytable.id;

alter table mytable alter column id set default nextval('id_seq');

\d mytable
                          Table "public.mytable"
    Column    | Type | Collation | Nullable |           Default
-------------+------+-----------+----------+-----------------------------
  id          | i32  |           | not null | nextval('id_seq'::regclass)
  description | t50  |           |          |
Indexes:
     "mytable_pkey" PRIMARY KEY, btree (id)


> 
> 
> 
> 
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
> 
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/4/19 1:27 PM, Adrian Klaver wrote:
> On 7/4/19 1:03 PM, PegoraroF10 wrote:
>> ok, thanks for the explanation but ... I cannot add a field and move 
>> data,
>> constraints, triggers, identity to it because my pk field will be
>> repositioned to the last field on that table and I have lots of other 
>> codes
>> which point to pk as the first field on every table.
> 
> I stay away from using the index position of field for this reason.
> 
>>
>> So, there is a way to convert that field to a identity field ?
> 
> See my second option in previous post.
> 
> Or, old school identity column:):
> 
> create sequence id_seq owned by mytable.id;
> 
> alter table mytable alter column id set default nextval('id_seq');
> 
> \d mytable
>                           Table "public.mytable"
>     Column    | Type | Collation | Nullable |           Default
> -------------+------+-----------+----------+-----------------------------
>   id          | i32  |           | not null | nextval('id_seq'::regclass)
>   description | t50  |           |          |
> Indexes:
>      "mytable_pkey" PRIMARY KEY, btree (id)
> 
> 

I know this worked, but then I got to wondering why?

Found the answer in sequence.c(init_params) in the if (as_type != NULL) 
section. When creating a sequence you can specify AS data_type as long 
as the type is one of smallint, integer or bigint. If data_type is not 
specified then the default is bigint. If I am following correctly in 
tablecommands.c when you create an IDENTITY column it uses the type it 
gets from the column for the AS data_type. In your case that would be a 
domain type which is != to the base types above.



-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/4/19 1:27 PM, Adrian Klaver wrote:
> On 7/4/19 1:03 PM, PegoraroF10 wrote:
>> ok, thanks for the explanation but ... I cannot add a field and move 
>> data,
>> constraints, triggers, identity to it because my pk field will be
>> repositioned to the last field on that table and I have lots of other 
>> codes
>> which point to pk as the first field on every table.
> 
> I stay away from using the index position of field for this reason.
> 
>>
>> So, there is a way to convert that field to a identity field ?
> 
> See my second option in previous post.
> 
> Or, old school identity column:):
> 
> create sequence id_seq owned by mytable.id;
> 
> alter table mytable alter column id set default nextval('id_seq');
> 
> \d mytable
>                           Table "public.mytable"
>     Column    | Type | Collation | Nullable |           Default
> -------------+------+-----------+----------+-----------------------------
>   id          | i32  |           | not null | nextval('id_seq'::regclass)
>   description | t50  |           |          |
> Indexes:
>      "mytable_pkey" PRIMARY KEY, btree (id)
> 

Further thinking pointed up the peril of the above. Your field is 
defined as integer and per my previous post a sequence without an AS 
data_type will be bigint. At some point the sequence is going to start 
trying to set nextval() to a number your field cannot handle. If you go 
that route you will need to do something like:

create sequence id_seq AS integer owned by mytable.id;


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Converting to identity columns with domains on PK columns

From
Laurenz Albe
Date:
PegoraroF10 wrote:
> Domains on Postgres are really strange to me. Am I creating a domain which is
> exactly equal to integer, right ?
> 
> create domain i32 as integer;
> create domain T50 as varchar(50);
> 
> Create table MyTable(
> ID I32 not null primary key,
> Description T50);
> 
> Then, after inserts and updates done to that table, I want to convert that
> primary key to a identity column.
> 
> alter table MyTable alter ID add generated always as identity;
> 
> ERROR: identity column type must be smallint, integer, or bigint
> 
> So, What do I need do to create this identity column ?
> Why Postgres consider different I32 and integer ?

A domain is more than just a different name for a data type, so
the system doesn't treat them as identical.

Another way to proceed would be:

ALTER TABLE mytable ALTER id TYPE integer;
ALTER TABLE mytable ALTER id ADD GENERATED ALWAYS AS IDENTITY;

That would not rewrite the table, just "relabel" the type name
to "integer" and then convert it to an identity column.


Why do you want that extra level of obfuscation rather than
calling an integer an integer?

Yours,
Laurenz Albe

-- 
Cybertec | https://www.cybertec-postgresql.com




Re: Converting to identity columns with domains on PK columns

From
PegoraroF10
Date:
Well, I think it´ll not as easy as you said. That tables has dependencies.
So, if I try to alter type it gives me ERROR: cannot alter type of a column
used in a trigger definition. I dropped all Triggers of that table and it
gives me ERROR: cannot alter type of a column used by a view or rule.
Then, if I need to drop everything to change that thing I think is better
just have an empty structure and copy entire database data to it.

And why do we used domains, because we were Firebird and on that database a
integer or a domain based on an integer is the same, so we converted to
Postgres using that way.

Thanks



--
Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/5/19 4:38 AM, PegoraroF10 wrote:
> Well, I think it´ll not as easy as you said. That tables has dependencies.
> So, if I try to alter type it gives me ERROR: cannot alter type of a column
> used in a trigger definition. I dropped all Triggers of that table and it
> gives me ERROR: cannot alter type of a column used by a view or rule.
> Then, if I need to drop everything to change that thing I think is better
> just have an empty structure and copy entire database data to it.
> 
> And why do we used domains, because we were Firebird and on that database a
> integer or a domain based on an integer is the same, so we converted to
> Postgres using that way.

That is not what the Firebird docs say:

https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-datatypes-custom.html

https://firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-ddl-domn.html

https://firebirdsql.org/file/documentation/release_notes/html/en/3_0/rnfb30-ddl-enhance.html

> 
> Thanks
> 
> 
> 
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
> 
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Converting to identity columns with domains on PK columns

From
PegoraroF10
Date:
Adrian, on Firebird if you create a table or procedure with fields or params
declared with domains, they can be used with those domains or with their
base type. On Postgres I32 is not equal to integer.

create procedure myproc(id i32) returns(x i32) as ...

select * from myproc(cast(5 as integer)) -- works on firebird. On Postgres
it doesn´t because that procedure or function expects for a I32 and not a
integer.



--
Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html



Re: Converting to identity columns with domains on PK columns

From
Tom Lane
Date:
PegoraroF10 <marcos@f10.com.br> writes:
> Adrian, on Firebird if you create a table or procedure with fields or params
> declared with domains, they can be used with those domains or with their
> base type. On Postgres I32 is not equal to integer.

> create procedure myproc(id i32) returns(x i32) as ... 

> select * from myproc(cast(5 as integer)) -- works on firebird. On Postgres
> it doesn´t because that procedure or function expects for a I32 and not a
> integer.

Really?

regression=# create domain i32 as int;
CREATE DOMAIN
regression=# create function myproc(id i32) returns i32 
language sql as 'select $1';
CREATE FUNCTION
regression=# select myproc(cast(5 as integer));
 myproc 
--------
      5
(1 row)

I think the primary reason we don't allow domains over int for identity
columns is that it's not clear how domain constraints ought to interact
with the identity-value-generating behavior.

            regards, tom lane



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/5/19 1:01 PM, PegoraroF10 wrote:
> Adrian, on Firebird if you create a table or procedure with fields or params
> declared with domains, they can be used with those domains or with their
> base type. On Postgres I32 is not equal to integer.
> 
> create procedure myproc(id i32) returns(x i32) as ...
> 
> select * from myproc(cast(5 as integer)) -- works on firebird. On Postgres
> it doesn´t because that procedure or function expects for a I32 and not a
> integer.

Tom beat me to the example. Still the point is that I think you are 
going to find that there is not that much difference between Firebird 
and Postgres on this point.

To get back to the original case, is there a specific reason you want a 
IDENTITY column on the id field?


> 
> 
> 
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Converting to identity columns with domains on PK columns

From
PegoraroF10
Date:
Sorry, the example I was thinking was this one, which works on Firebird,
using its way of writing, obviously.

create function myproc(id integer) returns I32 language sql as 'select $1';

On postgres ERROR: return type mismatch in function declared to return i32

What I mean is that Firebird sees I32 and integer as the same, Postgres
doesn´t.



--
Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html



Re: Converting to identity columns with domains on PK columns

From
PegoraroF10
Date:
- Because we don´t need to give rigths to user on sequences;
- Nobody will change values of pk fields, because we would like to have
GENERATE ALWAYS on those PK Fields.



--
Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/5/19 1:49 PM, PegoraroF10 wrote:
> Sorry, the example I was thinking was this one, which works on Firebird,
> using its way of writing, obviously.
> 
> create function myproc(id integer) returns I32 language sql as 'select $1';
> 
> On postgres ERROR: return type mismatch in function declared to return i32
> 
> What I mean is that Firebird sees I32 and integer as the same, Postgres
> doesn´t.

Yeah, but if you reverse the casting you did in your first example it works:

create function myproc(id integer) returns I32 language sql as 'select 
$1::i32';
CREATE FUNCTION

test_(aklaver)> select myproc(5);
  myproc
--------
       5
(1 row)

test_(aklaver)> select pg_typeof(myproc(5));
  pg_typeof
-----------
  i32
(1 row)

> 
> 
> 
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/5/19 1:55 PM, PegoraroF10 wrote:
> - Because we don´t need to give rigths to user on sequences;
> - Nobody will change values of pk fields, because we would like to have
> GENERATE ALWAYS on those PK Fields.

An IDENTITY column is still backed by a sequence:

create table identity_test(id integer PRIMARY KEY GENERATED BY DEFAULT 
AS IDENTITY);

\ds identity_test_id_seq
                  List of relations
  Schema |         Name         |   Type   |  Owner
--------+----------------------+----------+---------
  public | identity_test_id_seq | sequence | aklaver

You end up with same thing as using a sequence(with some additional 
syntax over its behavior):

create table seq_id_test(id integer PRIMARY KEY);

create sequence seq_id_test_seq AS integer OWNED BY seq_id_test.id;

\ds seq_id_test_seq
                List of relations
  Schema |      Name       |   Type   |  Owner
--------+-----------------+----------+---------
  public | seq_id_test_seq | sequence | aklaver


Rights are the same:

\c - production
You are now connected to database "test" as user "production".

test_(production)> insert into identity_test (id) values(default);
ERROR:  permission denied for table identity_test
test_(production)> insert into seq_id_test (id) values(default);
ERROR:  permission denied for table seq_id_test

A user can change the PK by using OVERRIDING SYSTEM VALUE in an INSERT.


> 
> 
> 
> --
> Sent from: http://www.postgresql-archive.org/PostgreSQL-general-f1843780.html
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Converting to identity columns with domains on PK columns

From
Tom Lane
Date:
Adrian Klaver <adrian.klaver@aklaver.com> writes:
> On 7/5/19 1:49 PM, PegoraroF10 wrote:
>> Sorry, the example I was thinking was this one, which works on Firebird,
>> using its way of writing, obviously.
>> create function myproc(id integer) returns I32 language sql as 'select $1';
>> 
>> On postgres ERROR: return type mismatch in function declared to return i32
>> What I mean is that Firebird sees I32 and integer as the same, Postgres
>> doesn´t.

> Yeah, but if you reverse the casting you did in your first example it works:
> create function myproc(id integer) returns I32 language sql as 'select 
> $1::i32';
> CREATE FUNCTION

Yeah.  This isn't an inherent property of Postgres, it's just that
SQL-language functions aren't defined to provide any implicit casting
of their results.  The given expression must yield exactly the declared
function result type.

Most other places in PG are laxer and will automatically perform
implicit (and maybe assignment) casts for you.  I don't remember
offhand whether there are good reasons for SQL functions to be
picky about this or it's just a shortage of round tuits.  I have
a vague feeling that there might be some compatibility issues
in there, though.

            regards, tom lane



Re: Converting to identity columns with domains on PK columns

From
Adrian Klaver
Date:
On 7/5/19 3:32 PM, Tom Lane wrote:
> Adrian Klaver <adrian.klaver@aklaver.com> writes:
>> On 7/5/19 1:49 PM, PegoraroF10 wrote:
>>> Sorry, the example I was thinking was this one, which works on Firebird,
>>> using its way of writing, obviously.
>>> create function myproc(id integer) returns I32 language sql as 'select $1';
>>>
>>> On postgres ERROR: return type mismatch in function declared to return i32
>>> What I mean is that Firebird sees I32 and integer as the same, Postgres
>>> doesn´t.
> 
>> Yeah, but if you reverse the casting you did in your first example it works:
>> create function myproc(id integer) returns I32 language sql as 'select
>> $1::i32';
>> CREATE FUNCTION
> 
> Yeah.  This isn't an inherent property of Postgres, it's just that
> SQL-language functions aren't defined to provide any implicit casting
> of their results.  The given expression must yield exactly the declared
> function result type.

Aah:

CREATE OR REPLACE FUNCTION public.domain_test(id integer)
  RETURNS i32
  LANGUAGE plpgsql
AS $function$
BEGIN
     RETURN id;
END;
$function$
;

test=> select domain_test(5);
  domain_test
-------------
            5

test=> select pg_typeof(domain_test(5));
  pg_typeof
-----------
  i32
(1 row)

So it works in plpgsql.


> 
> Most other places in PG are laxer and will automatically perform
> implicit (and maybe assignment) casts for you.  I don't remember
> offhand whether there are good reasons for SQL functions to be
> picky about this or it's just a shortage of round tuits.  I have
> a vague feeling that there might be some compatibility issues
> in there, though.
> 
>             regards, tom lane
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com