refs #729: seed RDS carrier performance demo

This commit is contained in:
devmrko
2026-07-24 14:04:42 +09:00
parent 0d9028ef13
commit 65a2b27fcc
4 changed files with 767 additions and 0 deletions

View File

@@ -0,0 +1,300 @@
-- ============================================================
-- HMM demo carrier performance source data for PostgreSQL.
--
-- Target: AWS RDS for PostgreSQL, database postgres
-- Schema: hmm_demo
-- Redmine: #729
--
-- This script is idempotent. It owns only C001..C008 and their
-- 2025-02 through 2026-07 monthly demo rows. It never stores an
-- RDS endpoint, password, or certificate path.
--
-- Example:
-- PGPASSWORD="$PG_PASSWORD" psql \
-- "host=$PG_HOST port=5432 dbname=postgres user=$PG_USER \
-- sslmode=verify-full sslrootcert=$PG_SSL_ROOT_CERT" \
-- -X -f sql/source/postgres_hmm_carrier_performance.sql
-- ============================================================
\set ON_ERROR_STOP on
\echo === HMM carrier performance demo: begin ===
BEGIN;
CREATE SCHEMA IF NOT EXISTS hmm_demo AUTHORIZATION postgres;
COMMENT ON SCHEMA hmm_demo IS
'HMM federation demonstration data. All carrier names and KPI values are fictional.';
REVOKE CREATE ON SCHEMA hmm_demo FROM PUBLIC;
CREATE TABLE IF NOT EXISTS hmm_demo.carriers (
carrier_code VARCHAR(10) PRIMARY KEY,
carrier_name VARCHAR(120) NOT NULL UNIQUE,
country_code CHAR(2) NOT NULL CHECK (country_code ~ '^[A-Z]{2}$'),
alliance_name VARCHAR(80),
service_region VARCHAR(40) NOT NULL,
performance_tier VARCHAR(10) NOT NULL
CHECK (performance_tier IN ('CORE', 'GROWTH', 'WATCH')),
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE hmm_demo.carriers IS
'Fictional carrier master data for the HMM ADB and PostgreSQL federation demo.';
COMMENT ON COLUMN hmm_demo.carriers.carrier_code IS 'Fictional carrier identifier.';
COMMENT ON COLUMN hmm_demo.carriers.performance_tier IS
'Demo portfolio tier: CORE, GROWTH, or WATCH.';
CREATE TABLE IF NOT EXISTS hmm_demo.carrier_monthly_performance (
carrier_code VARCHAR(10) NOT NULL
REFERENCES hmm_demo.carriers (carrier_code),
performance_month DATE NOT NULL
CHECK (performance_month = date_trunc('month', performance_month)::DATE),
contracted_teu INTEGER NOT NULL CHECK (contracted_teu >= 0),
shipped_teu INTEGER NOT NULL CHECK (shipped_teu >= 0),
revenue_usd NUMERIC(16,2) NOT NULL CHECK (revenue_usd >= 0),
gross_margin_usd NUMERIC(16,2) NOT NULL,
schedule_reliability_pct NUMERIC(5,2) NOT NULL
CHECK (schedule_reliability_pct BETWEEN 0 AND 100),
vessel_utilization_pct NUMERIC(5,2) NOT NULL
CHECK (vessel_utilization_pct BETWEEN 0 AND 100),
claim_rate_pct NUMERIC(5,2) NOT NULL
CHECK (claim_rate_pct BETWEEN 0 AND 100),
yoy_growth_pct NUMERIC(7,2) NOT NULL,
risk_level VARCHAR(10) NOT NULL
CHECK (risk_level IN ('GREEN', 'AMBER', 'RED')),
updated_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (carrier_code, performance_month)
);
COMMENT ON TABLE hmm_demo.carrier_monthly_performance IS
'Deterministic fictional monthly carrier KPIs for 2025-02 through 2026-07.';
COMMENT ON COLUMN hmm_demo.carrier_monthly_performance.performance_month IS
'First calendar day of the KPI month.';
COMMENT ON COLUMN hmm_demo.carrier_monthly_performance.risk_level IS
'Derived demo risk level based on margin, reliability, and claim rate.';
CREATE INDEX IF NOT EXISTS ix_carrier_perf_month
ON hmm_demo.carrier_monthly_performance (performance_month, carrier_code);
CREATE INDEX IF NOT EXISTS ix_carrier_perf_risk
ON hmm_demo.carrier_monthly_performance (risk_level, performance_month);
INSERT INTO hmm_demo.carriers (
carrier_code,
carrier_name,
country_code,
alliance_name,
service_region,
performance_tier,
active
) VALUES
('C001', 'Bluewave Maritime', 'SG', 'North Pacific Network', 'ASIA-PACIFIC', 'CORE', TRUE),
('C002', 'Pacific Star Lines', 'JP', 'East Ocean Connect', 'ASIA-PACIFIC', 'CORE', TRUE),
('C003', 'Korea Ocean Link', 'KR', 'East Ocean Connect', 'ASIA-PACIFIC', 'GROWTH', TRUE),
('C004', 'Nordic Seaways', 'DK', 'Northern Trade Group', 'EUROPE', 'CORE', TRUE),
('C005', 'Orient Bridge Shipping', 'TW', 'North Pacific Network', 'ASIA-PACIFIC', 'WATCH', TRUE),
('C006', 'Atlantic Merchant Line', 'DE', 'Atlantic Cargo Forum', 'EUROPE-AMERICAS', 'WATCH', TRUE),
('C007', 'Global Horizon Cargo', 'US', 'Atlantic Cargo Forum', 'AMERICAS', 'GROWTH', TRUE),
('C008', 'Southern Cross Marine', 'FR', 'Southern Ocean Group', 'OCEANIA', 'WATCH', TRUE)
ON CONFLICT (carrier_code) DO UPDATE SET
carrier_name = EXCLUDED.carrier_name,
country_code = EXCLUDED.country_code,
alliance_name = EXCLUDED.alliance_name,
service_region = EXCLUDED.service_region,
performance_tier = EXCLUDED.performance_tier,
active = EXCLUDED.active;
WITH month_series AS (
SELECT
month_index,
(DATE '2025-02-01' + make_interval(months => month_index))::DATE AS performance_month,
CASE month_index % 6
WHEN 0 THEN -1
WHEN 1 THEN 0
WHEN 2 THEN 1
WHEN 3 THEN 2
WHEN 4 THEN 1
ELSE -1
END::NUMERIC AS seasonal_factor
FROM generate_series(0, 17) AS series(month_index)
),
profiles (
carrier_code,
base_teu,
monthly_teu_step,
seasonal_teu,
utilization_base,
utilization_step,
revenue_per_teu,
revenue_step,
margin_pct_base,
margin_pct_step,
reliability_base,
reliability_step,
claim_rate_base,
claim_rate_step,
yoy_growth_base,
yoy_growth_step
) AS (
VALUES
('C001', 1800::NUMERIC, 25::NUMERIC, 80::NUMERIC, 91.5::NUMERIC, 0.10::NUMERIC, 1850::NUMERIC, 4::NUMERIC, 15.0::NUMERIC, 0.10::NUMERIC, 95.0::NUMERIC, 0.05::NUMERIC, 0.35::NUMERIC, -0.005::NUMERIC, 8.0::NUMERIC, 0.25::NUMERIC),
('C002', 1500::NUMERIC, 5::NUMERIC, 60::NUMERIC, 88.5::NUMERIC, 0.02::NUMERIC, 1780::NUMERIC, 3::NUMERIC, 12.0::NUMERIC, 0.00::NUMERIC, 92.5::NUMERIC, 0.00::NUMERIC, 0.45::NUMERIC, 0.005::NUMERIC, 3.0::NUMERIC, 0.05::NUMERIC),
('C003', 1050::NUMERIC, 15::NUMERIC, 50::NUMERIC, 83.0::NUMERIC, 0.20::NUMERIC, 1920::NUMERIC, 2::NUMERIC, 8.0::NUMERIC, 0.15::NUMERIC, 88.5::NUMERIC, 0.25::NUMERIC, 1.20::NUMERIC, -0.040::NUMERIC, -4.0::NUMERIC, 0.60::NUMERIC),
('C004', 1350::NUMERIC, 0::NUMERIC, 180::NUMERIC, 86.0::NUMERIC, 0.00::NUMERIC, 1810::NUMERIC, 3::NUMERIC, 10.0::NUMERIC, 0.00::NUMERIC, 93.0::NUMERIC, 0.00::NUMERIC, 0.55::NUMERIC, 0.010::NUMERIC, 1.0::NUMERIC, 0.10::NUMERIC),
('C005', 1300::NUMERIC, -20::NUMERIC, 70::NUMERIC, 88.0::NUMERIC, -0.10::NUMERIC, 1850::NUMERIC, -2::NUMERIC, 8.0::NUMERIC, -0.20::NUMERIC, 91.0::NUMERIC, -0.15::NUMERIC, 0.80::NUMERIC, 0.030::NUMERIC, -2.0::NUMERIC, -0.50::NUMERIC),
('C006', 1250::NUMERIC, -10::NUMERIC, 90::NUMERIC, 84.0::NUMERIC, -0.12::NUMERIC, 1800::NUMERIC, -3::NUMERIC, 6.0::NUMERIC, -0.15::NUMERIC, 87.0::NUMERIC, -0.25::NUMERIC, 1.50::NUMERIC, 0.050::NUMERIC, -5.0::NUMERIC, -0.70::NUMERIC),
('C007', 800::NUMERIC, 30::NUMERIC, 100::NUMERIC, 82.0::NUMERIC, 0.25::NUMERIC, 2050::NUMERIC, 5::NUMERIC, 9.0::NUMERIC, 0.18::NUMERIC, 90.5::NUMERIC, 0.15::NUMERIC, 0.70::NUMERIC, -0.020::NUMERIC, 12.0::NUMERIC, 0.50::NUMERIC),
('C008', 1150::NUMERIC, -25::NUMERIC, 60::NUMERIC, 80.0::NUMERIC, -0.15::NUMERIC, 1750::NUMERIC, -4::NUMERIC, 3.0::NUMERIC, -0.20::NUMERIC, 85.0::NUMERIC, -0.30::NUMERIC, 2.00::NUMERIC, 0.080::NUMERIC, -8.0::NUMERIC, -0.80::NUMERIC)
),
base_metrics AS (
SELECT
p.carrier_code,
m.performance_month,
ROUND(
p.base_teu
+ p.monthly_teu_step * m.month_index
+ p.seasonal_teu * m.seasonal_factor
)::INTEGER AS contracted_teu,
LEAST(
98.0::NUMERIC,
GREATEST(
60.0::NUMERIC,
p.utilization_base
+ p.utilization_step * m.month_index
+ m.seasonal_factor * 0.50
)
) AS utilization_pct,
p.revenue_per_teu + p.revenue_step * m.month_index AS unit_revenue,
p.margin_pct_base + p.margin_pct_step * m.month_index AS margin_pct,
LEAST(
99.5::NUMERIC,
GREATEST(
60.0::NUMERIC,
p.reliability_base
+ p.reliability_step * m.month_index
+ m.seasonal_factor * 0.20
)
) AS reliability_pct,
LEAST(
100.0::NUMERIC,
GREATEST(
0.0::NUMERIC,
p.claim_rate_base + p.claim_rate_step * m.month_index
)
) AS claim_pct,
p.yoy_growth_base + p.yoy_growth_step * m.month_index AS yoy_pct
FROM profiles p
CROSS JOIN month_series m
),
volume_metrics AS (
SELECT
b.*,
ROUND(b.contracted_teu * b.utilization_pct / 100.0)::INTEGER AS shipped_teu
FROM base_metrics b
),
financial_metrics AS (
SELECT
v.*,
ROUND(v.shipped_teu * v.unit_revenue, 2) AS revenue_usd
FROM volume_metrics v
),
final_metrics AS (
SELECT
f.*,
ROUND(f.revenue_usd * f.margin_pct / 100.0, 2) AS gross_margin_usd
FROM financial_metrics f
)
INSERT INTO hmm_demo.carrier_monthly_performance (
carrier_code,
performance_month,
contracted_teu,
shipped_teu,
revenue_usd,
gross_margin_usd,
schedule_reliability_pct,
vessel_utilization_pct,
claim_rate_pct,
yoy_growth_pct,
risk_level,
updated_at
)
SELECT
f.carrier_code,
f.performance_month,
f.contracted_teu,
f.shipped_teu,
f.revenue_usd,
f.gross_margin_usd,
ROUND(f.reliability_pct, 2),
ROUND(f.utilization_pct, 2),
ROUND(f.claim_pct, 2),
ROUND(f.yoy_pct, 2),
CASE
WHEN f.gross_margin_usd < 0
OR f.reliability_pct < 87
OR f.claim_pct >= 2 THEN 'RED'
WHEN f.margin_pct < 7
OR f.reliability_pct < 92
OR f.claim_pct >= 1 THEN 'AMBER'
ELSE 'GREEN'
END,
TIMESTAMPTZ '2026-07-24 00:00:00+00'
FROM final_metrics f
ON CONFLICT (carrier_code, performance_month) DO UPDATE SET
contracted_teu = EXCLUDED.contracted_teu,
shipped_teu = EXCLUDED.shipped_teu,
revenue_usd = EXCLUDED.revenue_usd,
gross_margin_usd = EXCLUDED.gross_margin_usd,
schedule_reliability_pct = EXCLUDED.schedule_reliability_pct,
vessel_utilization_pct = EXCLUDED.vessel_utilization_pct,
claim_rate_pct = EXCLUDED.claim_rate_pct,
yoy_growth_pct = EXCLUDED.yoy_growth_pct,
risk_level = EXCLUDED.risk_level,
updated_at = EXCLUDED.updated_at;
CREATE OR REPLACE VIEW hmm_demo.carrier_performance_latest_v AS
SELECT
c.carrier_code,
c.carrier_name,
c.country_code,
c.alliance_name,
c.service_region,
c.performance_tier,
p.performance_month,
p.contracted_teu,
p.shipped_teu,
p.revenue_usd,
p.gross_margin_usd,
p.schedule_reliability_pct,
p.vessel_utilization_pct,
p.claim_rate_pct,
p.yoy_growth_pct,
p.risk_level
FROM hmm_demo.carriers c
JOIN hmm_demo.carrier_monthly_performance p
ON p.carrier_code = c.carrier_code
WHERE p.performance_month = (
SELECT MAX(latest.performance_month)
FROM hmm_demo.carrier_monthly_performance latest
);
COMMENT ON VIEW hmm_demo.carrier_performance_latest_v IS
'Latest fictional monthly KPI for each active demo carrier.';
DO $role$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'hmm_federation_reader') THEN
CREATE ROLE hmm_federation_reader NOLOGIN;
END IF;
END
$role$;
GRANT USAGE ON SCHEMA hmm_demo TO hmm_federation_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA hmm_demo TO hmm_federation_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA hmm_demo
GRANT SELECT ON TABLES TO hmm_federation_reader;
COMMIT;
\echo === HMM carrier performance demo: complete ===
\echo Next: run sql/source/postgres_hmm_carrier_performance_verify.sql

View File

@@ -0,0 +1,324 @@
-- ============================================================
-- Verification for HMM demo carrier performance data.
-- Redmine: #729
-- ============================================================
\set ON_ERROR_STOP on
\pset pager off
\echo === HMM carrier performance demo: verification begin ===
DO $verify$
DECLARE
carrier_count INTEGER;
performance_count INTEGER;
invalid_month_count INTEGER;
latest_month DATE;
earliest_month DATE;
wrong_series_count INTEGER;
latest_risk_count INTEGER;
wrong_trend_count INTEGER;
reader_can_login BOOLEAN;
BEGIN
SELECT COUNT(*)
INTO carrier_count
FROM hmm_demo.carriers
WHERE carrier_code BETWEEN 'C001' AND 'C008';
SELECT COUNT(*), MIN(performance_month), MAX(performance_month)
INTO performance_count, earliest_month, latest_month
FROM hmm_demo.carrier_monthly_performance
WHERE carrier_code BETWEEN 'C001' AND 'C008'
AND performance_month BETWEEN DATE '2025-02-01' AND DATE '2026-07-01';
SELECT COUNT(*)
INTO invalid_month_count
FROM hmm_demo.carrier_monthly_performance
WHERE performance_month <> date_trunc('month', performance_month)::DATE;
SELECT COUNT(*)
INTO wrong_series_count
FROM (
SELECT carrier_code, COUNT(*) AS month_count
FROM hmm_demo.carrier_monthly_performance
WHERE carrier_code BETWEEN 'C001' AND 'C008'
AND performance_month BETWEEN DATE '2025-02-01' AND DATE '2026-07-01'
GROUP BY carrier_code
HAVING COUNT(*) <> 18
) wrong_series;
SELECT COUNT(DISTINCT risk_level)
INTO latest_risk_count
FROM hmm_demo.carrier_performance_latest_v;
WITH boundaries AS (
SELECT
carrier_code,
MAX(revenue_usd) FILTER (WHERE performance_month = DATE '2025-02-01')
AS first_revenue,
MAX(revenue_usd) FILTER (WHERE performance_month = DATE '2026-07-01')
AS latest_revenue
FROM hmm_demo.carrier_monthly_performance
WHERE carrier_code BETWEEN 'C001' AND 'C008'
GROUP BY carrier_code
)
SELECT COUNT(*)
INTO wrong_trend_count
FROM boundaries
WHERE (carrier_code IN ('C001', 'C002', 'C003', 'C004', 'C007')
AND latest_revenue <= first_revenue)
OR (carrier_code IN ('C005', 'C006', 'C008')
AND latest_revenue >= first_revenue);
SELECT rolcanlogin
INTO reader_can_login
FROM pg_roles
WHERE rolname = 'hmm_federation_reader';
IF carrier_count <> 8 THEN
RAISE EXCEPTION 'Expected 8 carriers, found %', carrier_count;
END IF;
IF performance_count <> 144 THEN
RAISE EXCEPTION 'Expected 144 monthly performance rows, found %', performance_count;
END IF;
IF earliest_month <> DATE '2025-02-01' OR latest_month <> DATE '2026-07-01' THEN
RAISE EXCEPTION 'Unexpected period: % through %', earliest_month, latest_month;
END IF;
IF invalid_month_count <> 0 OR wrong_series_count <> 0 THEN
RAISE EXCEPTION
'Invalid monthly series: invalid dates %, wrong carrier series %',
invalid_month_count,
wrong_series_count;
END IF;
IF latest_risk_count <> 3 THEN
RAISE EXCEPTION 'Expected GREEN, AMBER, RED latest risks, found % levels',
latest_risk_count;
END IF;
IF wrong_trend_count <> 0 THEN
RAISE EXCEPTION 'Unexpected first-to-latest trend for % carriers', wrong_trend_count;
END IF;
IF reader_can_login IS DISTINCT FROM FALSE THEN
RAISE EXCEPTION 'hmm_federation_reader must remain a NOLOGIN role';
END IF;
IF NOT has_schema_privilege('hmm_federation_reader', 'hmm_demo', 'USAGE') THEN
RAISE EXCEPTION 'hmm_federation_reader lacks schema USAGE';
END IF;
IF has_schema_privilege('hmm_federation_reader', 'hmm_demo', 'CREATE') THEN
RAISE EXCEPTION 'hmm_federation_reader unexpectedly has schema CREATE';
END IF;
IF NOT has_table_privilege(
'hmm_federation_reader',
'hmm_demo.carrier_monthly_performance',
'SELECT'
) THEN
RAISE EXCEPTION 'hmm_federation_reader lacks SELECT';
END IF;
IF has_table_privilege(
'hmm_federation_reader',
'hmm_demo.carrier_monthly_performance',
'INSERT'
) THEN
RAISE EXCEPTION 'hmm_federation_reader unexpectedly has INSERT';
END IF;
END
$verify$;
DO $constraint_tests$
BEGIN
BEGIN
INSERT INTO hmm_demo.carrier_monthly_performance (
carrier_code,
performance_month,
contracted_teu,
shipped_teu,
revenue_usd,
gross_margin_usd,
schedule_reliability_pct,
vessel_utilization_pct,
claim_rate_pct,
yoy_growth_pct,
risk_level,
updated_at
) VALUES (
'C001',
DATE '2026-07-02',
1,
1,
1,
1,
90,
90,
1,
1,
'GREEN',
CURRENT_TIMESTAMP
);
RAISE EXCEPTION 'Month-first-day constraint did not reject invalid input';
EXCEPTION
WHEN check_violation THEN
NULL;
END;
BEGIN
INSERT INTO hmm_demo.carrier_monthly_performance (
carrier_code,
performance_month,
contracted_teu,
shipped_teu,
revenue_usd,
gross_margin_usd,
schedule_reliability_pct,
vessel_utilization_pct,
claim_rate_pct,
yoy_growth_pct,
risk_level,
updated_at
) VALUES (
'UNKNOWN',
DATE '2026-08-01',
1,
1,
1,
1,
90,
90,
1,
1,
'GREEN',
CURRENT_TIMESTAMP
);
RAISE EXCEPTION 'Foreign key constraint did not reject unknown carrier';
EXCEPTION
WHEN foreign_key_violation THEN
NULL;
END;
BEGIN
INSERT INTO hmm_demo.carrier_monthly_performance (
carrier_code,
performance_month,
contracted_teu,
shipped_teu,
revenue_usd,
gross_margin_usd,
schedule_reliability_pct,
vessel_utilization_pct,
claim_rate_pct,
yoy_growth_pct,
risk_level,
updated_at
) VALUES (
'C001',
DATE '2026-08-01',
1,
1,
1,
1,
101,
90,
1,
1,
'GREEN',
CURRENT_TIMESTAMP
);
RAISE EXCEPTION 'Percentage constraint did not reject invalid input';
EXCEPTION
WHEN check_violation THEN
NULL;
END;
END
$constraint_tests$;
\echo --- Row counts and period ---
SELECT
COUNT(DISTINCT carrier_code) AS carrier_count,
COUNT(*) AS monthly_row_count,
MIN(performance_month) AS first_month,
MAX(performance_month) AS latest_month
FROM hmm_demo.carrier_monthly_performance
WHERE carrier_code BETWEEN 'C001' AND 'C008'
AND performance_month BETWEEN DATE '2025-02-01' AND DATE '2026-07-01';
\echo --- Per-carrier series ---
SELECT
c.carrier_code,
c.carrier_name,
COUNT(p.*) AS month_count,
MIN(p.performance_month) AS first_month,
MAX(p.performance_month) AS latest_month
FROM hmm_demo.carriers c
JOIN hmm_demo.carrier_monthly_performance p
ON p.carrier_code = c.carrier_code
WHERE c.carrier_code BETWEEN 'C001' AND 'C008'
GROUP BY c.carrier_code, c.carrier_name
ORDER BY c.carrier_code;
\echo --- Latest KPI and risk distribution ---
SELECT
carrier_code,
carrier_name,
performance_month,
shipped_teu,
revenue_usd,
gross_margin_usd,
schedule_reliability_pct,
claim_rate_pct,
risk_level
FROM hmm_demo.carrier_performance_latest_v
ORDER BY carrier_code;
SELECT risk_level, COUNT(*) AS carrier_count
FROM hmm_demo.carrier_performance_latest_v
GROUP BY risk_level
ORDER BY risk_level;
\echo --- First-to-latest revenue trend ---
WITH boundaries AS (
SELECT
carrier_code,
MAX(revenue_usd) FILTER (WHERE performance_month = DATE '2025-02-01') AS first_revenue,
MAX(revenue_usd) FILTER (WHERE performance_month = DATE '2026-07-01') AS latest_revenue
FROM hmm_demo.carrier_monthly_performance
WHERE carrier_code BETWEEN 'C001' AND 'C008'
GROUP BY carrier_code
)
SELECT
carrier_code,
first_revenue,
latest_revenue,
CASE
WHEN latest_revenue > first_revenue THEN 'UP'
WHEN latest_revenue < first_revenue THEN 'DOWN'
ELSE 'FLAT'
END AS trend
FROM boundaries
ORDER BY carrier_code;
\echo --- TLS session ---
SELECT
ssl,
version AS tls_version,
cipher,
bits
FROM pg_stat_ssl
WHERE pid = pg_backend_pid();
\echo --- Read-only role privileges ---
SELECT
(SELECT NOT rolcanlogin
FROM pg_roles
WHERE rolname = 'hmm_federation_reader') AS no_login,
has_schema_privilege('hmm_federation_reader', 'hmm_demo', 'USAGE') AS schema_usage,
has_schema_privilege('hmm_federation_reader', 'hmm_demo', 'CREATE') AS schema_create,
has_table_privilege(
'hmm_federation_reader',
'hmm_demo.carrier_monthly_performance',
'SELECT'
) AS can_select,
has_table_privilege(
'hmm_federation_reader',
'hmm_demo.carrier_monthly_performance',
'INSERT'
) AS can_insert;
\echo === HMM carrier performance demo: verification complete ===