Create comprehensive documentation mapping all entity types to their exact schema fields across the submission pipeline to prevent future schema mismatches. This document includes an overview of the pipeline flow, details on each entity type (parks, rides, companies, ride models, photos, timeline events), their main, submission, and version tables, and a reference to critical functions.
18 KiB
Submission Pipeline Schema Reference
Critical Document: This reference maps all entity types to their exact database schema fields across the entire submission pipeline to prevent schema mismatches.
Last Updated: 2025-11-08
Status: ✅ All schemas audited and verified
Table of Contents
- Overview
- Parks
- Rides
- Companies
- Ride Models
- Photos
- Timeline Events
- Critical Functions Reference
- Common Pitfalls
Overview
Pipeline Flow
User Input → *_submissions table → submission_items → Moderation →
process_approval_transaction → create/update_entity_from_submission →
Main entity table → Version trigger → *_versions table
Entity Types
park- Theme parks and amusement parksride- Individual rides and attractionscompany- Used for:manufacturer,operator,designer,property_ownerride_model- Ride model templatesphoto- Entity photostimeline_event- Historical events
Parks
Main Table: parks
Required Fields:
id(uuid, PK)name(text, NOT NULL)slug(text, NOT NULL, UNIQUE)park_type(text, NOT NULL) - Values:theme_park,amusement_park,water_park, etc.status(text, NOT NULL) - Values:operating,closed,under_construction, etc.
Optional Fields:
description(text)location_id(uuid, FK → locations)operator_id(uuid, FK → companies)property_owner_id(uuid, FK → companies)opening_date(date)closing_date(date)opening_date_precision(text) - Values:year,month,dayclosing_date_precision(text)website_url(text)phone(text)email(text)banner_image_url(text)banner_image_id(text)card_image_url(text)card_image_id(text)
Metadata Fields:
view_count_all(integer, default: 0)view_count_30d(integer, default: 0)view_count_7d(integer, default: 0)average_rating(numeric, default: 0.00)review_count(integer, default: 0)created_at(timestamptz)updated_at(timestamptz)is_test_data(boolean, default: false)
Submission Table: park_submissions
Schema Identical to Main Table (excluding auto-generated fields like id, timestamps)
Additional Field:
submission_id(uuid, NOT NULL, FK → content_submissions)temp_location_data(jsonb) - For pending location creation
Version Table: park_versions
All Main Table Fields PLUS:
version_id(uuid, PK)park_id(uuid, NOT NULL, FK → parks)version_number(integer, NOT NULL)change_type(version_change_type, NOT NULL) - Values:created,updated,restoredchange_reason(text)is_current(boolean, default: true)created_by(uuid, FK → auth.users)created_at(timestamptz)submission_id(uuid, FK → content_submissions)
Rides
Main Table: rides
Required Fields:
id(uuid, PK)name(text, NOT NULL)slug(text, NOT NULL, UNIQUE)park_id(uuid, NOT NULL, FK → parks)category(text, NOT NULL) ⚠️ CRITICAL: This field is required- Values:
roller_coaster,water_ride,dark_ride,flat_ride,transport,kids_ride
- Values:
status(text, NOT NULL)- Values:
operating,closed,under_construction,sbno, etc.
- Values:
⚠️ IMPORTANT: rides table does NOT have ride_type column!
ride_typeonly exists inride_modelstable- Using
ride_typein rides updates will cause "column does not exist" error
Optional Relationship Fields:
manufacturer_id(uuid, FK → companies)designer_id(uuid, FK → companies)ride_model_id(uuid, FK → ride_models)
Optional Descriptive Fields:
description(text)opening_date(date)closing_date(date)opening_date_precision(text)closing_date_precision(text)
Optional Technical Fields:
height_requirement(integer) - Height requirement in cmage_requirement(integer)max_speed_kmh(numeric)duration_seconds(integer)capacity_per_hour(integer)max_g_force(numeric)inversions(integer) - Number of inversionslength_meters(numeric)max_height_meters(numeric)drop_height_meters(numeric)
Category-Specific Fields:
Roller Coasters:
ride_sub_type(text)coaster_type(text)seating_type(text)intensity_level(text)track_material(text)support_material(text)propulsion_method(text)
Water Rides:
water_depth_cm(integer)splash_height_meters(numeric)wetness_level(text)flume_type(text)boat_capacity(integer)
Dark Rides:
theme_name(text)story_description(text)show_duration_seconds(integer)animatronics_count(integer)projection_type(text)ride_system(text)scenes_count(integer)
Flat Rides:
rotation_type(text)motion_pattern(text)platform_count(integer)swing_angle_degrees(numeric)rotation_speed_rpm(numeric)arm_length_meters(numeric)max_height_reached_meters(numeric)
Kids Rides:
min_age(integer)max_age(integer)educational_theme(text)character_theme(text)
Transport:
transport_type(text)route_length_meters(numeric)stations_count(integer)vehicle_capacity(integer)vehicles_count(integer)round_trip_duration_seconds(integer)
Image Fields:
banner_image_url(text)banner_image_id(text)card_image_url(text)card_image_id(text)image_url(text) - Legacy field
Metadata Fields:
view_count_all(integer, default: 0)view_count_30d(integer, default: 0)view_count_7d(integer, default: 0)average_rating(numeric, default: 0.00)review_count(integer, default: 0)created_at(timestamptz)updated_at(timestamptz)is_test_data(boolean, default: false)
Submission Table: ride_submissions
Schema Identical to Main Table (excluding auto-generated fields)
Additional Fields:
submission_id(uuid, NOT NULL, FK → content_submissions)
Version Table: ride_versions
All Main Table Fields PLUS:
version_id(uuid, PK)ride_id(uuid, NOT NULL, FK → rides)version_number(integer, NOT NULL)change_type(version_change_type, NOT NULL)change_reason(text)is_current(boolean, default: true)created_by(uuid, FK → auth.users)created_at(timestamptz)submission_id(uuid, FK → content_submissions)
⚠️ Field Name Differences (Version Table vs Main Table):
height_requirement_cmin versions →height_requirementin ridesgforce_maxin versions →max_g_forcein ridesinversions_countin versions →inversionsin ridesheight_metersin versions →max_height_metersin ridesdrop_metersin versions →drop_height_metersin rides
Companies
Used For: manufacturer, operator, designer, property_owner
Main Table: companies
Required Fields:
id(uuid, PK)name(text, NOT NULL)slug(text, NOT NULL, UNIQUE)company_type(text, NOT NULL)- Values:
manufacturer,operator,designer,property_owner
- Values:
Optional Fields:
description(text)person_type(text, default: 'company')- Values:
company,individual
- Values:
founded_year(integer)founded_date(date)founded_date_precision(text)headquarters_location(text)website_url(text)logo_url(text)banner_image_url(text)banner_image_id(text)card_image_url(text)card_image_id(text)
Metadata Fields:
view_count_all(integer, default: 0)view_count_30d(integer, default: 0)view_count_7d(integer, default: 0)average_rating(numeric, default: 0.00)review_count(integer, default: 0)created_at(timestamptz)updated_at(timestamptz)is_test_data(boolean, default: false)
Submission Table: company_submissions
Schema Identical to Main Table (excluding auto-generated fields)
Additional Field:
submission_id(uuid, NOT NULL, FK → content_submissions)
Version Table: company_versions
All Main Table Fields PLUS:
version_id(uuid, PK)company_id(uuid, NOT NULL, FK → companies)version_number(integer, NOT NULL)change_type(version_change_type, NOT NULL)change_reason(text)is_current(boolean, default: true)created_by(uuid, FK → auth.users)created_at(timestamptz)submission_id(uuid, FK → content_submissions)
Ride Models
Main Table: ride_models
Required Fields:
id(uuid, PK)name(text, NOT NULL)slug(text, NOT NULL, UNIQUE)manufacturer_id(uuid, NOT NULL, FK → companies)category(text, NOT NULL) ⚠️ CRITICAL: This field is required- Values:
roller_coaster,water_ride,dark_ride,flat_ride,transport,kids_ride
- Values:
Optional Fields:
ride_type(text) ⚠️ This field exists in ride_models but NOT in rides- More specific classification than category
- Example: category =
roller_coaster, ride_type =inverted_coaster
description(text)banner_image_url(text)banner_image_id(text)card_image_url(text)card_image_id(text)
Metadata Fields:
view_count_all(integer, default: 0)view_count_30d(integer, default: 0)view_count_7d(integer, default: 0)average_rating(numeric, default: 0.00)review_count(integer, default: 0)installations_count(integer, default: 0)created_at(timestamptz)updated_at(timestamptz)is_test_data(boolean, default: false)
Submission Table: ride_model_submissions
Schema Identical to Main Table (excluding auto-generated fields)
Additional Field:
submission_id(uuid, NOT NULL, FK → content_submissions)
Version Table: ride_model_versions
All Main Table Fields PLUS:
version_id(uuid, PK)ride_model_id(uuid, NOT NULL, FK → ride_models)version_number(integer, NOT NULL)change_type(version_change_type, NOT NULL)change_reason(text)is_current(boolean, default: true)created_by(uuid, FK → auth.users)created_at(timestamptz)submission_id(uuid, FK → content_submissions)
Photos
Main Table: photos
Required Fields:
id(uuid, PK)cloudflare_id(text, NOT NULL)url(text, NOT NULL)entity_type(text, NOT NULL)entity_id(uuid, NOT NULL)uploader_id(uuid, NOT NULL, FK → auth.users)
Optional Fields:
title(text)caption(text)taken_date(date)taken_date_precision(text)photographer_name(text)order_index(integer, default: 0)is_primary(boolean, default: false)status(text, default: 'active')
Metadata Fields:
created_at(timestamptz)updated_at(timestamptz)is_test_data(boolean, default: false)
Submission Table: photo_submissions
Required Fields:
id(uuid, PK)submission_id(uuid, NOT NULL, FK → content_submissions)entity_type(text, NOT NULL)entity_id(uuid, NOT NULL)cloudflare_id(text, NOT NULL)url(text, NOT NULL)
Optional Fields:
title(text)caption(text)taken_date(date)taken_date_precision(text)photographer_name(text)order_index(integer)
Note: Photos do NOT have version tables - they are immutable after approval
Timeline Events
Main Table: entity_timeline_events
Required Fields:
id(uuid, PK)entity_type(text, NOT NULL)entity_id(uuid, NOT NULL)event_type(text, NOT NULL)- Values:
opening,closing,relocation,renovation,name_change,ownership_change, etc.
- Values:
title(text, NOT NULL)event_date(date, NOT NULL)
Optional Fields:
description(text)event_date_precision(text, default: 'day')from_value(text)to_value(text)from_entity_id(uuid)to_entity_id(uuid)from_location_id(uuid)to_location_id(uuid)is_public(boolean, default: true)display_order(integer, default: 0)
Approval Fields:
created_by(uuid, FK → auth.users)approved_by(uuid, FK → auth.users)submission_id(uuid, FK → content_submissions)
Metadata Fields:
created_at(timestamptz)updated_at(timestamptz)
Submission Table: timeline_event_submissions
Schema Identical to Main Table (excluding auto-generated fields)
Additional Field:
submission_id(uuid, NOT NULL, FK → content_submissions)
Note: Timeline events do NOT have version tables
Critical Functions Reference
1. create_entity_from_submission
Purpose: Creates new entities from approved submissions
Parameters:
p_entity_type(text) - Entity type identifierp_data(jsonb) - Entity data from submissionp_created_by(uuid) - User who created itp_submission_id(uuid) - Source submission
Critical Requirements:
- ✅ MUST extract
categoryfor rides and ride_models - ✅ MUST NOT use
ride_typefor rides (doesn't exist) - ✅ MUST use
ride_typefor ride_models (does exist) - ✅ MUST handle all required NOT NULL fields
Returns: uuid - New entity ID
2. update_entity_from_submission
Purpose: Updates existing entities from approved edits
Parameters:
p_entity_type(text) - Entity type identifierp_data(jsonb) - Updated entity datap_entity_id(uuid) - Existing entity IDp_changed_by(uuid) - User who changed it
Critical Requirements:
- ✅ MUST use COALESCE to preserve existing values
- ✅ MUST include
categoryfor rides and ride_models - ✅ MUST NOT use
ride_typefor rides - ✅ MUST use
ride_typefor ride_models - ✅ MUST update
updated_attimestamp
Returns: uuid - Updated entity ID
3. process_approval_transaction
Purpose: Atomic transaction for selective approval
Parameters:
p_submission_id(uuid)p_item_ids(uuid[]) - Specific items to approvep_moderator_id(uuid)p_change_reason(text)
Critical Requirements:
- ✅ MUST validate all item dependencies first
- ✅ MUST extract correct fields from submission tables
- ✅ MUST set session variables for triggers
- ✅ MUST handle rollback on any error
Called By: Edge function process-selective-approval
4. create_submission_with_items
Purpose: Creates multi-item submissions atomically
Parameters:
p_submission_id(uuid)p_entity_type(text)p_action_type(text) -createoreditp_items(jsonb) - Array of submission itemsp_user_id(uuid)
Critical Requirements:
- ✅ MUST resolve dependencies in order
- ✅ MUST validate all required fields per entity type
- ✅ MUST link items to submission correctly
Common Pitfalls
1. ❌ Using ride_type for rides
-- WRONG
UPDATE rides SET ride_type = 'inverted_coaster' WHERE id = $1;
-- ERROR: column "ride_type" does not exist
-- CORRECT
UPDATE rides SET category = 'roller_coaster' WHERE id = $1;
2. ❌ Missing category field
-- WRONG - Missing required category
INSERT INTO rides (name, slug, park_id, status) VALUES (...);
-- ERROR: null value violates not-null constraint
-- CORRECT
INSERT INTO rides (name, slug, park_id, category, status) VALUES (..., 'roller_coaster', ...);
3. ❌ Wrong column names in version tables
-- WRONG
SELECT height_requirement FROM ride_versions WHERE ride_id = $1;
-- Returns null
-- CORRECT
SELECT height_requirement_cm FROM ride_versions WHERE ride_id = $1;
4. ❌ Forgetting COALESCE in updates
-- WRONG - Overwrites fields with NULL
UPDATE rides SET
name = (p_data->>'name'),
description = (p_data->>'description')
WHERE id = $1;
-- CORRECT - Preserves existing values if not provided
UPDATE rides SET
name = COALESCE(p_data->>'name', name),
description = COALESCE(p_data->>'description', description)
WHERE id = $1;
5. ❌ Not handling submission_id in version triggers
-- WRONG - Version doesn't link back to submission
INSERT INTO ride_versions (ride_id, ...) VALUES (...);
-- CORRECT - Trigger must read session variable
v_submission_id := current_setting('app.submission_id', true)::uuid;
INSERT INTO ride_versions (ride_id, submission_id, ...) VALUES (..., v_submission_id, ...);
Validation Checklist
Before deploying any submission pipeline changes:
- All entity tables have matching submission tables
- All required NOT NULL fields are included in CREATE functions
- All required NOT NULL fields are included in UPDATE functions
categoryis extracted for rides and ride_modelsride_typeis NOT used for ridesride_typeIS used for ride_models- COALESCE is used for all UPDATE statements
- Version table column name differences are handled
- Session variables are set for version triggers
- Foreign key relationships are validated
- Dependency resolution works correctly
- Error handling and rollback logic is present
Maintenance
When adding new entity types:
- Create main table with all fields
- Create matching submission table +
submission_idFK - Create version table with all fields + version metadata
- Add case to
create_entity_from_submission - Add case to
update_entity_from_submission - Add case to
process_approval_transaction - Add case to
create_submission_with_items - Create version trigger for main table
- Update this documentation
- Run full test suite
When modifying schemas:
- Check if field exists in ALL three tables (main, submission, version)
- Update ALL three tables in migration
- Update ALL functions that reference the field
- Update this documentation
- Test create, update, and rollback flows