commit
31849411a8
1578 files changed
+290463
No files matched your search
@@ -0,0 +1,37 @@
|
||||
# PostgreSQL target schema: `th_hotel_booking`
|
||||
|
||||
This package is the isolated PostgreSQL V0.1 target model for the booking-email workflow. The current Spring runtime remains on its existing MySQL/H2 migration line; these scripts must not be added to that Flyway location or executed against `public`.
|
||||
|
||||
## Scope
|
||||
|
||||
- Owns only schema `th_hotel_booking` and objects qualified with that schema.
|
||||
- Covers source mail/revision evidence, deterministic processing runs, normalized facts, order tasks/cards, task relations and confirmation history.
|
||||
- Keeps explicit `retention_until` values with a three-month default. No deletion scheduler is included in V0.1.
|
||||
- Does not contain PMS/Opera execution tables or commands.
|
||||
- Does not create roles, extensions, databases, objects in `public`, or objects in another project schema.
|
||||
|
||||
## Safe execution order
|
||||
|
||||
1. Connect to the explicitly approved database with an interactive password prompt or a secret environment injected outside the repository.
|
||||
2. Run `preflight_read_only.sql` and retain its output.
|
||||
3. Confirm `can_create_schema=true` and inspect any existing `th_hotel_booking` objects. If an unexpected object exists, stop.
|
||||
4. Run `V001__bootstrap_th_hotel_booking.sql` with `ON_ERROR_STOP=1` in one transaction.
|
||||
5. Run `postflight_read_only.sql` and compare non-project schema counts with the preflight output.
|
||||
|
||||
Example (password is prompted and never placed in the command):
|
||||
|
||||
```sh
|
||||
psql -X -W -h <host> -p <port> -U <user> -d <database> -v ON_ERROR_STOP=1 -f database/postgresql/preflight_read_only.sql
|
||||
psql -X -W -h <host> -p <port> -U <user> -d <database> -v ON_ERROR_STOP=1 -1 -f database/postgresql/V001__bootstrap_th_hotel_booking.sql
|
||||
psql -X -W -h <host> -p <port> -U <user> -d <database> -v ON_ERROR_STOP=1 -f database/postgresql/postflight_read_only.sql
|
||||
```
|
||||
|
||||
## Primary supported queries
|
||||
|
||||
- Find one actual email and all processing revisions by `(hotel_id, provider, external_message_id)`.
|
||||
- Read a conversation in received order through `external_conversation_id`.
|
||||
- List open/review-required tasks and cards by hotel, type and status.
|
||||
- Follow linked Trace/Allotment relationships through `task_relation`.
|
||||
- Reconstruct who confirmed or overrode a card and when.
|
||||
- Find records whose three-month retention window has expired without deleting them automatically.
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
\set ON_ERROR_STOP on
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS th_hotel_booking AUTHORIZATION CURRENT_USER;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.schema_migration (
|
||||
version VARCHAR(32) PRIMARY KEY,
|
||||
description VARCHAR(255) NOT NULL,
|
||||
installed_by VARCHAR(128) NOT NULL DEFAULT CURRENT_USER,
|
||||
installed_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.source_message (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
hotel_id VARCHAR(64) NOT NULL,
|
||||
provider VARCHAR(64) NOT NULL,
|
||||
channel VARCHAR(32) NOT NULL,
|
||||
external_message_id VARCHAR(256) NOT NULL,
|
||||
external_conversation_id VARCHAR(256) NOT NULL,
|
||||
message_sha256 CHAR(64) NOT NULL,
|
||||
subject TEXT,
|
||||
sender_summary TEXT,
|
||||
source_sent_at TIMESTAMPTZ,
|
||||
received_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
source_schema_version VARCHAR(64) NOT NULL,
|
||||
original_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
retention_until TIMESTAMPTZ NOT NULL DEFAULT (CURRENT_TIMESTAMP + INTERVAL '3 months'),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_booking_source_actual_message
|
||||
UNIQUE (hotel_id, provider, external_message_id),
|
||||
CONSTRAINT ck_booking_source_sha256
|
||||
CHECK (message_sha256 ~ '^[0-9a-f]{64}$'),
|
||||
CONSTRAINT ck_booking_source_payload_object
|
||||
CHECK (jsonb_typeof(original_payload) = 'object')
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_source_conversation
|
||||
ON th_hotel_booking.source_message (hotel_id, external_conversation_id, received_at, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_source_retention
|
||||
ON th_hotel_booking.source_message (retention_until);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.source_attachment (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
source_message_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.source_message (id) ON DELETE CASCADE,
|
||||
attachment_ordinal INTEGER NOT NULL,
|
||||
file_name TEXT NOT NULL,
|
||||
content_type VARCHAR(255),
|
||||
size_bytes BIGINT NOT NULL,
|
||||
content_sha256 CHAR(64) NOT NULL,
|
||||
object_reference TEXT,
|
||||
is_current_material BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
retention_until TIMESTAMPTZ NOT NULL DEFAULT (CURRENT_TIMESTAMP + INTERVAL '3 months'),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_booking_source_attachment_ordinal
|
||||
UNIQUE (source_message_id, attachment_ordinal),
|
||||
CONSTRAINT ck_booking_attachment_ordinal_positive
|
||||
CHECK (attachment_ordinal > 0),
|
||||
CONSTRAINT ck_booking_attachment_size_nonnegative
|
||||
CHECK (size_bytes >= 0),
|
||||
CONSTRAINT ck_booking_attachment_sha256
|
||||
CHECK (content_sha256 ~ '^[0-9a-f]{64}$')
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_attachment_source
|
||||
ON th_hotel_booking.source_attachment (source_message_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.processing_run (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
source_message_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.source_message (id) ON DELETE CASCADE,
|
||||
revision_number INTEGER NOT NULL,
|
||||
run_status VARCHAR(32) NOT NULL,
|
||||
catalog_version VARCHAR(64),
|
||||
parser_version VARCHAR(64),
|
||||
agent_profile_version VARCHAR(128),
|
||||
input_fingerprint CHAR(64) NOT NULL,
|
||||
warning_payload JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
started_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMPTZ,
|
||||
retention_until TIMESTAMPTZ NOT NULL DEFAULT (CURRENT_TIMESTAMP + INTERVAL '3 months'),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_booking_processing_revision
|
||||
UNIQUE (source_message_id, revision_number),
|
||||
CONSTRAINT ck_booking_processing_revision_positive
|
||||
CHECK (revision_number > 0),
|
||||
CONSTRAINT ck_booking_processing_status
|
||||
CHECK (run_status IN ('RECEIVED', 'PARSING', 'AGENT_REVIEW', 'VALIDATED', 'TASKS_CREATED', 'FAILED')),
|
||||
CONSTRAINT ck_booking_processing_fingerprint
|
||||
CHECK (input_fingerprint ~ '^[0-9a-f]{64}$'),
|
||||
CONSTRAINT ck_booking_processing_warnings_array
|
||||
CHECK (jsonb_typeof(warning_payload) = 'array')
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_processing_source
|
||||
ON th_hotel_booking.processing_run (source_message_id, revision_number DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_processing_status
|
||||
ON th_hotel_booking.processing_run (run_status, started_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.booking_fact (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
processing_run_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.processing_run (id) ON DELETE CASCADE,
|
||||
source_event_index INTEGER NOT NULL,
|
||||
order_ref VARCHAR(160) NOT NULL,
|
||||
event_type VARCHAR(48) NOT NULL,
|
||||
booking_type VARCHAR(16),
|
||||
names_payload JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
group_code VARCHAR(128),
|
||||
group_name TEXT,
|
||||
arrival_date DATE,
|
||||
departure_date DATE,
|
||||
room_items_payload JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
trace_items_payload JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
recognition_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
warning_payload JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
requires_review BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
retention_until TIMESTAMPTZ NOT NULL DEFAULT (CURRENT_TIMESTAMP + INTERVAL '3 months'),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_booking_fact_event_index
|
||||
UNIQUE (processing_run_id, source_event_index),
|
||||
CONSTRAINT ck_booking_fact_event_index_nonnegative
|
||||
CHECK (source_event_index >= 0),
|
||||
CONSTRAINT ck_booking_fact_event_type
|
||||
CHECK (event_type IN ('NEW_BOOKING', 'UPDATE_BOOKING', 'CANCEL_BOOKING', 'TRACE_RESERVATION_NOTES', 'ROOMING_LIST', 'PAYMENT')),
|
||||
CONSTRAINT ck_booking_fact_booking_type
|
||||
CHECK (booking_type IS NULL OR booking_type IN ('FIT', 'GROUP')),
|
||||
CONSTRAINT ck_booking_fact_names_array
|
||||
CHECK (jsonb_typeof(names_payload) = 'array'),
|
||||
CONSTRAINT ck_booking_fact_rooms_array
|
||||
CHECK (jsonb_typeof(room_items_payload) = 'array'),
|
||||
CONSTRAINT ck_booking_fact_traces_array
|
||||
CHECK (jsonb_typeof(trace_items_payload) = 'array'),
|
||||
CONSTRAINT ck_booking_fact_recognition_object
|
||||
CHECK (jsonb_typeof(recognition_payload) = 'object'),
|
||||
CONSTRAINT ck_booking_fact_warnings_array
|
||||
CHECK (jsonb_typeof(warning_payload) = 'array'),
|
||||
CONSTRAINT ck_booking_fact_stay_dates
|
||||
CHECK (arrival_date IS NULL OR departure_date IS NULL OR departure_date > arrival_date)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_fact_run_order
|
||||
ON th_hotel_booking.booking_fact (processing_run_id, order_ref, source_event_index);
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_fact_type_review
|
||||
ON th_hotel_booking.booking_fact (event_type, requires_review);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.order_task (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
hotel_id VARCHAR(64) NOT NULL,
|
||||
source_message_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.source_message (id) ON DELETE CASCADE,
|
||||
processing_run_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.processing_run (id) ON DELETE CASCADE,
|
||||
order_ref VARCHAR(160) NOT NULL,
|
||||
idempotency_key VARCHAR(320) NOT NULL,
|
||||
task_status VARCHAR(32) NOT NULL,
|
||||
target_booking_type VARCHAR(16),
|
||||
target_locator_type VARCHAR(32),
|
||||
target_locator_value VARCHAR(256),
|
||||
target_resolution_status VARCHAR(32) NOT NULL DEFAULT 'UNRESOLVED',
|
||||
retention_until TIMESTAMPTZ NOT NULL DEFAULT (CURRENT_TIMESTAMP + INTERVAL '3 months'),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_booking_order_task_idempotency
|
||||
UNIQUE (hotel_id, idempotency_key),
|
||||
CONSTRAINT ck_booking_order_task_status
|
||||
CHECK (task_status IN ('OPEN', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED')),
|
||||
CONSTRAINT ck_booking_order_task_booking_type
|
||||
CHECK (target_booking_type IS NULL OR target_booking_type IN ('FIT', 'GROUP')),
|
||||
CONSTRAINT ck_booking_order_task_resolution
|
||||
CHECK (target_resolution_status IN ('UNRESOLVED', 'RESOLVED', 'CONFLICT'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_order_task_queue
|
||||
ON th_hotel_booking.order_task (hotel_id, task_status, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_order_task_source
|
||||
ON th_hotel_booking.order_task (source_message_id, processing_run_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.task_card (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
order_task_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.order_task (id) ON DELETE CASCADE,
|
||||
source_event_index INTEGER,
|
||||
card_type VARCHAR(64) NOT NULL,
|
||||
event_type VARCHAR(48),
|
||||
task_subtype VARCHAR(96),
|
||||
card_status VARCHAR(32) NOT NULL,
|
||||
review_status VARCHAR(32),
|
||||
display_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
confirmed_payload JSONB,
|
||||
manual_review_payload JSONB,
|
||||
row_version BIGINT NOT NULL DEFAULT 0,
|
||||
confirmed_by VARCHAR(128),
|
||||
confirmed_at TIMESTAMPTZ,
|
||||
retention_until TIMESTAMPTZ NOT NULL DEFAULT (CURRENT_TIMESTAMP + INTERVAL '3 months'),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_booking_task_card_source
|
||||
UNIQUE (order_task_id, card_type, source_event_index),
|
||||
CONSTRAINT ck_booking_task_card_status
|
||||
CHECK (card_status IN ('READONLY', 'PENDING_CONFIRM', 'REVIEW_REQUIRED', 'CONFIRMED', 'CANCELLED')),
|
||||
CONSTRAINT ck_booking_task_card_review
|
||||
CHECK (review_status IS NULL OR review_status IN ('PENDING', 'RESOLVED')),
|
||||
CONSTRAINT ck_booking_task_card_display_object
|
||||
CHECK (jsonb_typeof(display_payload) = 'object'),
|
||||
CONSTRAINT ck_booking_task_card_confirmed_object
|
||||
CHECK (confirmed_payload IS NULL OR jsonb_typeof(confirmed_payload) = 'object'),
|
||||
CONSTRAINT ck_booking_task_card_manual_review_object
|
||||
CHECK (manual_review_payload IS NULL OR jsonb_typeof(manual_review_payload) = 'object'),
|
||||
CONSTRAINT ck_booking_task_card_version_nonnegative
|
||||
CHECK (row_version >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_task_card_work_queue
|
||||
ON th_hotel_booking.task_card (card_status, review_status, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_task_card_order
|
||||
ON th_hotel_booking.task_card (order_task_id, id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.task_relation (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
source_order_task_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.order_task (id) ON DELETE CASCADE,
|
||||
target_order_task_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.order_task (id) ON DELETE CASCADE,
|
||||
relationship_type VARCHAR(96) NOT NULL,
|
||||
relationship_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT uq_booking_task_relation
|
||||
UNIQUE (source_order_task_id, target_order_task_id, relationship_type),
|
||||
CONSTRAINT ck_booking_task_relation_not_self
|
||||
CHECK (source_order_task_id <> target_order_task_id),
|
||||
CONSTRAINT ck_booking_task_relation_payload_object
|
||||
CHECK (jsonb_typeof(relationship_payload) = 'object')
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_task_relation_target
|
||||
ON th_hotel_booking.task_relation (target_order_task_id, relationship_type);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS th_hotel_booking.confirmation_action (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
order_task_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.order_task (id) ON DELETE CASCADE,
|
||||
task_card_id BIGINT NOT NULL
|
||||
REFERENCES th_hotel_booking.task_card (id) ON DELETE CASCADE,
|
||||
action_type VARCHAR(32) NOT NULL,
|
||||
field_overrides JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
reason TEXT,
|
||||
actor_id VARCHAR(128) NOT NULL,
|
||||
acted_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
retention_until TIMESTAMPTZ NOT NULL DEFAULT (CURRENT_TIMESTAMP + INTERVAL '3 months'),
|
||||
CONSTRAINT ck_booking_confirmation_action
|
||||
CHECK (action_type IN ('CONFIRM', 'RESOLVE_REVIEW', 'REOPEN')),
|
||||
CONSTRAINT ck_booking_confirmation_overrides_array
|
||||
CHECK (jsonb_typeof(field_overrides) = 'array')
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_confirmation_card_time
|
||||
ON th_hotel_booking.confirmation_action (task_card_id, acted_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_booking_confirmation_actor_time
|
||||
ON th_hotel_booking.confirmation_action (actor_id, acted_at DESC);
|
||||
|
||||
INSERT INTO th_hotel_booking.schema_migration (version, description)
|
||||
VALUES ('001', 'bootstrap booking email to confirmation target model')
|
||||
ON CONFLICT (version) DO NOTHING;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
\set ON_ERROR_STOP on
|
||||
|
||||
BEGIN READ ONLY;
|
||||
|
||||
SELECT
|
||||
current_database() AS current_database,
|
||||
current_user AS current_user,
|
||||
n.nspname AS schema_name,
|
||||
pg_get_userbyid(n.nspowner) AS schema_owner,
|
||||
has_schema_privilege(current_user, n.oid, 'USAGE') AS can_use,
|
||||
has_schema_privilege(current_user, n.oid, 'CREATE') AS can_create_objects
|
||||
FROM pg_namespace n
|
||||
WHERE n.nspname = 'th_hotel_booking';
|
||||
|
||||
SELECT version, description, installed_by, installed_at
|
||||
FROM th_hotel_booking.schema_migration
|
||||
ORDER BY version;
|
||||
|
||||
SELECT
|
||||
c.relkind,
|
||||
c.relname AS object_name
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname = 'th_hotel_booking'
|
||||
ORDER BY c.relkind, c.relname;
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
c.relkind,
|
||||
count(*) AS object_count
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname NOT LIKE 'pg_%'
|
||||
AND n.nspname <> 'information_schema'
|
||||
GROUP BY n.nspname, c.relkind
|
||||
ORDER BY n.nspname, c.relkind;
|
||||
|
||||
SELECT
|
||||
conrelid::regclass::text AS constrained_table,
|
||||
confrelid::regclass::text AS referenced_table,
|
||||
conname
|
||||
FROM pg_constraint
|
||||
WHERE contype = 'f'
|
||||
AND connamespace = 'th_hotel_booking'::regnamespace
|
||||
AND confrelid::regclass::text NOT LIKE 'th_hotel_booking.%'
|
||||
ORDER BY constrained_table, conname;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,41 @@
|
||||
\set ON_ERROR_STOP on
|
||||
|
||||
BEGIN READ ONLY;
|
||||
|
||||
SELECT
|
||||
current_database() AS current_database,
|
||||
current_user AS current_user,
|
||||
inet_server_addr()::text AS server_address,
|
||||
inet_server_port() AS server_port,
|
||||
has_database_privilege(current_user, current_database(), 'CREATE') AS can_create_schema;
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
pg_get_userbyid(n.nspowner) AS schema_owner,
|
||||
has_schema_privilege(current_user, n.oid, 'USAGE') AS can_use,
|
||||
has_schema_privilege(current_user, n.oid, 'CREATE') AS can_create_objects
|
||||
FROM pg_namespace n
|
||||
WHERE n.nspname = 'th_hotel_booking';
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
c.relkind,
|
||||
count(*) AS object_count
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname NOT LIKE 'pg_%'
|
||||
AND n.nspname <> 'information_schema'
|
||||
GROUP BY n.nspname, c.relkind
|
||||
ORDER BY n.nspname, c.relkind;
|
||||
|
||||
SELECT
|
||||
n.nspname AS schema_name,
|
||||
c.relname AS object_name,
|
||||
c.relkind
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||
WHERE n.nspname = 'th_hotel_booking'
|
||||
ORDER BY c.relkind, c.relname;
|
||||
|
||||
COMMIT;
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
\set ON_ERROR_STOP on
|
||||
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO th_hotel_booking.source_message (
|
||||
hotel_id,
|
||||
provider,
|
||||
channel,
|
||||
external_message_id,
|
||||
external_conversation_id,
|
||||
message_sha256,
|
||||
subject,
|
||||
source_schema_version
|
||||
) VALUES (
|
||||
'HOTEL-SMOKE',
|
||||
'SMOKE_TEST',
|
||||
'EMAIL',
|
||||
'smoke-message-v001',
|
||||
'smoke-conversation-v001',
|
||||
repeat('a', 64),
|
||||
'Transactional schema smoke test',
|
||||
'smoke-v1'
|
||||
) RETURNING id \gset smoke_source_
|
||||
|
||||
INSERT INTO th_hotel_booking.processing_run (
|
||||
source_message_id,
|
||||
revision_number,
|
||||
run_status,
|
||||
catalog_version,
|
||||
parser_version,
|
||||
input_fingerprint
|
||||
) VALUES (
|
||||
:smoke_source_id,
|
||||
1,
|
||||
'TASKS_CREATED',
|
||||
'booking-catalog-v20260808',
|
||||
'booking-email-parser-v0.1',
|
||||
repeat('b', 64)
|
||||
) RETURNING id \gset smoke_run_
|
||||
|
||||
INSERT INTO th_hotel_booking.booking_fact (
|
||||
processing_run_id,
|
||||
source_event_index,
|
||||
order_ref,
|
||||
event_type,
|
||||
booking_type,
|
||||
names_payload,
|
||||
room_items_payload,
|
||||
recognition_payload
|
||||
) VALUES (
|
||||
:smoke_run_id,
|
||||
0,
|
||||
'SMOKE-ORDER-1',
|
||||
'NEW_BOOKING',
|
||||
'GROUP',
|
||||
'["SMOKE-NAME"]'::jsonb,
|
||||
'[{"room_type_code":"RM3","room_count":5}]'::jsonb,
|
||||
'{"selection_status":"STRICT_CURRENT_CANDIDATE"}'::jsonb
|
||||
);
|
||||
|
||||
INSERT INTO th_hotel_booking.order_task (
|
||||
hotel_id,
|
||||
source_message_id,
|
||||
processing_run_id,
|
||||
order_ref,
|
||||
idempotency_key,
|
||||
task_status,
|
||||
target_booking_type
|
||||
) VALUES (
|
||||
'HOTEL-SMOKE',
|
||||
:smoke_source_id,
|
||||
:smoke_run_id,
|
||||
'SMOKE-ORDER-1',
|
||||
'smoke-idempotency-v001',
|
||||
'OPEN',
|
||||
'GROUP'
|
||||
) RETURNING id \gset smoke_task_
|
||||
|
||||
INSERT INTO th_hotel_booking.task_card (
|
||||
order_task_id,
|
||||
source_event_index,
|
||||
card_type,
|
||||
event_type,
|
||||
task_subtype,
|
||||
card_status,
|
||||
review_status,
|
||||
display_payload
|
||||
) VALUES (
|
||||
:smoke_task_id,
|
||||
0,
|
||||
'ROOM_INFORMATION',
|
||||
'NEW_BOOKING',
|
||||
'new_group_block',
|
||||
'REVIEW_REQUIRED',
|
||||
'PENDING',
|
||||
'{"room_information":{"booking_type":"GROUP"}}'::jsonb
|
||||
) RETURNING id \gset smoke_card_
|
||||
|
||||
INSERT INTO th_hotel_booking.confirmation_action (
|
||||
order_task_id,
|
||||
task_card_id,
|
||||
action_type,
|
||||
field_overrides,
|
||||
reason,
|
||||
actor_id
|
||||
) VALUES (
|
||||
:smoke_task_id,
|
||||
:smoke_card_id,
|
||||
'RESOLVE_REVIEW',
|
||||
'[{"field_pointer":"/room_information/final_values/group_code","value":"SMOKE-GROUP"}]'::jsonb,
|
||||
'Rollback-only schema smoke test',
|
||||
'codex-smoke'
|
||||
);
|
||||
|
||||
SELECT
|
||||
count(*) AS joined_smoke_rows
|
||||
FROM th_hotel_booking.source_message sm
|
||||
JOIN th_hotel_booking.processing_run pr ON pr.source_message_id = sm.id
|
||||
JOIN th_hotel_booking.order_task ot ON ot.processing_run_id = pr.id
|
||||
JOIN th_hotel_booking.task_card tc ON tc.order_task_id = ot.id
|
||||
JOIN th_hotel_booking.confirmation_action ca ON ca.task_card_id = tc.id
|
||||
WHERE sm.id = :smoke_source_id;
|
||||
|
||||
ROLLBACK;
|
||||
|
||||
SELECT count(*) AS persisted_smoke_rows
|
||||
FROM th_hotel_booking.source_message
|
||||
WHERE external_message_id = 'smoke-message-v001';
|
||||
Reference in new issue
Block a user