package main import ( "context" "log/slog" "github.com/jackc/pgx/v5/pgxpool" ) // initSchema creates the normalized inventory schema on an ingest-owned database // (a faithful replica of inventory-service/database.py). Run only when this // instance owns its DB (READ_ONLY=false) — never against production. Idempotent; // logs and continues per statement. func initSchema(ctx context.Context, pool *pgxpool.Pool, log *slog.Logger) { stmts := []string{ `CREATE TABLE IF NOT EXISTS items ( id SERIAL PRIMARY KEY, character_name VARCHAR(50) NOT NULL, item_id BIGINT NOT NULL, timestamp TIMESTAMP NOT NULL, name VARCHAR(200) NOT NULL, icon INTEGER NOT NULL, object_class INTEGER NOT NULL, value INTEGER DEFAULT 0, burden INTEGER DEFAULT 0, current_wielded_location INTEGER DEFAULT 0, container_id BIGINT DEFAULT 0, slot INTEGER DEFAULT -1, bonded INTEGER DEFAULT 0, attuned INTEGER DEFAULT 0, "unique" BOOLEAN DEFAULT false, stack_size INTEGER DEFAULT 1, max_stack_size INTEGER DEFAULT 1, items_capacity INTEGER, containers_capacity INTEGER, structure INTEGER, max_structure INTEGER, rare_id INTEGER, lifespan INTEGER, remaining_lifespan INTEGER, has_id_data BOOLEAN DEFAULT false, last_id_time BIGINT DEFAULT 0, CONSTRAINT uq_char_item UNIQUE (character_name, item_id) )`, `CREATE INDEX IF NOT EXISTS ix_items_character_name ON items (character_name)`, `CREATE INDEX IF NOT EXISTS ix_items_name ON items (name)`, `CREATE INDEX IF NOT EXISTS ix_items_object_class ON items (object_class)`, `CREATE INDEX IF NOT EXISTS ix_items_current_wielded_location ON items (current_wielded_location)`, `CREATE TABLE IF NOT EXISTS item_combat_stats ( item_id INTEGER PRIMARY KEY REFERENCES items(id), max_damage INTEGER, damage INTEGER, damage_type INTEGER, damage_bonus DOUBLE PRECISION, elemental_damage_bonus INTEGER, elemental_damage_vs_monsters DOUBLE PRECISION, variance DOUBLE PRECISION, cleaving INTEGER, crit_damage_rating INTEGER, damage_over_time INTEGER, attack_bonus DOUBLE PRECISION, weapon_time INTEGER, weapon_skill INTEGER, armor_level INTEGER, shield_value INTEGER, melee_defense_bonus DOUBLE PRECISION, missile_defense_bonus DOUBLE PRECISION, magic_defense_bonus DOUBLE PRECISION, resist_magic INTEGER, crit_resist_rating INTEGER, crit_damage_resist_rating INTEGER, dot_resist_rating INTEGER, life_resist_rating INTEGER, nether_resist_rating INTEGER, heal_over_time INTEGER, healing_resist_rating INTEGER, mana_conversion_bonus DOUBLE PRECISION, pk_damage_rating INTEGER, pk_damage_resist_rating INTEGER, gear_pk_damage_rating INTEGER, gear_pk_damage_resist_rating INTEGER, base_armor_level INTEGER, base_max_damage INTEGER, base_attack_bonus DOUBLE PRECISION, base_melee_defense_bonus DOUBLE PRECISION, base_elemental_damage_vs_monsters DOUBLE PRECISION, base_mana_conversion_bonus DOUBLE PRECISION )`, `CREATE INDEX IF NOT EXISTS ix_combat_armor ON item_combat_stats (armor_level)`, `CREATE TABLE IF NOT EXISTS item_requirements ( item_id INTEGER PRIMARY KEY REFERENCES items(id), wield_level INTEGER, wield_requirement INTEGER, skill_level INTEGER, lore_requirement INTEGER, equip_skill VARCHAR(50), mastery VARCHAR(50) )`, `CREATE INDEX IF NOT EXISTS ix_req_level ON item_requirements (wield_level)`, `CREATE TABLE IF NOT EXISTS item_enhancements ( item_id INTEGER PRIMARY KEY REFERENCES items(id), material VARCHAR(50), imbue VARCHAR(50), tinks INTEGER, workmanship DOUBLE PRECISION, salvage_workmanship DOUBLE PRECISION, num_times_tinkered INTEGER DEFAULT 0, free_tinkers_bitfield INTEGER, num_items_in_material INTEGER, imbue_attempts INTEGER DEFAULT 0, imbue_successes INTEGER DEFAULT 0, imbued_effect2 INTEGER, imbued_effect3 INTEGER, imbued_effect4 INTEGER, imbued_effect5 INTEGER, imbue_stacking_bits INTEGER, item_set VARCHAR(100), equipment_set_extra INTEGER, aetheria_bitfield INTEGER, heritage_specific_armor INTEGER, shared_cooldown INTEGER )`, `CREATE INDEX IF NOT EXISTS ix_enh_material_set ON item_enhancements (material, item_set)`, `CREATE TABLE IF NOT EXISTS item_ratings ( item_id INTEGER PRIMARY KEY REFERENCES items(id), damage_rating INTEGER, damage_resist_rating INTEGER, crit_rating INTEGER, crit_resist_rating INTEGER, crit_damage_rating INTEGER, crit_damage_resist_rating INTEGER, heal_boost_rating INTEGER, vitality_rating INTEGER, healing_rating INTEGER, mana_conversion_rating INTEGER, weakness_rating INTEGER, nether_over_time INTEGER, healing_resist_rating INTEGER, nether_resist_rating INTEGER, dot_resist_rating INTEGER, life_resist_rating INTEGER, sneak_attack_rating INTEGER, recklessness_rating INTEGER, deception_rating INTEGER, pk_damage_rating INTEGER, pk_damage_resist_rating INTEGER, gear_pk_damage_rating INTEGER, gear_pk_damage_resist_rating INTEGER, gear_damage INTEGER, gear_damage_resist INTEGER, gear_crit INTEGER, gear_crit_resist INTEGER, gear_crit_damage INTEGER, gear_crit_damage_resist INTEGER, gear_healing_boost INTEGER, gear_max_health INTEGER, gear_nether_resist INTEGER, gear_life_resist INTEGER, gear_overpower INTEGER, gear_overpower_resist INTEGER, total_rating INTEGER )`, `CREATE TABLE IF NOT EXISTS item_spells ( item_id INTEGER REFERENCES items(id), spell_id INTEGER, is_active BOOLEAN DEFAULT false, PRIMARY KEY (item_id, spell_id) )`, `CREATE TABLE IF NOT EXISTS item_raw_data ( item_id INTEGER PRIMARY KEY REFERENCES items(id), int_values JSONB, double_values JSONB, string_values JSONB, bool_values JSONB, original_json JSONB )`, } ok, failed := 0, 0 for _, s := range stmts { if _, err := pool.Exec(ctx, s); err != nil { failed++ log.Warn("schema statement failed (continuing)", "err", err) continue } ok++ } log.Info("inventory schema init complete", "ok", ok, "failed", failed) }