Version: 1
Target: PostgreSQL 18
File Extension: .pgd
Encoding: UTF-8
Validation: pgd-format.xsd (see lfdfq pgd-format.xsd)
PGD is a git-friendly XML format for describing PostgreSQL database schemas. It provides structural modeling for all major PostgreSQL DDL constructs (tables, indexes, types, sequences, views, functions, triggers, policies, roles, extensions, grants, comments) and includes diagram layout metadata for visual schema design.
Git-friendly — single-line attributes, predictable element ordering, clean diffs
Complete PG18 coverage — all DDL constructs modeled structurally
Raw SQL only where necessary — function bodies, CHECK expressions, WHERE predicates, partition bounds
Layout separated from model — diagram metadata in <layouts>, can be split to a separate file
No IDs — objects referenced by name (like SQL itself), no artificial numeric identifiers
<?xml version =" 1.0" encoding =" UTF-8" ?>
<pgd version =" 1" pg-version =" 18" default-schema =" public" >
<project >...</project >
<database >...</database >
<roles >...</roles >
<tablespaces >...</tablespaces >
<extensions >...</extensions >
<types >...</types >
<sequences >...</sequences >
<schema name =" public" >...</schema >
<schema name =" app" >...</schema >
<views >...</views >
<functions >...</functions >
<triggers >...</triggers >
<rules >...</rules >
<policies >...</policies >
<comments >...</comments >
<grants >...</grants >
<layouts >...</layouts >
</pgd >
All top-level children are optional. The order shown above is the canonical order.
Attribute
Type
Required
Default
Description
version
int
yes
—
Format version (currently 1)
pg-version
string
no
—
Target PostgreSQL major version (14–18)
default-schema
string
no
public
Default schema when schema is omitted
1. Project Metadata: <project>
<project name =" myproject" description =" My database schema" >
<settings >
<naming convention =" snake_case" tables =" plural" ></naming >
<defaults nullable =" true" on-delete =" restrict" on-update =" restrict" ></defaults >
<lint ignore-rules =" W015,I009" ></lint >
</settings >
</project >
Attribute
Type
Required
Description
name
string
yes
Project name
description
string
no
Project description
Attribute
Type
Default
Values
convention
enum
camelCase
camelCase, snake_case, PascalCase
tables
enum
—
plural, singular (empty = no check)
Attribute
Type
Default
Description
nullable
boolean
true
Default column nullability
on-delete
enum
restrict
Default FK ON DELETE action
on-update
enum
restrict
Default FK ON UPDATE action
Attribute
Type
Description
ignore-rules
string
Comma-separated lint rule codes to suppress project-wide
<database name =" mydb" encoding =" UTF8" collation =" en_US.UTF-8"
ctype =" en_US.UTF-8" template =" template0" owner =" admin" ></database >
Attribute
Type
Required
Description
name
string
yes
Database name
encoding
string
no
Character encoding (e.g. UTF8)
collation
string
no
LC_COLLATE setting
ctype
string
no
LC_CTYPE setting
icu-locale
string
no
ICU collation (PG15+)
locale
string
no
Locale shorthand
template
string
no
Template database
tablespace
string
no
Default tablespace
owner
string
no
Database owner role
<roles >
<role name =" app_user" login =" true" inherit =" true" >
<in-role name =" app_readers" ></in-role >
</role >
<role name =" app_readers" login =" false" ></role >
</roles >
Attribute
Type
Default
Description
name
string
(required)
Role name
login
boolean
false
Can log in
inherit
boolean
true
Inherits privileges of granted roles
createdb
boolean
false
Can create databases
createrole
boolean
false
Can create roles
superuser
boolean
false
Superuser privileges
replication
boolean
false
Can initiate streaming replication
bypassrls
boolean
false
Bypasses row-level security
connection-limit
int
-1
Max connections (-1 = unlimited)
password-encrypted
boolean
true
Password stored encrypted
valid-until
string
—
Expiration timestamp (ISO 8601)
Attribute
Type
Description
name
string
Parent role to grant membership in
4. Tablespaces: <tablespaces>
<tablespaces >
<tablespace name =" fast_ssd" location =" /mnt/ssd/pg_data" owner =" postgres" ></tablespace >
</tablespaces >
Attribute
Type
Required
Description
name
string
yes
Tablespace name
location
string
yes
Filesystem path
owner
string
no
Owner role
5. Extensions: <extensions>
<extensions >
<extension name =" uuid-ossp" schema =" public" ></extension >
<extension name =" pgcrypto" ></extension >
</extensions >
Attribute
Type
Required
Description
name
string
yes
Extension name
schema
string
no
Install into schema
version
string
no
Specific version
cascade
boolean
no
Auto-install dependencies
Contains four sub-sections: <enum>, <composite>, <domain>, <range>.
<enum name =" status" schema =" public" >
<label >active</label >
<label >inactive</label >
<label >deleted</label >
</enum >
Attribute
Type
Required
Description
name
string
yes
Type name
schema
string
no
Schema
Child <label> elements contain enum value strings in order.
<composite name =" address" schema =" public" >
<field name =" street" type =" text" ></field >
<field name =" city" type =" varchar" length =" 100" ></field >
<field name =" zip" type =" varchar" length =" 10" ></field >
</composite >
<field> attributes:
Attribute
Type
Required
Description
name
string
yes
Field name
type
string
yes
Data type
length
int
no
Type length (for varchar, char)
collation
string
no
Collation
<domain name =" email" schema =" public" type =" varchar" length =" 255" default =" ''" >
<not-null ></not-null >
<constraint name =" email_check" ><![CDATA[ VALUE ~ '^.+@.+$']]> </constraint >
</domain >
Attribute
Type
Required
Description
name
string
yes
Domain name
schema
string
no
Schema
type
string
yes
Base type
length
int
no
Type length
precision
int
no
Numeric precision
scale
int
no
Numeric scale
collation
string
no
Collation
default
string
no
Default expression (raw SQL)
Child <not-null/> — presence means NOT NULL.
Child <constraint> — named CHECK constraint; text content is the expression (CDATA).
<range name =" float_range" subtype =" float8" ></range >
Attribute
Type
Required
Description
name
string
yes
Type name
schema
string
no
Schema
subtype
string
yes
Underlying element type
subtype-opclass
string
no
Operator class
collation
string
no
Collation
canonical
string
no
Canonical function
subtype-diff
string
no
Difference function
multirange-type-name
string
no
Multirange type name (PG14+)
7. Sequences: <sequences>
<sequences >
<sequence name =" order_seq" schema =" public" as =" bigint"
start =" 1000" increment =" 1" cache =" 10" ></sequence >
</sequences >
Attribute
Type
Default
Description
name
string
(required)
Sequence name
schema
string
—
Schema
as
string
—
Type: smallint, integer, bigint
start
long
1
Start value
increment
long
1
Increment
min
long
—
Minimum value
max
long
—
Maximum value
cache
long
1
Cache size
cycle
boolean
false
Wrap around on limit
owned-by
string
—
Owner column (table.column)
A schema contains tables and indexes. Multiple <schema> elements can appear at the top level.
<schema name =" public" >
<table name =" users" >...</table >
<table name =" orders" >...</table >
<index name =" idx_orders_user_id" table =" orders" >...</index >
</schema >
Attribute
Type
Required
Description
name
string
yes
Schema name
Note: Indexes are stored at the schema level, not inside tables.
<table name =" users" comment =" Application users" >
<column name =" id" type =" bigint" nullable =" false" >
<identity generated =" always" ></identity >
</column >
<column name =" email" type =" varchar" length =" 255" nullable =" false" ></column >
<column name =" name" type =" text" ></column >
<column name =" created_at" type =" timestamptz" nullable =" false" default =" now()" ></column >
<pk name =" pk_users" >
<column name =" id" ></column >
</pk >
<unique name =" uq_users_email" >
<column name =" email" ></column >
</unique >
<check name =" chk_email" ><![CDATA[ email <> '']]> </check >
</table >
Attribute
Type
Default
Description
name
string
(required)
Table name
unlogged
boolean
false
UNLOGGED table
temporary
boolean
false
TEMPORARY table
on-commit
string
—
preserve-rows, delete-rows, drop
tablespace
string
—
Tablespace name
comment
string
—
Table comment (shorthand for COMMENT ON TABLE)
row-level-security
boolean
false
Enable RLS
force-row-level-security
boolean
false
Force RLS for table owner
partition-of
string
—
Parent table name (Variant A, deprecated)
inherits
string
—
Comma-separated parent table names
using
string
—
Table access method (e.g. heap)
generate
string
—
PGDesigner: false to skip DDL generation
lint-ignore
string
—
PGDesigner: comma-separated lint rule codes
Child elements (in order)
<column> — column definitions (must come first)
<pk> — primary key (at most one)
<fk> — foreign keys
<unique> — unique constraints
<check> — check constraints
<exclude> — exclusion constraints
<with> — storage parameters
<partition-by> — partition key definition
<partition-bound> — partition bound (Variant A, deprecated)
<partition> — child partitions (Variant B)
<column name =" price" type =" numeric" precision =" 10" scale =" 2"
nullable =" false" default =" 0" comment =" Product price in cents" ></column >
Attribute
Type
Default
Description
name
string
(required)
Column name
type
string
(required)
Data type (append [] for arrays: integer[])
length
int
—
Length for varchar(n), char(n), bit(n)
precision
int
—
Total digits for numeric(p,s)
scale
int
—
Decimal digits for numeric(p,s)
nullable
boolean
true
Allows NULL (false = NOT NULL)
default
string
—
Default expression (raw SQL)
collation
string
—
COLLATE collation_name
storage
enum
—
plain, external, extended, main, default
compression
enum
—
lz4, pglz, default (PG14+)
comment
string
—
Column comment (shorthand for COMMENT ON COLUMN)
All PostgreSQL built-in types are supported as string values:
Category
Types
Integer
smallint, integer, bigint
Serial
smallserial, serial, bigserial
Decimal
numeric, decimal, real, double precision, money
Character
varchar, character varying, char, character, text
Binary
bytea
Boolean
boolean
Date/Time
date, time, timetz, timestamp, timestamptz, interval
UUID
uuid
JSON
json, jsonb
XML
xml
Network
inet, cidr, macaddr, macaddr8
Geometric
point, line, lseg, box, path, polygon, circle
Text Search
tsvector, tsquery
Bit String
bit, varbit, bit varying
Range
int4range, int8range, numrange, tsrange, tstzrange, daterange
Multirange
int4multirange, int8multirange, nummultirange, tsmultirange, tstzmultirange, datemultirange
Other
pg_lsn, pg_snapshot, oid, regclass, regtype
Custom types (enums, composites, domains) are referenced by name. Schema-qualified types use dot notation: myschema.my_type.
10.1 Identity Column: <identity>
<column name =" id" type =" bigint" nullable =" false" >
<identity generated =" always" >
<sequence start =" 100" increment =" 1" cache =" 10" ></sequence >
</identity >
</column >
<identity> attributes:
Attribute
Type
Required
Values
generated
enum
yes
always, by-default
<sequence> child (optional):
Attribute
Type
Description
start
long
Start value
increment
long
Increment
min
long
Minimum value
max
long
Maximum value
cache
long
Cache size
cycle
boolean
Wrap around
10.2 Generated Column: <generated>
<column name =" full_name" type =" text" >
<generated expression =" first_name || ' ' || last_name" stored =" true" ></generated >
</column >
Attribute
Type
Default
Description
expression
string
(required)
SQL expression
stored
boolean
true
true = STORED, false = VIRTUAL (PG18+)
<pk name =" pk_users" without-overlaps =" false" >
<column name =" id" ></column >
</pk >
Attribute
Type
Default
Description
name
string
—
Constraint name
without-overlaps
boolean
false
WITHOUT OVERLAPS (PG17+, temporal)
deferrable
boolean
false
DEFERRABLE
initially
enum
—
immediate, deferred
enforced
boolean
true
NOT ENFORCED (PG18+)
<fk name =" fk_orders_user" to-table =" users" on-delete =" cascade" on-update =" no action" >
<column name =" user_id" references =" id" ></column >
</fk >
Attribute
Type
Default
Description
name
string
—
Constraint name
to-table
string
(required)
Referenced table (can be schema.table)
to-schema
string
—
Referenced table schema (if different)
on-delete
enum
restrict
restrict, cascade, set-null, set-default, no action
on-update
enum
restrict
Same as on-delete
deferrable
boolean
false
DEFERRABLE
initially
enum
—
immediate, deferred
match
enum
simple
simple, full, partial
enforced
boolean
true
NOT ENFORCED (PG18+)
period
string
—
Column name for temporal FK (PG17+)
<column> child attributes:
Attribute
Type
Description
name
string
Local column name
references
string
Referenced column name
<unique name =" uq_users_email" nulls-distinct =" false" >
<column name =" email" ></column >
</unique >
Attribute
Type
Default
Description
name
string
—
Constraint name
nulls-distinct
boolean
true
false = NULLS NOT DISTINCT (PG15+)
without-overlaps
boolean
false
WITHOUT OVERLAPS (PG17+)
deferrable
boolean
false
DEFERRABLE
initially
enum
—
immediate, deferred
enforced
boolean
true
NOT ENFORCED (PG18+)
<check name =" chk_positive_amount" no-inherit =" false" ><![CDATA[ amount > 0]]> </check >
Attribute
Type
Default
Description
name
string
—
Constraint name
no-inherit
boolean
false
NO INHERIT
enforced
boolean
true
NOT ENFORCED (PG18+)
The element text content is the CHECK expression (use CDATA for special characters).
<exclude name =" excl_booking_overlap" using =" gist" >
<element column =" room_id" with =" =" ></element >
<element column =" period" with =" && " ></element >
</exclude >
Attribute
Type
Default
Description
name
string
—
Constraint name
using
string
gist
Index method
deferrable
boolean
false
DEFERRABLE
initially
enum
—
immediate, deferred
enforced
boolean
true
NOT ENFORCED (PG18+)
<element> attributes:
Attribute
Type
Description
column
string
Column name
expression
string
Expression (alternative to column)
with
string
Operator (e.g. =, &&, <>)
12. Storage Parameters: <with>
<with >
<param name =" fillfactor" value =" 90" ></param >
<param name =" autovacuum_enabled" value =" false" ></param >
</with >
Used in <table>, <partition>, <materialized-view>, and <index>.
PGD supports two variants for partition children. Variant B (nested) is the current standard.
13.1 Partition Key: <partition-by>
<partition-by type =" range" >
<column name =" created_at" ></column >
</partition-by >
Attribute
Type
Required
Values
type
enum
yes
range, list, hash
Children: <column> (by name) and/or <expression> (raw SQL).
13.2 Child Partitions: <partition> (Variant B)
<table name =" events" >
<column name =" id" type =" bigint" nullable =" false" ></column >
<column name =" created_at" type =" timestamptz" nullable =" false" ></column >
<pk name =" pk_events" >
<column name =" id" ></column >
<column name =" created_at" ></column >
</pk >
<partition-by type =" range" >
<column name =" created_at" ></column >
</partition-by >
<partition name =" events_2024" >
<bound >FOR VALUES FROM ('2024-01-01') TO ('2025-01-01')</bound >
</partition >
<partition name =" events_2025" >
<bound >FOR VALUES FROM ('2025-01-01') TO ('2026-01-01')</bound >
</partition >
<partition name =" events_default" >
<bound >DEFAULT</bound >
</partition >
</table >
<partition> attributes:
Attribute
Type
Description
name
string
Child table name
tablespace
string
Tablespace override
<partition> children:
Element
Description
<bound>
Partition bound clause (raw SQL: FOR VALUES ... or DEFAULT)
<partition-by>
Sub-partition key (for multi-level partitioning)
<partition>
Sub-partition children (recursive)
<with>
Storage parameters
13.3 Multi-level Partitioning
<partition name =" events_2024" >
<bound >FOR VALUES FROM ('2024-01-01') TO ('2025-01-01')</bound >
<partition-by type =" list" >
<column name =" region" ></column >
</partition-by >
<partition name =" events_2024_us" >
<bound >FOR VALUES IN ('us')</bound >
</partition >
<partition name =" events_2024_eu" >
<bound >FOR VALUES IN ('eu')</bound >
</partition >
</partition >
13.4 Variant A (Deprecated)
Variant A uses partition-of attribute on separate top-level table elements. This is deprecated; use Variant B (nested <partition> elements) instead. MigratePartitions() converts Variant A to Variant B automatically.
Indexes are stored at the schema level , not inside tables.
<index name =" idx_orders_user_id" table =" orders" >
<column name =" user_id" ></column >
</index >
<index name =" idx_orders_email_lower" table =" orders" unique =" true" using =" btree" >
<expression ><![CDATA[ lower(email)]]> </expression >
<where ><![CDATA[ deleted_at IS NULL]]> </where >
</index >
<index name =" idx_orders_covering" table =" orders" unique =" true" >
<column name =" order_number" ></column >
<include >
<column name =" status" ></column >
<column name =" total" ></column >
</include >
</index >
Attribute
Type
Default
Description
name
string
(required)
Index name
table
string
(required)
Table name
schema
string
—
Table schema (if different from parent <schema>)
unique
boolean
false
UNIQUE index
using
enum
btree
btree, hash, gist, spgist, gin, brin
nulls-distinct
boolean
true
false = NULLS NOT DISTINCT (PG15+)
tablespace
string
—
Tablespace
concurrently
boolean
false
CONCURRENTLY (used by ALTER generator)
Index <column> attributes
Attribute
Type
Description
name
string
Column name
order
enum
asc, desc
nulls
enum
first, last
opclass
string
Operator class (e.g. jsonb_path_ops, text_pattern_ops)
<expression> — expression index key (CDATA)
<where> — partial index predicate (CDATA)
<include> — covering index columns (PG11+)
Contains <column> elements (only name attribute used).
<view name =" v_active_users" schema =" public" >
<query ><![CDATA[ SELECT id, name FROM users WHERE deleted_at IS NULL]]> </query >
</view >
Attribute
Type
Default
Description
name
string
(required)
View name
schema
string
—
Schema
recursive
boolean
false
RECURSIVE view
security-barrier
boolean
false
security_barrier option
security-invoker
boolean
false
security_invoker option (PG15+)
check-option
string
—
local, cascaded
15.2 Materialized View: <materialized-view>
<materialized-view name =" mv_daily_stats" schema =" public" tablespace =" fast_ssd" >
<query ><![CDATA[ SELECT date, count(*) FROM events GROUP BY date]]> </query >
</materialized-view >
Attribute
Type
Default
Description
name
string
(required)
View name
schema
string
—
Schema
tablespace
string
—
Tablespace
using
string
—
Access method
with-data
boolean
true
false = WITH NO DATA
16. Functions: <functions>
<function name =" get_user_count" schema =" public" returns =" integer"
language =" sql" volatility =" stable" parallel =" safe" >
<body ><![CDATA[ SELECT count(*)::integer FROM users;]]> </body >
</function >
<function name =" create_user" schema =" public" returns =" bigint"
language =" plpgsql" volatility =" volatile" security =" definer" >
<arg name =" p_name" type =" text" ></arg >
<arg name =" p_email" type =" text" ></arg >
<arg name =" p_role" type =" text" default =" 'user'" ></arg >
<body ><![CDATA[
DECLARE
v_id bigint;
BEGIN
INSERT INTO users (name, email, role) VALUES (p_name, p_email, p_role) RETURNING id INTO v_id;
RETURN v_id;
END;
]]> </body >
</function >
Attribute
Type
Default
Description
name
string
(required)
Function name
schema
string
—
Schema
kind
string
function
function, procedure, aggregate
returns
string
—
Return type (omit when using <returns-table>)
language
string
(required)
plpgsql, sql, python, etc.
volatility
enum
volatile
immutable, stable, volatile
security
enum
invoker
invoker, definer
parallel
enum
unsafe
unsafe, restricted, safe
strict
boolean
false
RETURNS NULL ON NULL INPUT
leakproof
boolean
false
LEAKPROOF
window
boolean
false
Window function
cost
int
—
Execution cost estimate
rows
int
—
Estimated rows returned
Attribute
Type
Default
Description
name
string
—
Argument name
type
string
(required)
Data type
mode
string
in
in, out, inout, variadic
default
string
—
Default expression
<returns-table> (alternative to returns attribute)
<returns-table >
<column name =" id" type =" bigint" ></column >
<column name =" name" type =" text" ></column >
</returns-table >
Aggregate functions (kind="aggregate")
Additional attributes: sfunc, stype, finalfunc, combinefunc, initcond, sortop.
<trigger name =" trg_users_updated" schema =" public" table =" users"
timing =" before" events =" update" for-each =" row" >
<execute function =" set_updated_at" ></execute >
</trigger >
<trigger name =" trg_audit_insert" schema =" public" table =" orders"
timing =" after" events =" insert,update,delete" for-each =" row" >
<referencing new-table =" new_orders" old-table =" old_orders" ></referencing >
<when ><![CDATA[ NEW.status <> OLD.status]]> </when >
<execute function =" audit_changes" ></execute >
</trigger >
Attribute
Type
Default
Description
name
string
(required)
Trigger name
schema
string
—
Schema
table
string
(required)
Table name
timing
enum
(required)
before, after, instead-of
events
string
(required)
Comma-separated: insert, update, delete, truncate
for-each
enum
row
row, statement
constraint
boolean
false
CONSTRAINT trigger
deferrable
boolean
false
DEFERRABLE (constraint triggers)
initially
enum
—
immediate, deferred
of-columns
string
—
UPDATE OF columns (comma-separated)
<referencing> (transition tables)
Attribute
Type
Description
old-table
string
OLD TABLE alias
new-table
string
NEW TABLE alias
<when> — trigger condition (CDATA)
Attribute
Type
Required
Description
function
string
yes
Function name to execute
arguments
string
no
Arguments string
18. Policies: <policies> (Row-Level Security)
<policies >
<policy name =" users_own_data" schema =" public" table =" users"
type =" permissive" command =" all" to =" app_user" >
<using ><![CDATA[ id = current_user_id()]]> </using >
<with-check ><![CDATA[ id = current_user_id()]]> </with-check >
</policy >
</policies >
Attribute
Type
Default
Description
name
string
(required)
Policy name
schema
string
—
Schema
table
string
(required)
Table name
type
enum
permissive
permissive, restrictive
command
enum
all
all, select, insert, update, delete
to
string
public
Comma-separated role names
<using> — USING expression (CDATA)
<with-check> — WITH CHECK expression (CDATA)
19. Comments: <comments>
For comments on objects that don't have a comment attribute shorthand (or for long-form comments on any object).
<comments >
<comment on =" schema" name =" app" >Application schema</comment >
<comment on =" function" schema =" public" name =" get_user_count" >Returns total user count</comment >
<comment on =" extension" name =" pgcrypto" >Cryptographic functions</comment >
</comments >
Attribute
Type
Required
Description
on
enum
yes
Object type (see below)
schema
string
no
Schema (where applicable)
table
string
no
Table (for column, trigger, policy, constraint)
name
string
yes
Object name
Supported on values: schema, table, column, index, sequence, view, materialized-view, function, trigger, policy, type, domain, extension, role, database, constraint.
The element text content is the comment string.
Note: Tables and columns support a comment attribute shorthand directly on <table> and <column> elements. The <comments> section is for other object types or when you prefer centralized comments.
<grants >
<grant on =" schema" name =" app" privileges =" usage" to =" app_user" ></grant >
<grant on =" all-tables-in-schema" schema =" app" privileges =" select,insert,update,delete" to =" app_user" ></grant >
<grant on =" table" schema =" public" name =" users" privileges =" select" to =" readonly" ></grant >
<grant-role role =" app_readers" to =" app_user" with-inherit =" true" ></grant-role >
</grants >
Attribute
Type
Required
Description
on
enum
yes
Target type (see below)
schema
string
no
Schema
name
string
no
Object name (omitted for all-*-in-schema)
privileges
string
yes
Comma-separated: select, insert, update, delete, truncate, references, trigger, maintain, usage, create, execute, connect, temporary, all
to
string
yes
Role name or public
with-grant-option
boolean
no
WITH GRANT OPTION
Supported on values: schema, table, all-tables-in-schema, sequence, all-sequences-in-schema, function, all-functions-in-schema, type, database.
Attribute
Type
Default
Description
role
string
(required)
Role to grant
to
string
(required)
Grantee role
with-admin
boolean
false
WITH ADMIN OPTION
with-inherit
boolean
true
WITH INHERIT (PG16+)
with-set
boolean
true
WITH SET (PG16+)
21. Rules: <rules> (Deprecated)
<rules >
<rule name =" rule_name" schema =" public" table =" users" event =" insert" instead =" true" >
<![CDATA[ INSERT INTO audit_log (table_name) VALUES ('users')]]>
</rule >
</rules >
Attribute
Type
Required
Description
name
string
yes
Rule name
schema
string
no
Schema
table
string
yes
Table name
event
string
yes
select, insert, update, delete
instead
string
no
true = INSTEAD rule
where
string
no
Condition
Note: Rules are deprecated in PostgreSQL in favor of triggers. This element is retained for reverse-engineering compatibility.
22. Layouts: <layouts> (PGDesigner Metadata)
Diagram layout metadata is separated from the data model. It stores table positions, visual groups, and notes for the ERD canvas.
<layouts >
<layout name =" Default Diagram" default =" true" >
<entity schema =" public" table =" users" x =" 100" y =" 200" color =" #C1D8EE" ></entity >
<entity schema =" public" table =" orders" x =" 400" y =" 200" ></entity >
<group name =" Core Tables" color =" #E8F5E9" >
<member schema =" public" table =" users" ></member >
<member schema =" public" table =" orders" ></member >
</group >
<note x =" 50" y =" 50" w =" 200" h =" 80" color =" #FFF9C4" >Design notes here</note >
</layout >
</layouts >
Attribute
Type
Default
Description
name
string
(required)
Layout name
default
boolean
false
Primary layout shown on open
Attribute
Type
Default
Description
schema
string
—
Schema name
table
string
(required)
Table name
x
int
0
X position (pixels)
y
int
0
Y position (pixels)
color
string
—
Background color (CSS hex: #RRGGBB)
width
int
0
Width override (0 = auto)
height
int
0
Height override (0 = auto)
Attribute
Type
Description
name
string
Group name
color
string
Background color
Attribute
Type
Description
schema
string
Schema name
table
string
Table name
Attribute
Type
Description
x
int
X position
y
int
Y position
w
int
Width
h
int
Height
color
string
Background color
Text content is the note body.
Boolean values are represented as "true" / "false" strings. Omitting a boolean attribute uses its default (documented per element).
Several elements contain raw SQL as text content, typically wrapped in CDATA sections:
<check> — CHECK expression
<expression> — index expression
<where> — partial index predicate, trigger WHEN
<bound> — partition bound clause
<query> — view/materialized view query
<body> — function body
<using>, <with-check> — RLS policy expressions
<constraint> (domain) — CHECK expression
<rule> — rule actions
Objects are referenced by name throughout the format (no numeric IDs). Cross-schema references use dot notation (schema.table) in the to-table and owned-by attributes.
Each attribute on a single line for clean diffs
Predictable element ordering (columns before constraints, schemas before tables)
No auto-generated IDs that change across saves
CDATA sections for SQL to avoid XML escaping noise
Container elements (<roles>, <extensions>, <sequences>, etc.) may be present but empty, indicating the section exists but has no entries.
<?xml version =" 1.0" encoding =" UTF-8" ?>
<pgd version =" 1" pg-version =" 18" default-schema =" public" >
<project name =" example" description =" Example database" >
<settings >
<naming convention =" snake_case" tables =" plural" ></naming >
<defaults nullable =" true" on-delete =" restrict" on-update =" restrict" ></defaults >
</settings >
</project >
<roles >
<role name =" app_user" login =" true" ></role >
</roles >
<tablespaces ></tablespaces >
<extensions >
<extension name =" uuid-ossp" ></extension >
</extensions >
<types >
<enum name =" user_status" >
<label >active</label >
<label >inactive</label >
</enum >
</types >
<sequences >
<sequence name =" order_number_seq" start =" 1000" increment =" 1" ></sequence >
</sequences >
<schema name =" public" >
<table name =" users" comment =" Application users" >
<column name =" id" type =" bigint" nullable =" false" >
<identity generated =" always" ></identity >
</column >
<column name =" email" type =" varchar" length =" 255" nullable =" false" ></column >
<column name =" name" type =" text" ></column >
<column name =" status" type =" user_status" nullable =" false" default =" 'active'" ></column >
<column name =" created_at" type =" timestamptz" nullable =" false" default =" now()" ></column >
<pk name =" pk_users" >
<column name =" id" ></column >
</pk >
<unique name =" uq_users_email" >
<column name =" email" ></column >
</unique >
</table >
<table name =" orders" >
<column name =" id" type =" bigint" nullable =" false" >
<identity generated =" always" ></identity >
</column >
<column name =" user_id" type =" bigint" nullable =" false" ></column >
<column name =" order_number" type =" bigint" nullable =" false"
default =" nextval('order_number_seq')" ></column >
<column name =" total" type =" numeric" precision =" 12" scale =" 2"
nullable =" false" default =" 0" ></column >
<column name =" created_at" type =" timestamptz" nullable =" false" default =" now()" ></column >
<pk name =" pk_orders" >
<column name =" id" ></column >
</pk >
<fk name =" fk_orders_user" to-table =" users" on-delete =" cascade" on-update =" no action" >
<column name =" user_id" references =" id" ></column >
</fk >
<check name =" chk_positive_total" ><![CDATA[ total >= 0]]> </check >
</table >
<index name =" idx_orders_user_id" table =" orders" >
<column name =" user_id" ></column >
</index >
<index name =" idx_orders_created_at" table =" orders" >
<column name =" created_at" order =" desc" ></column >
</index >
</schema >
<views >
<view name =" v_active_users" >
<query ><![CDATA[ SELECT id, email, name FROM users WHERE status = 'active']]> </query >
</view >
</views >
<functions ></functions >
<triggers ></triggers >
<policies ></policies >
<comments >
<comment on =" schema" name =" public" >Default public schema</comment >
</comments >
<grants >
<grant on =" all-tables-in-schema" schema =" public" privileges =" select" to =" app_user" ></grant >
</grants >
<layouts >
<layout name =" Default Diagram" default =" true" >
<entity table =" users" x =" 100" y =" 100" ></entity >
<entity table =" orders" x =" 400" y =" 100" ></entity >
</layout >
</layouts >
</pgd >