Overview
These rules flag the use of PostgreSQL types that have problematic behavior or better alternatives. They help you avoid common type-related pitfalls. Rules covered:ban-char-type: Flagschar(n)/character(n)typestimestamp-without-timezone: Flagstimestampwithout time zone
ban-char-type
When This Rule Triggers
This rule triggers when:char(n)orcharacter(n)is used inCREATE TABLEchar(n)orcharacter(n)is used inALTER TABLE ADD COLUMN::char(n)or::character(n)is used in aCAST
varchar(n)orcharacter varying(n)is usedtextis used- Other string types are used
Why It Matters
Space Padding Behavior
Thechar(n) type is fixed-length and pads values with spaces to fill the specified length:
Problems This Causes
- Storage waste: Always uses the full
nbytes, even for short values - Surprising behavior: Space padding confuses application code
- Comparison inconsistencies:
=ignores trailing spaces, butLIKEdoesn’t - Export/import issues: Trailing spaces may be preserved or stripped depending on the tool
- No performance benefit: Modern PostgreSQL treats
varcharandcharidentically
Better Alternatives
- Use
text: Variable-length, no padding, no arbitrary limits - Use
varchar(n): Variable-length with optional limit, no padding - Use check constraints: For validation, use
CHECK (length(col) <= n)instead
Examples
Problematic: char(n) in CREATE TABLE
Problematic: char(n) in CREATE TABLE
Problematic: character(n) in CREATE TABLE
Problematic: character(n) in CREATE TABLE
character(n) is just an alias for char(n). Same padding problem.Problematic: CAST to char
Problematic: CAST to char
Good: Use varchar
Good: Use varchar
Good: Use text
Good: Use text
Good: CAST to text
Good: CAST to text
timestamp-without-timezone
When This Rule Triggers
This rule triggers when:timestamp(without time zone) is used inCREATE TABLEtimestamp without time zoneis explicitly used::timestamp(without time zone) is used in aCAST
timestamptzortimestamp with time zoneis used
Why It Matters
Lost Timezone Context
Thetimestamp type stores date and time without timezone information:
- Ambiguity: You don’t know what timezone the timestamp represents
- DST issues: Daylight saving time transitions create duplicate or missing hours
- Global applications: Users in different timezones see different local times with no context
- Comparison issues: Comparing timestamps from different timezones is meaningless
Real-World Example
Better Alternative: timestamptz
Always usetimestamptz (or timestamp with time zone):
Benefits of timestamptz
- Unambiguous: Timezone is part of the value
- Correct comparisons: Times are stored in UTC, compared accurately
- Automatic conversion: PostgreSQL converts to/from your session timezone
- DST-safe: Handles daylight saving time correctly
- Global-ready: Works correctly for users worldwide
Common Misconceptions
Myth: “timestamptz stores the timezone”- Reality:
timestamptzstores UTC internally, displays in session timezone
- Reality: Performance difference is negligible; correctness matters more
- Reality: Even local users experience DST; plus your app may expand globally
Examples
Problematic: timestamp in CREATE TABLE
Problematic: timestamp in CREATE TABLE
Problematic: timestamp without time zone (explicit)
Problematic: timestamp without time zone (explicit)
Problematic: CAST to timestamp
Problematic: CAST to timestamp
now().Good: Use timestamptz
Good: Use timestamptz
Good: timestamp with time zone (explicit)
Good: timestamp with time zone (explicit)
timestamp with time zone is equivalent to timestamptz.Good: CAST to timestamptz
Good: CAST to timestamptz
now() already returns timestamptz).Implementation Details
ban-char-type
The rule works by:- Checking
CREATE TABLEandALTER TABLE ADD COLUMNforColumnDefnodes - Checking
CASTexpressions forTypeCastnodes - Inspecting
TypeNameto see if it’sbpchar(internal name forchar) - Reporting a warning at the column or cast location
ban_char_type.go
timestamp-without-timezone
The rule works by:- Checking
CREATE TABLEandALTER TABLE ADD COLUMNforColumnDefnodes - Checking
CASTexpressions forTypeCastnodes - Inspecting
TypeNameto see if it’stimestamp(without timezone) - Reporting a warning at the column or cast location
timestamp_without_timezone.go