Add ConnectPlain to open the DB without the custom search_path and switch migration and CLI flows to run on that plain connection. Wrap multi-statement goose migrations with StatementBegin/End to ensure statements are executed atomically. Move Stripe price outbox seeding into a dedicated stripe migration.
264 lines
13 KiB
SQL
264 lines
13 KiB
SQL
-- +goose Up
|
|
-- +goose StatementBegin
|
|
|
|
-- Shared namespace for resource types
|
|
CREATE TABLE entitlements.resource_keys (
|
|
resource_key VARCHAR(100) PRIMARY KEY,
|
|
display_name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
unit VARCHAR(50) NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Containers for entitlements, belonging to organizations
|
|
CREATE TABLE entitlements.resource_pools (
|
|
pool_id UUID PRIMARY KEY DEFAULT uuidv7(),
|
|
org_id UUID NOT NULL REFERENCES organization.organizations(org_id),
|
|
name VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(100) NOT NULL,
|
|
pool_type VARCHAR(20) NOT NULL,
|
|
is_auto_managed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
description TEXT,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
suspended_at TIMESTAMPTZ,
|
|
archived_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE (org_id, slug)
|
|
);
|
|
|
|
-- Administrative/promotional access grants
|
|
CREATE TABLE entitlements.grants (
|
|
grant_id UUID PRIMARY KEY DEFAULT uuidv7(),
|
|
product_id UUID NOT NULL REFERENCES billing.products(product_id),
|
|
granted_to_billing_account_id UUID,
|
|
granted_to_org_id UUID REFERENCES organization.organizations(org_id),
|
|
granted_to_person_id UUID REFERENCES identity.persons(person_id),
|
|
granted_by_person_id UUID NOT NULL REFERENCES identity.persons(person_id),
|
|
grant_reason VARCHAR(50) NOT NULL,
|
|
description TEXT,
|
|
quantity INTEGER NOT NULL DEFAULT 1,
|
|
valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
valid_until TIMESTAMPTZ,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
revoked_at TIMESTAMPTZ,
|
|
revoked_by_person_id UUID REFERENCES identity.persons(person_id),
|
|
revocation_reason TEXT,
|
|
metadata JSONB,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
-- Exclusive arc: exactly one recipient must be set (D3)
|
|
CONSTRAINT chk_grants_recipient CHECK (
|
|
(granted_to_billing_account_id IS NOT NULL AND granted_to_org_id IS NULL AND granted_to_person_id IS NULL)
|
|
OR (granted_to_billing_account_id IS NULL AND granted_to_org_id IS NOT NULL AND granted_to_person_id IS NULL)
|
|
OR (granted_to_billing_account_id IS NULL AND granted_to_org_id IS NULL AND granted_to_person_id IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
-- Uniform interface: provisions link sources to pools
|
|
CREATE TABLE entitlements.pool_provisions (
|
|
provision_id UUID PRIMARY KEY DEFAULT uuidv7(),
|
|
pool_id UUID NOT NULL REFERENCES entitlements.resource_pools(pool_id),
|
|
billing_account_id UUID,
|
|
subscription_id UUID,
|
|
purchase_id UUID,
|
|
grant_id UUID REFERENCES entitlements.grants(grant_id),
|
|
product_id UUID NOT NULL REFERENCES billing.products(product_id),
|
|
quantity INTEGER NOT NULL DEFAULT 1,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
activated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
suspended_at TIMESTAMPTZ,
|
|
ended_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
-- Exclusive arc: exactly one source must be set (D4)
|
|
CONSTRAINT chk_pool_provisions_source CHECK (
|
|
(subscription_id IS NOT NULL AND purchase_id IS NULL AND grant_id IS NULL)
|
|
OR (subscription_id IS NULL AND purchase_id IS NOT NULL AND grant_id IS NULL)
|
|
OR (subscription_id IS NULL AND purchase_id IS NULL AND grant_id IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
-- Workspace-to-pool links
|
|
CREATE TABLE entitlements.pool_assignments (
|
|
assignment_id UUID PRIMARY KEY DEFAULT uuidv7(),
|
|
pool_id UUID NOT NULL REFERENCES entitlements.resource_pools(pool_id),
|
|
workspace_id UUID NOT NULL REFERENCES organization.workspaces(workspace_id),
|
|
is_primary BOOLEAN NOT NULL DEFAULT FALSE,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
suspended_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE (pool_id, workspace_id)
|
|
);
|
|
|
|
-- Rules defining what products grant
|
|
CREATE TABLE entitlements.product_entitlement_rules (
|
|
rule_id UUID PRIMARY KEY DEFAULT uuidv7(),
|
|
product_id UUID NOT NULL REFERENCES billing.products(product_id),
|
|
rule_type VARCHAR(20) NOT NULL,
|
|
resource_key VARCHAR(100) REFERENCES entitlements.resource_keys(resource_key),
|
|
resource_value BIGINT,
|
|
resource_per_unit BOOLEAN,
|
|
stacking_policy VARCHAR(20) DEFAULT 'additive',
|
|
reset_period VARCHAR(20),
|
|
credit_amount INTEGER,
|
|
credit_currency VARCHAR(3),
|
|
description TEXT,
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
-- Single-table inheritance constraint
|
|
CONSTRAINT chk_product_entitlement_rules_type CHECK (
|
|
(rule_type = 'boolean' AND resource_key IS NOT NULL
|
|
AND resource_value IS NULL AND stacking_policy IS NULL
|
|
AND reset_period IS NULL AND resource_per_unit IS NULL
|
|
AND credit_amount IS NULL AND credit_currency IS NULL)
|
|
OR (rule_type = 'limit' AND resource_key IS NOT NULL AND resource_value IS NOT NULL
|
|
AND reset_period IS NULL
|
|
AND credit_amount IS NULL AND credit_currency IS NULL)
|
|
OR (rule_type = 'quota' AND resource_key IS NOT NULL AND resource_value IS NOT NULL
|
|
AND reset_period IS NOT NULL
|
|
AND credit_amount IS NULL AND credit_currency IS NULL)
|
|
OR (rule_type = 'credit' AND credit_amount IS NOT NULL AND credit_currency IS NOT NULL
|
|
AND resource_key IS NULL AND resource_value IS NULL
|
|
AND stacking_policy IS NULL AND reset_period IS NULL AND resource_per_unit IS NULL)
|
|
)
|
|
);
|
|
|
|
-- Materialized numeric entitlements on a pool
|
|
CREATE TABLE entitlements.numeric_entitlements (
|
|
entitlement_id UUID PRIMARY KEY DEFAULT uuidv7(),
|
|
pool_id UUID NOT NULL REFERENCES entitlements.resource_pools(pool_id),
|
|
resource_key VARCHAR(100) NOT NULL REFERENCES entitlements.resource_keys(resource_key),
|
|
entitlement_type VARCHAR(20) NOT NULL,
|
|
resource_limit BIGINT NOT NULL DEFAULT 0,
|
|
reset_period VARCHAR(20),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE (pool_id, resource_key)
|
|
);
|
|
|
|
-- Provenance: which provisions contribute to an entitlement
|
|
CREATE TABLE entitlements.numeric_entitlement_contributions (
|
|
contribution_id UUID PRIMARY KEY DEFAULT uuidv7(),
|
|
entitlement_id UUID NOT NULL REFERENCES entitlements.numeric_entitlements(entitlement_id),
|
|
provision_id UUID NOT NULL REFERENCES entitlements.pool_provisions(provision_id),
|
|
contributed_value BIGINT NOT NULL,
|
|
stacking_policy VARCHAR(20) NOT NULL DEFAULT 'additive',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE (entitlement_id, provision_id)
|
|
);
|
|
|
|
-- Mutable usage state, separated for write isolation
|
|
CREATE TABLE entitlements.numeric_entitlement_usage (
|
|
usage_id UUID PRIMARY KEY DEFAULT uuidv7(),
|
|
entitlement_id UUID NOT NULL UNIQUE REFERENCES entitlements.numeric_entitlements(entitlement_id),
|
|
pool_id UUID NOT NULL REFERENCES entitlements.resource_pools(pool_id),
|
|
resource_key VARCHAR(100) NOT NULL,
|
|
current_usage BIGINT NOT NULL DEFAULT 0,
|
|
current_period_start TIMESTAMPTZ,
|
|
current_period_end TIMESTAMPTZ,
|
|
last_reset_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Indexes
|
|
CREATE INDEX idx_resource_pools_org_id ON entitlements.resource_pools(org_id);
|
|
CREATE INDEX idx_grants_org_id ON entitlements.grants(granted_to_org_id);
|
|
CREATE INDEX idx_grants_product_id ON entitlements.grants(product_id);
|
|
CREATE INDEX idx_pool_provisions_pool_id ON entitlements.pool_provisions(pool_id);
|
|
CREATE INDEX idx_pool_provisions_grant_id ON entitlements.pool_provisions(grant_id);
|
|
CREATE INDEX idx_pool_assignments_workspace_id ON entitlements.pool_assignments(workspace_id);
|
|
CREATE INDEX idx_pool_assignments_pool_id ON entitlements.pool_assignments(pool_id);
|
|
CREATE INDEX idx_product_entitlement_rules_product_id ON entitlements.product_entitlement_rules(product_id);
|
|
CREATE INDEX idx_numeric_entitlements_pool_id ON entitlements.numeric_entitlements(pool_id);
|
|
CREATE INDEX idx_numeric_entitlement_contributions_entitlement_id ON entitlements.numeric_entitlement_contributions(entitlement_id);
|
|
CREATE INDEX idx_numeric_entitlement_contributions_provision_id ON entitlements.numeric_entitlement_contributions(provision_id);
|
|
-- Hot-path composite index for entitlement checks (D11)
|
|
CREATE INDEX idx_numeric_entitlement_usage_pool_resource ON entitlements.numeric_entitlement_usage(pool_id, resource_key);
|
|
|
|
-- Updated-at triggers
|
|
CREATE TRIGGER trigger_resource_pools_updated_at
|
|
BEFORE UPDATE ON entitlements.resource_pools
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER trigger_grants_updated_at
|
|
BEFORE UPDATE ON entitlements.grants
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER trigger_pool_provisions_updated_at
|
|
BEFORE UPDATE ON entitlements.pool_provisions
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER trigger_pool_assignments_updated_at
|
|
BEFORE UPDATE ON entitlements.pool_assignments
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER trigger_product_entitlement_rules_updated_at
|
|
BEFORE UPDATE ON entitlements.product_entitlement_rules
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER trigger_numeric_entitlements_updated_at
|
|
BEFORE UPDATE ON entitlements.numeric_entitlements
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER trigger_numeric_entitlement_contributions_updated_at
|
|
BEFORE UPDATE ON entitlements.numeric_entitlement_contributions
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
CREATE TRIGGER trigger_numeric_entitlement_usage_updated_at
|
|
BEFORE UPDATE ON entitlements.numeric_entitlement_usage
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
-- Per-schema role grants (Decision 115).
|
|
GRANT ALL ON ALL TABLES IN SCHEMA entitlements TO entitlements_owner;
|
|
GRANT ALL ON ALL TABLES IN SCHEMA entitlements TO entitlements_writer;
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA entitlements TO entitlements_reader;
|
|
|
|
-- +goose StatementEnd
|
|
|
|
-- +goose Down
|
|
-- +goose StatementBegin
|
|
DROP TRIGGER IF EXISTS trigger_numeric_entitlement_usage_updated_at ON entitlements.numeric_entitlement_usage;
|
|
DROP TRIGGER IF EXISTS trigger_numeric_entitlement_contributions_updated_at ON entitlements.numeric_entitlement_contributions;
|
|
DROP TRIGGER IF EXISTS trigger_numeric_entitlements_updated_at ON entitlements.numeric_entitlements;
|
|
DROP TRIGGER IF EXISTS trigger_product_entitlement_rules_updated_at ON entitlements.product_entitlement_rules;
|
|
DROP TRIGGER IF EXISTS trigger_pool_assignments_updated_at ON entitlements.pool_assignments;
|
|
DROP TRIGGER IF EXISTS trigger_pool_provisions_updated_at ON entitlements.pool_provisions;
|
|
DROP TRIGGER IF EXISTS trigger_grants_updated_at ON entitlements.grants;
|
|
DROP TRIGGER IF EXISTS trigger_resource_pools_updated_at ON entitlements.resource_pools;
|
|
DROP INDEX IF EXISTS entitlements.idx_numeric_entitlement_usage_pool_resource;
|
|
DROP INDEX IF EXISTS entitlements.idx_numeric_entitlement_contributions_provision_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_numeric_entitlement_contributions_entitlement_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_numeric_entitlements_pool_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_product_entitlement_rules_product_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_pool_assignments_pool_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_pool_assignments_workspace_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_pool_provisions_grant_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_pool_provisions_pool_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_grants_product_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_grants_org_id;
|
|
DROP INDEX IF EXISTS entitlements.idx_resource_pools_org_id;
|
|
DROP TABLE IF EXISTS entitlements.numeric_entitlement_usage;
|
|
DROP TABLE IF EXISTS entitlements.numeric_entitlement_contributions;
|
|
DROP TABLE IF EXISTS entitlements.numeric_entitlements;
|
|
DROP TABLE IF EXISTS entitlements.product_entitlement_rules;
|
|
DROP TABLE IF EXISTS entitlements.pool_assignments;
|
|
DROP TABLE IF EXISTS entitlements.pool_provisions;
|
|
DROP TABLE IF EXISTS entitlements.grants;
|
|
DROP TABLE IF EXISTS entitlements.resource_pools;
|
|
DROP TABLE IF EXISTS entitlements.resource_keys;
|
|
|
|
-- +goose StatementEnd
|