File: serial-columns.md

package info (click to toggle)
golang-ariga-atlas 0.7.2-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 5,676 kB
  • sloc: javascript: 592; sql: 404; makefile: 10
file content (258 lines) | stat: -rw-r--r-- 7,122 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
---
id: serial-columns
title: Serial Type Columns in PostgreSQL
slug: /guides/postgres/serial-columns
---

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":

```sql
CREATE TABLE serials(
    c1 smallserial,
    c2 serial,
    c3 bigserial
);
```

```sql title="Serials Description"
 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.

:::info
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:

```sql
CREATE TABLE t(
    c integer not null primary key
);

ALTER TABLE t ALTER COLUMN c TYPE serial;
// highlight-next-line-error-message
ERROR: type "serial" does not exist
```

We can achieve this by manually creating a [sequence](https://www.postgresql.org/docs/current/sql-createsequence.html)
owned by the column `c`, and setting the column `DEFAULT` value to the incremental counter of the sequence using the
[`nextval`](https://www.postgresql.org/docs/current/functions-sequence.html) function.

:::note
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".
:::

```sql
-- 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:

```sql
SELECT "c" FROM "t";
// highlight-start
 c
---
 2
 3
// highlight-end

-- Works!
INSERT INTO "t" DEFAULT VALUES;
-- Fails!
INSERT INTO "t" DEFAULT VALUES;
// highlight-next-line-error-message
ERROR:  duplicate key value violates unique constraint "t_pkey"
// highlight-next-line-error-message
DETAIL:  Key (c)=(2) already exists.
```

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.

```sql
SELECT setval('"public"."t_c_seq"', (SELECT MAX("c") FROM "t"));
// highlight-start
 setval
--------
   3
// highlight-end

-- Works!
INSERT INTO "t" DEFAULT VALUES;
SELECT "c" FROM "t";
// highlight-start
 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`](../../reference.md#atlas-schema-inspect) command to get a representation
of the table we created above in the Atlas HCL format :

```console
atlas schema inspect -u "postgres://postgres:pass@:5432/test?sslmode=disable" > schema.hcl
```

```hcl title="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`

```hcl title="schema.hcl"
table "t" {
  schema = schema.public
  column "c" {
    null = false
    // highlight-start
    type = bigserial
    // highlight-end
  }
  primary_key {
    columns = [column.c]
  }
}
schema "public" {
}
```

Next, running `schema apply` will plan and execute the following changes:

```console
atlas schema apply -u "postgres://postgres:pass@:5432/test?sslmode=disable" -f schema.hcl

-- Planned Changes:
-- Modify "t" table
// highlight-next-line-info
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`

```hcl title="schema.hcl"
table "t" {
  schema = schema.public
  column "c" {
    null = false
    // highlight-start
    type = bigint
    // highlight-end
  }
  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:

```console
atlas schema apply -u "postgres://postgres:pass@:5432/test?sslmode=disable" -f schema.hcl

-- Planned Changes:
-- Modify "t" table
// highlight-next-line-info
ALTER TABLE "public"."t" ALTER COLUMN "c" DROP DEFAULT
-- Drop sequence used by serial column "c"
// highlight-next-line-info
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`

```hcl title="schema.hcl"
table "t" {
  schema = schema.public
  column "c" {
    null = false
    // highlight-start
    type = serial
    // highlight-end
  }
  primary_key {
    columns = [column.c]
  }
}
schema "public" {
}
```

Changing a column type from `bigint` to `serial` requires 3 changes:
1. Create a sequence named `t_c_seq` owned by `c`.
2. Set the `DEFAULT` value of `c` to `nextval('"public"."t_c_seq"')`.
3. Alter the column type, as `serial` maps to `integer` (!= `bigint`).

We call [`atlas schema apply`](../../reference.md#atlas-schema-apply) to plan and execute this three step process
with Atlas:

```console
atlas schema apply -u "postgres://postgres:pass@:5432/test?sslmode=disable" -f schema.hcl

-- Planned Changes:
-- Create sequence for serial column "c"
// highlight-next-line-info
CREATE SEQUENCE IF NOT EXISTS "public"."t_c_seq" OWNED BY "public"."t"."c"
-- Modify "t" table
// highlight-next-line-info
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](https://discord.gg/zZ6sWVg6NT) for early access to features and the ability to provide
exclusive feedback that improves your Database Management Tooling.