/* ───────────────────────── user_profile ───────────────────────── */ ALTER TABLE user_profile MODIFY firstname VARCHAR(400), MODIFY lastname VARCHAR(400), MODIFY email VARCHAR(512), MODIFY phone_e164 VARCHAR(128), MODIFY zipcode VARCHAR(64), MODIFY stripe_customer_id VARCHAR(128), MODIFY interest_inventory_answers MEDIUMTEXT, MODIFY riasec_scores VARCHAR(768), MODIFY career_priorities MEDIUMTEXT, MODIFY career_list MEDIUMTEXT; /* ───────────────────────── financial_profiles ─────────────────── */ ALTER TABLE financial_profiles MODIFY current_salary VARCHAR(128), MODIFY additional_income VARCHAR(128), MODIFY monthly_expenses VARCHAR(128), MODIFY monthly_debt_payments VARCHAR(128), MODIFY retirement_savings VARCHAR(128), MODIFY emergency_fund VARCHAR(128), MODIFY retirement_contribution VARCHAR(128), MODIFY emergency_contribution VARCHAR(128), MODIFY extra_cash_emergency_pct VARCHAR(64), MODIFY extra_cash_retirement_pct VARCHAR(64); /* ───────────────────────── career_profiles ────────────────────── */ ALTER TABLE career_profiles MODIFY COLUMN career_name VARCHAR(255) NULL, MODIFY COLUMN start_date VARCHAR(32) NULL, MODIFY COLUMN retirement_start_date VARCHAR(32) NULL, MODIFY COLUMN planned_monthly_expenses VARCHAR(128) NULL, MODIFY COLUMN planned_monthly_debt_payments VARCHAR(128) NULL, MODIFY COLUMN planned_monthly_retirement_contribution VARCHAR(128) NULL, MODIFY COLUMN planned_monthly_emergency_contribution VARCHAR(128) NULL, MODIFY COLUMN planned_surplus_emergency_pct VARCHAR(128) NULL, MODIFY COLUMN planned_surplus_retirement_pct VARCHAR(128) NULL, MODIFY COLUMN planned_additional_income VARCHAR(128) NULL, MODIFY COLUMN career_goals MEDIUMTEXT NULL, MODIFY COLUMN desired_retirement_income_monthly VARCHAR(128) NULL, MODIFY COLUMN scenario_title VARCHAR(255) NULL; encryption_canary(id INTEGER PRIMARY KEY, value TEXT) ALTER TABLE college_profiles MODIFY COLUMN selected_school VARCHAR(512) NULL, MODIFY COLUMN selected_program VARCHAR(512) NULL, MODIFY COLUMN annual_financial_aid VARCHAR(128) NULL, MODIFY COLUMN existing_college_debt VARCHAR(128) NULL, MODIFY COLUMN tuition VARCHAR(128) NULL, MODIFY COLUMN tuition_paid VARCHAR(128) NULL, MODIFY COLUMN loan_deferral_until_graduation VARCHAR(128) NULL, MODIFY COLUMN loan_term VARCHAR(128) NULL, MODIFY COLUMN interest_rate VARCHAR(128) NULL, MODIFY COLUMN extra_payment VARCHAR(128) NULL, MODIFY COLUMN expected_salary VARCHAR(128) NULL; ALTER TABLE user_profile ADD COLUMN stripe_customer_id_hash CHAR(64) NULL, ADD INDEX idx_customer_hash (stripe_customer_id_hash); /*─────────────────── STEP 1 – drop old indexes ───────────────────*/ SHOW INDEX FROM college_profiles\G ALTER TABLE college_profiles DROP FOREIGN KEY fk_college_profiles_user, DROP FOREIGN KEY fk_college_profiles_career; ALTER TABLE college_profiles DROP INDEX user_id; /*─────────────────── STEP 2 – widen columns (512‑byte text columns ≈ 684 B once encrypted/Base64‑encoded) ───────────────────*/ ALTER TABLE college_profiles MODIFY selected_school VARCHAR(512), MODIFY selected_program VARCHAR(512), MODIFY annual_financial_aid VARCHAR(128), MODIFY existing_college_debt VARCHAR(128), MODIFY tuition VARCHAR(128), MODIFY tuition_paid VARCHAR(128), MODIFY loan_deferral_until_graduation VARCHAR(64), MODIFY loan_term VARCHAR(64), MODIFY interest_rate VARCHAR(64), MODIFY extra_payment VARCHAR(128), MODIFY expected_salary VARCHAR(128); ALTER TABLE college_profiles ADD UNIQUE KEY ux_user_school_prog ( user_id, career_profile_id, selected_school(192), selected_program(192), program_type ); ALTER TABLE college_profiles ADD CONSTRAINT fk_college_profiles_user FOREIGN KEY (user_id) REFERENCES user_profile(id) ON DELETE CASCADE ON UPDATE CASCADE, ADD CONSTRAINT fk_college_profiles_career FOREIGN KEY (career_profile_id) REFERENCES career_profiles(id) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE college_profiles ADD INDEX idx_college_user (user_id), ADD INDEX idx_college_career (career_profile_id); /*─────────────────── STEP 3 – recreate indexes with safe prefixes (optional) If you *don’t* need to query by these columns any more, just comment‑out or delete this block. ───────────────────*/ CREATE INDEX idx_school ON college_profiles (selected_school(191)); CREATE INDEX idx_program ON college_profiles (selected_program(191)); CREATE INDEX idx_school_prog ON college_profiles (selected_school(191), selected_program(191)); /* ───────────────────────── misc small tables ──────────────────── */ ALTER TABLE milestones MODIFY COLUMN title VARCHAR(255) NULL, MODIFY COLUMN description MEDIUMTEXT NULL, MODIFY COLUMN date VARCHAR(32) NULL, MODIFY COLUMN progress VARCHAR(16) NULL; ALTER TABLE tasks MODIFY COLUMN title VARCHAR(255) NULL, MODIFY COLUMN description MEDIUMTEXT NULL, MODIFY COLUMN due_date VARCHAR(32) NULL; ALTER TABLE reminders MODIFY COLUMN phone_e164 VARCHAR(128) NULL, MODIFY COLUMN message_body MEDIUMTEXT NULL; ALTER TABLE milestone_impacts MODIFY COLUMN impact_type VARCHAR(64) NULL, MODIFY COLUMN direction VARCHAR(32) NULL, MODIFY COLUMN amount VARCHAR(128) NULL; ALTER TABLE user_profile ADD UNIQUE KEY ux_email_lookup (email_lookup); ALTER TABLE ai_risk_analysis MODIFY reasoning MEDIUMTEXT, MODIFY raw_prompt MEDIUMTEXT; ALTER TABLE ai_generated_ksa MODIFY knowledge_json MEDIUMTEXT, MODIFY abilities_json MEDIUMTEXT, MODIFY skills_json MEDIUMTEXT; ALTER TABLE ai_suggested_milestones MODIFY suggestion_text MEDIUMTEXT; ALTER TABLE context_cache MODIFY ctx_text MEDIUMTEXT; ALTER TABLE user_profile ADD COLUMN email_lookup CHAR(64) NOT NULL DEFAULT '' AFTER email; CREATE INDEX idx_user_profile_email_lookup ON user_profile (email_lookup); CREATE INDEX idx_password_resets_token_hash ON password_resets (token_hash); ALTER TABLE user_auth ADD COLUMN password_changed_at BIGINT UNSIGNED NULL AFTER hashed_password; -- Optional but useful: CREATE INDEX ix_user_auth_userid_changedat ON user_auth (user_id, password_changed_at); UPDATE user_auth SET hashed_password = ?, password_changed_at = FROM_UNIXTIME(?/1000) WHERE user_id = ? -- MySQL CREATE TABLE IF NOT EXISTS onboarding_drafts ( user_id BIGINT NOT NULL, id CHAR(36) NOT NULL, step TINYINT NOT NULL DEFAULT 0, data JSON NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (user_id), UNIQUE KEY uniq_id (id) ); -- ai_chat_threads: one row per conversation CREATE TABLE IF NOT EXISTS ai_chat_threads ( id CHAR(36) PRIMARY KEY, user_id BIGINT NOT NULL, bot_type ENUM('support','retire','coach') NOT NULL, title VARCHAR(200) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX (user_id, bot_type, updated_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ai_chat_messages: ordered messages in a thread CREATE TABLE IF NOT EXISTS ai_chat_messages ( id BIGINT AUTO_INCREMENT PRIMARY KEY, thread_id CHAR(36) NOT NULL, user_id BIGINT NOT NULL, role ENUM('user','assistant','system') NOT NULL, content MEDIUMTEXT NOT NULL, meta_json JSON NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX (thread_id, created_at), CONSTRAINT fk_chat_thread FOREIGN KEY (thread_id) REFERENCES ai_chat_threads(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- Orphan message thread_ids (no matching thread row) SELECT DISTINCT m.thread_id FROM ai_chat_messages m LEFT JOIN ai_chat_threads t ON t.id = m.thread_id WHERE t.id IS NULL; INSERT INTO ai_chat_threads (id, user_id, bot_type, title) SELECT m.thread_id, 58, 'coach', 'CareerCoach chat' FROM ai_chat_messages m LEFT JOIN ai_chat_threads t ON t.id = m.thread_id WHERE t.id IS NULL; ALTER TABLE ai_chat_messages ADD CONSTRAINT fk_messages_thread FOREIGN KEY (thread_id) REFERENCES ai_chat_threads(id) ON DELETE CASCADE;