refs #741 #742: reorganize repository by application

This commit is contained in:
devmrko
2026-08-03 10:20:36 +09:00
parent 2e44ed0b97
commit e9d50e6a32
445 changed files with 1523 additions and 1885 deletions

View File

@@ -0,0 +1,51 @@
-- ============================================================
-- database/source/mysql_setup.sql
-- Run on the REMOTE MySQL instance (mysql CLI).
--
-- Creates the customers table in the `ecommerce_poc` schema with
-- sample data spanning multiple regions (APAC / EMEA / AMER).
--
-- 동일 customer_id 가 Postgres seed 와 겹치지만, 다른 DB 이므로 무관.
-- 의도적으로 일부 row 의 region 을 다르게 줘서 두 소스가 독립적임을 보임.
--
-- 멱등성: INSERT IGNORE 로 중복 PK skip.
--
-- 호출 예시 (run.sh source 가 자동 실행):
-- mysql -h $MY_HOST -P $MY_PORT -u $MY_USER -p"$MY_PASSWORD" \
-- $MY_DB < database/source/mysql_setup.sql
-- ============================================================
SELECT '=== Creating ecommerce_poc.customers (idempotent) ===' AS status;
CREATE TABLE IF NOT EXISTS customers (
customer_id INT NOT NULL PRIMARY KEY,
full_name VARCHAR(80) NOT NULL,
email VARCHAR(120) NOT NULL,
signup_date DATE NOT NULL,
region VARCHAR(8) NOT NULL,
CONSTRAINT chk_region CHECK (region IN ('APAC','EMEA','AMER'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
SELECT '=== Seeding sample rows (12 rows across 3 regions) ===' AS status;
INSERT IGNORE INTO customers (customer_id, full_name, email, signup_date, region) VALUES
(101, 'Aki Tanaka', 'aki.tanaka@shop.example', '2024-01-20', 'APAC'),
(102, 'Bohyun Lee', 'bohyun.lee@shop.example', '2024-02-08', 'APAC'),
(103, 'Cheng Ho', 'cheng.ho@shop.example', '2024-02-25', 'APAC'),
(104, 'Devi Patel', 'devi.patel@shop.example', '2024-03-12', 'APAC'),
(105, 'Erik Johansson', 'erik.j@shop.example', '2024-03-22', 'EMEA'),
(106, 'Fatima Al-Hassan','fatima.h@shop.example', '2024-04-05', 'EMEA'),
(107, 'Greta Lindqvist', 'greta.l@shop.example', '2024-04-18', 'EMEA'),
(108, 'Hans Becker', 'hans.becker@shop.example', '2024-05-01', 'EMEA'),
(109, 'Ines Martinez', 'ines.martinez@shop.example', '2024-05-13', 'AMER'),
(110, 'Jorge Silva', 'jorge.silva@shop.example', '2024-05-25', 'AMER'),
(111, 'Kayla Anderson', 'kayla.a@shop.example', '2024-06-07', 'AMER'),
(112, 'Lucas Costa', 'lucas.costa@shop.example', '2024-06-19', 'AMER');
SELECT '=== Verifying ===' AS status;
SELECT region, COUNT(*) AS row_count
FROM customers
GROUP BY region
ORDER BY region;
SELECT '=== mysql_setup done ===' AS status;

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 database/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 database/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 ===

View File

@@ -0,0 +1,49 @@
-- ============================================================
-- database/source/postgres_setup.sql
-- Run on the REMOTE PostgreSQL instance (psql).
--
-- Creates the customers table in the `public` schema with sample
-- data spanning multiple regions (APAC / EMEA / AMER). The VPD
-- policy on ADB will filter rows from this table per logged-in user.
--
-- 멱등성: 기존 데이터가 있으면 그대로 두고 INSERT 시 ON CONFLICT 로 skip.
--
-- 호출 예시 (run.sh source 가 자동 실행):
-- PGPASSWORD=$PG_PASSWORD psql -h $PG_HOST -U $PG_USER -d $PG_DB \
-- -f database/source/postgres_setup.sql
-- ============================================================
\echo === Creating public.customers (idempotent) ===
CREATE TABLE IF NOT EXISTS public.customers (
customer_id INTEGER PRIMARY KEY,
full_name TEXT NOT NULL,
email TEXT NOT NULL,
signup_date DATE NOT NULL,
region VARCHAR(8) NOT NULL CHECK (region IN ('APAC','EMEA','AMER'))
);
\echo === Seeding sample rows (12 rows across 3 regions) ===
INSERT INTO public.customers (customer_id, full_name, email, signup_date, region) VALUES
( 1, 'Alex Kim', 'alex.kim@example.com', DATE '2024-01-15', 'APAC'),
( 2, 'Bora Park', 'bora.park@example.com', DATE '2024-02-03', 'APAC'),
( 3, 'Chen Wei', 'chen.wei@example.com', DATE '2024-02-21', 'APAC'),
( 4, 'Daisuke Sato', 'daisuke.sato@example.com', DATE '2024-03-09', 'APAC'),
( 5, 'Emma Mueller', 'emma.mueller@example.com', DATE '2024-03-18', 'EMEA'),
( 6, 'Francois Dubois', 'francois.d@example.com', DATE '2024-04-02', 'EMEA'),
( 7, 'Giulia Rossi', 'giulia.rossi@example.com', DATE '2024-04-14', 'EMEA'),
( 8, 'Henry Smith', 'henry.smith@example.com', DATE '2024-04-27', 'EMEA'),
( 9, 'Isabella Garcia', 'isabella.g@example.com', DATE '2024-05-08', 'AMER'),
(10, 'James Brown', 'james.brown@example.com', DATE '2024-05-19', 'AMER'),
(11, 'Karen Lopez', 'karen.lopez@example.com', DATE '2024-06-01', 'AMER'),
(12, 'Liam Wilson', 'liam.wilson@example.com', DATE '2024-06-13', 'AMER')
ON CONFLICT (customer_id) DO NOTHING;
\echo === Verifying ===
SELECT region, COUNT(*) AS row_count
FROM public.customers
GROUP BY region
ORDER BY region;
\echo === postgres_setup done ===