@0xsown/vibe-code-fe 1.0.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/bin/index.js +181 -0
- package/package.json +32 -0
- package/skills/claude-md-improver/SKILL.md +179 -0
- package/skills/claude-md-improver/references/quality-criteria.md +109 -0
- package/skills/claude-md-improver/references/templates.md +253 -0
- package/skills/claude-md-improver/references/update-guidelines.md +150 -0
- package/skills/find-skills/SKILL.md +133 -0
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/SKILL.md +42 -0
- package/skills/next-best-practices/SKILL.md +153 -0
- package/skills/next-best-practices/async-patterns.md +87 -0
- package/skills/next-best-practices/bundling.md +180 -0
- package/skills/next-best-practices/data-patterns.md +297 -0
- package/skills/next-best-practices/debug-tricks.md +105 -0
- package/skills/next-best-practices/directives.md +73 -0
- package/skills/next-best-practices/error-handling.md +227 -0
- package/skills/next-best-practices/file-conventions.md +140 -0
- package/skills/next-best-practices/font.md +245 -0
- package/skills/next-best-practices/functions.md +108 -0
- package/skills/next-best-practices/hydration-error.md +91 -0
- package/skills/next-best-practices/image.md +173 -0
- package/skills/next-best-practices/metadata.md +301 -0
- package/skills/next-best-practices/parallel-routes.md +287 -0
- package/skills/next-best-practices/route-handlers.md +146 -0
- package/skills/next-best-practices/rsc-boundaries.md +159 -0
- package/skills/next-best-practices/runtime-selection.md +39 -0
- package/skills/next-best-practices/scripts.md +141 -0
- package/skills/next-best-practices/self-hosting.md +371 -0
- package/skills/next-best-practices/suspense-boundaries.md +67 -0
- package/skills/next-cache-components/SKILL.md +411 -0
- package/skills/shadcn-ui/README.md +248 -0
- package/skills/shadcn-ui/SKILL.md +326 -0
- package/skills/shadcn-ui/examples/auth-layout.tsx +177 -0
- package/skills/shadcn-ui/examples/data-table.tsx +313 -0
- package/skills/shadcn-ui/examples/form-pattern.tsx +177 -0
- package/skills/shadcn-ui/resources/component-catalog.md +481 -0
- package/skills/shadcn-ui/resources/customization-guide.md +516 -0
- package/skills/shadcn-ui/resources/migration-guide.md +463 -0
- package/skills/shadcn-ui/resources/setup-guide.md +412 -0
- package/skills/shadcn-ui/scripts/verify-setup.sh +134 -0
- package/skills/supabase-postgres-best-practices/AGENTS.md +68 -0
- package/skills/supabase-postgres-best-practices/CLAUDE.md +68 -0
- package/skills/supabase-postgres-best-practices/README.md +116 -0
- package/skills/supabase-postgres-best-practices/SKILL.md +64 -0
- package/skills/supabase-postgres-best-practices/references/advanced-full-text-search.md +55 -0
- package/skills/supabase-postgres-best-practices/references/advanced-jsonb-indexing.md +49 -0
- package/skills/supabase-postgres-best-practices/references/conn-idle-timeout.md +46 -0
- package/skills/supabase-postgres-best-practices/references/conn-limits.md +44 -0
- package/skills/supabase-postgres-best-practices/references/conn-pooling.md +41 -0
- package/skills/supabase-postgres-best-practices/references/conn-prepared-statements.md +46 -0
- package/skills/supabase-postgres-best-practices/references/data-batch-inserts.md +54 -0
- package/skills/supabase-postgres-best-practices/references/data-n-plus-one.md +53 -0
- package/skills/supabase-postgres-best-practices/references/data-pagination.md +50 -0
- package/skills/supabase-postgres-best-practices/references/data-upsert.md +50 -0
- package/skills/supabase-postgres-best-practices/references/lock-advisory.md +56 -0
- package/skills/supabase-postgres-best-practices/references/lock-deadlock-prevention.md +68 -0
- package/skills/supabase-postgres-best-practices/references/lock-short-transactions.md +50 -0
- package/skills/supabase-postgres-best-practices/references/lock-skip-locked.md +54 -0
- package/skills/supabase-postgres-best-practices/references/monitor-explain-analyze.md +45 -0
- package/skills/supabase-postgres-best-practices/references/monitor-pg-stat-statements.md +55 -0
- package/skills/supabase-postgres-best-practices/references/monitor-vacuum-analyze.md +55 -0
- package/skills/supabase-postgres-best-practices/references/query-composite-indexes.md +44 -0
- package/skills/supabase-postgres-best-practices/references/query-covering-indexes.md +40 -0
- package/skills/supabase-postgres-best-practices/references/query-index-types.md +48 -0
- package/skills/supabase-postgres-best-practices/references/query-missing-indexes.md +43 -0
- package/skills/supabase-postgres-best-practices/references/query-partial-indexes.md +45 -0
- package/skills/supabase-postgres-best-practices/references/schema-constraints.md +80 -0
- package/skills/supabase-postgres-best-practices/references/schema-data-types.md +46 -0
- package/skills/supabase-postgres-best-practices/references/schema-foreign-key-indexes.md +59 -0
- package/skills/supabase-postgres-best-practices/references/schema-lowercase-identifiers.md +55 -0
- package/skills/supabase-postgres-best-practices/references/schema-partitioning.md +55 -0
- package/skills/supabase-postgres-best-practices/references/schema-primary-keys.md +61 -0
- package/skills/supabase-postgres-best-practices/references/security-privileges.md +54 -0
- package/skills/supabase-postgres-best-practices/references/security-rls-basics.md +50 -0
- package/skills/supabase-postgres-best-practices/references/security-rls-performance.md +57 -0
- package/skills/tailwind-design-system/SKILL.md +874 -0
- package/skills/vercel-composition-patterns/AGENTS.md +946 -0
- package/skills/vercel-composition-patterns/README.md +60 -0
- package/skills/vercel-composition-patterns/SKILL.md +89 -0
- package/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
- package/skills/vercel-react-best-practices/AGENTS.md +2934 -0
- package/skills/vercel-react-best-practices/README.md +123 -0
- package/skills/vercel-react-best-practices/SKILL.md +136 -0
- package/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/skills/vercel-react-native-skills/AGENTS.md +2897 -0
- package/skills/vercel-react-native-skills/README.md +165 -0
- package/skills/vercel-react-native-skills/SKILL.md +121 -0
- package/skills/vercel-react-native-skills/rules/animation-derived-value.md +53 -0
- package/skills/vercel-react-native-skills/rules/animation-gesture-detector-press.md +95 -0
- package/skills/vercel-react-native-skills/rules/animation-gpu-properties.md +65 -0
- package/skills/vercel-react-native-skills/rules/design-system-compound-components.md +66 -0
- package/skills/vercel-react-native-skills/rules/fonts-config-plugin.md +71 -0
- package/skills/vercel-react-native-skills/rules/imports-design-system-folder.md +68 -0
- package/skills/vercel-react-native-skills/rules/js-hoist-intl.md +61 -0
- package/skills/vercel-react-native-skills/rules/list-performance-callbacks.md +44 -0
- package/skills/vercel-react-native-skills/rules/list-performance-function-references.md +132 -0
- package/skills/vercel-react-native-skills/rules/list-performance-images.md +53 -0
- package/skills/vercel-react-native-skills/rules/list-performance-inline-objects.md +97 -0
- package/skills/vercel-react-native-skills/rules/list-performance-item-expensive.md +94 -0
- package/skills/vercel-react-native-skills/rules/list-performance-item-memo.md +82 -0
- package/skills/vercel-react-native-skills/rules/list-performance-item-types.md +104 -0
- package/skills/vercel-react-native-skills/rules/list-performance-virtualize.md +67 -0
- package/skills/vercel-react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
- package/skills/vercel-react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
- package/skills/vercel-react-native-skills/rules/navigation-native-navigators.md +188 -0
- package/skills/vercel-react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
- package/skills/vercel-react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
- package/skills/vercel-react-native-skills/rules/react-state-dispatcher.md +91 -0
- package/skills/vercel-react-native-skills/rules/react-state-fallback.md +56 -0
- package/skills/vercel-react-native-skills/rules/react-state-minimize.md +65 -0
- package/skills/vercel-react-native-skills/rules/rendering-no-falsy-and.md +74 -0
- package/skills/vercel-react-native-skills/rules/rendering-text-in-text-component.md +36 -0
- package/skills/vercel-react-native-skills/rules/scroll-position-no-state.md +82 -0
- package/skills/vercel-react-native-skills/rules/state-ground-truth.md +80 -0
- package/skills/vercel-react-native-skills/rules/ui-expo-image.md +66 -0
- package/skills/vercel-react-native-skills/rules/ui-image-gallery.md +104 -0
- package/skills/vercel-react-native-skills/rules/ui-measure-views.md +78 -0
- package/skills/vercel-react-native-skills/rules/ui-menus.md +174 -0
- package/skills/vercel-react-native-skills/rules/ui-native-modals.md +77 -0
- package/skills/vercel-react-native-skills/rules/ui-pressable.md +61 -0
- package/skills/vercel-react-native-skills/rules/ui-safe-area-scroll.md +65 -0
- package/skills/vercel-react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
- package/skills/vercel-react-native-skills/rules/ui-styling.md +87 -0
- package/skills/web-design-guidelines/SKILL.md +39 -0
- package/templates/AGENTS.md +31 -0
- package/templates/CLAUDE.md +31 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Advisory Locks for Application-Level Locking
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Efficient coordination without row-level lock overhead
|
|
5
|
+
tags: advisory-locks, coordination, application-locks
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Advisory Locks for Application-Level Locking
|
|
9
|
+
|
|
10
|
+
Advisory locks provide application-level coordination without requiring database rows to lock.
|
|
11
|
+
|
|
12
|
+
**Incorrect (creating rows just for locking):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Creating dummy rows to lock on
|
|
16
|
+
create table resource_locks (
|
|
17
|
+
resource_name text primary key
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
insert into resource_locks values ('report_generator');
|
|
21
|
+
|
|
22
|
+
-- Lock by selecting the row
|
|
23
|
+
select * from resource_locks where resource_name = 'report_generator' for update;
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Correct (advisory locks):**
|
|
27
|
+
|
|
28
|
+
```sql
|
|
29
|
+
-- Session-level advisory lock (released on disconnect or unlock)
|
|
30
|
+
select pg_advisory_lock(hashtext('report_generator'));
|
|
31
|
+
-- ... do exclusive work ...
|
|
32
|
+
select pg_advisory_unlock(hashtext('report_generator'));
|
|
33
|
+
|
|
34
|
+
-- Transaction-level lock (released on commit/rollback)
|
|
35
|
+
begin;
|
|
36
|
+
select pg_advisory_xact_lock(hashtext('daily_report'));
|
|
37
|
+
-- ... do work ...
|
|
38
|
+
commit; -- Lock automatically released
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Try-lock for non-blocking operations:
|
|
42
|
+
|
|
43
|
+
```sql
|
|
44
|
+
-- Returns immediately with true/false instead of waiting
|
|
45
|
+
select pg_try_advisory_lock(hashtext('resource_name'));
|
|
46
|
+
|
|
47
|
+
-- Use in application
|
|
48
|
+
if (acquired) {
|
|
49
|
+
-- Do work
|
|
50
|
+
select pg_advisory_unlock(hashtext('resource_name'));
|
|
51
|
+
} else {
|
|
52
|
+
-- Skip or retry later
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Reference: [Advisory Locks](https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prevent Deadlocks with Consistent Lock Ordering
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: Eliminate deadlock errors, improve reliability
|
|
5
|
+
tags: deadlocks, locking, transactions, ordering
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prevent Deadlocks with Consistent Lock Ordering
|
|
9
|
+
|
|
10
|
+
Deadlocks occur when transactions lock resources in different orders. Always
|
|
11
|
+
acquire locks in a consistent order.
|
|
12
|
+
|
|
13
|
+
**Incorrect (inconsistent lock ordering):**
|
|
14
|
+
|
|
15
|
+
```sql
|
|
16
|
+
-- Transaction A -- Transaction B
|
|
17
|
+
begin; begin;
|
|
18
|
+
update accounts update accounts
|
|
19
|
+
set balance = balance - 100 set balance = balance - 50
|
|
20
|
+
where id = 1; where id = 2; -- B locks row 2
|
|
21
|
+
|
|
22
|
+
update accounts update accounts
|
|
23
|
+
set balance = balance + 100 set balance = balance + 50
|
|
24
|
+
where id = 2; -- A waits for B where id = 1; -- B waits for A
|
|
25
|
+
|
|
26
|
+
-- DEADLOCK! Both waiting for each other
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Correct (lock rows in consistent order first):**
|
|
30
|
+
|
|
31
|
+
```sql
|
|
32
|
+
-- Explicitly acquire locks in ID order before updating
|
|
33
|
+
begin;
|
|
34
|
+
select * from accounts where id in (1, 2) order by id for update;
|
|
35
|
+
|
|
36
|
+
-- Now perform updates in any order - locks already held
|
|
37
|
+
update accounts set balance = balance - 100 where id = 1;
|
|
38
|
+
update accounts set balance = balance + 100 where id = 2;
|
|
39
|
+
commit;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Alternative: use a single statement to update atomically:
|
|
43
|
+
|
|
44
|
+
```sql
|
|
45
|
+
-- Single statement acquires all locks atomically
|
|
46
|
+
begin;
|
|
47
|
+
update accounts
|
|
48
|
+
set balance = balance + case id
|
|
49
|
+
when 1 then -100
|
|
50
|
+
when 2 then 100
|
|
51
|
+
end
|
|
52
|
+
where id in (1, 2);
|
|
53
|
+
commit;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Detect deadlocks in logs:
|
|
57
|
+
|
|
58
|
+
```sql
|
|
59
|
+
-- Check for recent deadlocks
|
|
60
|
+
select * from pg_stat_database where deadlocks > 0;
|
|
61
|
+
|
|
62
|
+
-- Enable deadlock logging
|
|
63
|
+
set log_lock_waits = on;
|
|
64
|
+
set deadlock_timeout = '1s';
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Reference:
|
|
68
|
+
[Deadlocks](https://www.postgresql.org/docs/current/explicit-locking.html#LOCKING-DEADLOCKS)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Keep Transactions Short to Reduce Lock Contention
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: 3-5x throughput improvement, fewer deadlocks
|
|
5
|
+
tags: transactions, locking, contention, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Keep Transactions Short to Reduce Lock Contention
|
|
9
|
+
|
|
10
|
+
Long-running transactions hold locks that block other queries. Keep transactions as short as possible.
|
|
11
|
+
|
|
12
|
+
**Incorrect (long transaction with external calls):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
begin;
|
|
16
|
+
select * from orders where id = 1 for update; -- Lock acquired
|
|
17
|
+
|
|
18
|
+
-- Application makes HTTP call to payment API (2-5 seconds)
|
|
19
|
+
-- Other queries on this row are blocked!
|
|
20
|
+
|
|
21
|
+
update orders set status = 'paid' where id = 1;
|
|
22
|
+
commit; -- Lock held for entire duration
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (minimal transaction scope):**
|
|
26
|
+
|
|
27
|
+
```sql
|
|
28
|
+
-- Validate data and call APIs outside transaction
|
|
29
|
+
-- Application: response = await paymentAPI.charge(...)
|
|
30
|
+
|
|
31
|
+
-- Only hold lock for the actual update
|
|
32
|
+
begin;
|
|
33
|
+
update orders
|
|
34
|
+
set status = 'paid', payment_id = $1
|
|
35
|
+
where id = $2 and status = 'pending'
|
|
36
|
+
returning *;
|
|
37
|
+
commit; -- Lock held for milliseconds
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use `statement_timeout` to prevent runaway transactions:
|
|
41
|
+
|
|
42
|
+
```sql
|
|
43
|
+
-- Abort queries running longer than 30 seconds
|
|
44
|
+
set statement_timeout = '30s';
|
|
45
|
+
|
|
46
|
+
-- Or per-session
|
|
47
|
+
set local statement_timeout = '5s';
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Reference: [Transaction Management](https://www.postgresql.org/docs/current/tutorial-transactions.html)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use SKIP LOCKED for Non-Blocking Queue Processing
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: 10x throughput for worker queues
|
|
5
|
+
tags: skip-locked, queue, workers, concurrency
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use SKIP LOCKED for Non-Blocking Queue Processing
|
|
9
|
+
|
|
10
|
+
When multiple workers process a queue, SKIP LOCKED allows workers to process different rows without waiting.
|
|
11
|
+
|
|
12
|
+
**Incorrect (workers block each other):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Worker 1 and Worker 2 both try to get next job
|
|
16
|
+
begin;
|
|
17
|
+
select * from jobs where status = 'pending' order by created_at limit 1 for update;
|
|
18
|
+
-- Worker 2 waits for Worker 1's lock to release!
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (SKIP LOCKED for parallel processing):**
|
|
22
|
+
|
|
23
|
+
```sql
|
|
24
|
+
-- Each worker skips locked rows and gets the next available
|
|
25
|
+
begin;
|
|
26
|
+
select * from jobs
|
|
27
|
+
where status = 'pending'
|
|
28
|
+
order by created_at
|
|
29
|
+
limit 1
|
|
30
|
+
for update skip locked;
|
|
31
|
+
|
|
32
|
+
-- Worker 1 gets job 1, Worker 2 gets job 2 (no waiting)
|
|
33
|
+
|
|
34
|
+
update jobs set status = 'processing' where id = $1;
|
|
35
|
+
commit;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Complete queue pattern:
|
|
39
|
+
|
|
40
|
+
```sql
|
|
41
|
+
-- Atomic claim-and-update in one statement
|
|
42
|
+
update jobs
|
|
43
|
+
set status = 'processing', worker_id = $1, started_at = now()
|
|
44
|
+
where id = (
|
|
45
|
+
select id from jobs
|
|
46
|
+
where status = 'pending'
|
|
47
|
+
order by created_at
|
|
48
|
+
limit 1
|
|
49
|
+
for update skip locked
|
|
50
|
+
)
|
|
51
|
+
returning *;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Reference: [SELECT FOR UPDATE SKIP LOCKED](https://www.postgresql.org/docs/current/sql-select.html#SQL-FOR-UPDATE-SHARE)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use EXPLAIN ANALYZE to Diagnose Slow Queries
|
|
3
|
+
impact: LOW-MEDIUM
|
|
4
|
+
impactDescription: Identify exact bottlenecks in query execution
|
|
5
|
+
tags: explain, analyze, diagnostics, query-plan
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use EXPLAIN ANALYZE to Diagnose Slow Queries
|
|
9
|
+
|
|
10
|
+
EXPLAIN ANALYZE executes the query and shows actual timings, revealing the true performance bottlenecks.
|
|
11
|
+
|
|
12
|
+
**Incorrect (guessing at performance issues):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Query is slow, but why?
|
|
16
|
+
select * from orders where customer_id = 123 and status = 'pending';
|
|
17
|
+
-- "It must be missing an index" - but which one?
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (use EXPLAIN ANALYZE):**
|
|
21
|
+
|
|
22
|
+
```sql
|
|
23
|
+
explain (analyze, buffers, format text)
|
|
24
|
+
select * from orders where customer_id = 123 and status = 'pending';
|
|
25
|
+
|
|
26
|
+
-- Output reveals the issue:
|
|
27
|
+
-- Seq Scan on orders (cost=0.00..25000.00 rows=50 width=100) (actual time=0.015..450.123 rows=50 loops=1)
|
|
28
|
+
-- Filter: ((customer_id = 123) AND (status = 'pending'::text))
|
|
29
|
+
-- Rows Removed by Filter: 999950
|
|
30
|
+
-- Buffers: shared hit=5000 read=15000
|
|
31
|
+
-- Planning Time: 0.150 ms
|
|
32
|
+
-- Execution Time: 450.500 ms
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Key things to look for:
|
|
36
|
+
|
|
37
|
+
```sql
|
|
38
|
+
-- Seq Scan on large tables = missing index
|
|
39
|
+
-- Rows Removed by Filter = poor selectivity or missing index
|
|
40
|
+
-- Buffers: read >> hit = data not cached, needs more memory
|
|
41
|
+
-- Nested Loop with high loops = consider different join strategy
|
|
42
|
+
-- Sort Method: external merge = work_mem too low
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Reference: [EXPLAIN](https://supabase.com/docs/guides/database/inspect)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Enable pg_stat_statements for Query Analysis
|
|
3
|
+
impact: LOW-MEDIUM
|
|
4
|
+
impactDescription: Identify top resource-consuming queries
|
|
5
|
+
tags: pg-stat-statements, monitoring, statistics, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Enable pg_stat_statements for Query Analysis
|
|
9
|
+
|
|
10
|
+
pg_stat_statements tracks execution statistics for all queries, helping identify slow and frequent queries.
|
|
11
|
+
|
|
12
|
+
**Incorrect (no visibility into query patterns):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Database is slow, but which queries are the problem?
|
|
16
|
+
-- No way to know without pg_stat_statements
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (enable and query pg_stat_statements):**
|
|
20
|
+
|
|
21
|
+
```sql
|
|
22
|
+
-- Enable the extension
|
|
23
|
+
create extension if not exists pg_stat_statements;
|
|
24
|
+
|
|
25
|
+
-- Find slowest queries by total time
|
|
26
|
+
select
|
|
27
|
+
calls,
|
|
28
|
+
round(total_exec_time::numeric, 2) as total_time_ms,
|
|
29
|
+
round(mean_exec_time::numeric, 2) as mean_time_ms,
|
|
30
|
+
query
|
|
31
|
+
from pg_stat_statements
|
|
32
|
+
order by total_exec_time desc
|
|
33
|
+
limit 10;
|
|
34
|
+
|
|
35
|
+
-- Find most frequent queries
|
|
36
|
+
select calls, query
|
|
37
|
+
from pg_stat_statements
|
|
38
|
+
order by calls desc
|
|
39
|
+
limit 10;
|
|
40
|
+
|
|
41
|
+
-- Reset statistics after optimization
|
|
42
|
+
select pg_stat_statements_reset();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Key metrics to monitor:
|
|
46
|
+
|
|
47
|
+
```sql
|
|
48
|
+
-- Queries with high mean time (candidates for optimization)
|
|
49
|
+
select query, mean_exec_time, calls
|
|
50
|
+
from pg_stat_statements
|
|
51
|
+
where mean_exec_time > 100 -- > 100ms average
|
|
52
|
+
order by mean_exec_time desc;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Reference: [pg_stat_statements](https://supabase.com/docs/guides/database/extensions/pg_stat_statements)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Maintain Table Statistics with VACUUM and ANALYZE
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: 2-10x better query plans with accurate statistics
|
|
5
|
+
tags: vacuum, analyze, statistics, maintenance, autovacuum
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Maintain Table Statistics with VACUUM and ANALYZE
|
|
9
|
+
|
|
10
|
+
Outdated statistics cause the query planner to make poor decisions. VACUUM reclaims space, ANALYZE updates statistics.
|
|
11
|
+
|
|
12
|
+
**Incorrect (stale statistics):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Table has 1M rows but stats say 1000
|
|
16
|
+
-- Query planner chooses wrong strategy
|
|
17
|
+
explain select * from orders where status = 'pending';
|
|
18
|
+
-- Shows: Seq Scan (because stats show small table)
|
|
19
|
+
-- Actually: Index Scan would be much faster
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (maintain fresh statistics):**
|
|
23
|
+
|
|
24
|
+
```sql
|
|
25
|
+
-- Manually analyze after large data changes
|
|
26
|
+
analyze orders;
|
|
27
|
+
|
|
28
|
+
-- Analyze specific columns used in WHERE clauses
|
|
29
|
+
analyze orders (status, created_at);
|
|
30
|
+
|
|
31
|
+
-- Check when tables were last analyzed
|
|
32
|
+
select
|
|
33
|
+
relname,
|
|
34
|
+
last_vacuum,
|
|
35
|
+
last_autovacuum,
|
|
36
|
+
last_analyze,
|
|
37
|
+
last_autoanalyze
|
|
38
|
+
from pg_stat_user_tables
|
|
39
|
+
order by last_analyze nulls first;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Autovacuum tuning for busy tables:
|
|
43
|
+
|
|
44
|
+
```sql
|
|
45
|
+
-- Increase frequency for high-churn tables
|
|
46
|
+
alter table orders set (
|
|
47
|
+
autovacuum_vacuum_scale_factor = 0.05, -- Vacuum at 5% dead tuples (default 20%)
|
|
48
|
+
autovacuum_analyze_scale_factor = 0.02 -- Analyze at 2% changes (default 10%)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
-- Check autovacuum status
|
|
52
|
+
select * from pg_stat_progress_vacuum;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Reference: [VACUUM](https://supabase.com/docs/guides/database/database-size#vacuum-operations)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Create Composite Indexes for Multi-Column Queries
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: 5-10x faster multi-column queries
|
|
5
|
+
tags: indexes, composite-index, multi-column, query-optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Create Composite Indexes for Multi-Column Queries
|
|
9
|
+
|
|
10
|
+
When queries filter on multiple columns, a composite index is more efficient than separate single-column indexes.
|
|
11
|
+
|
|
12
|
+
**Incorrect (separate indexes require bitmap scan):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Two separate indexes
|
|
16
|
+
create index orders_status_idx on orders (status);
|
|
17
|
+
create index orders_created_idx on orders (created_at);
|
|
18
|
+
|
|
19
|
+
-- Query must combine both indexes (slower)
|
|
20
|
+
select * from orders where status = 'pending' and created_at > '2024-01-01';
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Correct (composite index):**
|
|
24
|
+
|
|
25
|
+
```sql
|
|
26
|
+
-- Single composite index (leftmost column first for equality checks)
|
|
27
|
+
create index orders_status_created_idx on orders (status, created_at);
|
|
28
|
+
|
|
29
|
+
-- Query uses one efficient index scan
|
|
30
|
+
select * from orders where status = 'pending' and created_at > '2024-01-01';
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Column order matters** - place equality columns first, range columns last:
|
|
34
|
+
|
|
35
|
+
```sql
|
|
36
|
+
-- Good: status (=) before created_at (>)
|
|
37
|
+
create index idx on orders (status, created_at);
|
|
38
|
+
|
|
39
|
+
-- Works for: WHERE status = 'pending'
|
|
40
|
+
-- Works for: WHERE status = 'pending' AND created_at > '2024-01-01'
|
|
41
|
+
-- Does NOT work for: WHERE created_at > '2024-01-01' (leftmost prefix rule)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Reference: [Multicolumn Indexes](https://www.postgresql.org/docs/current/indexes-multicolumn.html)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Covering Indexes to Avoid Table Lookups
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: 2-5x faster queries by eliminating heap fetches
|
|
5
|
+
tags: indexes, covering-index, include, index-only-scan
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Covering Indexes to Avoid Table Lookups
|
|
9
|
+
|
|
10
|
+
Covering indexes include all columns needed by a query, enabling index-only scans that skip the table entirely.
|
|
11
|
+
|
|
12
|
+
**Incorrect (index scan + heap fetch):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
create index users_email_idx on users (email);
|
|
16
|
+
|
|
17
|
+
-- Must fetch name and created_at from table heap
|
|
18
|
+
select email, name, created_at from users where email = 'user@example.com';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (index-only scan with INCLUDE):**
|
|
22
|
+
|
|
23
|
+
```sql
|
|
24
|
+
-- Include non-searchable columns in the index
|
|
25
|
+
create index users_email_idx on users (email) include (name, created_at);
|
|
26
|
+
|
|
27
|
+
-- All columns served from index, no table access needed
|
|
28
|
+
select email, name, created_at from users where email = 'user@example.com';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use INCLUDE for columns you SELECT but don't filter on:
|
|
32
|
+
|
|
33
|
+
```sql
|
|
34
|
+
-- Searching by status, but also need customer_id and total
|
|
35
|
+
create index orders_status_idx on orders (status) include (customer_id, total);
|
|
36
|
+
|
|
37
|
+
select status, customer_id, total from orders where status = 'shipped';
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Reference: [Index-Only Scans](https://www.postgresql.org/docs/current/indexes-index-only-scans.html)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Choose the Right Index Type for Your Data
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: 10-100x improvement with correct index type
|
|
5
|
+
tags: indexes, btree, gin, gist, brin, hash, index-types
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Choose the Right Index Type for Your Data
|
|
9
|
+
|
|
10
|
+
Different index types excel at different query patterns. The default B-tree isn't always optimal.
|
|
11
|
+
|
|
12
|
+
**Incorrect (B-tree for JSONB containment):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- B-tree cannot optimize containment operators
|
|
16
|
+
create index products_attrs_idx on products (attributes);
|
|
17
|
+
select * from products where attributes @> '{"color": "red"}';
|
|
18
|
+
-- Full table scan - B-tree doesn't support @> operator
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (GIN for JSONB):**
|
|
22
|
+
|
|
23
|
+
```sql
|
|
24
|
+
-- GIN supports @>, ?, ?&, ?| operators
|
|
25
|
+
create index products_attrs_idx on products using gin (attributes);
|
|
26
|
+
select * from products where attributes @> '{"color": "red"}';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Index type guide:
|
|
30
|
+
|
|
31
|
+
```sql
|
|
32
|
+
-- B-tree (default): =, <, >, BETWEEN, IN, IS NULL
|
|
33
|
+
create index users_created_idx on users (created_at);
|
|
34
|
+
|
|
35
|
+
-- GIN: arrays, JSONB, full-text search
|
|
36
|
+
create index posts_tags_idx on posts using gin (tags);
|
|
37
|
+
|
|
38
|
+
-- GiST: geometric data, range types, nearest-neighbor (KNN) queries
|
|
39
|
+
create index locations_idx on places using gist (location);
|
|
40
|
+
|
|
41
|
+
-- BRIN: large time-series tables (10-100x smaller)
|
|
42
|
+
create index events_time_idx on events using brin (created_at);
|
|
43
|
+
|
|
44
|
+
-- Hash: equality-only (slightly faster than B-tree for =)
|
|
45
|
+
create index sessions_token_idx on sessions using hash (token);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Reference: [Index Types](https://www.postgresql.org/docs/current/indexes-types.html)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Add Indexes on WHERE and JOIN Columns
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
impactDescription: 100-1000x faster queries on large tables
|
|
5
|
+
tags: indexes, performance, sequential-scan, query-optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Add Indexes on WHERE and JOIN Columns
|
|
9
|
+
|
|
10
|
+
Queries filtering or joining on unindexed columns cause full table scans, which become exponentially slower as tables grow.
|
|
11
|
+
|
|
12
|
+
**Incorrect (sequential scan on large table):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- No index on customer_id causes full table scan
|
|
16
|
+
select * from orders where customer_id = 123;
|
|
17
|
+
|
|
18
|
+
-- EXPLAIN shows: Seq Scan on orders (cost=0.00..25000.00 rows=100 width=85)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Correct (index scan):**
|
|
22
|
+
|
|
23
|
+
```sql
|
|
24
|
+
-- Create index on frequently filtered column
|
|
25
|
+
create index orders_customer_id_idx on orders (customer_id);
|
|
26
|
+
|
|
27
|
+
select * from orders where customer_id = 123;
|
|
28
|
+
|
|
29
|
+
-- EXPLAIN shows: Index Scan using orders_customer_id_idx (cost=0.42..8.44 rows=100 width=85)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
For JOIN columns, always index the foreign key side:
|
|
33
|
+
|
|
34
|
+
```sql
|
|
35
|
+
-- Index the referencing column
|
|
36
|
+
create index orders_customer_id_idx on orders (customer_id);
|
|
37
|
+
|
|
38
|
+
select c.name, o.total
|
|
39
|
+
from customers c
|
|
40
|
+
join orders o on o.customer_id = c.id;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Reference: [Query Optimization](https://supabase.com/docs/guides/database/query-optimization)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Partial Indexes for Filtered Queries
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: 5-20x smaller indexes, faster writes and queries
|
|
5
|
+
tags: indexes, partial-index, query-optimization, storage
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Partial Indexes for Filtered Queries
|
|
9
|
+
|
|
10
|
+
Partial indexes only include rows matching a WHERE condition, making them smaller and faster when queries consistently filter on the same condition.
|
|
11
|
+
|
|
12
|
+
**Incorrect (full index includes irrelevant rows):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- Index includes all rows, even soft-deleted ones
|
|
16
|
+
create index users_email_idx on users (email);
|
|
17
|
+
|
|
18
|
+
-- Query always filters active users
|
|
19
|
+
select * from users where email = 'user@example.com' and deleted_at is null;
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct (partial index matches query filter):**
|
|
23
|
+
|
|
24
|
+
```sql
|
|
25
|
+
-- Index only includes active users
|
|
26
|
+
create index users_active_email_idx on users (email)
|
|
27
|
+
where deleted_at is null;
|
|
28
|
+
|
|
29
|
+
-- Query uses the smaller, faster index
|
|
30
|
+
select * from users where email = 'user@example.com' and deleted_at is null;
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Common use cases for partial indexes:
|
|
34
|
+
|
|
35
|
+
```sql
|
|
36
|
+
-- Only pending orders (status rarely changes once completed)
|
|
37
|
+
create index orders_pending_idx on orders (created_at)
|
|
38
|
+
where status = 'pending';
|
|
39
|
+
|
|
40
|
+
-- Only non-null values
|
|
41
|
+
create index products_sku_idx on products (sku)
|
|
42
|
+
where sku is not null;
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Reference: [Partial Indexes](https://www.postgresql.org/docs/current/indexes-partial.html)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Add Constraints Safely in Migrations
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Prevents migration failures and enables idempotent schema changes
|
|
5
|
+
tags: constraints, migrations, schema, alter-table
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Add Constraints Safely in Migrations
|
|
9
|
+
|
|
10
|
+
PostgreSQL does not support `ADD CONSTRAINT IF NOT EXISTS`. Migrations using this syntax will fail.
|
|
11
|
+
|
|
12
|
+
**Incorrect (causes syntax error):**
|
|
13
|
+
|
|
14
|
+
```sql
|
|
15
|
+
-- ERROR: syntax error at or near "not" (SQLSTATE 42601)
|
|
16
|
+
alter table public.profiles
|
|
17
|
+
add constraint if not exists profiles_birthchart_id_unique unique (birthchart_id);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (idempotent constraint creation):**
|
|
21
|
+
|
|
22
|
+
```sql
|
|
23
|
+
-- Use DO block to check before adding
|
|
24
|
+
do $$
|
|
25
|
+
begin
|
|
26
|
+
if not exists (
|
|
27
|
+
select 1 from pg_constraint
|
|
28
|
+
where conname = 'profiles_birthchart_id_unique'
|
|
29
|
+
and conrelid = 'public.profiles'::regclass
|
|
30
|
+
) then
|
|
31
|
+
alter table public.profiles
|
|
32
|
+
add constraint profiles_birthchart_id_unique unique (birthchart_id);
|
|
33
|
+
end if;
|
|
34
|
+
end $$;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For all constraint types:
|
|
38
|
+
|
|
39
|
+
```sql
|
|
40
|
+
-- Check constraints
|
|
41
|
+
do $$
|
|
42
|
+
begin
|
|
43
|
+
if not exists (
|
|
44
|
+
select 1 from pg_constraint
|
|
45
|
+
where conname = 'check_age_positive'
|
|
46
|
+
) then
|
|
47
|
+
alter table users add constraint check_age_positive check (age > 0);
|
|
48
|
+
end if;
|
|
49
|
+
end $$;
|
|
50
|
+
|
|
51
|
+
-- Foreign keys
|
|
52
|
+
do $$
|
|
53
|
+
begin
|
|
54
|
+
if not exists (
|
|
55
|
+
select 1 from pg_constraint
|
|
56
|
+
where conname = 'profiles_birthchart_id_fkey'
|
|
57
|
+
) then
|
|
58
|
+
alter table profiles
|
|
59
|
+
add constraint profiles_birthchart_id_fkey
|
|
60
|
+
foreign key (birthchart_id) references birthcharts(id);
|
|
61
|
+
end if;
|
|
62
|
+
end $$;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Check if constraint exists:
|
|
66
|
+
|
|
67
|
+
```sql
|
|
68
|
+
-- Query to check constraint existence
|
|
69
|
+
select conname, contype, pg_get_constraintdef(oid)
|
|
70
|
+
from pg_constraint
|
|
71
|
+
where conrelid = 'public.profiles'::regclass;
|
|
72
|
+
|
|
73
|
+
-- contype values:
|
|
74
|
+
-- 'p' = PRIMARY KEY
|
|
75
|
+
-- 'f' = FOREIGN KEY
|
|
76
|
+
-- 'u' = UNIQUE
|
|
77
|
+
-- 'c' = CHECK
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Reference: [Constraints](https://www.postgresql.org/docs/current/ddl-constraints.html)
|