Serial to Identity Column Conversion
Scanned 9/2/2026
Install to Claude Code
npx -y skills add CarlosCaPe/octorato --skill serial-to-identity-conversion --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Serial To Identity Conversion?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/carloscape-serial-to-identity-conversion)More formats (shields.io, HTML) on the badges page.
---
name: serial-to-identity-conversion
description: "Serial to Identity Column Conversion"
metadata:
short-description: "Serial to Identity Column Conversion"
original-index: 17
---
# Serial to Identity Column Conversion
## What
Migrating legacy `serial` / `bigserial` columns (which use implicit sequences
via `nextval()`) to the SQL-standard `GENERATED BY DEFAULT AS IDENTITY` syntax.
## Why
`serial` is a PostgreSQL shorthand that creates a separate sequence and a
`DEFAULT nextval(...)` expression. This has drawbacks:
- The sequence is **not tied** to the column in `pg_depend` -- dropping the
column may leave orphaned sequences
- `pg_dump` does not always restore the relationship correctly
- It's non-standard SQL (not portable)
- `IDENTITY` columns are the modern PostgreSQL standard (10+)
## How
### Step 1: Check current state
```sql
SELECT column_name, column_default, is_identity
FROM information_schema.columns
WHERE table_name = 'FeatureFlags' AND column_name = 'FlagId';
-- If column_default = nextval('...') and is_identity = 'NO', needs conversion
```
### Step 2: Remove the old default
```sql
ALTER TABLE feature_flags."FeatureFlags"
ALTER COLUMN "FlagId" DROP DEFAULT;
```
### Step 3: Drop the orphaned sequence
```sql
DROP SEQUENCE IF EXISTS feature_flags."FeatureFlags_FlagId_seq";
```
### Step 4: Add identity
```sql
ALTER TABLE feature_flags."FeatureFlags"
ALTER COLUMN "FlagId"
ADD GENERATED BY DEFAULT AS IDENTITY;
```
### Step 5: Sync the sequence to max value
```sql
-- Critical: set the identity sequence to the current max value
SELECT setval(
pg_get_serial_sequence('feature_flags."FeatureFlags"', 'FlagId'),
COALESCE((SELECT MAX("FlagId") FROM feature_flags."FeatureFlags"), 1)
);
```
## GENERATED BY DEFAULT vs GENERATED ALWAYS
| Mode | Behavior | Use When |
|------|----------|----------|
| `GENERATED BY DEFAULT` | Auto-generates but allows explicit INSERT | App sometimes provides the ID |
| `GENERATED ALWAYS` | Rejects explicit INSERT (unless `OVERRIDING SYSTEM VALUE`) | ID must always be auto-generated |
Use `BY DEFAULT` for backward compatibility with existing app code.
**Official PG 16 behavior** (sql-createtable.html):
- IDENTITY columns are implicitly `NOT NULL` -- no separate constraint needed
- The implicit sequence is tightly linked via `pg_attribute` (unlike serial
which only has an `OWNED BY` dependency in `pg_depend`)
- `GENERATED ALWAYS` still allows explicit values with
`INSERT INTO t OVERRIDING SYSTEM VALUE VALUES (...)`
## When to Use
- Modernizing legacy schemas with `serial` columns
- After audit findings flag orphaned sequences
- When preparing for cross-database portability
## Where We Used It
- ****: Converted `FeatureFlags.FlagId` from serial to IDENTITY
## References
- [PostgreSQL Best Practices](../DOCUMENTS/PostgreSQL_BestPractices_Azure.md)
-- see Backlog #7 (Replace explicit sequences with IDENTITY)
and "Schema design and data types" section
## Gotchas
- **Must sync the sequence** after conversion -- if you skip Step 5, the next
INSERT will get a duplicate key error
- Check for any triggers or procedures that reference the old sequence by name
- `GENERATED ALWAYS` may break app code that does explicit ID inserts
- The old sequence name may differ from the new identity sequence name
---
*Category: DDL | Origin: *
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!