Skip to content

Commit df1656f

Browse files
committed
init
1 parent ba6d890 commit df1656f

11 files changed

Lines changed: 1102 additions & 0 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CLAUDE.md

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Roger Welin
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
EXTENSION = pg_column_tetris
2+
DATA = pg_column_tetris--0.1.0.sql
3+
4+
PG_CONFIG = pg_config
5+
PGXS := $(shell $(PG_CONFIG) --pgxs)
6+
include $(PGXS)

README.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# pg_column_tetris
2+
3+
A PostgreSQL extension that enforces optimal column alignment to minimize row padding waste.
4+
5+
## Why It Matters
6+
7+
PostgreSQL aligns columns to their type's natural boundary (1, 2, 4, or 8 bytes). When columns are ordered poorly, padding bytes fill the gaps. These wasted bytes don't just cost disk space — they cost **memory**. Padding is loaded as-is into `shared_buffers` and OS page cache, meaning more bytes per row → fewer rows per 8 KB page → more cache pressure → more disk I/O.
8+
9+
For a table with millions of rows, reordering columns can reclaim gigabytes of memory.
10+
11+
## Installation
12+
13+
```bash
14+
# From source
15+
make install
16+
17+
# Then in PostgreSQL
18+
CREATE EXTENSION pg_column_tetris;
19+
```
20+
21+
No compilation needed — pure SQL/PL/pgSQL. Works on any PostgreSQL instance that supports event triggers (RDS, Cloud SQL, Supabase, Neon, etc.).
22+
23+
## Quick Start
24+
25+
The extension installs in **warn** mode by default. Create a table with suboptimal column order:
26+
27+
```sql
28+
CREATE TABLE orders (
29+
is_shipped boolean,
30+
order_total numeric,
31+
user_id bigint,
32+
item_ct smallint,
33+
order_dt timestamptz,
34+
status smallint,
35+
ship_dt timestamptz
36+
);
37+
```
38+
39+
You'll see a NOTICE suggesting the optimal order:
40+
41+
```
42+
NOTICE: pg_column_tetris: suboptimal column alignment — 19 bytes of fixed-width padding wasted per row
43+
```
44+
45+
Switch to **strict** mode to block suboptimal tables entirely:
46+
47+
```sql
48+
SELECT pg_column_tetris.set_mode('strict');
49+
```
50+
51+
Now the same CREATE TABLE will fail with an error and a hint showing the optimal column order:
52+
53+
```
54+
ERROR: suboptimal column alignment — 19 bytes of fixed-width padding wasted per row
55+
HINT: Suggested order:
56+
CREATE TABLE orders (
57+
user_id bigint, -- 8-byte aligned
58+
order_dt timestamptz, -- 8-byte aligned
59+
ship_dt timestamptz, -- 8-byte aligned
60+
item_ct smallint, -- 2-byte aligned
61+
status smallint, -- 2-byte aligned
62+
is_shipped boolean, -- 1-byte aligned
63+
order_total numeric -- varlena (last)
64+
);
65+
```
66+
67+
## Optimal Column Order
68+
69+
The extension enforces this ordering rule for fixed-width columns:
70+
71+
1. **8-byte aligned** (`d`): `bigint`, `timestamptz`, `float8`, `interval`
72+
2. **4-byte aligned** (`i`): `integer`, `float4`, `date`, `oid`
73+
3. **2-byte aligned** (`s`): `smallint`
74+
4. **1-byte aligned** (`c`): `boolean`, `char(1)`
75+
5. **Variable-length** (varlena): `text`, `varchar`, `numeric`, `jsonb`, `bytea` — always last
76+
77+
Within each group, `NOT NULL` columns are preferred first (minor CPU optimization for tuple deforming).
78+
79+
## Auditing Existing Tables
80+
81+
```sql
82+
-- Detailed layout report for a single table
83+
SELECT * FROM pg_column_tetris.check('orders');
84+
85+
-- Generate migration DDL to fix a table
86+
SELECT pg_column_tetris.suggest_rewrite('orders');
87+
```
88+
89+
The `suggest_rewrite` function generates a complete migration script:
90+
91+
```sql
92+
BEGIN;
93+
ALTER TABLE public.orders RENAME TO orders_old;
94+
CREATE TABLE public.orders ( ...optimal order... );
95+
INSERT INTO public.orders SELECT ... FROM public.orders_old;
96+
DROP TABLE public.orders_old;
97+
COMMIT;
98+
```
99+
100+
## Configuration
101+
102+
```sql
103+
-- Set mode: 'strict' (block), 'warn' (notice only), 'off' (disable)
104+
SELECT pg_column_tetris.set_mode('strict');
105+
106+
-- Check current mode
107+
SELECT pg_column_tetris.mode();
108+
109+
-- Exclude a table from validation (e.g., matching an external schema)
110+
SELECT pg_column_tetris.exclude('legacy_imports');
111+
```
112+
113+
## What Gets Checked
114+
115+
- **CREATE TABLE** statements are validated by the event trigger
116+
- **ALTER TABLE** is deliberately skipped — you can't reorder existing columns, so warning would be noise
117+
- **Temp tables** and **system schemas** (`pg_catalog`, `information_schema`) are skipped
118+
- Tables in the `exclusions` list are skipped
119+
120+
## Requirements
121+
122+
- PostgreSQL 9.5+ (requires `pg_event_trigger_ddl_commands()`)
123+
- Superuser privileges to create the event trigger
124+
125+
## License
126+
127+
MIT

0 commit comments

Comments
 (0)