Serial Type Columns in PostgreSQL
PostgreSQL allows creating columns of types smallserial
, serial
, and bigserial
. These types are not
actual types, but more like "macros" for creating non-nullable integer columns with sequences attached.
We can see this in action by creating a table with 3 "serial columns":
CREATE TABLE serials(
c1 smallserial,
c2 serial,
c3 bigserial
);
Column | Type | Nullable | Default
--------+----------+----------+-------------------------------
c1 | smallint | not null | nextval('t_c1_seq'::regclass)
c2 | integer | not null | nextval('t_c2_seq'::regclass)
c3 | bigint | not null | nextval('t_c3_seq'::regclass)
As you can see, each serial column was created as non-nullable integer with a default value set to the next sequence value.
Note that nextval
increments the sequence by 1 and returns its value. Thus, the first call to
nextval('serials_c1_seq')
returns 1, the second returns 2, etc.
ALTER COLUMN
type to serial
Sometimes it is necessary to change the column type from integer
type to serial
. However, as mentioned above, the
serial
type is not a true type, and therefore, the following commands will fail:
CREATE TABLE t(
c integer not null primary key
);
ALTER TABLE t ALTER COLUMN c TYPE serial;
We can achieve this by manually creating a sequence
owned by the column c
, and setting the column DEFAULT
value to the incremental counter of the sequence using the
nextval
function.
Note that it is recommended to follow the PostgreSQL naming format (i.e. <table>_<column>_seq
)
when creating the sequence as some database tools know to detect such columns as "serial columns".
-- Create the sequence.
CREATE SEQUENCE "public"."t_c_seq" OWNED BY "public"."t"."c";
-- Assign it to the table default value.
ALTER TABLE "public"."t" ALTER COLUMN "c" SET DEFAULT nextval('"public"."t_c_seq"');
Update the sequence value
When a sequence is created, its value starts from 0 and the first call to nextval
returns 1. Thus, in case the column
c
from the example above already contains values, we may face a constraint error on insert when the sequence number
will reach to the minimum value of c
. Let's see an example:
SELECT "c" FROM "t";
c
---
2
3
-- Works!
INSERT INTO "t" DEFAULT VALUES;
-- Fails!
INSERT INTO "t" DEFAULT VALUES;
We can work around this by setting the sequence current value to the maximum value of c
, so the following call to
nextval
will return MAX(c)+1
, the one after MAX(c)+2
, and so on.
SELECT setval('"public"."t_c_seq"', (SELECT MAX("c") FROM "t"));
setval
--------
3
-- Works!
INSERT INTO "t" DEFAULT VALUES;
SELECT "c" FROM "t";
c
---
2
3
4
// highlight-end
Managing Serial Columns with Atlas
Atlas makes it easier to define and manipulate columns of serial
types. Let's use the
atlas schema inspect
command to get a representation
of the table we created above in the Atlas HCL format :
atlas schema inspect -u "postgres://postgres:pass@:5432/test?sslmode=disable" > schema.hcl
table "t" {
schema = schema.public
column "c" {
null = false
type = serial
}
primary_key {
columns = [column.c]
}
}
schema "public" {
}
After inspecting the schema, we can modify it to demonstrate Atlas's capabilities in migration planning:
Change a column type from serial
to bigserial
table "t" {
schema = schema.public
column "c" {
null = false
type = bigserial
}
primary_key {
columns = [column.c]
}
}
schema "public" {
}
Next, running schema apply
will plan and execute the following changes:
atlas schema apply -u "postgres://postgres:pass@:5432/test?sslmode=disable" -f schema.hcl
-- Planned Changes:
-- Modify "t" table
ALTER TABLE "public"."t" ALTER COLUMN "c" TYPE bigint
✔ Apply
As you can see, Atlas detected that only the underlying integer type was changed as serial
maps to integer
and
bigserial
maps to bigint
.
Change a column type from bigserial
to bigint
table "t" {
schema = schema.public
column "c" {
null = false
type = bigint
}
primary_key {
columns = [column.c]
}
}
schema "public" {
}
After changing column c
to bigint
, we can run schema apply
and let Atlas plan and execute the new changes:
atlas schema apply -u "postgres://postgres:pass@:5432/test?sslmode=disable" -f schema.hcl
-- Planned Changes:
-- Modify "t" table
ALTER TABLE "public"."t" ALTER COLUMN "c" DROP DEFAULT
-- Drop sequence used by serial column "c"
DROP SEQUENCE IF EXISTS "public"."t_c_seq"
✔ Apply
As you can see, Atlas dropped the DEFAULT
value that was created by the serial
type, and in addition removed
the sequence that was attached to it, as it is no longer used by the column.
Change a column type from bigint
to serial
table "t" {
schema = schema.public
column "c" {
null = false
type = serial
}
primary_key {
columns = [column.c]
}
}
schema "public" {
}
Changing a column type from bigint
to serial
requires 3 changes:
- Create a sequence named
t_c_seq
owned byc
. - Set the
DEFAULT
value ofc
tonextval('"public"."t_c_seq"')
. - Alter the column type, as
serial
maps tointeger
(!=bigint
).
We call atlas schema apply
to plan and execute this three step process
with Atlas:
atlas schema apply -u "postgres://postgres:pass@:5432/test?sslmode=disable" -f schema.hcl
-- Planned Changes:
-- Create sequence for serial column "c"
CREATE SEQUENCE IF NOT EXISTS "public"."t_c_seq" OWNED BY "public"."t"."c"
-- Modify "t" table
ALTER TABLE "public"."t" ALTER COLUMN "c" SET DEFAULT nextval('"public"."t_c_seq"'), ALTER COLUMN "c" TYPE integer
✔ Apply
Need More Help?
Join the Ariga Discord Server for early access to features and the ability to provide exclusive feedback that improves your Database Management Tooling.