-- ============================================================================ -- LightRAG Multi-Tenant PostgreSQL Schema Initialization -- Version: 3.0.0 -- Date: December 4, 2025 -- -- This script initializes the PostgreSQL database with multi-tenant support. -- It is automatically executed when PostgreSQL container starts. -- -- Architecture: -- • LIGHTRAG_* tables: Core storage with workspace-based multi-tenancy -- • tenants/knowledge_bases: Metadata registry for API layer -- • Generated columns: Auto-extract tenant_id/kb_id from workspace -- • Auto-sync triggers: Auto-register tenants/KBs on data insert -- -- Features: -- • pgvector support for embeddings -- • Automatic indexes for performance -- • RBAC with user_tenant_memberships -- • Sample data for testing -- ============================================================================ -- ============================================================================ -- Extensions -- ============================================================================ CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "vector"; CREATE EXTENSION IF NOT EXISTS "age"; -- ============================================================================ -- Schema Migrations Tracking Table -- ============================================================================ CREATE TABLE IF NOT EXISTS schema_migrations ( version VARCHAR(50) PRIMARY KEY, description TEXT, status VARCHAR(20) DEFAULT 'applied', applied_at TIMESTAMP DEFAULT NOW() ); -- ============================================================================ -- Tenants Table -- -- Stores tenant information for multi-tenant system -- Each tenant represents an organization, customer, or project -- ============================================================================ CREATE TABLE IF NOT EXISTS tenants ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id VARCHAR(255) UNIQUE NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, metadata JSONB DEFAULT '{}'::jsonb, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_tenants_id ON tenants(tenant_id); -- ============================================================================ -- Knowledge Bases Table -- -- Stores knowledge base metadata for each tenant -- Each tenant can have multiple KBs (prod, dev, staging, etc.) -- ============================================================================ CREATE TABLE IF NOT EXISTS knowledge_bases ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id VARCHAR(255) NOT NULL, kb_id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE, CONSTRAINT uk_kb_tenant_kb UNIQUE(tenant_id, kb_id) ); CREATE INDEX IF NOT EXISTS idx_kbs_tenant_kb ON knowledge_bases(tenant_id, kb_id); CREATE INDEX IF NOT EXISTS idx_kbs_tenant ON knowledge_bases(tenant_id); -- ============================================================================ -- LIGHTRAG Core Storage Tables -- -- These are the PRODUCTION storage tables used by postgres_impl.py. -- They use workspace-based multi-tenancy: workspace = "{tenant_id}:{kb_id}" -- Generated columns auto-extract tenant_id and kb_id for queries. -- ============================================================================ -- Document Status Table (tracks document processing) CREATE TABLE IF NOT EXISTS LIGHTRAG_DOC_STATUS ( workspace VARCHAR(255) NOT NULL, id VARCHAR(255) NOT NULL, content_summary VARCHAR(255) NULL, content_length INT NULL, chunks_count INT NULL, status VARCHAR(64) NULL, file_path TEXT NULL, chunks_list JSONB NULL DEFAULT '[]'::jsonb, track_id VARCHAR(255) NULL, metadata JSONB NULL DEFAULT '{}'::jsonb, error_msg TEXT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_DOC_STATUS_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_status_workspace ON LIGHTRAG_DOC_STATUS(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_status_status ON LIGHTRAG_DOC_STATUS(workspace, status); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_status_tenant ON LIGHTRAG_DOC_STATUS(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_status_tenant_kb ON LIGHTRAG_DOC_STATUS(tenant_id, kb_id); -- Full Documents Table (stores complete document content) CREATE TABLE IF NOT EXISTS LIGHTRAG_DOC_FULL ( id VARCHAR(255), workspace VARCHAR(255), doc_name VARCHAR(1024), content TEXT, meta JSONB, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_DOC_FULL_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_full_workspace ON LIGHTRAG_DOC_FULL(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_full_tenant ON LIGHTRAG_DOC_FULL(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_full_tenant_kb ON LIGHTRAG_DOC_FULL(tenant_id, kb_id); -- Document Chunks Table (stores chunked document content) CREATE TABLE IF NOT EXISTS LIGHTRAG_DOC_CHUNKS ( id VARCHAR(255), workspace VARCHAR(255), full_doc_id VARCHAR(256), chunk_order_index INTEGER, tokens INTEGER, content TEXT, file_path TEXT NULL, llm_cache_list JSONB NULL DEFAULT '[]'::jsonb, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_DOC_CHUNKS_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_chunks_workspace ON LIGHTRAG_DOC_CHUNKS(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_chunks_full_doc_id ON LIGHTRAG_DOC_CHUNKS(workspace, full_doc_id); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_chunks_tenant ON LIGHTRAG_DOC_CHUNKS(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_doc_chunks_tenant_kb ON LIGHTRAG_DOC_CHUNKS(tenant_id, kb_id); -- Vector DB Chunks Table (stores vector embeddings for chunks) CREATE TABLE IF NOT EXISTS LIGHTRAG_VDB_CHUNKS ( id VARCHAR(255), workspace VARCHAR(255), full_doc_id VARCHAR(256), chunk_order_index INTEGER, tokens INTEGER, content TEXT, content_vector VECTOR(1536), file_path TEXT NULL, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_VDB_CHUNKS_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_chunks_workspace ON LIGHTRAG_VDB_CHUNKS(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_chunks_vector ON LIGHTRAG_VDB_CHUNKS USING ivfflat (content_vector vector_cosine_ops); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_chunks_tenant ON LIGHTRAG_VDB_CHUNKS(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_chunks_tenant_kb ON LIGHTRAG_VDB_CHUNKS(tenant_id, kb_id); -- Vector DB Entities Table (stores knowledge graph entities with embeddings) CREATE TABLE IF NOT EXISTS LIGHTRAG_VDB_ENTITY ( id VARCHAR(255), workspace VARCHAR(255), entity_name VARCHAR(512), content TEXT, content_vector VECTOR(1536), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, chunk_ids VARCHAR(255)[] NULL, file_path TEXT NULL, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_VDB_ENTITY_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_entity_workspace ON LIGHTRAG_VDB_ENTITY(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_entity_id ON LIGHTRAG_VDB_ENTITY(id); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_entity_vector ON LIGHTRAG_VDB_ENTITY USING ivfflat (content_vector vector_cosine_ops); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_entity_hnsw_cosine ON LIGHTRAG_VDB_ENTITY USING hnsw (content_vector vector_cosine_ops) WITH (m = 16, ef_construction = 64); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_entity_tenant ON LIGHTRAG_VDB_ENTITY(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_entity_tenant_kb ON LIGHTRAG_VDB_ENTITY(tenant_id, kb_id); -- Vector DB Relations Table (stores knowledge graph relationships with embeddings) CREATE TABLE IF NOT EXISTS LIGHTRAG_VDB_RELATION ( id VARCHAR(255), workspace VARCHAR(255), source_id VARCHAR(512), target_id VARCHAR(512), content TEXT, content_vector VECTOR(1536), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, chunk_ids VARCHAR(255)[] NULL, file_path TEXT NULL, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_VDB_RELATION_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_relation_workspace ON LIGHTRAG_VDB_RELATION(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_relation_vector ON LIGHTRAG_VDB_RELATION USING ivfflat (content_vector vector_cosine_ops); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_relation_tenant ON LIGHTRAG_VDB_RELATION(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_vdb_relation_tenant_kb ON LIGHTRAG_VDB_RELATION(tenant_id, kb_id); -- LLM Cache Table (caches LLM responses) CREATE TABLE IF NOT EXISTS LIGHTRAG_LLM_CACHE ( workspace VARCHAR(255) NOT NULL, id VARCHAR(255) NOT NULL, original_prompt TEXT, return_value TEXT, chunk_id VARCHAR(255) NULL, cache_type VARCHAR(32), queryparam JSONB NULL, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_LLM_CACHE_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_llm_cache_workspace ON LIGHTRAG_LLM_CACHE(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_llm_cache_chunk_id ON LIGHTRAG_LLM_CACHE(workspace, chunk_id); CREATE INDEX IF NOT EXISTS idx_lightrag_llm_cache_tenant ON LIGHTRAG_LLM_CACHE(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_llm_cache_tenant_kb ON LIGHTRAG_LLM_CACHE(tenant_id, kb_id); -- Full Entities Table (aggregated entity data) CREATE TABLE IF NOT EXISTS LIGHTRAG_FULL_ENTITIES ( id VARCHAR(255), workspace VARCHAR(255), entity_names JSONB, count INTEGER, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_FULL_ENTITIES_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_full_entities_workspace ON LIGHTRAG_FULL_ENTITIES(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_full_entities_tenant ON LIGHTRAG_FULL_ENTITIES(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_full_entities_tenant_kb ON LIGHTRAG_FULL_ENTITIES(tenant_id, kb_id); -- Full Relations Table (aggregated relation data) CREATE TABLE IF NOT EXISTS LIGHTRAG_FULL_RELATIONS ( id VARCHAR(255), workspace VARCHAR(255), relation_pairs JSONB, count INTEGER, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Generated columns for multi-tenant queries tenant_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 1) ELSE workspace END ) STORED, kb_id VARCHAR(255) GENERATED ALWAYS AS ( CASE WHEN workspace LIKE '%:%' THEN SPLIT_PART(workspace, ':', 2) ELSE 'default' END ) STORED, CONSTRAINT LIGHTRAG_FULL_RELATIONS_PK PRIMARY KEY (workspace, id) ); CREATE INDEX IF NOT EXISTS idx_lightrag_full_relations_workspace ON LIGHTRAG_FULL_RELATIONS(workspace); CREATE INDEX IF NOT EXISTS idx_lightrag_full_relations_tenant ON LIGHTRAG_FULL_RELATIONS(tenant_id); CREATE INDEX IF NOT EXISTS idx_lightrag_full_relations_tenant_kb ON LIGHTRAG_FULL_RELATIONS(tenant_id, kb_id); -- ============================================================================ -- Auto-Sync Trigger Function -- -- Automatically registers tenants and knowledge bases when data is inserted -- into any LIGHTRAG_* table with a workspace column -- ============================================================================ CREATE OR REPLACE FUNCTION sync_tenant_from_workspace() RETURNS TRIGGER AS $$ DECLARE v_tenant_id VARCHAR(255); v_kb_id VARCHAR(255); BEGIN -- Extract tenant_id and kb_id from workspace v_tenant_id := SPLIT_PART(NEW.workspace, ':', 1); v_kb_id := SPLIT_PART(NEW.workspace, ':', 2); -- Skip if workspace doesn't contain colon (old format) IF v_kb_id = '' OR v_kb_id IS NULL THEN RETURN NEW; END IF; -- Insert tenant if not exists INSERT INTO tenants (tenant_id, name, created_at, updated_at) VALUES (v_tenant_id, v_tenant_id, NOW(), NOW()) ON CONFLICT (tenant_id) DO NOTHING; -- Insert knowledge base if not exists INSERT INTO knowledge_bases (tenant_id, kb_id, name, created_at, updated_at) VALUES (v_tenant_id, v_kb_id, v_kb_id, NOW(), NOW()) ON CONFLICT (tenant_id, kb_id) DO NOTHING; RETURN NEW; END; $$ LANGUAGE plpgsql; -- ============================================================================ -- Auto-Sync Triggers for all LIGHTRAG_* tables -- ============================================================================ CREATE TRIGGER trg_sync_tenant_doc_full BEFORE INSERT OR UPDATE ON LIGHTRAG_DOC_FULL FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); CREATE TRIGGER trg_sync_tenant_doc_chunks BEFORE INSERT OR UPDATE ON LIGHTRAG_DOC_CHUNKS FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); CREATE TRIGGER trg_sync_tenant_doc_status BEFORE INSERT OR UPDATE ON LIGHTRAG_DOC_STATUS FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); CREATE TRIGGER trg_sync_tenant_vdb_chunks BEFORE INSERT OR UPDATE ON LIGHTRAG_VDB_CHUNKS FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); CREATE TRIGGER trg_sync_tenant_vdb_entity BEFORE INSERT OR UPDATE ON LIGHTRAG_VDB_ENTITY FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); CREATE TRIGGER trg_sync_tenant_vdb_relation BEFORE INSERT OR UPDATE ON LIGHTRAG_VDB_RELATION FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); CREATE TRIGGER trg_sync_tenant_full_entities BEFORE INSERT OR UPDATE ON LIGHTRAG_FULL_ENTITIES FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); CREATE TRIGGER trg_sync_tenant_full_relations BEFORE INSERT OR UPDATE ON LIGHTRAG_FULL_RELATIONS FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); CREATE TRIGGER trg_sync_tenant_llm_cache BEFORE INSERT OR UPDATE ON LIGHTRAG_LLM_CACHE FOR EACH ROW EXECUTE FUNCTION sync_tenant_from_workspace(); -- ============================================================================ -- User Tenant Memberships Table (RBAC) -- ============================================================================ CREATE TABLE IF NOT EXISTS user_tenant_memberships ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id VARCHAR(255) NOT NULL, tenant_id VARCHAR(255) NOT NULL, role VARCHAR(50) NOT NULL DEFAULT 'viewer', permissions JSONB DEFAULT '[]'::jsonb, created_by VARCHAR(255), created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE, CONSTRAINT uk_user_tenant UNIQUE(user_id, tenant_id), CONSTRAINT chk_role CHECK (role IN ('owner', 'admin', 'editor', 'viewer')) ); CREATE INDEX IF NOT EXISTS idx_user_memberships_user ON user_tenant_memberships(user_id); CREATE INDEX IF NOT EXISTS idx_user_memberships_tenant ON user_tenant_memberships(tenant_id); -- ============================================================================ -- Tenant Access Helper Function -- ============================================================================ CREATE OR REPLACE FUNCTION has_tenant_access( p_user_id VARCHAR(255), p_tenant_id VARCHAR(255), p_required_role VARCHAR(50) DEFAULT 'viewer' ) RETURNS BOOLEAN AS $$ DECLARE v_user_role VARCHAR(50); v_role_hierarchy INTEGER; v_required_hierarchy INTEGER; BEGIN -- Get user's role for the tenant SELECT role INTO v_user_role FROM user_tenant_memberships WHERE user_id = p_user_id AND tenant_id = p_tenant_id; -- If no membership found, check if tenant is public IF v_user_role IS NULL THEN RETURN EXISTS ( SELECT 1 FROM tenants WHERE tenant_id = p_tenant_id AND (metadata->>'is_public')::boolean = true ); END IF; -- Role hierarchy: owner(4) > admin(3) > editor(2) > viewer(1) v_role_hierarchy := CASE v_user_role WHEN 'owner' THEN 4 WHEN 'admin' THEN 3 WHEN 'editor' THEN 2 WHEN 'viewer' THEN 1 ELSE 0 END; v_required_hierarchy := CASE p_required_role WHEN 'owner' THEN 4 WHEN 'admin' THEN 3 WHEN 'editor' THEN 2 WHEN 'viewer' THEN 1 ELSE 0 END; RETURN v_role_hierarchy >= v_required_hierarchy; END; $$ LANGUAGE plpgsql; -- ============================================================================ -- Sample Data for Testing Multi-Tenant Features -- ============================================================================ -- Insert sample tenants INSERT INTO tenants (tenant_id, name, description) VALUES ('acme-corp', 'Acme Corporation', 'Enterprise customer - production deployment'), ('techstart', 'TechStart Inc', 'Startup customer - evaluation environment') ON CONFLICT (tenant_id) DO NOTHING; -- Insert sample knowledge bases for Acme Corp INSERT INTO knowledge_bases (tenant_id, kb_id, name, description) VALUES ('acme-corp', 'kb-prod', 'Production KB', 'Production knowledge base for Acme Corp'), ('acme-corp', 'kb-dev', 'Development KB', 'Development knowledge base for Acme Corp') ON CONFLICT (tenant_id, kb_id) DO NOTHING; -- Insert sample knowledge bases for TechStart INSERT INTO knowledge_bases (tenant_id, kb_id, name, description) VALUES ('techstart', 'kb-main', 'Main KB', 'Main knowledge base for TechStart'), ('techstart', 'kb-backup', 'Backup KB', 'Backup knowledge base for TechStart') ON CONFLICT (tenant_id, kb_id) DO NOTHING; -- ============================================================================ -- Record Migrations -- ============================================================================ INSERT INTO schema_migrations (version, description, status, applied_at) VALUES ('1.0.0', 'Initial schema with LIGHTRAG_* tables', 'applied', NOW()), ('2.0.0', 'Add generated columns (tenant_id, kb_id) and indexes', 'applied', NOW()), ('2.1.0', 'Add auto-sync trigger for tenant/KB registration', 'applied', NOW()), ('2.2.0', 'Create user_tenant_memberships table for RBAC', 'applied', NOW()), ('3.0.0', 'Remove unused tables, clean schema', 'applied', NOW()) ON CONFLICT (version) DO UPDATE SET status = 'applied', applied_at = NOW(); -- ============================================================================ -- Grant Permissions -- ============================================================================ GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO lightrag; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO lightrag; GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO lightrag; GRANT CREATE ON SCHEMA public TO lightrag; -- ============================================================================ -- Schema Statistics -- ============================================================================ ANALYZE tenants; ANALYZE knowledge_bases; ANALYZE user_tenant_memberships; -- ============================================================================ -- Initialization Complete -- ============================================================================