Skip to main content
rule

postgresql · sanjeed5 — Cursor rule: SQL guidance and setup

This Cursor rule guides PostgreSQL query and schema work, covering naming, joins, data types, and date ranges. Use it when an agent edits SQL across a project.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/postgresql.mdc
Open on GitHub

What postgresql does

The rule sets conventions for identifiers, query layout, and comments. It calls for explicit joins, favors CTEs over nested subqueries, and recommends replacing NOT IN with NOT EXISTS or LEFT JOIN ... IS NULL. Use EXPLAIN ANALYZE to inspect query plans.

Schema guidance covers identity-based bigint primary keys, text fields, and numeric or decimal for monetary values. Timestamps should use timestamptz in UTC; date ranges should use >= and <. Its **/* glob applies the rule across files, and the caution around NOT IN matters when results may contain NULL.

What it helps with

  • format PostgreSQL queries consistently
  • replace implicit table joins with explicit joins
  • rewrite nested subqueries as CTEs
  • choose data types for IDs, text, and monetary values
  • inspect query plans with EXPLAIN ANALYZE

Example prompts for the agent

  • Rewrite this query to follow the PostgreSQL rule in Cursor.
  • Review this migration and use explicit joins instead of selecting every column.
  • Check the date range and field types in this PostgreSQL schema.

How to install postgresql

Manually: put the files in place

.cursor/rules/postgresql.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about postgresql

How do I install postgresql in Cursor?

Add the rule file at .cursor/rules/postgresql.mdc.

What does the postgresql rule do?

It guides PostgreSQL SQL style, joins, data types, date handling, and query-plan checks.

Why does the rule warn against NOT IN?

Its behavior with NULL can cause problems. The rule suggests NOT EXISTS or LEFT JOIN ... IS NULL instead.

Source text

postgresql.mdc · CC0-1.0
---
description: This guide defines the definitive best practices for writing clean, performant, and maintainable PostgreSQL SQL, focusing on modern conventions and avoiding common pitfalls.
globs: **/*
---
# PostgreSQL Best Practices

This document outlines the mandatory best practices for all PostgreSQL SQL development. Adherence ensures consistency, readability, performance, and maintainability across our codebase.

## 1. Code Organization and Structure

### 1.1. Naming Conventions
Always use `snake_case` for all database identifiers (tables, columns, functions, schemas). SQL keywords must be lowercase. Keep names descriptive but concise.

❌ **BAD**:
```sql
CREATE TABLE UserData (
    UserID INT PRIMARY KEY,
    UserName VARCHAR(255)
);

SELECT UserID, UserName FROM UserData;
```

✅ **GOOD**:
```sql
create table user_data (
    user_id bigint generated always as identity primary key,
    user_name text not null
);

select user_id, user_name from user_data;
```

### 1.2. Formatting
Structure queries for maximum readability. Root keywords (`SELECT`, `FROM`, `WHERE`) belong on their own line, with arguments indented. Use explicit `AS` for all aliases.

❌ **BAD**:
```sql
SELECT t.client_id, DATE(t.created_at) day FROM telemetry t, users u WHERE t.user_id = u.id AND t.submission_date > '2019-07-01' GROUP BY 1, 2;
```

✅ **GOOD**:
```sql
select
    t.client_id as client_id,
    date(t.created_at) as day
from
    telemetry as t
inner join
    users as u
    on t.user_id = u.id
where
    t.submission_date > '2019-07-01'
    and t.sample_id = '10'
group by
    t.client_id,
    day;
```

### 1.3. Comments
Use block comments (`/* ... */`) for multi-line descriptions and line comments (`--`) for single-line notes.

```sql
/*
This query retrieves active users and their recent orders.
It joins the users table with the orders table.
*/
select
    u.user_id, -- Unique user identifier
    u.user_name,
    o.order_id,
    o.order_date
from
    users as u
inner join
    orders as o
    on u.user_id = o.user_id;
```

## 2. Common Patterns and Anti-patterns

### 2.1. Explicit JOINs
Always use explicit `INNER JOIN`, `LEFT JOIN`, `RIGHT JOIN`, or `FULL JOIN`. Never use implicit joins in the `FROM` clause.

❌ **BAD**:
```sql
select u.user_name, o.order_date from users u, orders o where u.user_id = o.user_id;
```

✅ **GOOD**:
```sql
select
    u.user_name,
    o.order_date
from
    users as u
inner join
    orders as o
    on u.user_id = o.user_id;
```

### 2.2. Common Table Expressions (CTEs)
Prefer CTEs (`WITH` clauses) over nested subqueries for improved readability and modularity.

❌ **BAD**:
```sql
select
    count(*)
from (
    select
        user_id
    from
        orders
    where
        order_date >= '2023-01-01'
    group by
        user_id
    having
        count(*) > 5
) as frequent_buyers;
```

✅ **GOOD**:
```sql
with frequent_buyers as (
    select
        user_id
    from
        orders
    where
        order_date >= '2023-01-01'
    group by
        user_id
    having
        count(*) > 5
)
select
    count(*)
from
    frequent_buyers;
```

### 2.3. Avoid `NOT IN`
Never use `NOT IN` due to its problematic behavior with `NULL` values. Prefer `NOT EXISTS` or `LEFT JOIN ... IS NULL`.

❌ **BAD**:
```sql
select user_name from users where user_id not in (select user_id from orders where status = 'cancelled');
```

✅ **GOOD` (NOT EXISTS)`**:
```sql
select
    user_name
from
    users as u
where not exists (
    select 1 from orders as o where o.user_id = u.user_id and o.status = 'cancelled'
);
```

✅ **GOOD` (LEFT JOIN ... IS NULL)`**:
```sql
select
    u.user_name
from
    users as u
left join
    orders as o
    on u.user_id = o.user_id and o.status = 'cancelled'
where
    o.user_id is null;
```

## 3. Performance Considerations

### 3.1. Explicit Column Selection
Always specify columns explicitly. Avoid `SELECT *` in production code. This improves performance, reduces network traffic, and prevents issues when schema changes.

❌ **BAD**:
```sql
select * from products;
```

✅ **GOOD**:
```sql
select
    product_id,
    product_name,
    price,
    stock_quantity
from
    products;
```

### 3.2. Query Optimization
Use `EXPLAIN ANALYZE` to understand and optimize query plans. Focus on reducing sequential scans and improving index usage.

```sql
explain analyze
select
    o.order_id,
    c.customer_name
from
    orders as o
inner join
    customers as c
    on o.customer_id = c.customer_id
where
    o.order_date >= '2023-01-01'
order by
    o.order_date desc
limit 100;
```

## 4. Common Pitfalls and Gotchas

### 4.1. Date/Time Storage
Store all timestamps as `timestamp with time zone` (`timestamptz`) and always in UTC. Convert to local time zones only at the application layer for display.

❌ **BAD**:
```sql
create table events (
    event_id int,
    event_time timestamp without time zone -- Prone to timezone issues
);
```

✅ **GOOD**:
```sql
create table events (
    event_id bigint generated always as identity primary key,
    event_time timestamptz default now() -- Always store in UTC
);
```

### 4.2. `BETWEEN` with Timestamps
Avoid `BETWEEN` for date/time ranges, especially when precision matters. It includes both start and end points, which can lead to off-by-one errors. Use explicit `>=` and `<` operators.

❌ **BAD**:
```sql
select * from orders where order_date between '2023-01-01' and '2023-01-31';
```

✅ **GOOD**:
```sql
select
    *
from
    orders
where
    order_date >= '2023-01-01T00:00:00Z'
    and order_date < '2023-02-01T00:00:00Z'; -- Correctly handles the entire month of January
```

### 4.3. Data Type Choices
*   **IDs**: Use `bigint generated always as identity` for primary keys. Never use `serial` or `bigserial`.
*   **Text**: Prefer `text` over `varchar(n)` unless there's a specific, strict length constraint. Never use `char(n)`.
*   **Money**: Never use the `money` type. Use `numeric` or `decimal` with explicit precision (e.g., `numeric(19, 4)`).

❌ **BAD**:
```sql
create table products (
    id serial primary key,
    product_code char(10),
    description varchar(255),
    price money
);
```

✅ **GOOD**:
```sql
create table products (
    product_id bigint generated always as identity primary key,
    product_code varchar(10) not null, -- Use varchar for fixed-length codes
    description text,
    price numeric(19, 4) not null -- Explicit precision for currency
);
```

### 4.4. Avoid Legacy Constructs
Never use PostgreSQL `rules` or `table inheritance`. Use `triggers` for event-driven logic and native `table partitioning` for large tables.

## 5. Security Best Practices

### 5.1. Prepared Statements
Always use prepared statements (e.g., via parameterized queries in your application code) to prevent SQL injection vulnerabilities.

❌ **BAD` (Application Code)`**:
```python
cursor.execute(f"select * from users where user_name = '{user_input}';")
```

✅ **GOOD` (Application Code)`**:
```python
cursor.execute("select * from users where user_name = %s;", (user_input,))
```

### 5.2. Least Privilege
Grant database roles only the minimum necessary permissions. Avoid granting `ALL PRIVILEGES`.

```sql
-- Create a read-only role
create role app_reader nologin;
grant connect on database my_app_db to app_reader;
grant usage on schema public to app_reader;
grant select on all tables in schema public to app_reader;
alter default privileges in schema public grant select on tables to app_reader;

-- Create a read-write role
create role app_writer nologin;
grant app_reader to app_writer; -- Inherit read permissions
grant insert, update, delete on all tables in schema public to app_writer;
alter default privileges in schema public grant insert, update, delete on tables to app_writer;
```

## 6. Data Modeling

### 6.1. Foreign Key Naming
Name foreign key columns as `<referenced_table>_id` (singular form of the referenced table name).

❌ **BAD**:
```sql
create table orders (
    order_id bigint generated always as identity primary key,
    customerid bigint not null -- Inconsistent naming
);
```

✅ **GOOD**:
```sql
create table orders (
    order_id bigint generated always as identity primary key,
    customer_id bigint not null, -- Correct foreign key naming
    constraint fk_customer foreign key (customer_id) references customers (customer_id)
);
```

### 6.2. Table Comments
Always add descriptive comments to tables and columns using `COMMENT ON`. This is crucial for documentation and understanding schema intent.

```sql
comment on table users is 'Stores information about application users.';
comment on column users.user_name is 'Full name of the user.';
```

## 7. Testing Approaches

### 7.1. Transactional Tests
Wrap database tests in transactions that are rolled back at the end. This ensures a clean state for each test run.

```sql
begin; -- Start transaction
    -- Insert test data
    insert into users (user_name) values ('Test User');

    -- Run assertions
    select count(*) from users where user_name = 'Test User'; -- Should be 1

rollback; -- Rollback all changes
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

All IDE rules for Data and databases →

postgresql by other authors comes first.

postgresql PatrickJS A Cursor rule that pins down how the agent writes PostgreSQL: parameterized queries, TIMESTAMPTZ, indexes on foreign keys. Useful once the agent starts touching your SQL. ★ 41,028repo stars postgresql-database-engineering manutej Guides database design, index selection, and query optimization in PostgreSQL. Useful when tuning slow queries, setting up table partitioning, or configuring replication. CX Codex +1 1,800installs postgresql-best-practices Mindrally A skill that feeds your agent PostgreSQL rules: data types, schema design, indexing, query plans and database maintenance. CX Codex +1 1,500installs PostgreSQL Database Administration Claude A PostgreSQL administration skill: schema design, indexing, backups and query tuning. Useful when the agent writes SQL for a ticketing system or reporting. CX Codex +1 452installs postgresql-expert PCL Team A skill that turns the agent into a PostgreSQL expert: advanced queries, indexes, performance tuning and production operations. Useful whenever a task touches schema, SQL or query plans. CX Codex +1 424installs postgresql-expert-best-practices-code-review wispbit A rule set for PostgreSQL schemas, migrations and indexes. Your agent reads it while writing or reviewing SQL. CX Codex +1 387installs PostgreSQL Database Administrator official github Subagent for managing PostgreSQL databases with the VS Code extension. It executes queries, inspects schemas, and handles admin tasks directly in Copilot. ★ 40,478repo stars

From the same set240

The whole set →

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor