-- Migration: 021_location_hierarchy.sql -- Description: Restructure location tables to use comprehensive geographic hierarchy -- Data Source: GeoDB Cities (https://github.com/dr5hn/countries-states-cities-database) -- ============================================================================ -- PHASE 1: Backup existing tables -- ============================================================================ -- Rename old tables to preserve data ALTER TABLE IF EXISTS regions RENAME TO regions_old; ALTER TABLE IF EXISTS cities RENAME TO cities_old; -- Drop old indexes (they will conflict) DROP INDEX IF EXISTS idx_regions_country; DROP INDEX IF EXISTS idx_cities_region; -- ============================================================================ -- PHASE 2: Create new location hierarchy -- ============================================================================ -- 2.1 Continents (formerly "regions" in GeoDB) CREATE TABLE IF NOT EXISTS continents ( id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name VARCHAR(100) NOT NULL, translations TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, flag SMALLINT DEFAULT 1 NOT NULL, wiki_data_id VARCHAR(255) ); COMMENT ON TABLE continents IS 'Geographic continents (Africa, Americas, Asia, Europe, Oceania, Polar)'; -- 2.2 Subregions (e.g., Northern Africa, South America) CREATE TABLE IF NOT EXISTS subregions ( id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name VARCHAR(100) NOT NULL, continent_id BIGINT NOT NULL REFERENCES continents(id), translations TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, flag SMALLINT DEFAULT 1 NOT NULL, wiki_data_id VARCHAR(255) ); CREATE INDEX idx_subregions_continent ON subregions(continent_id); COMMENT ON TABLE subregions IS 'Geographic subregions within continents'; -- 2.3 Countries CREATE TABLE IF NOT EXISTS countries ( id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name VARCHAR(100) NOT NULL, iso2 CHAR(2), iso3 CHAR(3), numeric_code CHAR(3), phonecode VARCHAR(255), capital VARCHAR(255), currency VARCHAR(255), currency_name VARCHAR(255), currency_symbol VARCHAR(255), tld VARCHAR(255), native VARCHAR(255), continent_id BIGINT REFERENCES continents(id), subregion_id BIGINT REFERENCES subregions(id), nationality VARCHAR(255), latitude DECIMAL(10,8), longitude DECIMAL(11,8), emoji VARCHAR(10), emoji_u VARCHAR(50), timezones TEXT, translations TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, flag SMALLINT DEFAULT 1 NOT NULL, wiki_data_id VARCHAR(255) ); CREATE INDEX idx_countries_iso2 ON countries(iso2); CREATE INDEX idx_countries_iso3 ON countries(iso3); CREATE INDEX idx_countries_continent ON countries(continent_id); CREATE INDEX idx_countries_subregion ON countries(subregion_id); COMMENT ON TABLE countries IS 'All countries with ISO codes, currencies, and metadata'; -- 2.4 States/Provinces CREATE TABLE IF NOT EXISTS states ( id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name VARCHAR(255) NOT NULL, country_id BIGINT NOT NULL REFERENCES countries(id), country_code CHAR(2) NOT NULL, iso2 VARCHAR(10), fips_code VARCHAR(255), type VARCHAR(191), latitude DECIMAL(10,8), longitude DECIMAL(11,8), timezone VARCHAR(255), translations TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, flag SMALLINT DEFAULT 1 NOT NULL, wiki_data_id VARCHAR(255) ); CREATE INDEX idx_states_country ON states(country_id); CREATE INDEX idx_states_country_code ON states(country_code); CREATE INDEX idx_states_iso2 ON states(iso2); COMMENT ON TABLE states IS 'States, provinces, and administrative regions'; -- 2.5 Cities CREATE TABLE IF NOT EXISTS cities ( id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, name VARCHAR(255) NOT NULL, state_id BIGINT NOT NULL REFERENCES states(id), state_code VARCHAR(255), country_id BIGINT NOT NULL REFERENCES countries(id), country_code CHAR(2) NOT NULL, latitude DECIMAL(10,8) NOT NULL, longitude DECIMAL(11,8) NOT NULL, population BIGINT, timezone VARCHAR(255), translations TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, flag SMALLINT DEFAULT 1 NOT NULL, wiki_data_id VARCHAR(255) ); CREATE INDEX idx_cities_state ON cities(state_id); CREATE INDEX idx_cities_country ON cities(country_id); CREATE INDEX idx_cities_country_code ON cities(country_code); CREATE INDEX idx_cities_population ON cities(population); COMMENT ON TABLE cities IS 'Cities with coordinates and population data'; -- ============================================================================ -- PHASE 3: Update companies table FKs -- ============================================================================ -- Drop old FK constraint if exists (ignore errors) ALTER TABLE companies DROP CONSTRAINT IF EXISTS companies_region_id_fkey; ALTER TABLE companies DROP CONSTRAINT IF EXISTS companies_city_id_fkey; -- Update column types to BIGINT (to match new tables) ALTER TABLE companies ALTER COLUMN region_id TYPE BIGINT USING region_id::BIGINT; ALTER TABLE companies ALTER COLUMN city_id TYPE BIGINT USING city_id::BIGINT; -- Rename region_id to country_id for clarity ALTER TABLE companies RENAME COLUMN region_id TO country_id; -- Add new FK constraints (without REFERENCES for now - data will be populated by seeder) -- These will be validated after seeder populates the data -- Update indexes DROP INDEX IF EXISTS idx_companies_region; CREATE INDEX idx_companies_country ON companies(country_id); CREATE INDEX idx_companies_city ON companies(city_id); -- Add comments COMMENT ON COLUMN companies.country_id IS 'Reference to countries table'; COMMENT ON COLUMN companies.city_id IS 'Reference to cities table';