Przejdź do treści
Biblioteka skilli
Dane i analityka · Budowanie i kod

postgres-patterns

/postgres-patterns

Projektuje schematy PostgreSQL, typy danych, indeksy, upserty, paginację keyset, bezpieczne migracje i polityki RLS.

Repozytorium
The-Insectoids ↗
Licencja
MIT
Ostatnia zmiana repozytorium
12.11.2026

Ocena i bezpieczeństwo

100/ 100Klasa: dobry
Format25 / 25
Frontmatter z nazwą i opisem, poprawna nazwa, opis do 1024 znaków, nazwa zgodna z folderem.
Odkrywalność25 / 25
Opis mówi, kiedy użyć skilla, ma sensowną długość i jest w 3. osobie.
Treść35 / 35
Co najmniej 150 słów i do 500 linii, nagłówki, listy kroków, przykłady albo kod.
Higiena15 / 15
Bez zaślepek typu TODO, bez ścieżek Windows, z licencją.
Ryzykobrak sygnałów w skanie
Pliki w paczce
2
Skrypty
0
Komentarze HTML
0
Adresy URL w SKILL.md
0
Przestarzałość

Brak sygnałów. Repozytorium zmienione 12.11.2026, bez odwołań do wycofanych modeli i API.

Licencja

MIT, otwarta: można używać i zmieniać, z zachowaniem informacji o autorze.

Co robi i kiedy

Projektuje schematy PostgreSQL, typy danych, indeksy, upserty, paginację keyset, bezpieczne migracje i polityki RLS.

Oryginalny opis (EN)

PostgreSQL schema design, data types, indexing, query patterns (upsert, keyset pagination, job queues), safe online migrations, connection pooling, timeouts, roles and row-level security, and reading query plans. Use when designing tables or indexes, writing SQL or migrations for Postgres, adding RLS policies, or when a Postgres query is slow.

Jak działa

  1. When to use
  2. Safety first
  3. Data types
  4. Schema rules
  5. Indexes
  6. Query patterns
  7. Transactions and locking
  8. Safe online migrations
  9. Connections and timeouts
  10. Roles and row-level security
Sekcje z nagłówków SKILL.md, w oryginale · 123 linii, 1 340 słów
Podgląd SKILL.md
---
name: postgres-patterns
description: PostgreSQL schema design, data types, indexing, query patterns (upsert, keyset pagination, job queues), safe online migrations, connection pooling, timeouts, roles and row-level security, and reading query plans. Use when designing tables or indexes, writing SQL or migrations for Postgres, adding RLS policies, or when a Postgres query is slow.
metadata:
  kind: skill
  castes: [coder, reviewer]
  tags: [database, postgres, sql, performance, security]
  version: 1
  status: vetted
  sources:
    - repo: affaan-m/ecc
      path: skills/postgres-patterns/SKILL.md
      license: MIT
      note: ecc credits Supabase Agent Skills (MIT) as its basis
---

# PostgreSQL Patterns

Let the database enforce integrity, index for the queries you actually run, and prove performance with a plan, not a guess.

## When to use

- Designing or changing tables, constraints or indexes.
- Writing SQL (raw or via an ORM) or migrations that will run on Postgres.
- Adding row-level security or database roles.
- Investigating a slow query or lock contention.

Also load: reversible-migration (migration workflow), django-patterns or fastapi-patterns (ORM side).

## Safety first

- Never run exploratory writes, `EXPLAIN ANALYZE` on write statements, or schema changes against production yourself. Work on a local or staging copy. Production changes go through a reviewed migration and need user approval.
- Dropping or truncating tables or columns, or deleting data, always needs user approval.
- Always use parameterised queries (driver placeholders or an ORM). Never build SQL from user input with string formatting. Examples here write parameters as `:name`; in raw Postgres they are numbered positional parameters (dollar sign + number), and Python drivers use `%s`.

## Data types

| Use case | Use | Avoid |
|---|---|---|
| Primary keys | `bigint GENERATED ALWAYS AS IDENTITY` | `serial`, `int` (overflows) |
| Public or distributed IDs | UUIDv7 (time-ordered; built-in `uuidv7()` as of Postgres 18) | random UUIDv4 as the clustered key on hot tables |
| Text | `text`, with a `CHECK (length(col) <= n)` if a limit matters | `varchar(255)` by habit |
| Timestamps | `timestamptz` | `timestamp` without time zone |
| Money / exact decimals | `numeric(p, s)` or integer minor units | `float`, `real`, `money` |
| Flags | `boolean` | `int` or `'Y'/'N'` |
| Semi-structured | `jsonb` | `json`, or jsonb for fields you filter on constantly |
| Enumerations | lookup table or `text` + `CHECK`; native enums are hard to change | free text |

## Schema rules

- `NOT NULL` by default; allow null only when "unknown" is a real state.
- Every foreign key column gets an index (Postgres does not create one automatically). Unindexed FKs make deletes and joins slow.
- Uniqueness, ranges and relationships are constraints in the database (`UNIQUE`, `CHECK`, `FOREIGN KEY`), not only application checks.
- `snake_case` lowercase names; name constraints and indexes explicitly.
- `created_at timestamptz NOT NULL DEFAULT now()`; maintain `updated_at` in the app or a trigger.

## Indexes

| Query pattern | Index |
|---|---|
| `WHERE col = x`, ranges, `ORDER BY col` | B-tree (default) |
| `WHERE a = x AND b > y` | composite `(a, b)`: equality columns first, then range/sort |
| `WHERE jsonb_col @> '{...}'`, arrays, full-text `@@` | GIN |
| Huge append-only table filtered by time | BRIN on the time column |
| `WHERE lower(email) = ...` | expression index `(lower(email))` |
| Only a subset is queried (`WHERE deleted_at IS NULL`) | partial index |
| Avoid heap lookups for a hot read | covering index `... INCLUDE (col2, col3)` |

Rules:
- Index for real queries; every index slows writes and uses memory. Remove unused ones (check `pg_stat_user_indexes.idx_scan`).
- On live tables create and drop with `CONCURRENTLY` (cannot run inside a transaction block).
- A composite index `(a, b)` also serves queries on `a` alone; do not add a separate `(a)`.

## Query patterns

- **Select only needed columns.** `SELECT *` breaks covering indexes and wastes I/O.
- **Upsert:** `INSERT ... ON CONFLICT (key) DO UPDATE SET col = EXCLUDED.col` (needs a unique constraint on `key`).
- **Keyset pagination** for large or deep lists: `WHERE (created_at, id) < (:last_created_at, :last_id) ORDER BY created_at DESC, id DESC LIMIT 20`, with an index on `(created_at, id)`. `OFFSET` cost grows with the offset.
- **Job queue:** claim rows with `FOR UPDATE SKIP LOCKED` inside a short transaction so workers never block each other.
- **Batching:** insert many rows per statement (or `COPY`); update or delete large sets in batches of a few thousand to keep locks and WAL bursts small.
- **N+1:** fetch related rows with a join or `WHERE id = ANY(:ids)`, not one query per row.
- **Counting:** exact `count(*)` on huge tables is slow; use estimates (`pg_class.reltuples`) where exactness is not needed.

## Transactions and locking

- Keep transactions short; never wait on network calls or user input inside one.
- Default isolation is READ COMMITTED. For read-modify-write, use `SELECT ... FOR UPDATE`, an atomic `UPDATE ... SET n = n - 1 WHERE n > 0`, or SERIALIZABLE with retry on serialization failure.
- Lock rows in a consistent order to avoid deadlocks.
- Set `lock_timeout` (for example `5s`) in migrations so a blocked DDL fails fast instead of queuing every query behind it.

## Safe online migrations

- Adding a nullable column, or a column with a constant default, is fast (no table rewrite since Postgres 11).
- Adding `NOT NULL` to an existing column: add `CHECK (col IS NOT NULL) NOT VALID`, then `VALIDATE CONSTRAINT`, then `SET NOT NULL` (Postgres 12+ uses the validated check and skips the scan).
- Adding a foreign key: `ADD CONSTRAINT ... NOT VALID`, then `VALIDATE CONSTRAINT` separately.
- Indexes: `CREATE INDEX CONCURRENTLY`.
- Changing a column type usually rewrites the table: add a new column, backfill in batches, switch reads, drop the old column later (expand/contract).
- Renames and drops break the running app version; ship them in a later release after the code stops using the old name.

## Connections and timeouts

- Use a connection pool in the app and, for many app instances or serverless, a server-side pooler (for example PgBouncer in transaction mode). Transaction-mode pooling breaks session state (session-level `SET`, advisory locks, some prepared-statement setups); check driver settings.
- Size pools from database capacity: total connections across all instances must stay under `max_connections` with headroom.
- Set `statement_timeout` and `idle_in_transaction_session_timeout` per role or database (for example 30s for web traffic, longer for a batch role). Server configuration changes on production need user approval.
- Enable `pg_stat_statements` to find the most expensive queries.

## Roles and row-level security

- The application connects as a least-privilege role that does not own the tables. Migrations run as the owner role.
- Grant only needed privileges per schema/table; as of Postgres 15 the `public` schema no longer allows `CREATE` for everyone, but check older databases and revoke it.
- RLS: `ALTER TABLE t ENABLE ROW LEVEL SECURITY;` plus `FORCE ROW LEVEL SECURITY` if the owner role must also be filtered. Write a policy per operation.
- In policies, wrap function calls in a sub-select so they run once per query, not once per row: `USING (user_id = (SELECT current_setting('app.user_id')::bigint))`. On Supabase the same applies to `(SELECT auth.uid())`.
- Index the columns policies filter on.
- Test policies as the application role: one user must not read or modify another's rows.

## Investigating a slow query

1. Get the exact query and parameters (from `pg_stat_statements` or logs).
2. On a non-production copy with realistic data, run `EXPLAIN (ANALYZE, BUFFERS) <query>`. `ANALYZE` executes the statement; wrap writes in `BEGIN; ... ROLLBACK;`.
3. Look for: sequential scans on large tables with selective filters, row estimates far from actual (run `ANALYZE` on the table), nested loops over many rows, sorts or hashes spilling to disk.
4. Fix the cause (index, rewrite, fresh statistics, smaller result), then re-run the plan and compare timings.

Diagnostic queries (unindexed FKs, slow queries, unused indexes, bloat, locks): [references/diagnostics.md](references/diagnostics.md).

## Red flags (review)

- FK columns without indexes; missing `NOT NULL`/`UNIQUE` the code relies on.
- `timestamp` without time zone, `float` for money, `serial` in new tables.
- String-built SQL; `SELECT *` in hot paths; `OFFSET` deep pagination.
- `CREATE INDEX` without `CONCURRENTLY` or blocking DDL without `lock_timeout` on large live tables.
- Long transactions wrapping external calls.
- RLS enabled without tests, or app connecting as table owner or superuser.

## Done when

- Constraints express the data rules; each new query has a supporting index or a plan showing it does not need one.
- Migrations are online-safe and reversible, with destructive steps approved.
Pliki w paczce (2)
  • SKILL.md 9 KB
  • references/diagnostics.md 5 KB

Wersje

RepozytoriumWynikLicencja
Ta strona · Sanit-mann/The-Insectoids100MIT
SHRIJANAGAIN/Nemo-Cli100Apache-2.0
ghvinashvili/specagent100Apache-2.0
pekral/cursor-rules100MIT
tranhieutt/software_development_department100MIT

Migawka bazy z 13.11.2026 · wersja paczki ae8a3730899e (skrót SHA-256 całej paczki)

Pobrania w ostatnich 30 dniach: 0 · ranking

Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.

Podobne skille

  • 100data
    Projektuje lub przegląda schemat Supabase i Postgres, migracje, polityki RLS, funkcje SECURITY DEFINER, indeksy i wydajność zapytań.
  • 90postgres-schema-rls
    Projektuje schematy PostgreSQL, indeksy i bezpieczeństwo wierszy na podstawie jawnych kontraktów dostępu, również dla Supabase.
  • 100supabase-postgresql-engineering
    Projektuje schematy, klucze UUID, indeksy, ograniczenia i polityki RLS dla Supabase Postgres za Django, z pulą połączeń i migracjami.
  • 100postgres
    Projektuje i stroi schematy PostgreSQL, zapytania, indeksy, blokady i vacuum, a także pomaga przy migracjach na żywej tabeli.
  • 100postgres
    Wykonuje doraźne zapytania SELECT do PostgreSQL, analizuje plany EXPLAIN i ANALYZE, sprawdza schematy, indeksy i blokady oraz bada wolne zapytania, domyślnie tylko do odczytu.
  • 100design-postgres-tables
    Wspiera ogólne projektowanie tabel PostgreSQL: typy danych, ograniczenia, indeksy, normalizację, schematy JSONB i tabele intensywnie aktualizowane lub z upsertami.

Uwagi i zgłoszenia

Wiadomość trafi do Bartosza Sokolińskiego. Adres e-mail jest potrzebny tylko do odpowiedzi. Szczegóły w polityce prywatności.