@evertonferreira96/agents 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/api.md +45 -0
- package/.claude/agents/app.md +41 -0
- package/.claude/agents/architect.md +49 -0
- package/.claude/agents/infra.md +27 -0
- package/.claude/agents/product.md +58 -0
- package/.claude/agents/quick.md +33 -0
- package/.claude/agents/security.md +39 -0
- package/.claude/agents/ui.md +54 -0
- package/.claude/agents/verifier.md +56 -0
- package/.claude/agents/web.md +41 -0
- package/.claude/hooks/config-update-check.mjs +14 -0
- package/.claude/hooks/context-budget.mjs +51 -0
- package/.claude/hooks/graphify-update.sh +16 -0
- package/.claude/hooks/guard-bash.sh +64 -0
- package/.claude/hooks/orchestrator-guard.sh +49 -0
- package/.claude/hooks/post-compact.sh +26 -0
- package/.claude/hooks/readonly-guard.sh +54 -0
- package/.claude/hooks/stop-verify.sh +52 -0
- package/.claude/rules/api.md +21 -0
- package/.claude/rules/db.md +9 -0
- package/.claude/rules/delivery-diagram.md +16 -0
- package/.claude/rules/git-workflow.md +34 -0
- package/.claude/rules/graphify.md +3 -0
- package/.claude/rules/mobile.md +22 -0
- package/.claude/rules/tickets.md +9 -0
- package/.claude/rules/ui.md +14 -0
- package/.claude/rules/web.md +27 -0
- package/.claude/settings.json +114 -0
- package/.claude/skills/animate/RECIPES.md +324 -0
- package/.claude/skills/animate/SKILL.md +208 -0
- package/.claude/skills/animate/agents/openai.yaml +5 -0
- package/.claude/skills/animate-expo/RECIPES.md +385 -0
- package/.claude/skills/animate-expo/SKILL.md +264 -0
- package/.claude/skills/animate-expo/agents/openai.yaml +5 -0
- package/.claude/skills/animation-performance/SKILL.md +223 -0
- package/.claude/skills/animation-performance/agents/openai.yaml +5 -0
- package/.claude/skills/apple-design/SKILL.md +291 -0
- package/.claude/skills/apple-design/agents/openai.yaml +5 -0
- package/.claude/skills/arch-api/SKILL.md +291 -0
- package/.claude/skills/arch-api/agents/openai.yaml +3 -0
- package/.claude/skills/arch-api/references/common.md +174 -0
- package/.claude/skills/arch-api/references/contract.md +289 -0
- package/.claude/skills/arch-api/references/domain.md +301 -0
- package/.claude/skills/arch-api/references/endpoints.md +184 -0
- package/.claude/skills/arch-api/references/review.md +228 -0
- package/.claude/skills/arch-api/references/security.md +151 -0
- package/.claude/skills/arch-app/SKILL.md +278 -0
- package/.claude/skills/arch-app/agents/openai.yaml +3 -0
- package/.claude/skills/arch-app/references/navigation-session.md +257 -0
- package/.claude/skills/arch-app/references/review.md +155 -0
- package/.claude/skills/arch-app/references/screens.md +214 -0
- package/.claude/skills/arch-app/references/services.md +301 -0
- package/.claude/skills/arch-app/references/ui-mobile.md +124 -0
- package/.claude/skills/arch-db/SKILL.md +181 -0
- package/.claude/skills/arch-db/agents/openai.yaml +3 -0
- package/.claude/skills/arch-db/references/indexes-queries.md +251 -0
- package/.claude/skills/arch-db/references/migrations.md +191 -0
- package/.claude/skills/arch-db/references/modeling.md +214 -0
- package/.claude/skills/arch-db/references/mysql.md +262 -0
- package/.claude/skills/arch-db/references/pooling-ops.md +112 -0
- package/.claude/skills/arch-db/references/review.md +143 -0
- package/.claude/skills/arch-db/references/transactions-locks.md +141 -0
- package/.claude/skills/arch-web/SKILL.md +307 -0
- package/.claude/skills/arch-web/agents/openai.yaml +3 -0
- package/.claude/skills/arch-web/references/auth-session-env.md +165 -0
- package/.claude/skills/arch-web/references/blocks.md +198 -0
- package/.claude/skills/arch-web/references/cache.md +124 -0
- package/.claude/skills/arch-web/references/review.md +142 -0
- package/.claude/skills/arch-web/references/services.md +297 -0
- package/.claude/skills/arch-web/references/tests.md +159 -0
- package/.claude/skills/arch-web/references/ui-forms.md +143 -0
- package/.claude/skills/break-ui/CATALOG.md +135 -0
- package/.claude/skills/break-ui/SKILL.md +176 -0
- package/.claude/skills/break-ui/agents/openai.yaml +5 -0
- package/.claude/skills/brief/SKILL.md +91 -0
- package/.claude/skills/brief/agents/openai.yaml +3 -0
- package/.claude/skills/diagnosing-bugs/SKILL.md +138 -0
- package/.claude/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/.claude/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/.claude/skills/emil-design-eng/SKILL.md +675 -0
- package/.claude/skills/emil-design-eng/agents/openai.yaml +5 -0
- package/.claude/skills/find-animation-opportunities/SKILL.md +141 -0
- package/.claude/skills/find-animation-opportunities/agents/openai.yaml +5 -0
- package/.claude/skills/graphify/.graphify_version +1 -0
- package/.claude/skills/graphify/SKILL.md +742 -0
- package/.claude/skills/graphify/agents/openai.yaml +3 -0
- package/.claude/skills/graphify/references/add-watch.md +56 -0
- package/.claude/skills/graphify/references/exports.md +91 -0
- package/.claude/skills/graphify/references/extraction-spec.md +70 -0
- package/.claude/skills/graphify/references/github-and-merge.md +46 -0
- package/.claude/skills/graphify/references/hooks.md +33 -0
- package/.claude/skills/graphify/references/query.md +317 -0
- package/.claude/skills/graphify/references/transcribe.md +52 -0
- package/.claude/skills/graphify/references/update.md +221 -0
- package/.claude/skills/grill-me/SKILL.md +7 -0
- package/.claude/skills/grill-me/agents/openai.yaml +5 -0
- package/.claude/skills/grill-with-docs/SKILL.md +64 -0
- package/.claude/skills/grill-with-docs/agents/openai.yaml +5 -0
- package/.claude/skills/grilling/SKILL.md +30 -0
- package/.claude/skills/grilling/agents/openai.yaml +3 -0
- package/.claude/skills/handoff/SKILL.md +28 -0
- package/.claude/skills/handoff/agents/openai.yaml +5 -0
- package/.claude/skills/impeccable/SKILL.md +89 -0
- package/.claude/skills/impeccable/agents/openai.yaml +5 -0
- package/.claude/skills/impeccable/reference/adapt.md +318 -0
- package/.claude/skills/impeccable/reference/adapt.native.md +58 -0
- package/.claude/skills/impeccable/reference/android.md +46 -0
- package/.claude/skills/impeccable/reference/animate.md +89 -0
- package/.claude/skills/impeccable/reference/audit.md +137 -0
- package/.claude/skills/impeccable/reference/audit.native.md +139 -0
- package/.claude/skills/impeccable/reference/bolder.md +33 -0
- package/.claude/skills/impeccable/reference/clarify.md +94 -0
- package/.claude/skills/impeccable/reference/colorize.md +86 -0
- package/.claude/skills/impeccable/reference/component-review.md +63 -0
- package/.claude/skills/impeccable/reference/craft-floor.md +44 -0
- package/.claude/skills/impeccable/reference/craft.md +5 -0
- package/.claude/skills/impeccable/reference/critique.md +809 -0
- package/.claude/skills/impeccable/reference/degraded/asset-producer.md +42 -0
- package/.claude/skills/impeccable/reference/degraded/documenter.md +24 -0
- package/.claude/skills/impeccable/reference/degraded/finish-reviewer.md +38 -0
- package/.claude/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/.claude/skills/impeccable/reference/delight.md +70 -0
- package/.claude/skills/impeccable/reference/distill.md +111 -0
- package/.claude/skills/impeccable/reference/doctor.md +54 -0
- package/.claude/skills/impeccable/reference/document.md +416 -0
- package/.claude/skills/impeccable/reference/extract.md +69 -0
- package/.claude/skills/impeccable/reference/generate.md +101 -0
- package/.claude/skills/impeccable/reference/harden.md +345 -0
- package/.claude/skills/impeccable/reference/hooks.md +113 -0
- package/.claude/skills/impeccable/reference/init.md +131 -0
- package/.claude/skills/impeccable/reference/ios.md +51 -0
- package/.claude/skills/impeccable/reference/layout.md +84 -0
- package/.claude/skills/impeccable/reference/live-setup.md +104 -0
- package/.claude/skills/impeccable/reference/live.md +325 -0
- package/.claude/skills/impeccable/reference/mode-operate.md +21 -0
- package/.claude/skills/impeccable/reference/mode-persuade.md +19 -0
- package/.claude/skills/impeccable/reference/mode-read.md +21 -0
- package/.claude/skills/impeccable/reference/new-work.md +154 -0
- package/.claude/skills/impeccable/reference/onboard.md +234 -0
- package/.claude/skills/impeccable/reference/operate.md +61 -0
- package/.claude/skills/impeccable/reference/optimize.md +258 -0
- package/.claude/skills/impeccable/reference/overdrive.md +127 -0
- package/.claude/skills/impeccable/reference/polish.md +105 -0
- package/.claude/skills/impeccable/reference/quieter.md +99 -0
- package/.claude/skills/impeccable/reference/region-map.md +26 -0
- package/.claude/skills/impeccable/reference/routing.md +24 -0
- package/.claude/skills/impeccable/reference/shape.md +59 -0
- package/.claude/skills/impeccable/reference/typeset.md +80 -0
- package/.claude/skills/impeccable/reference/visualize.md +47 -0
- package/.claude/skills/impeccable/scripts/VERSION +1 -0
- package/.claude/skills/impeccable/scripts/command-metadata.json +98 -0
- package/.claude/skills/impeccable/scripts/data/font-index-failures.json +121 -0
- package/.claude/skills/impeccable/scripts/data/font-index.json +1 -0
- package/.claude/skills/impeccable/scripts/impeccable +206 -0
- package/.claude/skills/impeccable/scripts/impeccable.cmd +214 -0
- package/.claude/skills/impeccable/scripts/live-browser-dom.js +281 -0
- package/.claude/skills/impeccable/scripts/live-browser-ignores.js +242 -0
- package/.claude/skills/impeccable/scripts/live-browser-session.js +148 -0
- package/.claude/skills/impeccable/scripts/live-browser.js +13532 -0
- package/.claude/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/.claude/skills/improve-animations/AUDIT.md +115 -0
- package/.claude/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
- package/.claude/skills/improve-animations/SKILL.md +110 -0
- package/.claude/skills/improve-animations/agents/openai.yaml +5 -0
- package/.claude/skills/mobile-native/SKILL.md +311 -0
- package/.claude/skills/mobile-native/agents/openai.yaml +3 -0
- package/.claude/skills/orchestrate/SKILL.md +181 -0
- package/.claude/skills/orchestrate/agents/openai.yaml +3 -0
- package/.claude/skills/pick-ui-library/SKILL.md +85 -0
- package/.claude/skills/pick-ui-library/agents/openai.yaml +5 -0
- package/.claude/skills/ponytail/SKILL.md +52 -0
- package/.claude/skills/ponytail/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-audit/SKILL.md +103 -0
- package/.claude/skills/ponytail-audit/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-debt/SKILL.md +44 -0
- package/.claude/skills/ponytail-debt/agents/openai.yaml +3 -0
- package/.claude/skills/ponytail-review/SKILL.md +101 -0
- package/.claude/skills/ponytail-review/agents/openai.yaml +3 -0
- package/.claude/skills/postgres-best-practices/SKILL.md +42 -0
- package/.claude/skills/postgres-best-practices/agents/openai.yaml +3 -0
- package/.claude/skills/postgres-best-practices/references/backup-restore.md +372 -0
- package/.claude/skills/postgres-best-practices/references/bulk-loading.md +315 -0
- package/.claude/skills/postgres-best-practices/references/connection-pooling.md +310 -0
- package/.claude/skills/postgres-best-practices/references/hot-standby.md +221 -0
- package/.claude/skills/postgres-best-practices/references/indexing.md +318 -0
- package/.claude/skills/postgres-best-practices/references/logical-replication.md +610 -0
- package/.claude/skills/postgres-best-practices/references/major-version-upgrades.md +334 -0
- package/.claude/skills/postgres-best-practices/references/performance-diagnostics.md +553 -0
- package/.claude/skills/postgres-best-practices/references/query-optimization.md +294 -0
- package/.claude/skills/postgres-best-practices/references/query-patterns.md +566 -0
- package/.claude/skills/postgres-best-practices/references/schema-design.md +362 -0
- package/.claude/skills/postgres-best-practices/references/security-roles.md +489 -0
- package/.claude/skills/postgres-best-practices/references/transaction-isolation.md +344 -0
- package/.claude/skills/project-docs/SKILL.md +330 -0
- package/.claude/skills/project-docs/agents/openai.yaml +3 -0
- package/.claude/skills/prototype/PICKER.md +197 -0
- package/.claude/skills/prototype/SKILL.md +98 -0
- package/.claude/skills/prototype/agents/openai.yaml +5 -0
- package/.claude/skills/retro/SKILL.md +43 -0
- package/.claude/skills/retro/agents/openai.yaml +5 -0
- package/.claude/skills/review/SKILL.md +74 -0
- package/.claude/skills/review/agents/openai.yaml +3 -0
- package/.claude/skills/review/references/smells.md +23 -0
- package/.claude/skills/review-animations/SKILL.md +120 -0
- package/.claude/skills/review-animations/STANDARDS.md +187 -0
- package/.claude/skills/review-animations/agents/openai.yaml +5 -0
- package/.claude/skills/tdd/SKILL.md +38 -0
- package/.claude/skills/tdd/agents/openai.yaml +3 -0
- package/.claude/skills/tdd/mocking.md +59 -0
- package/.claude/skills/tdd/tests.md +77 -0
- package/.claude/skills/triage/SKILL.md +58 -0
- package/.claude/skills/triage/agents/openai.yaml +3 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +378 -0
- package/.claude/skills/ui-ux-pro-max/agents/openai.yaml +5 -0
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.claude/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/.claude/skills/writing-for-agents/SKILL.md +81 -0
- package/.claude/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/.worktreeinclude +3 -0
- package/AGENTS.md +44 -0
- package/README.md +14 -2
- package/VERSION +1 -0
- package/bin/agents.mjs +45 -0
- package/docs/adr/README.md +31 -0
- package/docs/brief-template.md +3 -0
- package/docs/plans/README.md +73 -0
- package/package.json +46 -4
- package/scripts/bootstrap.mjs +188 -0
- package/scripts/check-catalog.mjs +43 -0
- package/scripts/skills-update.mjs +51 -0
- package/skills-lock.json +269 -0
- package/templates/CLAUDE.project.md +41 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# Schema Design Reference
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
- Data type best practices
|
|
5
|
+
- Primary keys
|
|
6
|
+
- Foreign keys and referential integrity
|
|
7
|
+
- Check and exclusion constraints
|
|
8
|
+
- Normalization guidelines
|
|
9
|
+
- Denormalization patterns
|
|
10
|
+
- Partitioning
|
|
11
|
+
- Multi-tenant patterns
|
|
12
|
+
- Migration safety
|
|
13
|
+
|
|
14
|
+
## Data Type Best Practices
|
|
15
|
+
|
|
16
|
+
### Identity Columns
|
|
17
|
+
|
|
18
|
+
```sql
|
|
19
|
+
-- Preferred: GENERATED ALWAYS
|
|
20
|
+
CREATE TABLE orders (
|
|
21
|
+
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
22
|
+
...
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
-- If you need to occasionally override:
|
|
26
|
+
INSERT INTO orders OVERRIDING SYSTEM VALUE ...
|
|
27
|
+
|
|
28
|
+
-- UUID alternative (good for distributed systems)
|
|
29
|
+
CREATE TABLE orders (
|
|
30
|
+
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
31
|
+
...
|
|
32
|
+
);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**UUID v4 vs v7**: `gen_random_uuid()` generates UUIDv4 (random), which causes B-tree index fragmentation on large tables because inserts scatter across the index. UUIDv7 (time-ordered) preserves insert locality. **PG18+** has built-in `uuidv7()` (and explicit `uuidv4()`). On older versions, use the `pg_uuidv7` extension. Prefer UUIDv7 for primary keys.
|
|
36
|
+
|
|
37
|
+
```sql
|
|
38
|
+
-- PG18+: built-in UUIDv7
|
|
39
|
+
CREATE TABLE events (
|
|
40
|
+
id uuid DEFAULT uuidv7() PRIMARY KEY,
|
|
41
|
+
...
|
|
42
|
+
);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Avoid `serial` / `bigserial` — they create an implicit sequence with looser ownership semantics.
|
|
46
|
+
|
|
47
|
+
### Timestamps
|
|
48
|
+
|
|
49
|
+
Always use `timestamptz`. PostgreSQL stores it as UTC internally and converts on display.
|
|
50
|
+
|
|
51
|
+
```sql
|
|
52
|
+
CREATE TABLE events (
|
|
53
|
+
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
54
|
+
occurred_at timestamptz NOT NULL DEFAULT now(),
|
|
55
|
+
-- NOT: occurred_at timestamp (loses timezone context)
|
|
56
|
+
);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Text Fields
|
|
60
|
+
|
|
61
|
+
Prefer `text` with a `CHECK` constraint over `varchar(n)`:
|
|
62
|
+
|
|
63
|
+
```sql
|
|
64
|
+
CREATE TABLE users (
|
|
65
|
+
email text NOT NULL CHECK (length(email) <= 254),
|
|
66
|
+
username text NOT NULL CHECK (length(username) BETWEEN 3 AND 30)
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`varchar(n)` provides no performance benefit and makes future changes harder.
|
|
71
|
+
|
|
72
|
+
### UNIQUE NULLS NOT DISTINCT (PG15+)
|
|
73
|
+
|
|
74
|
+
By default, NULLs are considered distinct in unique constraints (multiple NULLs allowed). PG15 adds an option to treat NULLs as equal:
|
|
75
|
+
|
|
76
|
+
```sql
|
|
77
|
+
-- Standard: allows multiple rows with NULL in email
|
|
78
|
+
CREATE UNIQUE INDEX idx_email_standard ON users(email);
|
|
79
|
+
|
|
80
|
+
-- PG15+: only one NULL allowed
|
|
81
|
+
CREATE UNIQUE INDEX idx_email_nulls_not_distinct
|
|
82
|
+
ON users(email) NULLS NOT DISTINCT;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Naming Conventions
|
|
86
|
+
|
|
87
|
+
Use `snake_case` without quotes for all identifiers. Unquoted identifiers fold to lowercase automatically. Quoted mixed-case identifiers (e.g., `"userId"`) require quotes everywhere and break many ORMs, tools, and AI assistants.
|
|
88
|
+
|
|
89
|
+
### Enums vs. Check Constraints vs. Lookup Tables
|
|
90
|
+
|
|
91
|
+
| Approach | Pros | Cons |
|
|
92
|
+
|----------|------|------|
|
|
93
|
+
| `CHECK (col IN (...))` | Simple, no DDL type | Requires ALTER TABLE to add values |
|
|
94
|
+
| `CREATE TYPE ... AS ENUM` | Type safety, compact storage | Cannot remove values, ALTER TYPE needed |
|
|
95
|
+
| Lookup/reference table | FK enforced, can add metadata | Extra join |
|
|
96
|
+
|
|
97
|
+
For small, stable sets (status, priority): CHECK or ENUM.
|
|
98
|
+
For evolving sets or sets needing metadata: lookup table.
|
|
99
|
+
|
|
100
|
+
## Foreign Keys
|
|
101
|
+
|
|
102
|
+
### Basics
|
|
103
|
+
|
|
104
|
+
```sql
|
|
105
|
+
CREATE TABLE orders (
|
|
106
|
+
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
107
|
+
customer_id bigint NOT NULL REFERENCES customers(id),
|
|
108
|
+
...
|
|
109
|
+
);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### ON DELETE / ON UPDATE Actions
|
|
113
|
+
|
|
114
|
+
| Action | Use when |
|
|
115
|
+
|--------|----------|
|
|
116
|
+
| `NO ACTION` (default) | Reject the update or deletion if a violation remains when the constraint is checked; the check can be deferred when the foreign key is deferrable and currently deferred |
|
|
117
|
+
| `RESTRICT` | Reject the referenced operation immediately; the check cannot be deferred |
|
|
118
|
+
| `CASCADE` | Child rows are meaningless without parent (e.g., order_items when order is deleted) |
|
|
119
|
+
| `SET NULL` | Relationship is optional, preserve child row |
|
|
120
|
+
| `SET DEFAULT` | Rare; reassign to a valid default parent |
|
|
121
|
+
|
|
122
|
+
### Deferrable Foreign Keys
|
|
123
|
+
|
|
124
|
+
Useful when you need to insert rows in both tables within a single transaction regardless of order:
|
|
125
|
+
|
|
126
|
+
```sql
|
|
127
|
+
ALTER TABLE order_items
|
|
128
|
+
ADD CONSTRAINT fk_order
|
|
129
|
+
FOREIGN KEY (order_id) REFERENCES orders(id)
|
|
130
|
+
DEFERRABLE INITIALLY DEFERRED;
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Index the FK Column
|
|
134
|
+
|
|
135
|
+
PostgreSQL does NOT auto-create indexes on FK columns. Always add one:
|
|
136
|
+
|
|
137
|
+
```sql
|
|
138
|
+
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Without this index:
|
|
142
|
+
- JOINs on the FK are slow (seq scan on child table)
|
|
143
|
+
- DELETE on the parent table locks the child table and scans it fully
|
|
144
|
+
|
|
145
|
+
## Check Constraints
|
|
146
|
+
|
|
147
|
+
```sql
|
|
148
|
+
CREATE TABLE products (
|
|
149
|
+
price numeric(10,2) NOT NULL CHECK (price > 0),
|
|
150
|
+
discount_pct numeric(3,2) CHECK (discount_pct BETWEEN 0 AND 1),
|
|
151
|
+
start_date date NOT NULL,
|
|
152
|
+
end_date date,
|
|
153
|
+
CHECK (end_date IS NULL OR end_date > start_date)
|
|
154
|
+
);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Exclusion Constraints (Prevent Overlaps)
|
|
158
|
+
|
|
159
|
+
```sql
|
|
160
|
+
CREATE EXTENSION IF NOT EXISTS btree_gist;
|
|
161
|
+
|
|
162
|
+
CREATE TABLE room_bookings (
|
|
163
|
+
room_id int NOT NULL,
|
|
164
|
+
during tstzrange NOT NULL,
|
|
165
|
+
EXCLUDE USING gist (room_id WITH =, during WITH &&)
|
|
166
|
+
);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This prevents overlapping bookings for the same room at the database level.
|
|
170
|
+
|
|
171
|
+
### Temporal Constraints — WITHOUT OVERLAPS (PG18+)
|
|
172
|
+
|
|
173
|
+
PG18 adds built-in temporal constraint support, simplifying the exclusion constraint pattern above:
|
|
174
|
+
|
|
175
|
+
```sql
|
|
176
|
+
CREATE EXTENSION IF NOT EXISTS btree_gist;
|
|
177
|
+
|
|
178
|
+
CREATE TABLE room_bookings (
|
|
179
|
+
room_id int NOT NULL,
|
|
180
|
+
during tstzrange NOT NULL,
|
|
181
|
+
PRIMARY KEY (room_id, during WITHOUT OVERLAPS)
|
|
182
|
+
);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Foreign keys can also reference temporal primary keys using `PERIOD`:
|
|
186
|
+
|
|
187
|
+
```sql
|
|
188
|
+
CREATE TABLE reservations (
|
|
189
|
+
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
190
|
+
room_id int NOT NULL,
|
|
191
|
+
during tstzrange NOT NULL,
|
|
192
|
+
FOREIGN KEY (room_id, PERIOD during)
|
|
193
|
+
REFERENCES room_bookings (room_id, PERIOD during)
|
|
194
|
+
);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
This eliminates the need to write the exclusion constraint manually. The temporal key is implemented with GiST; scalar key columns such as `room_id int` still need an appropriate GiST operator class, supplied by `btree_gist` for common scalar types.
|
|
198
|
+
|
|
199
|
+
### NOT ENFORCED Constraints (PG18+)
|
|
200
|
+
|
|
201
|
+
CHECK and foreign key constraints can be marked as informational only — the database trusts the data without enforcing the constraint. Useful for documenting intent or helping the planner without paying enforcement cost:
|
|
202
|
+
|
|
203
|
+
```sql
|
|
204
|
+
ALTER TABLE orders ADD CONSTRAINT positive_total
|
|
205
|
+
CHECK (total > 0) NOT ENFORCED;
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Normalization Guidelines
|
|
209
|
+
|
|
210
|
+
### When to Normalize
|
|
211
|
+
|
|
212
|
+
- Data integrity is critical (financial, compliance)
|
|
213
|
+
- Multiple writers update the same logical data
|
|
214
|
+
- Storage is a concern (avoid data duplication)
|
|
215
|
+
|
|
216
|
+
### When to Denormalize
|
|
217
|
+
|
|
218
|
+
- Read-heavy workloads where join cost is measurable
|
|
219
|
+
- Materialized views can serve as denormalized read models
|
|
220
|
+
- JSONB columns for flexible, schema-less attributes that don't need FK integrity
|
|
221
|
+
|
|
222
|
+
### Materialized Views as Denormalization
|
|
223
|
+
|
|
224
|
+
```sql
|
|
225
|
+
CREATE MATERIALIZED VIEW mv_order_summary AS
|
|
226
|
+
SELECT
|
|
227
|
+
o.id,
|
|
228
|
+
o.created_at,
|
|
229
|
+
c.name AS customer_name,
|
|
230
|
+
sum(oi.quantity * oi.unit_price) AS total
|
|
231
|
+
FROM orders o
|
|
232
|
+
JOIN customers c ON c.id = o.customer_id
|
|
233
|
+
JOIN order_items oi ON oi.order_id = o.id
|
|
234
|
+
GROUP BY o.id, o.created_at, c.name;
|
|
235
|
+
|
|
236
|
+
CREATE UNIQUE INDEX ON mv_order_summary(id);
|
|
237
|
+
|
|
238
|
+
-- Refresh periodically or after batch writes:
|
|
239
|
+
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_order_summary;
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Partitioning (Declarative)
|
|
243
|
+
|
|
244
|
+
### When to Partition
|
|
245
|
+
|
|
246
|
+
- Table exceeds tens of millions of rows
|
|
247
|
+
- Queries consistently filter on the partition key
|
|
248
|
+
- You need efficient bulk deletion (DROP partition vs. DELETE)
|
|
249
|
+
- Maintenance operations (VACUUM, reindex) are slow on the full table
|
|
250
|
+
|
|
251
|
+
### Range Partitioning (most common)
|
|
252
|
+
|
|
253
|
+
```sql
|
|
254
|
+
CREATE TABLE events (
|
|
255
|
+
id bigint GENERATED ALWAYS AS IDENTITY,
|
|
256
|
+
occurred_at timestamptz NOT NULL,
|
|
257
|
+
payload jsonb
|
|
258
|
+
) PARTITION BY RANGE (occurred_at);
|
|
259
|
+
|
|
260
|
+
CREATE TABLE events_2024_q1 PARTITION OF events
|
|
261
|
+
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
|
|
262
|
+
CREATE TABLE events_2024_q2 PARTITION OF events
|
|
263
|
+
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### List Partitioning (multi-tenant)
|
|
267
|
+
|
|
268
|
+
```sql
|
|
269
|
+
CREATE TABLE tenant_data (
|
|
270
|
+
tenant_id int NOT NULL,
|
|
271
|
+
data jsonb
|
|
272
|
+
) PARTITION BY LIST (tenant_id);
|
|
273
|
+
|
|
274
|
+
CREATE TABLE tenant_data_1 PARTITION OF tenant_data FOR VALUES IN (1);
|
|
275
|
+
CREATE TABLE tenant_data_2 PARTITION OF tenant_data FOR VALUES IN (2);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Detaching Partitions (PG14+)
|
|
279
|
+
|
|
280
|
+
```sql
|
|
281
|
+
-- Non-blocking detach (PG14+): doesn't hold AccessExclusiveLock
|
|
282
|
+
ALTER TABLE events DETACH PARTITION events_2024_q1 CONCURRENTLY;
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Key Rules
|
|
286
|
+
|
|
287
|
+
- Partition key must be part of the primary key and all unique indexes
|
|
288
|
+
- Indexes defined on parent are auto-created on child partitions
|
|
289
|
+
- Foreign keys referencing partitioned tables are supported
|
|
290
|
+
- Exclusion constraints on partitioned tables require PG17+ (equality on partition key only)
|
|
291
|
+
|
|
292
|
+
## Virtual Generated Columns (PG18+)
|
|
293
|
+
|
|
294
|
+
PG18 defaults generated columns to `VIRTUAL` (computed at read time, not stored on disk). Use `STORED` when you need to index the expression.
|
|
295
|
+
|
|
296
|
+
```sql
|
|
297
|
+
CREATE TABLE orders (
|
|
298
|
+
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
299
|
+
quantity int NOT NULL,
|
|
300
|
+
unit_price numeric(10,2) NOT NULL,
|
|
301
|
+
description text NOT NULL,
|
|
302
|
+
-- Virtual: computed on read, no storage cost
|
|
303
|
+
total numeric GENERATED ALWAYS AS (quantity * unit_price) VIRTUAL,
|
|
304
|
+
-- Stored: persisted on disk, can be indexed
|
|
305
|
+
search_text tsvector GENERATED ALWAYS AS (
|
|
306
|
+
to_tsvector('english', description)
|
|
307
|
+
) STORED
|
|
308
|
+
);
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Virtual columns save storage and write I/O but cannot be indexed directly. Use `STORED` when you need an index on the expression.
|
|
312
|
+
|
|
313
|
+
## Multi-Tenant Patterns
|
|
314
|
+
|
|
315
|
+
### Row-Level Security (RLS)
|
|
316
|
+
|
|
317
|
+
```sql
|
|
318
|
+
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
|
|
319
|
+
|
|
320
|
+
CREATE POLICY tenant_isolation ON orders
|
|
321
|
+
USING (tenant_id = current_setting('app.current_tenant')::int);
|
|
322
|
+
|
|
323
|
+
-- Set per-connection:
|
|
324
|
+
SET app.current_tenant = '42';
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Shared Schema with tenant_id
|
|
328
|
+
|
|
329
|
+
- Add `tenant_id` to every table
|
|
330
|
+
- Include `tenant_id` in all indexes (composite with other columns)
|
|
331
|
+
- Use RLS or application-level filtering
|
|
332
|
+
|
|
333
|
+
## Migration Safety
|
|
334
|
+
|
|
335
|
+
### Safe Column Operations
|
|
336
|
+
|
|
337
|
+
| Operation | Safe online? | Notes |
|
|
338
|
+
|-----------|-------------|-------|
|
|
339
|
+
| `ADD COLUMN` (nullable, no default) | Yes | Instant metadata change |
|
|
340
|
+
| `ADD COLUMN ... DEFAULT x` | Yes | Default stored in catalog, no table rewrite |
|
|
341
|
+
| `DROP COLUMN` | Yes | Marks column as dropped, no rewrite |
|
|
342
|
+
| `ALTER COLUMN SET NOT NULL` | Caution | Full table scan to validate (use CHECK first) |
|
|
343
|
+
| `ALTER COLUMN TYPE` | No | Full table rewrite + exclusive lock |
|
|
344
|
+
| `ADD CONSTRAINT ... NOT VALID` | Yes | Doesn't scan existing rows |
|
|
345
|
+
| `VALIDATE CONSTRAINT` | Yes | ShareUpdateExclusiveLock (reads/writes allowed) |
|
|
346
|
+
|
|
347
|
+
### Safe NOT NULL Pattern
|
|
348
|
+
|
|
349
|
+
```sql
|
|
350
|
+
-- Step 1: Add CHECK constraint without validating existing rows
|
|
351
|
+
ALTER TABLE orders ADD CONSTRAINT orders_status_nn
|
|
352
|
+
CHECK (status IS NOT NULL) NOT VALID;
|
|
353
|
+
|
|
354
|
+
-- Step 2: Validate in background (non-blocking)
|
|
355
|
+
ALTER TABLE orders VALIDATE CONSTRAINT orders_status_nn;
|
|
356
|
+
|
|
357
|
+
-- Step 3: SET NOT NULL is instant if a valid CHECK exists
|
|
358
|
+
ALTER TABLE orders ALTER COLUMN status SET NOT NULL;
|
|
359
|
+
|
|
360
|
+
-- Step 4: Drop the now-redundant CHECK
|
|
361
|
+
ALTER TABLE orders DROP CONSTRAINT orders_status_nn;
|
|
362
|
+
```
|