feat(store): add PostgreSQL foundation [T-009]
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
-- Sense owns device desired state and reconciliation progress. It deliberately
|
||||
-- has no writable Site or quota truth table.
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS sense AUTHORIZATION sense_app;
|
||||
ALTER SCHEMA sense OWNER TO sense_app;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sense.schema_migrations (
|
||||
version bigint PRIMARY KEY,
|
||||
applied_at timestamptz NOT NULL DEFAULT clock_timestamp()
|
||||
);
|
||||
ALTER TABLE sense.schema_migrations OWNER TO sense_app;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sense.devices (
|
||||
id text PRIMARY KEY,
|
||||
tenant_id text NOT NULL,
|
||||
site_id text NOT NULL,
|
||||
serial_number text NOT NULL,
|
||||
name text NOT NULL,
|
||||
modality text NOT NULL,
|
||||
desired_state text NOT NULL,
|
||||
actual_state text NOT NULL,
|
||||
endpoint_ref text NOT NULL DEFAULT '',
|
||||
credential_ref text NOT NULL DEFAULT '',
|
||||
path_name text NOT NULL DEFAULT '',
|
||||
generation bigint NOT NULL DEFAULT 1,
|
||||
quota_source_version bigint,
|
||||
created_at timestamptz NOT NULL,
|
||||
updated_at timestamptz NOT NULL,
|
||||
CONSTRAINT sense_devices_identity_not_blank CHECK (
|
||||
btrim(id) <> '' AND btrim(tenant_id) <> '' AND btrim(site_id) <> ''
|
||||
AND btrim(serial_number) <> '' AND btrim(name) <> ''
|
||||
),
|
||||
CONSTRAINT sense_devices_modality CHECK (
|
||||
modality IN ('video', 'radar', 'contact', 'button', 'wearable', 'other')
|
||||
),
|
||||
CONSTRAINT sense_devices_desired_state CHECK (desired_state IN ('disabled', 'enabled')),
|
||||
CONSTRAINT sense_devices_actual_state CHECK (actual_state IN ('pending', 'online', 'offline', 'failed')),
|
||||
CONSTRAINT sense_devices_generation_positive CHECK (generation >= 1),
|
||||
CONSTRAINT sense_devices_quota_version_positive CHECK (
|
||||
quota_source_version IS NULL OR quota_source_version >= 1
|
||||
),
|
||||
UNIQUE (tenant_id, site_id, serial_number)
|
||||
);
|
||||
ALTER TABLE sense.devices OWNER TO sense_app;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sense.device_capabilities (
|
||||
device_id text NOT NULL REFERENCES sense.devices(id) ON DELETE CASCADE,
|
||||
capability text NOT NULL,
|
||||
PRIMARY KEY (device_id, capability),
|
||||
CONSTRAINT sense_device_capability_known CHECK (
|
||||
capability IN ('video_capture', 'audio_capture', 'spatial_rule', 'telemetry')
|
||||
)
|
||||
);
|
||||
ALTER TABLE sense.device_capabilities OWNER TO sense_app;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sense.reconcile_state (
|
||||
device_id text PRIMARY KEY REFERENCES sense.devices(id) ON DELETE CASCADE,
|
||||
failure_count integer NOT NULL DEFAULT 0,
|
||||
next_attempt_at timestamptz,
|
||||
last_error_code text,
|
||||
observed_generation bigint NOT NULL DEFAULT 0,
|
||||
updated_at timestamptz NOT NULL,
|
||||
CONSTRAINT sense_reconcile_failure_nonnegative CHECK (failure_count >= 0),
|
||||
CONSTRAINT sense_reconcile_generation_nonnegative CHECK (observed_generation >= 0)
|
||||
);
|
||||
ALTER TABLE sense.reconcile_state OWNER TO sense_app;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sense.site_quota_projection_state (
|
||||
tenant_id text NOT NULL,
|
||||
site_id text NOT NULL,
|
||||
source_version bigint NOT NULL,
|
||||
synced_at timestamptz NOT NULL,
|
||||
PRIMARY KEY (tenant_id, site_id),
|
||||
CONSTRAINT sense_quota_projection_version_positive CHECK (source_version >= 1)
|
||||
);
|
||||
ALTER TABLE sense.site_quota_projection_state OWNER TO sense_app;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS sense_devices_site_state_idx
|
||||
ON sense.devices(tenant_id, site_id, desired_state);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS sense_devices_path_name_idx
|
||||
ON sense.devices(path_name) WHERE path_name <> '';
|
||||
CREATE INDEX IF NOT EXISTS sense_reconcile_due_idx
|
||||
ON sense.reconcile_state(next_attempt_at);
|
||||
|
||||
INSERT INTO sense.schema_migrations(version) VALUES (1)
|
||||
ON CONFLICT (version) DO NOTHING;
|
||||
Reference in New Issue
Block a user