This document analyzes the AST changes between PostgreSQL 17 and PostgreSQL 18 based on the libpg_query comparison.
The PG17 to PG18 upgrade represents a moderate breaking change. While not as severe as the PG13 to PG17 migration, there are significant structural changes that will require updates to the parser, deparser, and transformer packages.
The primary changes center around:
- Enhanced RETURNING clause support (OLD/NEW table references per SQL:2011)
- Temporal/period constraint support (WITHOUT OVERLAPS)
- Constraint enforceability (ENFORCED/NOT ENFORCED)
- Virtual generated columns
- Comparison type generalization
New expression node for handling RETURNING clause expressions with OLD/NEW context.
message ReturningExpr {
Node xpr = 1;
int32 retlevelsup = 2;
bool retold = 3;
Node retexpr = 4;
}Options for RETURNING clause specifying OLD or NEW table reference.
message ReturningOption {
ReturningOptionKind option = 1;
string value = 2;
int32 location = 3;
}New structured RETURNING clause replacing the simple returning_list.
message ReturningClause {
repeated Node options = 1;
repeated Node exprs = 2;
}New node for ALTER CONSTRAINT operations with enhanced options.
message ATAlterConstraint {
string conname = 1;
bool alter_enforceability = 2;
bool is_enforced = 3;
bool alter_deferrability = 4;
bool deferrable = 5;
bool initdeferred = 6;
bool alter_inheritability = 7;
bool noinherit = 8;
}Removed (was an empty message in PG17 anyway).
Impact: HIGH - Affects all DML statements
The returning_list field (repeated Node) has been replaced with returning_clause (ReturningClause) in:
InsertStmtDeleteStmtUpdateStmtMergeStmt
Before (PG17):
message InsertStmt {
// ...
repeated Node returning_list = 5;
// ...
}After (PG18):
message InsertStmt {
// ...
ReturningClause returning_clause = 5;
// ...
}This enables SQL:2011 syntax like:
INSERT INTO t VALUES (1) RETURNING OLD AS o, NEW AS n, *;
DELETE FROM t RETURNING OLD.*, NEW.*;Impact: HIGH - Breaking enum change
The RowCompareType enum has been removed and replaced with a more general CompareType enum.
Removed:
enum RowCompareType {
ROWCOMPARE_LT = 1;
ROWCOMPARE_LE = 2;
ROWCOMPARE_EQ = 3;
ROWCOMPARE_GE = 4;
ROWCOMPARE_GT = 5;
ROWCOMPARE_NE = 6;
}Added:
enum CompareType {
COMPARE_INVALID = 1;
COMPARE_LT = 2;
COMPARE_LE = 3;
COMPARE_EQ = 4;
COMPARE_GE = 5;
COMPARE_GT = 6;
COMPARE_NE = 7;
COMPARE_OVERLAP = 8; // New for temporal
COMPARE_CONTAINED_BY = 9; // New for temporal
}RowCompareExpr field changed:
rctype(RowCompareType) ->cmptype(CompareType)
Impact: MEDIUM - Field position shifts
New fields added to Query:
has_group_rte(field 15) - New boolean for GROUP RTE trackingreturning_old_alias(field 27) - Alias for OLD table in RETURNINGreturning_new_alias(field 28) - Alias for NEW table in RETURNING
All subsequent fields are renumbered (16-45 instead of 15-42).
Impact: MEDIUM - New fields for temporal and enforceability
New fields added to Constraint:
is_enforced(field 5) - For NOT ENFORCED constraintsgenerated_kind(field 12) - For virtual generated columnswithout_overlaps(field 15) - For temporal PRIMARY KEY/UNIQUEfk_with_period(field 27) - For temporal foreign keyspk_with_period(field 28) - For temporal foreign keys
Removed: inhcount field
Added varreturningtype (VarReturningType enum) at field 9 for RETURNING OLD/NEW context.
Added nnconstraints (field 8) for separate NOT NULL constraint handling.
Added iswithoutoverlaps (field 19) for temporal index support.
Added reverse_sort (field 4) for explicit sort direction tracking.
Added groupexprs (field 30) for GROUP RTE support.
Added jumble_args (field 4) and location (field 6).
Added location (field 5) for better error reporting.
Added list_start and list_end fields for precise source location tracking.
Added rexpr_list_start and rexpr_list_end fields for IN-list location tracking.
view_query changed from generic Node to specific Query type.
enum ReturningOptionKind {
RETURNING_OPTION_OLD = 1;
RETURNING_OPTION_NEW = 2;
}enum VarReturningType {
VAR_RETURNING_DEFAULT = 1;
VAR_RETURNING_OLD = 2;
VAR_RETURNING_NEW = 3;
}See above - replaces RowCompareType with additional temporal comparison operators.
- Removed:
AT_CheckNotNull - All subsequent values renumbered (shifted down by 1)
- Added:
CONSTR_ATTR_ENFORCED(15),CONSTR_ATTR_NOT_ENFORCED(16)
- Added:
RTE_GROUP(10) for GROUP BY optimization
- Added:
JOIN_RIGHT_SEMI(7) - Subsequent values renumbered
ENFORCED- For constraint enforceabilityOBJECTS_P- New keywordPERIOD- For temporal constraintsVIRTUAL- For virtual generated columns
Removed: RECHECK
- ReturningClause handling - Must deparse new structure instead of simple list
- CompareType enum - Update RowCompareExpr deparsing
- New constraint syntax - WITHOUT OVERLAPS, ENFORCED/NOT ENFORCED
- Virtual generated columns - New GENERATED ALWAYS AS ... VIRTUAL syntax
- V17ToV18Transformer - New transformer needed
- RETURNING clause transformation - Convert between list and clause structures
- CompareType mapping - Map old RowCompareType values to new CompareType
- Constraint field mapping - Handle new fields with defaults
- Regenerate from new protobuf definitions
- Update all affected interfaces
- Add new node types and enums
| Upgrade | New Nodes | Removed Nodes | Breaking Changes | Effort |
|---|---|---|---|---|
| PG13 -> PG14 | ~5 | 0 | funcformat, A_Const | High |
| PG14 -> PG15 | ~3 | 0 | Boolean primitive | Medium |
| PG15 -> PG16 | ~2 | 0 | JSON functions | Low |
| PG16 -> PG17 | ~3 | 0 | JSON types | Low |
| PG17 -> PG18 | 5 | 1 | RETURNING, CompareType | Medium-High |
-
Phase 1: Types Generation
- Update proto file to 18-latest
- Regenerate @pgsql/types, @pgsql/enums, @pgsql/utils
- Update runtime schema
-
Phase 2: Deparser Updates
- Add ReturningClause visitor
- Update RowCompareExpr to use CompareType
- Add new constraint syntax support
- Add temporal syntax support
-
Phase 3: Transformer
- Create V17ToV18Transformer
- Handle returning_list -> returning_clause conversion
- Handle RowCompareType -> CompareType mapping
- Add default values for new fields
-
Phase 4: Testing
- Add fixtures for new PG18 syntax
- Verify round-trip parsing/deparsing
- Test transformation from PG17 ASTs
The PG17 to PG18 upgrade is more significant than the PG15-16 or PG16-17 upgrades but less disruptive than PG13-14. The main challenges are:
- The RETURNING clause restructuring affects all DML statements
- The RowCompareType -> CompareType change requires careful enum mapping
- New temporal/period features add complexity to constraint handling
However, most existing SQL will continue to work without changes. The new features (temporal constraints, enhanced RETURNING, virtual columns) are additive and won't break existing AST structures for queries that don't use them.