Indexes and Constraints

Indexes and constraints are catalog objects used by planning, execution, and data validation.

Constraints express correctness. Indexes express access paths. They often overlap, but they should be evaluated separately: a query can be correct without the right index, and an index can improve speed without enforcing an invariant.

Primary keys and uniqueness

CREATE TABLE users (
    id INT PRIMARY KEY,
    email TEXT UNIQUE,
    name TEXT NOT NULL
);

Use primary keys and unique constraints to express application invariants. In v0.1, validate conflict and error behavior with your client stack before relying on PostgreSQL-identical messages.

Conflict behavior should be part of driver testing:

INSERT INTO users VALUES (1, 'a@example.com', 'Ada');
INSERT INTO users VALUES (1, 'b@example.com', 'Grace');

Record SQLSTATE and transaction state after the failure. ORMs often depend on both.

Foreign-key style modeling

Relational relationships can be expressed with ordinary columns and joins:

CREATE TABLE tickets (
    id INT PRIMARY KEY,
    assigned_to INT
);

CREATE TABLE employees (
    id INT PRIMARY KEY,
    name TEXT
);

Graph edge labels can then describe how relationship tables map into graph traversal.

For v0.1 graph evaluation, keep foreign-key-style relationships visible in SQL. That lets you compare graph traversal against an ordinary join.

Recommended relationship pattern:

CREATE TABLE ticket_assignments (
    source_id INT NOT NULL,
    target_id INT NOT NULL,
    assigned_at TEXT
);

CREATE INDEX ticket_assignments_source_idx ON ticket_assignments (source_id);
CREATE INDEX ticket_assignments_target_idx ON ticket_assignments (target_id);

B-tree style indexes

CREATE INDEX tickets_assigned_to_idx ON tickets (assigned_to);
CREATE INDEX tickets_status_priority_idx ON tickets (status, priority);

Use ordinary indexes for selective filters and joins. Check plans when a query is unexpectedly slow.

Index columns that appear in:

Composite indexes should follow the query shape. For WHERE status = 'open' AND priority = 'high', an index on (status, priority) is more useful than two unrelated indexes when that predicate is common.

HNSW vector indexes

CREATE TABLE docs (
    id INT,
    embedding VECTOR(4)
);

CREATE INDEX docs_embedding_hnsw ON docs USING hnsw (embedding);

Vector columns require USING hnsw for vector indexing. Query metric and index metric must match for the optimizer to use an HNSW path.

Keep an exact brute-force vector query as a correctness reference before judging indexed vector behavior.

Dropping indexes

DROP INDEX docs_embedding_hnsw;

Dropping an index changes future planning. Re-run representative queries after index changes.

Constraint checklist

For every important table, decide:

Document anything left to application code. That prevents a future reader from assuming the database enforces an invariant that is only implicit.

Index checklist

For every important query, record:

If a benchmark depends on an index, include the index DDL with the benchmark output.