Why This Matters
In YugabyteDB, sequences are distributed objects. High CACHE settings and session-level caching can lead to larger gaps in generated IDs. Normally, gaps are harmlessm but if the sequence feeds an INT or SMALLINT column, you can hit the column’s hard limit much sooner than expected:
•
SMALLINTmax: 32,767•
INTmax: 2,147,483,647
Once the sequence exceeds that limit, inserts fail with “integer out of range” errors.
Worse, in YugabyteDB changing a column from INT → BIGINT requires a full table rewrite, which is painful on large production tables.
That’s why it’s critical to find and fix risky columns early.
Three Common Risk Patterns
These all create sequence-backed integer columns that can eventually overflow in a busy distributed cluster:
-- SERIAL (implicit INT sequence)
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_date date NOT NULL
);
-- IDENTITY column (explicit INT sequence)
CREATE TABLE customers (
customer_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text
);
-- Manual sequence in DEFAULT expression
CREATE SEQUENCE emp_seq;
CREATE TABLE emp (
emp_id INT DEFAULT nextval('emp_seq') PRIMARY KEY,
emp_name TEXT
);
The Detection Query
The query below will find all SMALLINT/INT columns linked to sequences — covering:
• SERIAL (owned sequence,
deptype = 'a')• IDENTITY (internal/owned,
deptype = 'i')• Manual nextval() default (dependency from
pg_attrdefto sequence)• Fallback regex parsing if dependencies are missing
It also:
• Shows sequence settings (
last_value,min_value,max_value,cache_size)• Calculates approx. remaining values until column overflow
• Flags mismatches where a column’s type max is less than the sequence’s
max_value
WITH base_cols AS (
SELECT
ns.nspname AS schema_name,
t.relname AS table_name,
a.attnum AS attnum,
a.attname AS column_name,
a.atttypid::regtype::text AS column_type,
(a.attidentity <> '') AS is_identity,
format('%I.%I', ns.nspname, t.relname) AS fq_table_name,
t.oid AS table_oid
FROM pg_class t
JOIN pg_namespace ns ON ns.oid = t.relnamespace
JOIN pg_attribute a ON a.attrelid = t.oid
AND a.attnum > 0
AND NOT a.attisdropped
WHERE t.relkind IN ('r','p')
AND a.atttypid IN ('int2'::regtype, 'int4'::regtype)
),
col_defaults AS (
SELECT
b.*, ad.oid AS ad_oid,
pg_get_expr(ad.adbin, ad.adrelid) AS def_expr
FROM base_cols b
LEFT JOIN pg_attrdef ad
ON ad.adrelid = b.table_oid
AND ad.adnum = b.attnum
),
by_func AS (
SELECT c.*, pg_get_serial_sequence(c.fq_table_name, c.column_name) AS seq_from_func
FROM col_defaults c
),
by_depend_owned AS (
SELECT c.schema_name, c.table_name, c.attnum, c.column_name, c.column_type, c.is_identity,
sn.nspname || '.' || s.relname AS seq_from_owned
FROM col_defaults c
JOIN pg_depend d
ON d.refobjid = c.table_oid
AND d.refobjsubid = c.attnum
AND d.deptype IN ('a','i')
JOIN pg_class s ON s.oid = d.objid AND s.relkind = 'S'
JOIN pg_namespace sn ON sn.oid = s.relnamespace
),
by_depend_default AS (
SELECT c.schema_name, c.table_name, c.attnum, c.column_name, c.column_type, c.is_identity,
sn.nspname || '.' || s.relname AS seq_from_default
FROM col_defaults c
JOIN pg_depend d
ON d.refobjid = c.ad_oid
AND d.deptype = 'n'
JOIN pg_class s ON s.oid = d.objid AND s.relkind = 'S'
JOIN pg_namespace sn ON sn.oid = s.relnamespace
),
by_regex AS (
SELECT c.schema_name, c.table_name, c.attnum, c.column_name, c.column_type, c.is_identity,
CASE
WHEN c.def_expr ~* $$nextval\s*\('([^']+)'\s*::\s*regclass\)$$ THEN
(SELECT n.nspname || '.' || cls.relname
FROM pg_class cls
JOIN pg_namespace n ON n.oid = cls.relnamespace
WHERE cls.oid = to_regclass(
regexp_replace(c.def_expr,
$$.*nextval\s*\('([^']+)'\s*::\s*regclass\).*$$,
'\1')
))
END AS seq_from_regex
FROM col_defaults c
),
risky AS (
SELECT DISTINCT
f.schema_name, f.table_name, f.column_name, f.column_type, f.is_identity,
COALESCE(f.seq_from_func, o.seq_from_owned, d.seq_from_default, r.seq_from_regex) AS sequence_name
FROM by_func f
LEFT JOIN by_depend_owned o USING (schema_name, table_name, attnum)
LEFT JOIN by_depend_default d USING (schema_name, table_name, attnum)
LEFT JOIN by_regex r USING (schema_name, table_name, attnum)
WHERE COALESCE(f.seq_from_func, o.seq_from_owned, d.seq_from_default, r.seq_from_regex) IS NOT NULL
),
seqs AS (
SELECT schemaname, sequencename, last_value, min_value, max_value, increment_by, cache_size
FROM pg_sequences
)
SELECT
r.schema_name, r.table_name, r.column_name, r.column_type, r.is_identity,
r.sequence_name,
s.last_value, s.min_value, s.max_value, s.increment_by, s.cache_size,
CASE
WHEN r.column_type = 'smallint' THEN (32767 - COALESCE(s.last_value, 0))::bigint
WHEN r.column_type = 'integer' THEN (2147483647 - COALESCE(s.last_value, 0))::bigint
END AS approx_remaining_until_overflow,
CASE
WHEN r.column_type = 'smallint' AND s.max_value > 32767 THEN '⚠ mismatch: seq max > SMALLINT max'
WHEN r.column_type = 'integer' AND s.max_value > 2147483647 THEN '⚠ mismatch: seq max > INT max'
END AS mismatch_warning
FROM risky r
LEFT JOIN seqs s
ON s.schemaname = split_part(r.sequence_name, '.', 1)
AND s.sequencename = split_part(r.sequence_name, '.', 2)
ORDER BY r.schema_name, r.table_name, r.column_name;
Example Output
For our sample tables:
schema_name | table_name | column_name | column_type | is_identity | sequence_name | last_value | min_value | max_value | increment_by | cache_size | approx_remaining_until_overflow | mismatch_warning
-------------+------------+-------------+-------------+-------------+----------------------------------+------------+-----------+---------------------+--------------+------------+---------------------------------+-------------------------------
public | customers | customer_id | integer | t | public.customers_customer_id_seq | | 1 | 2147483647 | 1 | 100 | 2147483647 |
public | emp | emp_id | integer | f | public.emp_seq | | 1 | 9223372036854775807 | 1 | 100 | 2147483647 | ⚠ mismatch: seq max > INT max
public | orders | order_id | integer | f | public.orders_order_id_seq | | 1 | 2147483647 | 1 | 100 | 2147483647 |
Takeaways
• Always start with BIGSERIAL or
BIGINT GENERATED ... AS IDENTITYin distributed systems.• Audit regularly for:
◦ INT/SMALLINT columns with sequences
◦ Mismatched sequence max values
• Fix early to avoid disruptive rewrites.
Have Fun!
