367 lines
11 KiB
MySQL
367 lines
11 KiB
MySQL
|
|
-- =============================================================
|
||
|
|
-- TRACKSOLID DWH SETUP & PERMISSIONS
|
||
|
|
-- Target Database: tracksolid_dwh
|
||
|
|
-- =============================================================
|
||
|
|
|
||
|
|
-- 1. EXTENSIONS
|
||
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||
|
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||
|
|
CREATE EXTENSION IF NOT EXISTS btree_gist;
|
||
|
|
CREATE EXTENSION IF NOT EXISTS postgis; -- REQUIRED for geometry(Point,4326) columns
|
||
|
|
|
||
|
|
-- 2. ROLE CREATION (Idempotent)
|
||
|
|
-- SECURITY: Passwords below are placeholders. Before applying this file:
|
||
|
|
-- 1. Generate two strong secrets (e.g. `openssl rand -hex 24`)
|
||
|
|
-- 2. Replace both CHANGE_ME_BEFORE_APPLY tokens in-session (do NOT commit real values)
|
||
|
|
-- 3. Store the generated secrets in the n8n / Grafana credential stores only
|
||
|
|
-- Rotation: `ALTER ROLE <role> PASSWORD '<new secret>'` as a superuser.
|
||
|
|
DO $$
|
||
|
|
BEGIN
|
||
|
|
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'dwh_owner') THEN
|
||
|
|
CREATE ROLE dwh_owner WITH LOGIN PASSWORD 'CHANGE_ME_BEFORE_APPLY';
|
||
|
|
END IF;
|
||
|
|
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'grafana_ro') THEN
|
||
|
|
CREATE ROLE grafana_ro WITH LOGIN PASSWORD 'CHANGE_ME_BEFORE_APPLY';
|
||
|
|
END IF;
|
||
|
|
END$$;
|
||
|
|
|
||
|
|
-- Grant database connection
|
||
|
|
GRANT CONNECT ON DATABASE tracksolid_dwh TO dwh_owner;
|
||
|
|
GRANT CONNECT ON DATABASE tracksolid_dwh TO grafana_ro;
|
||
|
|
|
||
|
|
-- 3. SCHEMAS
|
||
|
|
CREATE SCHEMA IF NOT EXISTS bronze AUTHORIZATION dwh_owner;
|
||
|
|
CREATE SCHEMA IF NOT EXISTS silver AUTHORIZATION dwh_owner;
|
||
|
|
CREATE SCHEMA IF NOT EXISTS gold AUTHORIZATION dwh_owner;
|
||
|
|
|
||
|
|
ALTER DATABASE tracksolid_dwh SET search_path TO bronze, silver, gold, public;
|
||
|
|
|
||
|
|
-- 4. PERMISSIONS & DEFAULT PRIVILEGES (Critical for Security & Automation)
|
||
|
|
-- Schema access
|
||
|
|
GRANT USAGE, CREATE ON SCHEMA bronze TO dwh_owner;
|
||
|
|
GRANT USAGE, CREATE ON SCHEMA silver TO dwh_owner;
|
||
|
|
GRANT USAGE, CREATE ON SCHEMA gold TO dwh_owner;
|
||
|
|
GRANT USAGE ON SCHEMA bronze TO grafana_ro;
|
||
|
|
GRANT USAGE ON SCHEMA silver TO grafana_ro;
|
||
|
|
GRANT USAGE ON SCHEMA gold TO grafana_ro;
|
||
|
|
GRANT USAGE ON SCHEMA public TO dwh_owner, grafana_ro;
|
||
|
|
|
||
|
|
-- Existing table access for Grafana
|
||
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA bronze TO grafana_ro;
|
||
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA silver TO grafana_ro;
|
||
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA gold TO grafana_ro;
|
||
|
|
|
||
|
|
-- FUTURE table access: Any table created by dwh_owner will automatically be readable by grafana_ro
|
||
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE dwh_owner IN SCHEMA bronze GRANT SELECT ON TABLES TO grafana_ro;
|
||
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE dwh_owner IN SCHEMA silver GRANT SELECT ON TABLES TO grafana_ro;
|
||
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE dwh_owner IN SCHEMA gold GRANT SELECT ON TABLES TO grafana_ro;
|
||
|
|
|
||
|
|
-- 5. BRONZE SCHEMA TABLES
|
||
|
|
-- Run as dwh_owner to ensure correct ownership & default privileges apply
|
||
|
|
SET ROLE dwh_owner;
|
||
|
|
SET search_path TO bronze, public;
|
||
|
|
|
||
|
|
-- 5.1 DEVICES (Slowly Changing Dimension - Type 2 handled in Silver)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.devices (
|
||
|
|
imei TEXT PRIMARY KEY,
|
||
|
|
device_name TEXT,
|
||
|
|
mc_type TEXT,
|
||
|
|
mc_type_use_scope TEXT,
|
||
|
|
vehicle_name TEXT,
|
||
|
|
vehicle_number TEXT,
|
||
|
|
vehicle_models TEXT,
|
||
|
|
vehicle_icon TEXT,
|
||
|
|
vin TEXT,
|
||
|
|
engine_number TEXT,
|
||
|
|
vehicle_brand TEXT,
|
||
|
|
fuel_100km NUMERIC(6,2),
|
||
|
|
driver_name TEXT,
|
||
|
|
driver_phone TEXT,
|
||
|
|
sim TEXT,
|
||
|
|
iccid TEXT,
|
||
|
|
imsi TEXT,
|
||
|
|
account TEXT,
|
||
|
|
customer_name TEXT,
|
||
|
|
device_group_id TEXT,
|
||
|
|
device_group TEXT,
|
||
|
|
activation_time TIMESTAMPTZ,
|
||
|
|
expiration TIMESTAMPTZ,
|
||
|
|
enabled_flag SMALLINT DEFAULT 1 NOT NULL,
|
||
|
|
status TEXT DEFAULT 'active'::text,
|
||
|
|
city TEXT,
|
||
|
|
current_mileage_km NUMERIC(12,2),
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
last_synced_at TIMESTAMPTZ,
|
||
|
|
vehicle_category TEXT,
|
||
|
|
cost_centre TEXT,
|
||
|
|
assigned_route TEXT,
|
||
|
|
depot_geom geometry(Point,4326),
|
||
|
|
depot_address TEXT,
|
||
|
|
assigned_city TEXT,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.2 POSITION HISTORY (High-volume fact table)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.position_history (
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
gps_time TIMESTAMPTZ NOT NULL,
|
||
|
|
geom geometry(Point,4326),
|
||
|
|
lat DOUBLE PRECISION,
|
||
|
|
lng DOUBLE PRECISION,
|
||
|
|
speed NUMERIC(7,2),
|
||
|
|
direction NUMERIC(6,2),
|
||
|
|
acc_status TEXT,
|
||
|
|
satellite SMALLINT,
|
||
|
|
current_mileage NUMERIC(12,2),
|
||
|
|
recorded_at TIMESTAMPTZ DEFAULT now(),
|
||
|
|
altitude NUMERIC(8,2),
|
||
|
|
post_type SMALLINT,
|
||
|
|
source TEXT DEFAULT 'poll'::text,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
PRIMARY KEY (imei, gps_time)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.3 TRIPS (Aggregated fact table)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.trips (
|
||
|
|
id BIGINT NOT NULL,
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
start_time TIMESTAMPTZ NOT NULL,
|
||
|
|
end_time TIMESTAMPTZ,
|
||
|
|
start_geom geometry(Point,4326),
|
||
|
|
end_geom geometry(Point,4326),
|
||
|
|
distance_km NUMERIC(12,2),
|
||
|
|
avg_speed_kmh NUMERIC(7,2),
|
||
|
|
max_speed_kmh NUMERIC(7,2),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||
|
|
fuel_consumed_l NUMERIC(8,2),
|
||
|
|
idle_time_s INTEGER,
|
||
|
|
driving_time_s INTEGER,
|
||
|
|
trip_seq INTEGER,
|
||
|
|
source TEXT DEFAULT 'poll'::text,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
PRIMARY KEY (id)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.4 ALARMS (Event log fact table)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.alarms (
|
||
|
|
id BIGINT PRIMARY KEY,
|
||
|
|
imei TEXT,
|
||
|
|
alarm_type TEXT,
|
||
|
|
alarm_time TIMESTAMPTZ,
|
||
|
|
geom geometry(Point,4326),
|
||
|
|
lat DOUBLE PRECISION,
|
||
|
|
lng DOUBLE PRECISION,
|
||
|
|
speed NUMERIC(7,2),
|
||
|
|
acc_status TEXT,
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||
|
|
alarm_name TEXT,
|
||
|
|
source TEXT DEFAULT 'poll'::text,
|
||
|
|
severity TEXT,
|
||
|
|
geofence_id TEXT,
|
||
|
|
geofence_name TEXT,
|
||
|
|
acknowledged_at TIMESTAMPTZ,
|
||
|
|
acknowledged_by TEXT,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.5 DEVICE EVENTS (Connection lifecycle)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.device_events (
|
||
|
|
id BIGINT PRIMARY KEY,
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
event_type TEXT NOT NULL,
|
||
|
|
event_time TIMESTAMPTZ NOT NULL,
|
||
|
|
timezone TEXT,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.6 DISPATCH LOG (Operational/SLA tracking)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.dispatch_log (
|
||
|
|
dispatch_id BIGINT PRIMARY KEY,
|
||
|
|
ticket_id TEXT NOT NULL,
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
driver_name TEXT,
|
||
|
|
job_lat DOUBLE PRECISION NOT NULL,
|
||
|
|
job_lng DOUBLE PRECISION NOT NULL,
|
||
|
|
job_geom geometry(Point,4326),
|
||
|
|
assigned_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
first_movement_at TIMESTAMPTZ,
|
||
|
|
on_site_at TIMESTAMPTZ,
|
||
|
|
resolved_at TIMESTAMPTZ,
|
||
|
|
cancelled_at TIMESTAMPTZ,
|
||
|
|
distance_km NUMERIC(8,2),
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.7 FAULT CODES (OBD/DTC diagnostics)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.fault_codes (
|
||
|
|
id BIGINT PRIMARY KEY,
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
reported_at TIMESTAMPTZ NOT NULL,
|
||
|
|
fault_code TEXT NOT NULL,
|
||
|
|
status_flags INTEGER,
|
||
|
|
lat DOUBLE PRECISION,
|
||
|
|
lng DOUBLE PRECISION,
|
||
|
|
geom geometry(Point,4326),
|
||
|
|
event_time TIMESTAMPTZ,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.8 FUEL READINGS
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.fuel_readings (
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
reading_time TIMESTAMPTZ NOT NULL,
|
||
|
|
sensor_path TEXT,
|
||
|
|
value NUMERIC(10,3),
|
||
|
|
unit TEXT,
|
||
|
|
lat DOUBLE PRECISION,
|
||
|
|
lng DOUBLE PRECISION,
|
||
|
|
geom geometry(Point,4326),
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
PRIMARY KEY (imei, reading_time)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.9 GEOFENCES (Dimension/Reference)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.geofences (
|
||
|
|
id BIGINT PRIMARY KEY,
|
||
|
|
fence_id TEXT,
|
||
|
|
fence_name TEXT NOT NULL,
|
||
|
|
fence_type TEXT,
|
||
|
|
geom geometry(Geometry,4326),
|
||
|
|
radius_m NUMERIC(10,2),
|
||
|
|
description TEXT,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.10 HEARTBEATS (Device health/ping)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.heartbeats (
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
gate_time TIMESTAMPTZ NOT NULL,
|
||
|
|
power_level SMALLINT,
|
||
|
|
gsm_signal SMALLINT,
|
||
|
|
acc_status SMALLINT,
|
||
|
|
power_status SMALLINT,
|
||
|
|
fortify SMALLINT,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
PRIMARY KEY (imei, gate_time)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.11 INGESTION LOG (Metadata for tracking loads)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.ingestion_log (
|
||
|
|
id BIGINT PRIMARY KEY,
|
||
|
|
run_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
endpoint TEXT NOT NULL,
|
||
|
|
imei_count INTEGER DEFAULT 0 NOT NULL,
|
||
|
|
rows_upserted INTEGER DEFAULT 0 NOT NULL,
|
||
|
|
rows_inserted INTEGER DEFAULT 0 NOT NULL,
|
||
|
|
duration_ms INTEGER DEFAULT 0 NOT NULL,
|
||
|
|
success BOOLEAN DEFAULT true NOT NULL,
|
||
|
|
error_code TEXT,
|
||
|
|
error_message TEXT,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.12 LBS READINGS (Fallback positioning)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.lbs_readings (
|
||
|
|
id BIGINT PRIMARY KEY,
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
gate_time TIMESTAMPTZ NOT NULL,
|
||
|
|
post_type TEXT,
|
||
|
|
lbs_data JSONB,
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.13 LIVE POSITIONS (Current state snapshot)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.live_positions (
|
||
|
|
imei TEXT PRIMARY KEY,
|
||
|
|
geom geometry(Point,4326),
|
||
|
|
lat DOUBLE PRECISION,
|
||
|
|
lng DOUBLE PRECISION,
|
||
|
|
pos_type TEXT,
|
||
|
|
confidence SMALLINT,
|
||
|
|
gps_time TIMESTAMPTZ,
|
||
|
|
hb_time TIMESTAMPTZ,
|
||
|
|
speed NUMERIC(7,2),
|
||
|
|
direction NUMERIC(6,2),
|
||
|
|
acc_status TEXT,
|
||
|
|
gps_signal SMALLINT,
|
||
|
|
gps_num SMALLINT,
|
||
|
|
elec_quantity NUMERIC(5,2),
|
||
|
|
power_value NUMERIC(5,2),
|
||
|
|
battery_power_val NUMERIC(5,2),
|
||
|
|
tracker_oil TEXT,
|
||
|
|
temperature NUMERIC(8,2),
|
||
|
|
current_mileage NUMERIC(12,2),
|
||
|
|
device_status TEXT,
|
||
|
|
expire_flag TEXT,
|
||
|
|
activation_flag TEXT,
|
||
|
|
loc_desc TEXT,
|
||
|
|
recorded_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.14 OBD READINGS (Vehicle diagnostics)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.obd_readings (
|
||
|
|
id BIGINT PRIMARY KEY,
|
||
|
|
imei TEXT,
|
||
|
|
reading_time TIMESTAMPTZ,
|
||
|
|
engine_rpm INTEGER,
|
||
|
|
fuel_level_pct NUMERIC(5,2),
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||
|
|
car_type SMALLINT,
|
||
|
|
acc_state SMALLINT,
|
||
|
|
status_flags INTEGER,
|
||
|
|
lat DOUBLE PRECISION,
|
||
|
|
lng DOUBLE PRECISION,
|
||
|
|
geom geometry(Point,4326),
|
||
|
|
obd_data JSONB,
|
||
|
|
coolant_temp_c NUMERIC(6,2),
|
||
|
|
battery_voltage NUMERIC(5,2),
|
||
|
|
intake_pressure NUMERIC(6,2),
|
||
|
|
throttle_pct NUMERIC(5,2),
|
||
|
|
vehicle_speed NUMERIC(7,2),
|
||
|
|
engine_load_pct NUMERIC(5,2),
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.15 PARKING EVENTS
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.parking_events (
|
||
|
|
id BIGINT PRIMARY KEY,
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
event_type TEXT,
|
||
|
|
start_time TIMESTAMPTZ NOT NULL,
|
||
|
|
end_time TIMESTAMPTZ,
|
||
|
|
duration_seconds INTEGER,
|
||
|
|
geom geometry(Point,4326),
|
||
|
|
address TEXT,
|
||
|
|
updated_at TIMESTAMPTZ DEFAULT now(),
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- 5.16 TEMPERATURE READINGS (Cold chain sensors)
|
||
|
|
CREATE TABLE IF NOT EXISTS bronze.temperature_readings (
|
||
|
|
imei TEXT NOT NULL,
|
||
|
|
reading_time TIMESTAMPTZ NOT NULL,
|
||
|
|
temperature NUMERIC(6,2),
|
||
|
|
humidity_pct NUMERIC(5,2),
|
||
|
|
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||
|
|
ingested_at TIMESTAMPTZ DEFAULT NOW(),
|
||
|
|
PRIMARY KEY (imei, reading_time)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Reset role back to superuser
|
||
|
|
RESET ROLE;
|
||
|
|
RESET search_path;
|
||
|
|
|
||
|
|
-- 6. VERIFICATION GRANTS (Ensure Grafana can query immediately)
|
||
|
|
GRANT SELECT ON ALL TABLES IN SCHEMA bronze TO grafana_ro;
|
||
|
|
GRANT USAGE ON SCHEMA bronze TO grafana_ro;
|