Skip to content

Commit 0fa40f0

Browse files
committed
feat: initial public release of pg-storage-visualizer
0 parents  commit 0fa40f0

27 files changed

Lines changed: 5631 additions & 0 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bin/
2+

LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
pg-storage-visualizer
2+
3+
Copyright (c) 2025, Radim Marek <radim@boringsql.com>
4+
5+
Permission to use, copy, modify, and distribute this software and its
6+
documentation for any purpose, without fee, and without a written agreement
7+
is hereby granted, provided that the above copyright notice and this
8+
paragraph and the following two paragraphs appear in all copies.
9+
10+
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR DIRECT,
11+
INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
12+
PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
13+
EVEN IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
14+
DAMAGE.
15+
16+
THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
17+
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18+
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
19+
AND THE COPYRIGHT HOLDER HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
20+
UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

Makefile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.PHONY: build run dev clean deps docker generate
2+
3+
BIN := bin/pg-storage-visualizer
4+
CMD := ./cmd/pg-storage-visualizer
5+
6+
build: generate
7+
go build -o $(BIN) $(CMD)
8+
9+
run: build
10+
$(BIN)
11+
12+
generate:
13+
@which templ > /dev/null || go install github.com/a-h/templ/cmd/templ@latest
14+
templ generate
15+
16+
clean:
17+
rm -rf bin/
18+
19+
deps:
20+
go mod tidy
21+
22+
docker:
23+
docker build -t pg-storage-visualizer .

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# pg-storage-visualizer
2+
3+
PostgreSQL storage visualization tool for education purposes. Explore tables and
4+
indexes - page structure, tuples, MVCC, HOT chains, bloat and more.
5+
6+
**IMPORTANT** this tool is not to be used for running against any production
7+
PostgreSQL cluster!
8+
9+
## Features
10+
11+
- **Table inspection** - heap pages, tuple layout, MVCC visibility
12+
- **Index visualization** - B-tree structure, page density, bloat analysis
13+
- **Try HOT update** - see heap-only tuples and ctid chains in action
14+
- **Row finder** - locate rows by PK or TID, see storage location
15+
- **No custom extension** - uses built-in `pageinspect` and `pgstattuple`
16+
- **Single binary** - no dependencies
17+
18+
## Quick Start
19+
20+
```bash
21+
make build
22+
./bin/pg-storage-visualizer
23+
24+
# Or specify connection
25+
./bin/pg-storage-visualizer -db "postgres://user:pass@localhost:5432/mydb"
26+
27+
open http://localhost:8080
28+
```
29+
30+
## Build Dependencies
31+
32+
[templ](https://templ.guide/) - HTML templating for Go:
33+
34+
```bash
35+
# via go
36+
go install github.com/a-h/templ/cmd/templ@latest
37+
38+
# via brew
39+
brew install templ
40+
```
41+
42+
## Requirements
43+
44+
PostgreSQL 13+ with extensions:
45+
46+
```sql
47+
CREATE EXTENSION IF NOT EXISTS pageinspect;
48+
CREATE EXTENSION IF NOT EXISTS pgstattuple;
49+
```

cmd/pg-storage-visualizer/main.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"log"
6+
"os"
7+
8+
"github.com/boringsql/pg-storage-visualizer"
9+
)
10+
11+
func main() {
12+
port := flag.Int("port", 8080, "HTTP server port")
13+
connStr := flag.String("db", "", "PostgreSQL connection string (or use DATABASE_URL env)")
14+
flag.Parse()
15+
16+
dbURL := *connStr
17+
if dbURL == "" {
18+
dbURL = os.Getenv("DATABASE_URL")
19+
}
20+
21+
if err := pgstoviz.Run(pgstoviz.Config{
22+
Port: *port,
23+
DBUrl: dbURL,
24+
}); err != nil {
25+
log.Fatal(err)
26+
}
27+
}

go.mod

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module github.com/boringsql/pg-storage-visualizer
2+
3+
go 1.23.0
4+
5+
require (
6+
github.com/a-h/templ v0.3.960
7+
github.com/jackc/pgx/v5 v5.7.2
8+
)
9+
10+
require (
11+
github.com/boringsql/queries v1.5.0 // indirect
12+
github.com/jackc/pgpassfile v1.0.0 // indirect
13+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
14+
github.com/jackc/puddle/v2 v2.2.2 // indirect
15+
golang.org/x/crypto v0.31.0 // indirect
16+
golang.org/x/sync v0.16.0 // indirect
17+
golang.org/x/text v0.21.0 // indirect
18+
)

go.sum

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
github.com/a-h/templ v0.3.960 h1:trshEpGa8clF5cdI39iY4ZrZG8Z/QixyzEyUnA7feTM=
2+
github.com/a-h/templ v0.3.960/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5HBo=
3+
github.com/boringsql/queries v1.5.0 h1:9+xPtC7HGzJeIsQ5QZHB+59QMnO8sWvV6+3D594k/Vk=
4+
github.com/boringsql/queries v1.5.0/go.mod h1:zRQzwzZZ8e9o8PZWTKMxPqxTTg8hGvvinwitEBd0FCQ=
5+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
7+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
9+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
10+
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
11+
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
12+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
13+
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
14+
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
15+
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
16+
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
17+
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
18+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
19+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
20+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
21+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
22+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
23+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
24+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
25+
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
26+
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
27+
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
28+
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
29+
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
30+
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
31+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
32+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
33+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
34+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)