-- Migration: Create Automotive Vehicle Selection Database -- Optimized for dropdown cascade queries -- Date: 2025-11-10 -- Drop existing tables if they exist DROP TABLE IF EXISTS vehicle_options CASCADE; DROP TABLE IF EXISTS engines CASCADE; DROP TABLE IF EXISTS transmissions CASCADE; DROP INDEX IF EXISTS idx_vehicle_year; DROP INDEX IF EXISTS idx_vehicle_make; DROP INDEX IF EXISTS idx_vehicle_model; DROP INDEX IF EXISTS idx_vehicle_trim; DROP INDEX IF EXISTS idx_vehicle_composite; -- Create engines table with detailed specifications CREATE TABLE engines ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, displacement VARCHAR(50), configuration VARCHAR(50), horsepower VARCHAR(100), torque VARCHAR(100), fuel_type VARCHAR(100), fuel_system VARCHAR(255), aspiration VARCHAR(100), specs_json JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Prevent duplicate engine display names (case-insensitive) CREATE UNIQUE INDEX IF NOT EXISTS uq_engines_name_lower ON engines (LOWER(name)); CREATE INDEX idx_engines_displacement ON engines(displacement); CREATE INDEX idx_engines_config ON engines(configuration); -- Create transmissions table CREATE TABLE transmissions ( id SERIAL PRIMARY KEY, type VARCHAR(100) NOT NULL, speeds VARCHAR(50), drive_type VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Prevent duplicate transmission display names (case-insensitive) CREATE UNIQUE INDEX IF NOT EXISTS uq_transmissions_type_lower ON transmissions (LOWER(type)); CREATE INDEX idx_transmissions_type ON transmissions(type); -- Create denormalized vehicle_options table optimized for dropdown queries CREATE TABLE vehicle_options ( id SERIAL PRIMARY KEY, year INTEGER NOT NULL, make VARCHAR(100) NOT NULL, model VARCHAR(255) NOT NULL, trim VARCHAR(255) NOT NULL, engine_id INTEGER REFERENCES engines(id) ON DELETE SET NULL, transmission_id INTEGER REFERENCES transmissions(id) ON DELETE SET NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Prevent duplicate vehicle option rows CREATE UNIQUE INDEX IF NOT EXISTS uq_vehicle_options_full ON vehicle_options ( year, make, model, trim, engine_id, transmission_id ); -- Indexes for cascading dropdown performance CREATE INDEX idx_vehicle_year ON vehicle_options(year); CREATE INDEX idx_vehicle_make ON vehicle_options(make); CREATE INDEX idx_vehicle_model ON vehicle_options(model); CREATE INDEX idx_vehicle_trim ON vehicle_options(trim); CREATE INDEX idx_vehicle_year_make ON vehicle_options(year, make); CREATE INDEX idx_vehicle_year_make_model ON vehicle_options(year, make, model); CREATE INDEX idx_vehicle_year_make_model_trim ON vehicle_options(year, make, model, trim); CREATE INDEX idx_vehicle_year_make_model_trim_engine ON vehicle_options(year, make, model, trim, engine_id); CREATE INDEX idx_vehicle_year_make_model_trim_trans ON vehicle_options(year, make, model, trim, transmission_id); -- Views for dropdown queries -- View: Get all available years CREATE OR REPLACE VIEW available_years AS SELECT DISTINCT year FROM vehicle_options ORDER BY year DESC; -- View: Get makes by year CREATE OR REPLACE VIEW makes_by_year AS SELECT DISTINCT year, make FROM vehicle_options ORDER BY year DESC, make ASC; -- View: Get models by year and make CREATE OR REPLACE VIEW models_by_year_make AS SELECT DISTINCT year, make, model FROM vehicle_options ORDER BY year DESC, make ASC, model ASC; -- View: Get trims by year, make, and model CREATE OR REPLACE VIEW trims_by_year_make_model AS SELECT DISTINCT year, make, model, trim FROM vehicle_options ORDER BY year DESC, make ASC, model ASC, trim ASC; -- View: Get complete vehicle configurations with engine and transmission details CREATE OR REPLACE VIEW complete_vehicle_configs AS SELECT vo.id, vo.year, vo.make, vo.model, vo.trim, e.name AS engine_name, e.displacement, e.configuration, e.horsepower, e.torque, e.fuel_type, t.type AS transmission_type, t.speeds AS transmission_speeds, t.drive_type FROM vehicle_options vo LEFT JOIN engines e ON vo.engine_id = e.id LEFT JOIN transmissions t ON vo.transmission_id = t.id ORDER BY vo.year DESC, vo.make ASC, vo.model ASC, vo.trim ASC; -- Function to get makes for a specific year CREATE OR REPLACE FUNCTION get_makes_for_year(p_year INTEGER) RETURNS TABLE(make VARCHAR) AS $$ BEGIN RETURN QUERY SELECT DISTINCT vehicle_options.make FROM vehicle_options WHERE vehicle_options.year = p_year ORDER BY vehicle_options.make ASC; END; $$ LANGUAGE plpgsql; -- Function to get models for a specific year and make CREATE OR REPLACE FUNCTION get_models_for_year_make(p_year INTEGER, p_make VARCHAR) RETURNS TABLE(model VARCHAR) AS $$ BEGIN RETURN QUERY SELECT DISTINCT vehicle_options.model FROM vehicle_options WHERE vehicle_options.year = p_year AND vehicle_options.make = p_make ORDER BY vehicle_options.model ASC; END; $$ LANGUAGE plpgsql; -- Function to get trims for a specific year, make, and model CREATE OR REPLACE FUNCTION get_trims_for_year_make_model(p_year INTEGER, p_make VARCHAR, p_model VARCHAR) RETURNS TABLE(trim_name VARCHAR) AS $$ BEGIN RETURN QUERY SELECT DISTINCT vehicle_options.trim FROM vehicle_options WHERE vehicle_options.year = p_year AND vehicle_options.make = p_make AND vehicle_options.model = p_model ORDER BY vehicle_options.trim ASC; END; $$ LANGUAGE plpgsql; -- Function to get engine and transmission options for a specific vehicle CREATE OR REPLACE FUNCTION get_options_for_vehicle(p_year INTEGER, p_make VARCHAR, p_model VARCHAR, p_trim VARCHAR) RETURNS TABLE( engine_name VARCHAR, engine_displacement VARCHAR, engine_horsepower VARCHAR, transmission_type VARCHAR, transmission_speeds VARCHAR, drive_type VARCHAR ) AS $$ BEGIN RETURN QUERY SELECT e.name, e.displacement, e.horsepower, t.type, t.speeds, t.drive_type FROM vehicle_options vo LEFT JOIN engines e ON vo.engine_id = e.id LEFT JOIN transmissions t ON vo.transmission_id = t.id WHERE vo.year = p_year AND vo.make = p_make AND vo.model = p_model AND vo.trim = p_trim; END; $$ LANGUAGE plpgsql; -- Helper functions for trim-level options and pair-safe filtering CREATE OR REPLACE FUNCTION get_transmissions_for_vehicle(p_year INTEGER, p_make VARCHAR, p_model VARCHAR, p_trim VARCHAR) RETURNS TABLE( transmission_id INTEGER, transmission_type VARCHAR ) AS $$ BEGIN RETURN QUERY SELECT DISTINCT t.id, t.type FROM vehicle_options vo JOIN transmissions t ON vo.transmission_id = t.id WHERE vo.year = p_year AND vo.make = p_make AND vo.model = p_model AND vo.trim = p_trim ORDER BY t.type ASC; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION get_engines_for_vehicle(p_year INTEGER, p_make VARCHAR, p_model VARCHAR, p_trim VARCHAR) RETURNS TABLE( engine_id INTEGER, engine_name VARCHAR ) AS $$ BEGIN RETURN QUERY SELECT DISTINCT e.id, e.name FROM vehicle_options vo JOIN engines e ON vo.engine_id = e.id WHERE vo.year = p_year AND vo.make = p_make AND vo.model = p_model AND vo.trim = p_trim ORDER BY e.name ASC; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION get_transmissions_for_vehicle_engine(p_year INTEGER, p_make VARCHAR, p_model VARCHAR, p_trim VARCHAR, p_engine_name VARCHAR) RETURNS TABLE( transmission_id INTEGER, transmission_type VARCHAR ) AS $$ BEGIN RETURN QUERY SELECT DISTINCT t.id, t.type FROM vehicle_options vo JOIN engines e ON vo.engine_id = e.id JOIN transmissions t ON vo.transmission_id = t.id WHERE vo.year = p_year AND vo.make = p_make AND vo.model = p_model AND vo.trim = p_trim AND e.name = p_engine_name ORDER BY t.type ASC; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION get_engines_for_vehicle_trans(p_year INTEGER, p_make VARCHAR, p_model VARCHAR, p_trim VARCHAR, p_trans_type VARCHAR) RETURNS TABLE( engine_id INTEGER, engine_name VARCHAR ) AS $$ BEGIN RETURN QUERY SELECT DISTINCT e.id, e.name FROM vehicle_options vo JOIN engines e ON vo.engine_id = e.id JOIN transmissions t ON vo.transmission_id = t.id WHERE vo.year = p_year AND vo.make = p_make AND vo.model = p_model AND vo.trim = p_trim AND t.type = p_trans_type ORDER BY e.name ASC; END; $$ LANGUAGE plpgsql; COMMENT ON TABLE vehicle_options IS 'Denormalized table optimized for cascading dropdown queries'; COMMENT ON TABLE engines IS 'Engine specifications with detailed technical data'; COMMENT ON TABLE transmissions IS 'Transmission specifications'; COMMENT ON VIEW available_years IS 'Returns all distinct years available in the database'; COMMENT ON VIEW makes_by_year IS 'Returns makes grouped by year for dropdown population'; COMMENT ON VIEW models_by_year_make IS 'Returns models grouped by year and make'; COMMENT ON VIEW trims_by_year_make_model IS 'Returns trims grouped by year, make, and model'; COMMENT ON VIEW complete_vehicle_configs IS 'Complete vehicle configurations with all details';