This document describes the database schema for the go-sql-store application, including table structures, relationships, constraints, and indexing strategies.
Stores user account information.
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
version INT NOT NULL DEFAULT 1
);Indexes:
idx_users_email- Fast lookups by email (login)idx_users_created_at- Efficient ordering for pagination
Design Notes:
versioncolumn supports optimistic locking if neededemailhas unique constraint for authentication- Timestamps track record lifecycle
Stores product catalog with inventory tracking.
CREATE TABLE products (
id BIGSERIAL PRIMARY KEY,
sku VARCHAR(100) NOT NULL UNIQUE,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL CHECK (price >= 0),
stock_quantity INT NOT NULL DEFAULT 0 CHECK (stock_quantity >= 0),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
version INT NOT NULL DEFAULT 1
);Indexes:
idx_products_sku- Fast lookups by SKUidx_products_created_at- Efficient ordering for paginationidx_products_stock(partial) - Only indexes products with stock > 0 for inventory queries
Design Notes:
skuis unique business identifierpriceuses DECIMAL to avoid floating-point precision issuesstock_quantityhas CHECK constraint to prevent negative inventoryversionenables optimistic locking for concurrent updates- Partial index on stock_quantity optimizes "available products" queries
Stores customer orders.
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
order_number VARCHAR(50) NOT NULL UNIQUE,
status VARCHAR(50) NOT NULL DEFAULT 'pending',
total_amount DECIMAL(10, 2) NOT NULL CHECK (total_amount >= 0),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
version INT NOT NULL DEFAULT 1,
CONSTRAINT valid_status CHECK (status IN ('pending', 'confirmed', 'shipped', 'delivered', 'cancelled'))
);Indexes:
idx_orders_user_id- Fast lookups of user's ordersidx_orders_status- Efficient filtering by statusidx_orders_created_at- Ordering for paginationidx_orders_user_created(composite) - Optimized for cursor pagination (user_id, created_at DESC, id DESC)
Design Notes:
order_numberis unique, user-friendly identifierstatusconstrained to valid valuesON DELETE RESTRICTprevents deleting users with orders- Composite index supports efficient cursor-based pagination for user orders
versionsupports optimistic locking
Many-to-many relationship between orders and products.
CREATE TABLE order_items (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
product_id BIGINT NOT NULL REFERENCES products(id) ON DELETE RESTRICT,
quantity INT NOT NULL CHECK (quantity > 0),
unit_price DECIMAL(10, 2) NOT NULL CHECK (unit_price >= 0),
subtotal DECIMAL(10, 2) NOT NULL CHECK (subtotal >= 0),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
UNIQUE(order_id, product_id)
);Indexes:
idx_order_items_order_id- Fast retrieval of items for an orderidx_order_items_product_id- Fast lookup of orders containing a product
Design Notes:
ON DELETE CASCADEautomatically removes items when order is deletedON DELETE RESTRICTprevents deleting products that are in ordersunit_pricedenormalized to preserve historical pricingsubtotaldenormalized for query performance- UNIQUE constraint prevents duplicate products in same order
users (1) ─────< orders (N)
│
│ (1)
│
└──< order_items (N) >──── (N) products
- Max open connections: 25
- Max idle connections: 5
- Connection max lifetime: 5 minutes
- Primary Keys: Automatic B-tree indexes on all BIGSERIAL id columns
- Foreign Keys: Explicit indexes on all foreign key columns for JOIN performance
- Unique Constraints: Automatic indexes on email, sku, order_number
- Cursor Pagination: Composite index (user_id, created_at DESC, id DESC) enables efficient keyset pagination
- Partial Indexes: Products with stock > 0 for inventory availability queries
- Use
FOR UPDATEcarefully to minimize lock duration - Cursor-based pagination prevents OFFSET scan for deep pages
- Composite indexes support multi-column sorting without table scan
- price >= 0: Prevents negative prices
- stock_quantity >= 0: Prevents negative inventory
- quantity > 0: Order items must have positive quantity
- valid_status: Ensures data integrity for order workflow
- ON DELETE RESTRICT: Prevents orphaning critical data (users with orders, products in order history)
- ON DELETE CASCADE: Automatically cleans up dependent data (order items when order deleted)
Migrations are applied in numerical order:
001_create_users- Foundation table002_create_products- Independent table003_create_orders- Depends on users004_create_order_items- Depends on orders and products
Each migration has a corresponding .down.sql for rollback with CASCADE to handle dependencies.