Files
tiku-backend.net/Tiku.Infrastructure/Persistence/Migrations/MigrationBuilderDatabaseObjectExtensions.cs
xiong 3383a65867
Some checks failed
ci / release-gate (push) Has been cancelled
chore(database): squash migrations into initial schema
2026-08-04 10:17:34 +08:00

159 lines
8.1 KiB
C#

using Microsoft.EntityFrameworkCore.Migrations;
namespace Tiku.Infrastructure.Persistence.Migrations;
internal static class MigrationBuilderDatabaseObjectExtensions
{
public static void EnsureAuthorizationCacheInvalidationTriggers(this MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("""
INSERT INTO authorization_scope_versions
(id, realm, tenant_id, version, created_at, updated_at)
VALUES
(gen_random_uuid(), 'platform', NULL, 1, now(), now())
ON CONFLICT DO NOTHING;
INSERT INTO authorization_scope_versions
(id, realm, tenant_id, version, created_at, updated_at)
SELECT gen_random_uuid(), 'tenant', id, 1, now(), now()
FROM tenants
ON CONFLICT DO NOTHING;
CREATE OR REPLACE FUNCTION tiku_bump_tenant_authorization_version()
RETURNS trigger
LANGUAGE plpgsql
AS $$
DECLARE
scope_tenant_id uuid;
next_version bigint;
BEGIN
IF TG_OP = 'DELETE' THEN
scope_tenant_id := OLD.tenant_id;
ELSE
scope_tenant_id := NEW.tenant_id;
END IF;
UPDATE authorization_scope_versions
SET version = version + 1, updated_at = now()
WHERE realm = 'tenant' AND tenant_id = scope_tenant_id
RETURNING version INTO next_version;
IF next_version IS NOT NULL THEN
INSERT INTO authorization_cache_invalidations
(id, target_type, tenant_id, realm, version, attempt_count, created_at, updated_at)
VALUES
(gen_random_uuid(), 'scope', scope_tenant_id, 'tenant', next_version, 0, now(), now());
END IF;
IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
RETURN NEW;
END;
$$;
CREATE OR REPLACE FUNCTION tiku_bump_platform_authorization_version()
RETURNS trigger
LANGUAGE plpgsql
AS $$
DECLARE next_version bigint;
BEGIN
UPDATE authorization_scope_versions
SET version = version + 1, updated_at = now()
WHERE realm = 'platform' AND tenant_id IS NULL
RETURNING version INTO next_version;
INSERT INTO authorization_cache_invalidations
(id, target_type, realm, version, attempt_count, created_at, updated_at)
VALUES
(gen_random_uuid(), 'scope', 'platform', next_version, 0, now(), now());
IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
RETURN NEW;
END;
$$;
CREATE OR REPLACE FUNCTION tiku_bump_all_authorization_versions()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE authorization_scope_versions SET version = version + 1, updated_at = now();
INSERT INTO authorization_cache_invalidations
(id, target_type, tenant_id, realm, version, attempt_count, created_at, updated_at)
SELECT gen_random_uuid(), 'scope', tenant_id, realm, version, 0, now(), now()
FROM authorization_scope_versions;
IF TG_OP = 'DELETE' THEN RETURN OLD; END IF;
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS trg_tenant_backend_roles_authorization_version ON tenant_backend_roles;
DROP TRIGGER IF EXISTS trg_tenant_backend_role_permissions_authorization_version ON tenant_backend_role_permissions;
DROP TRIGGER IF EXISTS trg_tenant_backend_user_roles_authorization_version ON tenant_backend_user_roles;
DROP TRIGGER IF EXISTS trg_platform_backend_roles_authorization_version ON platform_backend_roles;
DROP TRIGGER IF EXISTS trg_platform_backend_role_permissions_authorization_version ON platform_backend_role_permissions;
DROP TRIGGER IF EXISTS trg_platform_backend_user_roles_authorization_version ON platform_backend_user_roles;
DROP TRIGGER IF EXISTS trg_backend_permissions_authorization_version ON backend_permissions;
CREATE TRIGGER trg_tenant_backend_roles_authorization_version
AFTER INSERT OR UPDATE OR DELETE ON tenant_backend_roles
FOR EACH ROW EXECUTE FUNCTION tiku_bump_tenant_authorization_version();
CREATE TRIGGER trg_tenant_backend_role_permissions_authorization_version
AFTER INSERT OR UPDATE OR DELETE ON tenant_backend_role_permissions
FOR EACH ROW EXECUTE FUNCTION tiku_bump_tenant_authorization_version();
CREATE TRIGGER trg_tenant_backend_user_roles_authorization_version
AFTER INSERT OR UPDATE OR DELETE ON tenant_backend_user_roles
FOR EACH ROW EXECUTE FUNCTION tiku_bump_tenant_authorization_version();
CREATE TRIGGER trg_platform_backend_roles_authorization_version
AFTER INSERT OR UPDATE OR DELETE ON platform_backend_roles
FOR EACH ROW EXECUTE FUNCTION tiku_bump_platform_authorization_version();
CREATE TRIGGER trg_platform_backend_role_permissions_authorization_version
AFTER INSERT OR UPDATE OR DELETE ON platform_backend_role_permissions
FOR EACH ROW EXECUTE FUNCTION tiku_bump_platform_authorization_version();
CREATE TRIGGER trg_platform_backend_user_roles_authorization_version
AFTER INSERT OR UPDATE OR DELETE ON platform_backend_user_roles
FOR EACH ROW EXECUTE FUNCTION tiku_bump_platform_authorization_version();
CREATE TRIGGER trg_backend_permissions_authorization_version
AFTER INSERT OR UPDATE OR DELETE ON backend_permissions
FOR EACH STATEMENT EXECUTE FUNCTION tiku_bump_all_authorization_versions();
""");
}
public static void DropAuthorizationCacheInvalidationTriggers(this MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("""
DROP FUNCTION IF EXISTS tiku_bump_tenant_authorization_version() CASCADE;
DROP FUNCTION IF EXISTS tiku_bump_platform_authorization_version() CASCADE;
DROP FUNCTION IF EXISTS tiku_bump_all_authorization_versions() CASCADE;
""");
}
public static void EnsureQueryOptimizationIndexes(this MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("""
CREATE INDEX IF NOT EXISTS ix_scoreline_records_tenant_id_year_school_name_major_name_id
ON scoreline_records (tenant_id, year DESC, school_name, major_name, id);
CREATE INDEX IF NOT EXISTS ix_scoreline_records_field_values_jsonb_path
ON scoreline_records USING gin (field_values jsonb_path_ops);
CREATE INDEX IF NOT EXISTS ix_scoreline_records_school_name_trgm
ON scoreline_records USING gin (school_name gin_trgm_ops) WHERE school_name IS NOT NULL;
CREATE INDEX IF NOT EXISTS ix_scoreline_records_major_name_trgm
ON scoreline_records USING gin (major_name gin_trgm_ops) WHERE major_name IS NOT NULL;
CREATE INDEX IF NOT EXISTS ix_background_jobs_pending_due
ON background_jobs (run_after, created_at, id) WHERE status = 'pending';
CREATE INDEX IF NOT EXISTS ix_background_jobs_processing_lease
ON background_jobs (lock_expires_at, created_at, id) WHERE status = 'processing';
""");
}
public static void DropQueryOptimizationIndexes(this MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("""
DROP INDEX IF EXISTS ix_background_jobs_processing_lease;
DROP INDEX IF EXISTS ix_background_jobs_pending_due;
DROP INDEX IF EXISTS ix_scoreline_records_major_name_trgm;
DROP INDEX IF EXISTS ix_scoreline_records_school_name_trgm;
DROP INDEX IF EXISTS ix_scoreline_records_field_values_jsonb_path;
DROP INDEX IF EXISTS ix_scoreline_records_tenant_id_year_school_name_major_name_id;
""");
}
}