RepoPilotOpen in app →

go-pg/pg

Golang ORM with focus on PostgreSQL features and performance

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 5mo ago
  • 16 active contributors
  • BSD-2-Clause licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Slowing — last commit 5mo ago
  • Concentrated ownership — top contributor handles 51% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/go-pg/pg)](https://repopilot.app/r/go-pg/pg)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/go-pg/pg on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: go-pg/pg

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/go-pg/pg shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 5mo ago
  • 16 active contributors
  • BSD-2-Clause licensed
  • CI configured
  • Tests present
  • ⚠ Slowing — last commit 5mo ago
  • ⚠ Concentrated ownership — top contributor handles 51% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live go-pg/pg repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/go-pg/pg.

What it runs against: a local clone of go-pg/pg — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in go-pg/pg | Confirms the artifact applies here, not a fork | | 2 | License is still BSD-2-Clause | Catches relicense before you depend on it | | 3 | Default branch v10 exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 193 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>go-pg/pg</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of go-pg/pg. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/go-pg/pg.git
#   cd pg
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of go-pg/pg and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "go-pg/pg(\\.git)?\\b" \\
  && ok "origin remote is go-pg/pg" \\
  || miss "origin remote is not go-pg/pg (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(BSD-2-Clause)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"BSD-2-Clause\"" package.json 2>/dev/null) \\
  && ok "license is BSD-2-Clause" \\
  || miss "license drift — was BSD-2-Clause at generation time"

# 3. Default branch
git rev-parse --verify v10 >/dev/null 2>&1 \\
  && ok "default branch v10 exists" \\
  || miss "default branch v10 no longer exists"

# 4. Critical files exist
test -f "db.go" \\
  && ok "db.go" \\
  || miss "missing critical file: db.go"
test -f "orm/model.go" \\
  && ok "orm/model.go" \\
  || miss "missing critical file: orm/model.go"
test -f "internal/pool/pool.go" \\
  && ok "internal/pool/pool.go" \\
  || miss "missing critical file: internal/pool/pool.go"
test -f "orm/query.go" \\
  && ok "orm/query.go" \\
  || miss "missing critical file: orm/query.go"
test -f "messages.go" \\
  && ok "messages.go" \\
  || miss "missing critical file: messages.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 193 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~163d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/go-pg/pg"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

go-pg is a PostgreSQL client and ORM for Go that prioritizes PostgreSQL-specific features and performance. It provides type-safe query building, struct-to-table mapping, and native support for PostgreSQL arrays, hstore, composite types, and other advanced data types without forcing you through a generic SQL abstraction layer. Monolithic package structure with core ORM logic in root files (db.go, base.go, hook.go) and opt-in extras in extra/ (extra/pgdebug, extra/pgotel for OpenTelemetry tracing). Examples are inline as example_*_test.go files demonstrating queries, arrays, relationships, and soft deletes. Tests parallel the codebase (base_test.go, db_test.go, etc.).

👥Who it's for

Go backend developers building PostgreSQL-backed services who need an ORM that doesn't abstract away PostgreSQL's unique capabilities. Specifically useful for teams that want performance (connection pooling, prepared statements) and PostgreSQL semantics over generic SQL database portability.

🌱Maturity & risk

Production-ready but in maintenance mode. The codebase is stable with comprehensive test coverage (example_*_test.go files, bench_test.go for performance validation) and CI via GitHub Actions (.github/workflows/build.yml). However, the README explicitly states it's in maintenance mode—active development has moved to the Bun project, so expect critical fixes only, not new features.

Low risk for existing codebases, moderate risk for new projects. The library is well-tested and widely used (evidenced by example projects like monetr, bunrouter), but being in maintenance mode means feature requests won't be prioritized. Single-maintainer dependency risk is real—this is a vmihailenco project. No breaking changes visible in recent commits, but newer codebases should prefer Bun (which supports PostgreSQL, MySQL, MariaDB, SQLite).

Active areas of work

Project is in maintenance mode—no active feature development. The .github/workflows/build.yml runs on each commit for CI/CD. The CHANGELOG.md and .github/workflows/release.yml indicate v10.x is the current stable series. New feature work has moved to Bun (github.com/uptrace/bun).

🚀Get running

Clone, set up Go 1.24+, and run tests:

git clone https://github.com/go-pg/pg.git
cd pg
go test ./...

For examples, review example_*_test.go files or run a specific example:

go test -run ExampleDB_Query -v

Daily commands: This is a library, not a runnable service. Run tests via Makefile:

make test

Or directly:

go test -v ./...

For examples (which require a running PostgreSQL instance), run specific example tests:

go test -run Example -v

🗺️Map of the codebase

  • db.go — Core database connection and query execution engine; every ORM operation routes through this module
  • orm/model.go — Central struct that defines table/model metadata and reflection; foundation for all ORM query building
  • internal/pool/pool.go — Connection pooling and lifecycle management; critical for performance and resource management
  • orm/query.go — Query builder abstraction layer; orchestrates SELECT, INSERT, UPDATE, DELETE construction
  • messages.go — PostgreSQL wire protocol message encoding/decoding; essential for client-server communication
  • hook.go — Lifecycle hook system (BeforeSelect, AfterInsert, etc.); enables cross-cutting ORM concerns
  • internal/parser/parser.go — SQL query parsing and result parsing logic; handles deserialization of database responses

🧩Components & responsibilities

  • db.go (DB Client) (context.Context, internal/pool, orm/*) — Orchestrates query execution; routes queries through pool, formats SQL, executes hooks, scans results
    • Failure mode: Connection exhaustion

🛠️How to make changes

Add a new ORM query method (e.g., WHERE clause builder)

  1. Define the method signature in orm/query.go that accepts parameters and returns *Query for chaining (orm/query.go)
  2. Implement the method to append SQL fragments using orm/format.go utilities (appendString, appendColumn, etc.) (orm/format.go)
  3. Add tests following the pattern in orm/*_test.go files (orm/delete_test.go)

Support a new PostgreSQL data type

  1. Implement the Appender interface in base.go (AppendValue method) for your custom type (base.go)
  2. Add Scanner implementation (sql.Scanner interface) for deserialization in internal/parser/parser.go (internal/parser/parser.go)
  3. Register type handling in orm/composite.go or orm/format.go if special formatting is needed (orm/composite.go)
  4. Add example test in example_*.go following existing patterns (e.g., example_array_test.go) (example_array_test.go)

Add a model lifecycle hook

  1. Define hook registration on your struct type following the pattern in hook.go (AddHook method) (hook.go)
  2. The hook executor calls BeforeInsert, AfterInsert, etc. automatically during orm operations in orm/insert.go or orm/delete.go (orm/insert.go)
  3. Implement the Hook interface on your callback function type (orm/hook.go)

Create a custom observability/debugging extension

  1. Create a new directory in extra/ (e.g., extra/mycustom/) with its own go.mod that replaces go-pg/pg (extra/pgotel/go.mod)
  2. Use internal/log.go to hook into the logging interface or wrap db.go Query/Exec calls (internal/log.go)
  3. Export a middleware-style function that wraps *pg.DB methods, following pgotel.go pattern (extra/pgotel/pgotel.go)

🔧Why these technologies

  • Pure Go with cgo-free wire protocol — Enables cross-platform deployment without C dependencies; faster builds and simpler distribution
  • Reflection-based struct mapping — Eliminates boilerplate; tight integration with Go struct tags for table/column naming conventions
  • Connection pooling with sticky strategy — Reuses prepared statements and reduces PostgreSQL backend spawning; critical for high-throughput workloads
  • Hook system for model lifecycle — Decouples ORM core from application business logic (soft deletes, timestamps, validation)
  • Pluggable logging interface (internal/log.go) — Allows third-party integrations (pgdebug, pgotel) without modifying core

⚖️Trade-offs already made

  • PostgreSQL-only (no MySQL/SQLite support)

    • Why: Allows deep optimization for PostgreSQL-specific features (JSONB, HSTORE, arrays, composite types, window functions)
    • Consequence: Not suitable for multi-database applications; development team recommends Bun for polyglot needs
  • Maintenance mode; active development moved to Bun

    • Why: go-pg achieved stable API; maintainers prefer to innovate with a fresh codebase
    • Consequence: Only critical bugs fixed; new features not accepted; users should evaluate Bun for new projects
  • Eager relation loading (preload) vs. lazy loading

    • Why: Explicit preloading prevents N+1 queries and gives developer control
    • Consequence: Requires deliberate join queries; does not auto-fetch related objects
  • Manual query building vs. fluent DSL

    • Why: Explicit SQL-like syntax (Where, Select, Order) maps closely to PostgreSQL semantics
    • Consequence: Steeper learning curve than Django ORM but closer control; SQL knowledge required

🚫Non-goals (don't propose these)

  • Multi-database support (PostgreSQL only)
  • Automatic migration generation
  • Lazy loading of relationships
  • Real-time change subscriptions (beyond LISTEN/NOTIFY)
  • GraphQL API generation
  • Distributed transactions across databases

🪤Traps & gotchas

PostgreSQL instance required for example tests (not mocked)—tests will fail or skip without a running postgres service on localhost. Struct tag syntax is non-obvious (db:"column_name" for mapping, pg:"rel:has-many" for relationships)—see example_*_test.go for correct patterns. The library uses msgpack for certain type serializations; understand vmihailenco/msgpack behavior if customizing type handling. Connection pooling defaults may need tuning for high-concurrency scenarios (see db.go Options). No explicit transaction isolation level docs in top-level files—check postgres documentation.

🏗️Architecture

💡Concepts to learn

  • PostgreSQL Wire Protocol — go-pg speaks the native PostgreSQL wire protocol (not just generic SQL); understanding this explains how prepared statements, arrays, and composite types work without translation layers
  • Struct tags for ORM mapping — go-pg uses Go struct tags (db:"", pg:"") to declare column names, relationships, and marshaling hints; this is the primary configuration surface
  • Query builder fluent pattern — go-pg uses method chaining (db.Model(&obj).Where(...).Select(...)) to construct type-safe SQL; understanding this pattern is essential for all database operations
  • PostgreSQL array types — go-pg has first-class support for PostgreSQL ARRAY types via struct tags and Array wrapper; leveraging this avoids JSON serialization overhead for list columns
  • Hook-based lifecycle management — Hooks (BeforeSelect, AfterInsert, BeforeScan) in hook.go allow custom logic at ORM lifecycle points without subclassing; critical for implementing soft deletes, timestamps, validation
  • Connection pooling — go-pg manages a pool of PostgreSQL connections to avoid per-query connection overhead; tuning pool size via Options in db.go is critical for production performance
  • HStore (PostgreSQL key-value type) — go-pg natively supports PostgreSQL hstore type via hstore tags; useful for semi-structured data without full JSON overhead
  • uptrace/bun — Official successor to go-pg; actively developed ORM supporting PostgreSQL, MySQL, MariaDB, SQLite—use this for new projects
  • go-pg/migrations — Schema migration tool maintained by go-pg author; paired companion for managing database versions alongside go-pg models
  • dizzyfool/genna — Code generator that scaffolds go-pg models from existing PostgreSQL schema; saves boilerplate for struct generation
  • lib/pq — Lower-level PostgreSQL driver for Go (github.com/lib/pq); go-pg is built atop similar wire-protocol patterns but adds ORM layer
  • robinjoseph08/go-pg-migrations — Alternative migration tool for go-pg; some teams prefer this implementation over the official one

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive integration tests for internal/pool connection pooling edge cases

The internal/pool directory contains critical connection pool implementations (pool.go, pool_single.go, pool_sticky.go) with existing test files, but the test coverage appears incomplete for edge cases like concurrent connection acquisition under resource constraints, graceful degradation, and sticky pool session affinity verification. Given this is a PostgreSQL ORM handling real database connections, robust pool testing is high-value for production reliability.

  • [ ] Review existing test coverage in internal/pool/*_test.go files
  • [ ] Add stress tests to internal/pool/pool_test.go for concurrent connection acquisition exceeding pool size
  • [ ] Add regression tests to internal/pool/pool_sticky_test.go to verify sticky session behavior under connection failures
  • [ ] Add benchmarks to internal/pool/bench_test.go comparing single vs sticky vs standard pool under various concurrency levels
  • [ ] Document pool behavior expectations in internal/pool README or code comments

Add hook lifecycle tests covering all database operation stages (Insert, Update, Delete, Select)

The hook.go file implements before/after hooks for database operations, but hook_test.go likely has incomplete coverage of all hook trigger points across different query types. With multiple example_*_test.go files showing various ORM features, there's an opportunity to add comprehensive hook testing that validates hook execution order, error handling, and state mutations across all database operations.

  • [ ] Review hook_test.go and identify missing test cases for Insert, Update, Delete, Select operations
  • [ ] Add tests to hook_test.go verifying hook execution order (Before→After) for each operation type
  • [ ] Add tests for hook error handling and transaction rollback scenarios
  • [ ] Add tests for hook state mutations (e.g., modifying model data in Before hooks)
  • [ ] Add integration tests combining hooks with the example patterns shown in example_*_test.go files

Add parser tests for complex PostgreSQL query edge cases in internal/parser

The internal/parser directory handles parsing of PostgreSQL queries with two implementations (parser.go, streaming_parser.go), but there are no visible test files. Given go-pg's focus on PostgreSQL features and performance, comprehensive parser testing for edge cases (CTEs, window functions, complex JOINs, JSON operators, array operations) would ensure query correctness and catch regressions early.

  • [ ] Create internal/parser/parser_test.go with unit tests for parser.go
  • [ ] Create internal/parser/streaming_parser_test.go with tests for streaming_parser.go
  • [ ] Add test cases covering PostgreSQL-specific syntax: CTEs (WITH clauses), window functions, CASE expressions, JSON/JSONB operators
  • [ ] Add test cases for array operations and composite types (referenced in example_array_test.go and example_composite_test.go)
  • [ ] Add property-based tests (using go-quickcheck or similar) to validate parser robustness with malformed/unusual query patterns

🌿Good first issues

  • Add comprehensive godoc examples to base.go and db.go explaining query builder fluent API with real PostgreSQL examples (currently sparse inline comments make the API discoverable only via example_*_test.go)
  • Create a migration guide document from go-pg v9 to v10 with concrete diffs (the CHANGELOG.md exists but lacks before/after code snippets for common patterns)
  • Extend example_composite_test.go with more PostgreSQL composite type edge cases (nullable fields, nested composites) and add corresponding tests to ensure marshaling round-trips correctly

Top contributors

Click to expand

📝Recent commits

Click to expand
  • fda0d17 — build(deps): bump golang.org/x/crypto in /extra/pgotel/example (#2040) (dependabot[bot])
  • 2630433 — fix(deps): Fixing x/crypto/ssh security warnings (#2039) (elliotcourant)
  • e3f72a8 — build(deps): bump golang.org/x/crypto in /extra/pgdebug (#2038) (dependabot[bot])
  • 0e0b66e — build(deps): bump golang.org/x/crypto in /extra/pgotel (#2037) (dependabot[bot])
  • 4c50f9b — build(deps): bump gopkg.in/yaml.v3 from 3.0.0 to 3.0.1 (#2035) (dependabot[bot])
  • 2dc6c2b — Release v10.15.0 (release.sh) (#2034) (elliotcourant)
  • b3a2091 — build(deps): bump golang.org/x/crypto in /extra/pgdebug (#2030) (dependabot[bot])
  • e70ce6d — build(deps): bump golang.org/x/crypto in /extra/pgotel/example (#2028) (dependabot[bot])
  • 67a97a4 — Bump golang.org/x/net from 0.23.0 to 0.38.0 (#2021) (dependabot[bot])
  • eff50a4 — fix: add mandatory space before negative numbers to resolve CVE-2024-44905 (#2029) (martoche)

🔒Security observations

The go-pg codebase presents moderate security concerns, primarily stemming from outdated cryptographic and system-level dependencies (golang.org/x/crypto and golang.org/x/sys). The project is in maintenance mode with limited active security development. The ORM functionality requires careful attention to SQL injection prevention. While no hardcoded credentials or obvious critical vulnerabilities were detected in the file structure provided, the reliance on outdated dependencies and maintenance-mode status significantly increases risk. The project should either update all dependencies to current versions or migrate to the actively maintained Bun successor.

  • High · Outdated Go Cryptography Dependencies — extra/pgdebug/go.mod, go.mod (indirect dependency). The go.mod file specifies golang.org/x/crypto v0.45.0, which is outdated. Modern Go security patches and improvements may not be included. This is particularly critical for a database ORM that handles sensitive data and authentication. Fix: Update golang.org/x/crypto to the latest stable version (currently v0.50.0+). Run 'go get -u golang.org/x/crypto' to update across all modules.
  • High · Outdated System Libraries — extra/pgdebug/go.mod, go.mod (indirect dependency). golang.org/x/sys v0.38.0 is outdated. System-level security patches and OS integration improvements may be missing, which could affect platform-specific security features. Fix: Update golang.org/x/sys to the latest stable version (currently v0.52.0+). Run 'go get -u golang.org/x/sys' to update.
  • Medium · Potential SQL Injection Risk in ORM — db.go, orm/, messages.go. The codebase includes ORM functionality with raw SQL query capabilities (inferred from file structure: orm/*, messages.go, db.go). ORMs handling database queries require careful validation of query construction and parameter binding to prevent SQL injection. Fix: Audit all SQL query construction methods. Ensure parameterized queries (prepared statements) are used exclusively. Validate that user input is never concatenated directly into SQL strings. Add input validation and sanitization layers.
  • Medium · Maintenance Mode - Limited Security Updates — README.md, Project Status. According to the README, go-pg is in maintenance mode with only critical issues addressed. This means non-critical security vulnerabilities may not be fixed, and the project is not actively developed. Fix: Consider migrating to the actively maintained successor project 'Bun' (https://bun.uptrace.dev/) which offers similar functionality with ongoing security maintenance. If continuing with go-pg, implement additional security controls and regular security audits.
  • Medium · SASL Authentication Library Dependency — extra/pgdebug/go.mod (indirect dependency). mellium.im/sasl v0.3.1 is an indirect dependency used for authentication. Ensure this library is kept updated and is from a trusted source, as authentication libraries are critical for security. Fix: Monitor mellium.im/sasl for security updates. Consider auditing the authentication flow to ensure it follows best practices. Keep this dependency updated regularly.
  • Low · Third-party Serialization Library — go.mod, go.sum (indirect dependency). The codebase depends on msgpack (github.com/vmihailenco/msgpack/v5 v5.3.4) for serialization. Deserialization of untrusted data could potentially lead to code execution if not handled carefully. Fix: Ensure msgpack deserialization is only performed on trusted, validated data. Consider validating message structure and size limits before deserialization. Keep the library updated.
  • Low · Missing Security Policy Documentation — Repository root. No visible SECURITY.md or security policy file in the repository structure to guide responsible disclosure of security vulnerabilities. Fix: Create a SECURITY.md file with instructions for responsible vulnerability disclosure. Include contact information for security issues and define the process for security patches.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · go-pg/pg — RepoPilot