feat(*): Change how data is set up

This commit is contained in:
Josh Creek
2026-01-16 21:11:21 +00:00
parent 3f0003b006
commit ee73c01afe
22 changed files with 8253 additions and 1159 deletions
+46
View File
@@ -0,0 +1,46 @@
game,region,releaseYear
Red,Kanto,1996
Blue,Kanto,1996
Yellow,Kanto,1998
Gold,Johto,1999
Silver,Johto,1999
Crystal,Johto,2000
Ruby,Hoenn,2002
Sapphire,Hoenn,2002
Colosseum,Orre,2003
Emerald,Hoenn,2004
FireRed,Kanto,2004
LeafGreen,Kanto,2004
XD: Gale of Darkness,Orre,2005
Diamond,Sinnoh,2006
Pearl,Sinnoh,2006
Platinum,Sinnoh,2008
Ranch,Ranch,2008
Heart Gold,Johto,2009
Soul Silver,Johto,2009
Black,Unova,2010
White,Unova,2010
Black 2,Unova,2012
White 2,Unova,2012
Dream Radar,Unova,2012
X,Kalos,2013
Y,Kalos,2013
Omega Ruby,Hoenn,2014
Alpha Sapphire,Hoenn,2014
Sun,Alola,2016
Moon,Alola,2016
Moon (demo),Alola,2016
Go,Go,2016
Ultra Sun,Alola,2017
Ultra Moon,Alola,2017
LG: Pikachu,Kanto,2018
LG: Eevee,Kanto,2018
Sword,Galar,2019
Shield,Galar,2019
Home,Home,2020
Brilliant Diamond,Sinnoh,2021
Shining Pearl,Sinnoh,2021
Legends: Arceus,Hisui,2022
Scarlet,Paldea,2022
Violet,Paldea,2022
Legends: Z-A,Kalos,2025
1 game region releaseYear
2 Red Kanto 1996
3 Blue Kanto 1996
4 Yellow Kanto 1998
5 Gold Johto 1999
6 Silver Johto 1999
7 Crystal Johto 2000
8 Ruby Hoenn 2002
9 Sapphire Hoenn 2002
10 Colosseum Orre 2003
11 Emerald Hoenn 2004
12 FireRed Kanto 2004
13 LeafGreen Kanto 2004
14 XD: Gale of Darkness Orre 2005
15 Diamond Sinnoh 2006
16 Pearl Sinnoh 2006
17 Platinum Sinnoh 2008
18 Ranch Ranch 2008
19 Heart Gold Johto 2009
20 Soul Silver Johto 2009
21 Black Unova 2010
22 White Unova 2010
23 Black 2 Unova 2012
24 White 2 Unova 2012
25 Dream Radar Unova 2012
26 X Kalos 2013
27 Y Kalos 2013
28 Omega Ruby Hoenn 2014
29 Alpha Sapphire Hoenn 2014
30 Sun Alola 2016
31 Moon Alola 2016
32 Moon (demo) Alola 2016
33 Go Go 2016
34 Ultra Sun Alola 2017
35 Ultra Moon Alola 2017
36 LG: Pikachu Kanto 2018
37 LG: Eevee Kanto 2018
38 Sword Galar 2019
39 Shield Galar 2019
40 Home Home 2020
41 Brilliant Diamond Sinnoh 2021
42 Shining Pearl Sinnoh 2021
43 Legends: Arceus Hisui 2022
44 Scarlet Paldea 2022
45 Violet Paldea 2022
46 Legends: Z-A Kalos 2025
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
region
Kanto
Johto
Hoenn
Sinnoh
Unova
Galar
Alola
Kalos
Hisui
Paldea
Go
Home
Orre
Ranch
1 region
2 Kanto
3 Johto
4 Hoenn
5 Sinnoh
6 Unova
7 Galar
8 Alola
9 Kalos
10 Hisui
11 Paldea
12 Go
13 Home
14 Orre
15 Ranch
+3 -3
View File
@@ -2,8 +2,7 @@
export interface CatchRecord {
_id: string; // Maps to Supabase 'id' field for frontend compatibility
userId: string;
pokedexEntryId: string; // String representation of the foreign key
pokedexId: string; // NEW: Foreign key to pokedexes table
pokemonId: string;
haveToEvolve: boolean;
caught: boolean;
inHome: boolean;
@@ -15,7 +14,8 @@ export interface CatchRecord {
export interface CatchRecordDB {
id: string;
userId: string;
pokedexEntryId: number; // Numeric foreign key in database
/** Numeric foreign key in database (references `pokemon.id`) */
pokemonId: number;
pokedexId: string; // NEW: Foreign key to pokedexes table
haveToEvolve: boolean;
caught: boolean;
+16 -21
View File
@@ -15,8 +15,7 @@ class CatchRecordRepository {
return {
_id: record.id,
userId: record.userId,
pokedexEntryId: record.pokedexEntryId.toString(),
pokedexId: record.pokedexId,
pokemonId: record.pokemonId.toString(),
haveToEvolve: record.haveToEvolve,
caught: record.caught,
inHome: record.inHome,
@@ -28,12 +27,12 @@ class CatchRecordRepository {
// Transform frontend data to database format (minimal transformation)
private transformToDatabase(data: Partial<CatchRecord>): Partial<CatchRecordDB> {
const dbData: Partial<CatchRecordDB> = {};
if (data.pokedexEntryId !== undefined && data.pokedexEntryId !== null) {
const numericId = Number(data.pokedexEntryId);
if (data.pokemonId !== undefined && data.pokemonId !== null) {
const numericId = Number(data.pokemonId);
if (isNaN(numericId)) {
throw new Error(`Invalid pokedexEntryId: ${data.pokedexEntryId}`);
throw new Error(`Invalid pokemonId: ${data.pokemonId}`);
}
dbData.pokedexEntryId = numericId;
dbData.pokemonId = numericId;
}
if (data.pokedexId !== undefined) dbData.pokedexId = data.pokedexId;
if (data.haveToEvolve !== undefined) dbData.haveToEvolve = data.haveToEvolve;
@@ -53,14 +52,14 @@ class CatchRecordRepository {
* catch the error and fall back to per-record upserts.
*/
async bulkUpsert(
records: Array<PartialExcept<CatchRecord, 'pokedexEntryId'>>
records: Array<PartialExcept<CatchRecord, 'pokemonId'>>
): Promise<CatchRecord[]> {
if (records.length === 0) return [];
const dbRows: Partial<CatchRecordDB>[] = records.map((r, index) => {
if (r.pokedexEntryId === undefined || r.pokedexEntryId === null || r.pokedexEntryId === '') {
if (r.pokemonId === undefined || r.pokemonId === null || r.pokemonId === '') {
throw new Error(
`CatchRecordRepository.bulkUpsert: record at index ${index} is missing required field "pokedexEntryId"`
`CatchRecordRepository.bulkUpsert: record at index ${index} is missing required field "pokemonId"`
);
}
@@ -81,9 +80,9 @@ class CatchRecordRepository {
mapped.userId = this.userId;
mapped.pokedexId = this.pokedexId;
if (mapped.pokedexEntryId === undefined || mapped.pokedexEntryId === null) {
if (mapped.pokemonId === undefined || mapped.pokemonId === null) {
throw new Error(
`CatchRecordRepository.bulkUpsert: record at index ${index} produced no pokedexEntryId after transform`
`CatchRecordRepository.bulkUpsert: record at index ${index} produced no pokemonId after transform`
);
}
@@ -93,7 +92,7 @@ class CatchRecordRepository {
const { data: result, error } = await this.supabase
.from('catch_records')
.upsert(dbRows, {
onConflict: '"userId","pokedexId","pokedexEntryId"'
onConflict: '"userId","pokedexId","pokemonId"'
})
.select();
@@ -198,13 +197,9 @@ class CatchRecordRepository {
async upsert(data: Partial<CatchRecord>): Promise<CatchRecord | null> {
if (data._id) {
return this.update(data._id, data);
} else if (data.pokedexEntryId) {
} else if (data.pokemonId) {
// Try to find existing record for this user, pokemon, and pokedex
const existing = await this.findByUserAndPokemon(
this.userId,
data.pokedexEntryId,
this.pokedexId
);
const existing = await this.findByUserAndPokemon(this.userId, data.pokemonId, this.pokedexId);
if (existing) {
return this.update(existing._id, data);
} else {
@@ -216,10 +211,10 @@ class CatchRecordRepository {
async findByUserAndPokemon(
userId: string,
pokedexEntryId: string,
pokemonId: string,
pokedexId: string
): Promise<CatchRecord | null> {
const numericId = Number(pokedexEntryId);
const numericId = Number(pokemonId);
if (isNaN(numericId)) {
return null;
}
@@ -227,7 +222,7 @@ class CatchRecordRepository {
.from('catch_records')
.select('*')
.eq('userId', userId)
.eq('pokedexEntryId', numericId)
.eq('pokemonId', numericId)
.eq('pokedexId', pokedexId)
.single();
@@ -31,7 +31,8 @@ class CombinedDataRepository {
return {
_id: record.id,
userId: record.userId,
pokedexEntryId: record.pokedexEntryId.toString(),
// Backwards-compatible field name; DB column is pokemonId
pokedexEntryId: record.pokemonId.toString(),
pokedexId: record.pokedexId,
haveToEvolve: record.haveToEvolve,
caught: record.caught,
@@ -87,7 +88,7 @@ class CombinedDataRepository {
.select('*')
.eq('userId', userId)
.eq('pokedexId', this.pokedexId)
.in('pokedexEntryId', entryIds);
.in('pokemonId', entryIds);
if (!recordsError && records) {
catchRecords = records;
@@ -96,8 +97,7 @@ class CombinedDataRepository {
// Combine the data exactly like master branch
const combinedData = entries.map((entry) => {
const userCatchRecord =
catchRecords.find((record) => record.pokedexEntryId === entry.id) || null;
const userCatchRecord = catchRecords.find((record) => record.pokemonId === entry.id) || null;
const transformedEntry = this.transformPokedexEntry(entry);
const transformedCatchRecord = userCatchRecord
@@ -165,7 +165,7 @@ class CombinedDataRepository {
.select('*')
.eq('userId', userId)
.eq('pokedexId', this.pokedexId)
.in('pokedexEntryId', entryIds);
.in('pokemonId', entryIds);
if (!recordsError && records) {
catchRecords = records;
@@ -174,8 +174,7 @@ class CombinedDataRepository {
// Combine the data exactly like master branch
const combinedData = entries.map((entry) => {
const userCatchRecord =
catchRecords.find((record) => record.pokedexEntryId === entry.id) || null;
const userCatchRecord = catchRecords.find((record) => record.pokemonId === entry.id) || null;
const transformedEntry = this.transformPokedexEntry(entry);
const transformedCatchRecord = userCatchRecord
+9 -7
View File
@@ -20,11 +20,12 @@ export async function calculateExpectedEntries(
// Apply region filter: if gameScope is specified, filter by region
if (pokedex.gameScope) {
// Determine region from gameScope using region_game_mappings
// Determine region from gameScope using games table.
// We keep games.region as a denormalized convenience column.
const { data: regionData, error: regionError } = await supabase
.from('region_game_mappings')
.from('games')
.select('region')
.eq('game', pokedex.gameScope)
.eq('displayName', pokedex.gameScope)
.maybeSingle();
if (regionError) {
@@ -71,17 +72,18 @@ export async function populatePokedexMappings(
return;
}
const mappings = expectedEntryIds.map((pokedexEntryId) => ({
// New schema writes to pokedex_pokemon_mapping; column is pokemonId
const mappings = expectedEntryIds.map((pokemonId) => ({
pokedexId,
pokedexEntryId
pokemonId
}));
// Process in chunks to avoid request size limits
const CHUNK_SIZE = 500;
for (let i = 0; i < mappings.length; i += CHUNK_SIZE) {
const chunk = mappings.slice(i, i + CHUNK_SIZE);
const { error } = await supabase.from('pokedex_entries_mapping').upsert(chunk, {
onConflict: 'pokedexId,pokedexEntryId',
const { error } = await supabase.from('pokedex_pokemon_mapping').upsert(chunk, {
onConflict: 'pokedexId,pokemonId',
ignoreDuplicates: true
});
+1 -1
View File
@@ -53,7 +53,7 @@ export type FlushOptions = {
};
function keyFor(record: CatchRecord): string {
return `${record.userId}:${record.pokedexId}:${record.pokedexEntryId}`;
return `${record.userId}:${record.pokedexId}:${record.pokemonId}`;
}
function backoffMs(attempts: number): number {
@@ -1,21 +0,0 @@
-- Create regional_dex_numbers table for generic regional dex number storage
-- This table replaces region-specific columns (kanto_dex_number, johto_dex_number, etc.)
-- and allows adding new regions without schema changes
CREATE TABLE IF NOT EXISTS regional_dex_numbers (
id SERIAL PRIMARY KEY,
pokedex_entry_id INT NOT NULL REFERENCES pokedex_entries(id) ON DELETE CASCADE,
region TEXT NOT NULL,
dex_number INT NOT NULL,
UNIQUE(pokedex_entry_id, region)
);
-- Create indexes for better query performance
CREATE INDEX IF NOT EXISTS idx_regional_dex_numbers_region ON regional_dex_numbers(region, dex_number);
CREATE INDEX IF NOT EXISTS idx_regional_dex_numbers_entry ON regional_dex_numbers(pokedex_entry_id);
-- RLS policies (read-only reference data, same pattern as pokedex_entries)
ALTER TABLE regional_dex_numbers ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Allow public read access" ON regional_dex_numbers
FOR SELECT USING (true);
@@ -1,360 +0,0 @@
-- Seed Kanto region Pokémon (Gen 1)
-- Auto-generated from CSV files
-- Insert Kanto region-game mappings
INSERT INTO region_game_mappings (region, game) VALUES
('Kanto', 'Red'),
('Kanto', 'Blue'),
('Kanto', 'Yellow'),
('Kanto', 'LG: Pikachu'),
('Kanto', 'LG: Eevee')
ON CONFLICT (region, game) DO NOTHING;
-- Insert Kanto Pokémon entries
INSERT INTO pokedex_entries (
"pokedexNumber",
pokemon,
form,
"canGigantamax",
"regionToCatchIn",
"gamesToCatchIn"
) VALUES
(1, 'Bulbasaur', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(2, 'Ivysaur', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(3, 'Venusaur', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(3, 'Venusaur', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(4, 'Charmander', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(5, 'Charmeleon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(6, 'Charizard', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(7, 'Squirtle', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(8, 'Wartortle', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(9, 'Blastoise', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(10, 'Caterpie', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(11, 'Metapod', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(12, 'Butterfree', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(12, 'Butterfree', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(13, 'Weedle', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(14, 'Kakuna', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(15, 'Beedrill', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(16, 'Pidgey', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(17, 'Pidgeotto', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(18, 'Pidgeot', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(19, 'Rattata', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(19, 'Rattata', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(20, 'Raticate', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(20, 'Raticate', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(21, 'Spearow', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(22, 'Fearow', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(23, 'Ekans', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(24, 'Arbok', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(25, 'Pikachu', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(25, 'Pikachu', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(26, 'Raichu', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(26, 'Raichu', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(27, 'Sandshrew', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(28, 'Sandslash', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(29, 'Nidoran♀', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(30, 'Nidorina', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(31, 'Nidoqueen', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(32, 'Nidoran♂', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(33, 'Nidorino', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(34, 'Nidoking', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(35, 'Clefairy', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(36, 'Clefable', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(37, 'Vulpix', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(38, 'Ninetales', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(39, 'Jigglypuff', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(40, 'Wigglytuff', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(41, 'Zubat', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(41, 'Zubat', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(42, 'Golbat', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(42, 'Golbat', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(43, 'Oddish', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(44, 'Gloom', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(44, 'Gloom', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(45, 'Vileplume', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(45, 'Vileplume', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(46, 'Paras', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(47, 'Parasect', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(48, 'Venonat', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(49, 'Venomoth', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(50, 'Diglett', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(51, 'Dugtrio', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(52, 'Meowth', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(53, 'Persian', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(54, 'Psyduck', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(55, 'Golduck', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(56, 'Mankey', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(57, 'Primeape', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(58, 'Growlithe', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(59, 'Arcanine', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(60, 'Poliwag', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(61, 'Poliwhirl', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(62, 'Poliwrath', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(63, 'Abra', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(64, 'Kadabra', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(64, 'Kadabra', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(65, 'Alakazam', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(65, 'Alakazam', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(66, 'Machop', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(67, 'Machoke', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(68, 'Machamp', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(69, 'Bellsprout', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(70, 'Weepinbell', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(71, 'Victreebel', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(72, 'Tentacool', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(73, 'Tentacruel', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(74, 'Geodude', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(75, 'Graveler', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(76, 'Golem', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(77, 'Ponyta', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(78, 'Rapidash', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(79, 'Slowpoke', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(80, 'Slowbro', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(81, 'Magnemite', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(82, 'Magneton', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(83, 'Farfetchd', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(84, 'Doduo', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(84, 'Doduo', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(85, 'Dodrio', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(85, 'Dodrio', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(86, 'Seel', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(87, 'Dewgong', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(88, 'Grimer', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(89, 'Muk', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(90, 'Shellder', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(91, 'Cloyster', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(92, 'Gastly', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(93, 'Haunter', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(94, 'Gengar', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(95, 'Onix', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(96, 'Drowzee', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(97, 'Hypno', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(97, 'Hypno', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(98, 'Krabby', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(99, 'Kingler', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(100, 'Voltorb', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(101, 'Electrode', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(102, 'Exeggcute', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(103, 'Exeggutor', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(104, 'Cubone', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(105, 'Marowak', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(106, 'Hitmonlee', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(107, 'Hitmonchan', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(108, 'Lickitung', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(109, 'Koffing', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(110, 'Weezing', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(111, 'Rhyhorn', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(111, 'Rhyhorn', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(112, 'Rhydon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(112, 'Rhydon', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(113, 'Chansey', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(114, 'Tangela', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(115, 'Kangaskhan', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(116, 'Horsea', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(117, 'Seadra', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(118, 'Goldeen', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(118, 'Goldeen', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(119, 'Seaking', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(119, 'Seaking', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(120, 'Staryu', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(121, 'Starmie', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(122, 'Mr. Mime', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(123, 'Scyther', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(123, 'Scyther', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(124, 'Jynx', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(125, 'Electabuzz', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(126, 'Magmar', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(127, 'Pinsir', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(128, 'Tauros', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(129, 'Magikarp', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(129, 'Magikarp', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(130, 'Gyarados', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(130, 'Gyarados', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(131, 'Lapras', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(132, 'Ditto', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(133, 'Eevee', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(133, 'Eevee', 'Female', false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(134, 'Vaporeon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(135, 'Jolteon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(136, 'Flareon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(137, 'Porygon', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(138, 'Omanyte', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(139, 'Omastar', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(140, 'Kabuto', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(141, 'Kabutops', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(142, 'Aerodactyl', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(143, 'Snorlax', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(144, 'Articuno', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(145, 'Zapdos', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(146, 'Moltres', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(147, 'Dratini', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(148, 'Dragonair', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(149, 'Dragonite', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(150, 'Mewtwo', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee']),
(151, 'Mew', NULL, false, 'Kanto', ARRAY['Red', 'Blue', 'Yellow', 'LG: Pikachu', 'LG: Eevee'])
ON CONFLICT ON CONSTRAINT unique_pokemon_form DO NOTHING;
-- Insert Kanto regional dex numbers
INSERT INTO regional_dex_numbers (
pokedex_entry_id,
region,
dex_number
) VALUES
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 1 AND form IS NULL), 'Kanto', 1),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 2 AND form IS NULL), 'Kanto', 2),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 3 AND form IS NULL), 'Kanto', 3),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 4 AND form IS NULL), 'Kanto', 4),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 5 AND form IS NULL), 'Kanto', 5),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 6 AND form IS NULL), 'Kanto', 6),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 7 AND form IS NULL), 'Kanto', 7),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 8 AND form IS NULL), 'Kanto', 8),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 9 AND form IS NULL), 'Kanto', 9),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 10 AND form IS NULL), 'Kanto', 10),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 11 AND form IS NULL), 'Kanto', 11),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 12 AND form IS NULL), 'Kanto', 12),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 13 AND form IS NULL), 'Kanto', 13),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 14 AND form IS NULL), 'Kanto', 14),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 15 AND form IS NULL), 'Kanto', 15),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 16 AND form IS NULL), 'Kanto', 16),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 17 AND form IS NULL), 'Kanto', 17),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 18 AND form IS NULL), 'Kanto', 18),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 19 AND form IS NULL), 'Kanto', 19),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 20 AND form IS NULL), 'Kanto', 20),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 21 AND form IS NULL), 'Kanto', 21),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 22 AND form IS NULL), 'Kanto', 22),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 23 AND form IS NULL), 'Kanto', 23),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 24 AND form IS NULL), 'Kanto', 24),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 25 AND form IS NULL), 'Kanto', 25),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 26 AND form IS NULL), 'Kanto', 26),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 27 AND form IS NULL), 'Kanto', 27),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 28 AND form IS NULL), 'Kanto', 28),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 29 AND form IS NULL), 'Kanto', 29),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 30 AND form IS NULL), 'Kanto', 30),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 31 AND form IS NULL), 'Kanto', 31),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 32 AND form IS NULL), 'Kanto', 32),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 33 AND form IS NULL), 'Kanto', 33),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 34 AND form IS NULL), 'Kanto', 34),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 35 AND form IS NULL), 'Kanto', 35),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 36 AND form IS NULL), 'Kanto', 36),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 37 AND form IS NULL), 'Kanto', 37),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 38 AND form IS NULL), 'Kanto', 38),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 39 AND form IS NULL), 'Kanto', 39),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 40 AND form IS NULL), 'Kanto', 40),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 41 AND form IS NULL), 'Kanto', 41),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 42 AND form IS NULL), 'Kanto', 42),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 43 AND form IS NULL), 'Kanto', 43),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 44 AND form IS NULL), 'Kanto', 44),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 45 AND form IS NULL), 'Kanto', 45),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 46 AND form IS NULL), 'Kanto', 46),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 47 AND form IS NULL), 'Kanto', 47),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 48 AND form IS NULL), 'Kanto', 48),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 49 AND form IS NULL), 'Kanto', 49),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 50 AND form IS NULL), 'Kanto', 50),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 51 AND form IS NULL), 'Kanto', 51),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 52 AND form IS NULL), 'Kanto', 52),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 53 AND form IS NULL), 'Kanto', 53),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 54 AND form IS NULL), 'Kanto', 54),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 55 AND form IS NULL), 'Kanto', 55),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 56 AND form IS NULL), 'Kanto', 56),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 57 AND form IS NULL), 'Kanto', 57),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 58 AND form IS NULL), 'Kanto', 58),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 59 AND form IS NULL), 'Kanto', 59),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 60 AND form IS NULL), 'Kanto', 60),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 61 AND form IS NULL), 'Kanto', 61),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 62 AND form IS NULL), 'Kanto', 62),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 63 AND form IS NULL), 'Kanto', 63),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 64 AND form IS NULL), 'Kanto', 64),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 65 AND form IS NULL), 'Kanto', 65),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 66 AND form IS NULL), 'Kanto', 66),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 67 AND form IS NULL), 'Kanto', 67),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 68 AND form IS NULL), 'Kanto', 68),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 69 AND form IS NULL), 'Kanto', 69),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 70 AND form IS NULL), 'Kanto', 70),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 71 AND form IS NULL), 'Kanto', 71),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 72 AND form IS NULL), 'Kanto', 72),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 73 AND form IS NULL), 'Kanto', 73),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 74 AND form IS NULL), 'Kanto', 74),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 75 AND form IS NULL), 'Kanto', 75),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 76 AND form IS NULL), 'Kanto', 76),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 77 AND form IS NULL), 'Kanto', 77),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 78 AND form IS NULL), 'Kanto', 78),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 79 AND form IS NULL), 'Kanto', 79),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 80 AND form IS NULL), 'Kanto', 80),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 81 AND form IS NULL), 'Kanto', 81),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 82 AND form IS NULL), 'Kanto', 82),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 83 AND form IS NULL), 'Kanto', 83),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 84 AND form IS NULL), 'Kanto', 84),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 85 AND form IS NULL), 'Kanto', 85),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 86 AND form IS NULL), 'Kanto', 86),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 87 AND form IS NULL), 'Kanto', 87),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 88 AND form IS NULL), 'Kanto', 88),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 89 AND form IS NULL), 'Kanto', 89),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 90 AND form IS NULL), 'Kanto', 90),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 91 AND form IS NULL), 'Kanto', 91),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 92 AND form IS NULL), 'Kanto', 92),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 93 AND form IS NULL), 'Kanto', 93),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 94 AND form IS NULL), 'Kanto', 94),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 95 AND form IS NULL), 'Kanto', 95),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 96 AND form IS NULL), 'Kanto', 96),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 97 AND form IS NULL), 'Kanto', 97),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 98 AND form IS NULL), 'Kanto', 98),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 99 AND form IS NULL), 'Kanto', 99),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 100 AND form IS NULL), 'Kanto', 100),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 101 AND form IS NULL), 'Kanto', 101),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 102 AND form IS NULL), 'Kanto', 102),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 103 AND form IS NULL), 'Kanto', 103),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 104 AND form IS NULL), 'Kanto', 104),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 105 AND form IS NULL), 'Kanto', 105),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 106 AND form IS NULL), 'Kanto', 106),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 107 AND form IS NULL), 'Kanto', 107),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 108 AND form IS NULL), 'Kanto', 108),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 109 AND form IS NULL), 'Kanto', 109),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 110 AND form IS NULL), 'Kanto', 110),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 111 AND form IS NULL), 'Kanto', 111),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 112 AND form IS NULL), 'Kanto', 112),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 113 AND form IS NULL), 'Kanto', 113),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 114 AND form IS NULL), 'Kanto', 114),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 115 AND form IS NULL), 'Kanto', 115),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 116 AND form IS NULL), 'Kanto', 116),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 117 AND form IS NULL), 'Kanto', 117),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 118 AND form IS NULL), 'Kanto', 118),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 119 AND form IS NULL), 'Kanto', 119),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 120 AND form IS NULL), 'Kanto', 120),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 121 AND form IS NULL), 'Kanto', 121),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 122 AND form IS NULL), 'Kanto', 122),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 123 AND form IS NULL), 'Kanto', 123),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 124 AND form IS NULL), 'Kanto', 124),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 125 AND form IS NULL), 'Kanto', 125),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 126 AND form IS NULL), 'Kanto', 126),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 127 AND form IS NULL), 'Kanto', 127),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 128 AND form IS NULL), 'Kanto', 128),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 129 AND form IS NULL), 'Kanto', 129),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 130 AND form IS NULL), 'Kanto', 130),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 131 AND form IS NULL), 'Kanto', 131),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 132 AND form IS NULL), 'Kanto', 132),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 133 AND form IS NULL), 'Kanto', 133),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 134 AND form IS NULL), 'Kanto', 134),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 135 AND form IS NULL), 'Kanto', 135),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 136 AND form IS NULL), 'Kanto', 136),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 137 AND form IS NULL), 'Kanto', 137),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 138 AND form IS NULL), 'Kanto', 138),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 139 AND form IS NULL), 'Kanto', 139),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 140 AND form IS NULL), 'Kanto', 140),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 141 AND form IS NULL), 'Kanto', 141),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 142 AND form IS NULL), 'Kanto', 142),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 143 AND form IS NULL), 'Kanto', 143),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 144 AND form IS NULL), 'Kanto', 144),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 145 AND form IS NULL), 'Kanto', 145),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 146 AND form IS NULL), 'Kanto', 146),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 147 AND form IS NULL), 'Kanto', 147),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 148 AND form IS NULL), 'Kanto', 148),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 149 AND form IS NULL), 'Kanto', 149),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 150 AND form IS NULL), 'Kanto', 150),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 151 AND form IS NULL), 'Kanto', 151);
-- Add metadata
INSERT INTO metadata (key, value) VALUES
('kanto_seeded', 'true'),
('kanto_seed_date', NOW()::TEXT),
('kanto_pokemon_count', '151');
@@ -1,284 +0,0 @@
-- Seed Johto region Pokémon (Gen 2)
-- Auto-generated from CSV files
-- Insert Johto region-game mappings
INSERT INTO region_game_mappings (region, game) VALUES
('Johto', 'Gold'),
('Johto', 'Silver'),
('Johto', 'Crystal'),
('Johto', 'HeartGold'),
('Johto', 'SoulSilver')
ON CONFLICT (region, game) DO NOTHING;
-- Insert Johto Pokémon entries
INSERT INTO pokedex_entries (
"pokedexNumber",
pokemon,
form,
"canGigantamax",
"regionToCatchIn",
"gamesToCatchIn"
) VALUES
(152, 'Chikorita', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(153, 'Bayleef', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(154, 'Meganium', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(154, 'Meganium', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(155, 'Cyndaquil', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(156, 'Quilava', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(157, 'Typhlosion', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(158, 'Totodile', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(159, 'Croconaw', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(160, 'Feraligatr', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(161, 'Sentret', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(162, 'Furret', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(163, 'Hoothoot', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(164, 'Noctowl', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(165, 'Ledyba', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(165, 'Ledyba', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(166, 'Ledian', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(166, 'Ledian', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(167, 'Spinarak', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(168, 'Ariados', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(169, 'Crobat', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(170, 'Chinchou', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(171, 'Lanturn', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(172, 'Pichu', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(173, 'Cleffa', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(174, 'Igglybuff', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(175, 'Togepi', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(176, 'Togetic', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(177, 'Natu', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(178, 'Xatu', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(178, 'Xatu', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(179, 'Mareep', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(180, 'Flaaffy', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(181, 'Ampharos', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(182, 'Bellossom', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(183, 'Marill', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(184, 'Azumarill', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(185, 'Sudowoodo', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(185, 'Sudowoodo', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(186, 'Politoed', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(186, 'Politoed', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(187, 'Hoppip', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(188, 'Skiploom', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(189, 'Jumpluff', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(190, 'Aipom', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(190, 'Aipom', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(191, 'Sunkern', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(192, 'Sunflora', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(193, 'Yanma', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(194, 'Wooper', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(194, 'Wooper', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(195, 'Quagsire', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(195, 'Quagsire', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(196, 'Espeon', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(197, 'Umbreon', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(198, 'Murkrow', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(198, 'Murkrow', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(199, 'Slowking', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(200, 'Misdreavus', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'A', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'B', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'C', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'D', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'E', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'F', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'G', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'H', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'I', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'J', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'K', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'L', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'M', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'N', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'O', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'P', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'Q', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'R', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'S', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'T', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'U', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'V', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'W', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'X', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'Y', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', 'Z', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', '!', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(201, 'Unown', '?', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(202, 'Wobbuffet', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(202, 'Wobbuffet', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(203, 'Girafarig', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(203, 'Girafarig', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(204, 'Pineco', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(205, 'Forretress', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(206, 'Dunsparce', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(207, 'Gligar', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(207, 'Gligar', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(208, 'Steelix', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(208, 'Steelix', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(209, 'Snubbull', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(210, 'Granbull', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(211, 'Qwilfish', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(212, 'Scizor', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(212, 'Scizor', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(213, 'Shuckle', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(214, 'Heracross', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(214, 'Heracross', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(215, 'Sneasel', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(215, 'Sneasel', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(216, 'Teddiursa', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(217, 'Ursaring', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(217, 'Ursaring', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(218, 'Slugma', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(219, 'Magcargo', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(220, 'Swinub', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(221, 'Piloswine', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(221, 'Piloswine', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(222, 'Corsola', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(223, 'Remoraid', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(224, 'Octillery', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(224, 'Octillery', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(225, 'Delibird', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(226, 'Mantine', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(227, 'Skarmory', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(228, 'Houndour', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(229, 'Houndoom', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(229, 'Houndoom', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(230, 'Kingdra', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(231, 'Phanpy', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(232, 'Donphan', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(232, 'Donphan', 'Female', false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(233, 'Porygon2', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(234, 'Stantler', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(235, 'Smeargle', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(236, 'Tyrogue', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(237, 'Hitmontop', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(238, 'Smoochum', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(239, 'Elekid', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(240, 'Magby', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(241, 'Miltank', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(242, 'Blissey', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(243, 'Raikou', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(244, 'Entei', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(245, 'Suicune', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(246, 'Larvitar', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(247, 'Pupitar', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(248, 'Tyranitar', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(249, 'Lugia', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(250, 'Ho-Oh', NULL, false, 'Johto', ARRAY['Gold', 'Silver', 'Crystal', 'HeartGold', 'SoulSilver']),
(251, 'Celebi', NULL, false, 'Johto', ARRAY['Crystal', 'HeartGold', 'SoulSilver'])
ON CONFLICT ON CONSTRAINT unique_pokemon_form DO NOTHING;
-- Insert Johto regional dex numbers
INSERT INTO regional_dex_numbers (
pokedex_entry_id,
region,
dex_number
) VALUES
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 152 AND form IS NULL), 'Johto', 1),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 153 AND form IS NULL), 'Johto', 2),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 154 AND form IS NULL), 'Johto', 3),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 155 AND form IS NULL), 'Johto', 4),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 156 AND form IS NULL), 'Johto', 5),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 157 AND form IS NULL), 'Johto', 6),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 158 AND form IS NULL), 'Johto', 7),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 159 AND form IS NULL), 'Johto', 8),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 160 AND form IS NULL), 'Johto', 9),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 161 AND form IS NULL), 'Johto', 10),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 162 AND form IS NULL), 'Johto', 11),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 163 AND form IS NULL), 'Johto', 12),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 164 AND form IS NULL), 'Johto', 13),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 165 AND form IS NULL), 'Johto', 14),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 166 AND form IS NULL), 'Johto', 15),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 167 AND form IS NULL), 'Johto', 16),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 168 AND form IS NULL), 'Johto', 17),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 169 AND form IS NULL), 'Johto', 18),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 170 AND form IS NULL), 'Johto', 19),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 171 AND form IS NULL), 'Johto', 20),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 172 AND form IS NULL), 'Johto', 21),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 173 AND form IS NULL), 'Johto', 22),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 174 AND form IS NULL), 'Johto', 23),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 175 AND form IS NULL), 'Johto', 24),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 176 AND form IS NULL), 'Johto', 25),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 177 AND form IS NULL), 'Johto', 26),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 178 AND form IS NULL), 'Johto', 27),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 179 AND form IS NULL), 'Johto', 28),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 180 AND form IS NULL), 'Johto', 29),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 181 AND form IS NULL), 'Johto', 30),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 182 AND form IS NULL), 'Johto', 31),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 183 AND form IS NULL), 'Johto', 32),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 184 AND form IS NULL), 'Johto', 33),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 185 AND form IS NULL), 'Johto', 34),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 186 AND form IS NULL), 'Johto', 35),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 187 AND form IS NULL), 'Johto', 36),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 188 AND form IS NULL), 'Johto', 37),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 189 AND form IS NULL), 'Johto', 38),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 190 AND form IS NULL), 'Johto', 39),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 191 AND form IS NULL), 'Johto', 40),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 192 AND form IS NULL), 'Johto', 41),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 193 AND form IS NULL), 'Johto', 42),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 194 AND form IS NULL), 'Johto', 43),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 195 AND form IS NULL), 'Johto', 44),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 196 AND form IS NULL), 'Johto', 45),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 197 AND form IS NULL), 'Johto', 46),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 198 AND form IS NULL), 'Johto', 47),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 199 AND form IS NULL), 'Johto', 48),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 200 AND form IS NULL), 'Johto', 49),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 201 AND form = 'A'), 'Johto', 50),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 202 AND form IS NULL), 'Johto', 51),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 203 AND form IS NULL), 'Johto', 52),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 204 AND form IS NULL), 'Johto', 53),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 205 AND form IS NULL), 'Johto', 54),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 206 AND form IS NULL), 'Johto', 55),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 207 AND form IS NULL), 'Johto', 56),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 208 AND form IS NULL), 'Johto', 57),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 209 AND form IS NULL), 'Johto', 58),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 210 AND form IS NULL), 'Johto', 59),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 211 AND form IS NULL), 'Johto', 60),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 212 AND form IS NULL), 'Johto', 61),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 213 AND form IS NULL), 'Johto', 62),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 214 AND form IS NULL), 'Johto', 63),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 215 AND form IS NULL), 'Johto', 64),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 216 AND form IS NULL), 'Johto', 65),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 217 AND form IS NULL), 'Johto', 66),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 218 AND form IS NULL), 'Johto', 67),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 219 AND form IS NULL), 'Johto', 68),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 220 AND form IS NULL), 'Johto', 69),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 221 AND form IS NULL), 'Johto', 70),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 222 AND form IS NULL), 'Johto', 71),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 223 AND form IS NULL), 'Johto', 72),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 224 AND form IS NULL), 'Johto', 73),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 225 AND form IS NULL), 'Johto', 74),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 226 AND form IS NULL), 'Johto', 75),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 227 AND form IS NULL), 'Johto', 76),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 228 AND form IS NULL), 'Johto', 77),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 229 AND form IS NULL), 'Johto', 78),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 230 AND form IS NULL), 'Johto', 79),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 231 AND form IS NULL), 'Johto', 80),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 232 AND form IS NULL), 'Johto', 81),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 233 AND form IS NULL), 'Johto', 82),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 234 AND form IS NULL), 'Johto', 83),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 235 AND form IS NULL), 'Johto', 84),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 236 AND form IS NULL), 'Johto', 85),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 237 AND form IS NULL), 'Johto', 86),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 238 AND form IS NULL), 'Johto', 87),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 239 AND form IS NULL), 'Johto', 88),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 240 AND form IS NULL), 'Johto', 89),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 241 AND form IS NULL), 'Johto', 90),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 242 AND form IS NULL), 'Johto', 91),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 243 AND form IS NULL), 'Johto', 92),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 244 AND form IS NULL), 'Johto', 93),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 245 AND form IS NULL), 'Johto', 94),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 246 AND form IS NULL), 'Johto', 95),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 247 AND form IS NULL), 'Johto', 96),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 248 AND form IS NULL), 'Johto', 97),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 249 AND form IS NULL), 'Johto', 98),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 250 AND form IS NULL), 'Johto', 99),
((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = 251 AND form IS NULL), 'Johto', 100);
-- Add metadata
INSERT INTO metadata (key, value) VALUES
('johto_seeded', 'true'),
('johto_seed_date', NOW()::TEXT),
('johto_pokemon_count', '99');
@@ -1,217 +0,0 @@
-- Fix catch_records uniqueness for multi-pokédex support.
--
-- Intended invariant:
-- one catch record per (userId, pokedexId, pokedexEntryId)
--
-- This migration is written to be safe to run even if parts of the schema already exist.
-- 1) Ensure pokedexId column exists
ALTER TABLE public.catch_records
ADD COLUMN IF NOT EXISTS "pokedexId" UUID;
-- 1.5) Guards: fail early with diagnostics if the backfill would be unsafe.
--
-- Why:
-- - The backfill below only updates rows for users that have at least one pokedex.
-- If any user has catch_records but no pokedexes, those rows would remain NULL
-- and the NOT NULL enforcement would fail.
-- - The backfill assigns all NULL pokedexId rows to each user's oldest pokedex.
-- That can create duplicate (userId, pokedexId, pokedexEntryId) combinations.
--
-- If either condition is detected, this migration aborts and prints sample IDs.
DO $$
DECLARE
orphan_user_count bigint;
orphan_row_count bigint;
orphan_user_samples text;
dup_group_count bigint;
dup_group_samples text;
BEGIN
-- Guard A: Any catch_records with NULL pokedexId for users who have no pokedexes?
WITH orphan_users AS (
SELECT cr."userId" AS user_id,
COUNT(*)::bigint AS null_row_count
FROM public.catch_records cr
WHERE cr."pokedexId" IS NULL
AND NOT EXISTS (
SELECT 1
FROM public.pokedexes p
WHERE p."userId" = cr."userId"
)
GROUP BY cr."userId"
)
SELECT
COUNT(*)::bigint,
COALESCE(SUM(null_row_count), 0)::bigint,
(
SELECT string_agg(user_id::text, ', ' ORDER BY user_id::text)
FROM (
SELECT user_id, null_row_count
FROM orphan_users
ORDER BY null_row_count DESC, user_id
LIMIT 10
) s
)
INTO orphan_user_count, orphan_row_count, orphan_user_samples
FROM orphan_users;
IF orphan_user_count > 0 THEN
RAISE EXCEPTION
'USAFE MIGRATION: found % user(s) with % catch_records row(s) where "pokedexId" is NULL but the user has no pokedexes. Sample userId(s): %',
orphan_user_count,
orphan_row_count,
COALESCE(orphan_user_samples, '(none)');
END IF;
-- Guard B: Would assigning NULL pokedexId rows to each user''s oldest pokedex create duplicates?
WITH primary_pokedex AS (
SELECT DISTINCT ON ("userId") id AS pokedex_id, "userId"
FROM public.pokedexes
ORDER BY "userId", "createdAt" ASC
),
projected AS (
SELECT
cr."userId",
COALESCE(cr."pokedexId", pp.pokedex_id) AS projected_pokedex_id,
cr."pokedexEntryId"
FROM public.catch_records cr
LEFT JOIN primary_pokedex pp
ON pp."userId" = cr."userId"
),
dup_groups AS (
SELECT
"userId",
projected_pokedex_id,
"pokedexEntryId",
COUNT(*)::bigint AS row_count
FROM projected
WHERE projected_pokedex_id IS NOT NULL
GROUP BY "userId", projected_pokedex_id, "pokedexEntryId"
HAVING COUNT(*) > 1
)
SELECT
COUNT(*)::bigint,
(
SELECT string_agg(
format(
'(userId=%s, pokedexId=%s, pokedexEntryId=%s, rows=%s)',
"userId"::text,
projected_pokedex_id::text,
"pokedexEntryId"::text,
row_count::text
),
E'\n'
ORDER BY row_count DESC, "userId"::text
)
FROM (
SELECT * FROM dup_groups
ORDER BY row_count DESC, "userId"::text
LIMIT 10
) s
)
INTO dup_group_count, dup_group_samples
FROM dup_groups;
IF dup_group_count > 0 THEN
RAISE EXCEPTION
'USAFE MIGRATION: backfill would create % duplicate (userId, pokedexId, pokedexEntryId) group(s) after projecting NULL "pokedexId" to each user''s oldest pokedex. Sample group(s):\n%',
dup_group_count,
COALESCE(dup_group_samples, '(none)');
END IF;
END $$;
-- 2) Best-effort backfill for any NULL pokedexId rows.
-- Strategy: assign to the user's oldest pokedex.
WITH primary_pokedex AS (
SELECT DISTINCT ON ("userId") id AS pokedex_id, "userId"
FROM public.pokedexes
ORDER BY "userId", "createdAt" ASC
)
UPDATE public.catch_records cr
SET "pokedexId" = pp.pokedex_id
FROM primary_pokedex pp
WHERE cr."userId" = pp."userId"
AND cr."pokedexId" IS NULL;
-- 3) Enforce NOT NULL + FK
ALTER TABLE public.catch_records
ALTER COLUMN "pokedexId" SET NOT NULL;
-- Drop/recreate FK to avoid "already exists" name collisions.
ALTER TABLE public.catch_records
DROP CONSTRAINT IF EXISTS "catch_records_pokedexId_fkey";
ALTER TABLE public.catch_records
ADD CONSTRAINT "catch_records_pokedexId_fkey"
FOREIGN KEY ("pokedexId") REFERENCES public.pokedexes(id) ON DELETE CASCADE;
-- 4) Replace uniqueness constraint
ALTER TABLE public.catch_records
DROP CONSTRAINT IF EXISTS "catch_records_userId_pokedexEntryId_key";
ALTER TABLE public.catch_records
DROP CONSTRAINT IF EXISTS "catch_records_user_pokemon_pokedex_unique";
ALTER TABLE public.catch_records
ADD CONSTRAINT "catch_records_user_pokemon_pokedex_unique"
UNIQUE ("userId", "pokedexId", "pokedexEntryId");
-- 5) Helpful index
CREATE INDEX IF NOT EXISTS idx_catch_records_pokedex_id ON public.catch_records("pokedexId");
-- 6) RLS policies (recreate safely)
ALTER TABLE public.catch_records ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS "Users can view own catch records" ON public.catch_records;
DROP POLICY IF EXISTS "Users can insert own catch records" ON public.catch_records;
DROP POLICY IF EXISTS "Users can update own catch records" ON public.catch_records;
DROP POLICY IF EXISTS "Users can delete own catch records" ON public.catch_records;
CREATE POLICY "Users can view own catch records" ON public.catch_records
FOR SELECT USING (
auth.uid() = "userId" AND
EXISTS (
SELECT 1 FROM public.pokedexes
WHERE public.pokedexes.id = public.catch_records."pokedexId"
AND public.pokedexes."userId" = auth.uid()
)
);
CREATE POLICY "Users can insert own catch records" ON public.catch_records
FOR INSERT WITH CHECK (
auth.uid() = "userId" AND
EXISTS (
SELECT 1 FROM public.pokedexes
WHERE public.pokedexes.id = public.catch_records."pokedexId"
AND public.pokedexes."userId" = auth.uid()
)
);
CREATE POLICY "Users can update own catch records" ON public.catch_records
FOR UPDATE USING (
auth.uid() = "userId" AND
EXISTS (
SELECT 1 FROM public.pokedexes
WHERE public.pokedexes.id = public.catch_records."pokedexId"
AND public.pokedexes."userId" = auth.uid()
)
)
WITH CHECK (
auth.uid() = public.catch_records."userId" AND
EXISTS (
SELECT 1 FROM public.pokedexes
WHERE public.pokedexes.id = public.catch_records."pokedexId"
AND public.pokedexes."userId" = auth.uid()
)
);
CREATE POLICY "Users can delete own catch records" ON public.catch_records
FOR DELETE USING (
auth.uid() = "userId" AND
EXISTS (
SELECT 1 FROM public.pokedexes
WHERE public.pokedexes.id = public.catch_records."pokedexId"
AND public.pokedexes."userId" = auth.uid()
)
);
@@ -1,53 +0,0 @@
-- Create function to calculate and update stats in the cache table
-- This function runs the expensive queries and stores the results
CREATE OR REPLACE FUNCTION update_and_get_stats()
RETURNS TABLE (
pokemon_caught BIGINT,
total_users BIGINT,
completed_pokedexes BIGINT,
updated_at TIMESTAMPTZ
)
SECURITY DEFINER
SET search_path = public, pg_temp
LANGUAGE plpgsql
AS $$
DECLARE
v_pokemon_caught BIGINT;
v_total_users BIGINT;
v_completed_pokedexes BIGINT;
v_updated_at TIMESTAMPTZ := NOW();
BEGIN
-- Calculate current stats: count of caught Pokémon
SELECT COUNT(*) INTO v_pokemon_caught
FROM catch_records
WHERE caught = true;
-- Calculate current stats: count of total users
SELECT COUNT(*) INTO v_total_users
FROM auth.users;
-- Calculate current stats: count of completed pokedexes
-- A pokedex is completed when all its entries have caught = true
SELECT COUNT(*) INTO v_completed_pokedexes
FROM (
SELECT "pokedexId"
FROM catch_records
GROUP BY "pokedexId"
HAVING COUNT(*) = COUNT(*) FILTER (WHERE caught = true)
) completed;
-- Update cache using UPSERT pattern
INSERT INTO stats_cache (id, pokemon_caught, total_users, completed_pokedexes, updated_at)
VALUES (1, v_pokemon_caught, v_total_users, v_completed_pokedexes, v_updated_at)
ON CONFLICT (id) DO UPDATE
SET pokemon_caught = EXCLUDED.pokemon_caught,
total_users = EXCLUDED.total_users,
completed_pokedexes = EXCLUDED.completed_pokedexes,
updated_at = v_updated_at;
-- Return the stats
RETURN QUERY
SELECT v_pokemon_caught, v_total_users, v_completed_pokedexes, v_updated_at AS updated_at;
END;
$$;
@@ -1,36 +0,0 @@
-- Create function to get public stats with caching
-- Returns cached stats if fresh (within 24 hours), otherwise refreshes cache
CREATE OR REPLACE FUNCTION get_public_stats()
RETURNS TABLE (
pokemon_caught BIGINT,
total_users BIGINT,
completed_pokedexes BIGINT,
updated_at TIMESTAMPTZ
)
SECURITY DEFINER
LANGUAGE plpgsql
AS $$
DECLARE
v_cache_exists BOOLEAN;
BEGIN
-- Check if cache exists and is fresh (within 24 hours)
SELECT EXISTS(
SELECT 1 FROM stats_cache
WHERE stats_cache.updated_at > NOW() - INTERVAL '24 hours'
) INTO v_cache_exists;
IF v_cache_exists THEN
-- Return cached stats
RETURN QUERY
SELECT sc.pokemon_caught, sc.total_users, sc.completed_pokedexes, sc.updated_at
FROM stats_cache sc
ORDER BY sc.updated_at DESC
LIMIT 1;
ELSE
-- Cache is stale or doesn't exist, update it
RETURN QUERY
SELECT * FROM update_and_get_stats();
END IF;
END;
$$;
@@ -1,46 +0,0 @@
-- Add pokedex_entries_mapping table to explicitly define which entries belong to each pokedex
-- This fixes the stats functions ambiguity issue by providing an explicit definition of expected entries
-- Create pokedex_entries_mapping table
CREATE TABLE pokedex_entries_mapping (
id BIGSERIAL PRIMARY KEY,
"pokedexId" UUID NOT NULL REFERENCES pokedexes(id) ON DELETE CASCADE,
"pokedexEntryId" BIGINT NOT NULL REFERENCES pokedex_entries(id) ON DELETE CASCADE,
UNIQUE("pokedexId", "pokedexEntryId")
);
-- Create indexes for performance
CREATE INDEX idx_pokedex_entries_mapping_pokedex_id ON pokedex_entries_mapping("pokedexId");
CREATE INDEX idx_pokedex_entries_mapping_entry_id ON pokedex_entries_mapping("pokedexEntryId");
-- Enable RLS
ALTER TABLE pokedex_entries_mapping ENABLE ROW LEVEL SECURITY;
-- RLS Policies
-- Public read access (mapping is reference data)
CREATE POLICY "Anyone can view pokedex entries mapping"
ON pokedex_entries_mapping
FOR SELECT USING (true);
-- Users can only insert mappings for their own pokedexes
CREATE POLICY "Users can insert own pokedex mappings"
ON pokedex_entries_mapping
FOR INSERT WITH CHECK (
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = pokedex_entries_mapping."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
-- Users can only delete mappings for their own pokedexes
CREATE POLICY "Users can delete own pokedex mappings"
ON pokedex_entries_mapping
FOR DELETE USING (
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = pokedex_entries_mapping."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
@@ -1,24 +0,0 @@
-- Populate pokedex_entries_mapping for existing pokedexes
-- This is a one-time migration to backfill data for pokedexes created before the mapping table
-- Insert mappings for all existing pokedexes
-- Apply the same filtering logic that the application uses to determine expected entries
INSERT INTO pokedex_entries_mapping ("pokedexId", "pokedexEntryId")
SELECT
p.id AS "pokedexId",
pe.id AS "pokedexEntryId"
FROM pokedexes p
LEFT JOIN region_game_mappings rg ON rg.game = p."gameScope"
CROSS JOIN pokedex_entries pe
WHERE
-- Form filter: if isFormDex is false, only include base forms
-- Base forms have form IS NULL, except Unown which has no base form (use 'A')
(p."isFormDex" = true OR (pe.form IS NULL OR (pe.pokemon = 'Unown' AND pe.form = 'A')))
-- Game scope filter: if gameScope is specified, filter by game
AND (p."gameScope" IS NULL OR pe."gamesToCatchIn" @> ARRAY[p."gameScope"]::TEXT[])
-- Region filter: only enforce when a mapping exists
AND (p."gameScope" IS NULL OR rg.region IS NULL OR pe."regionToCatchIn" = rg.region)
ON CONFLICT ("pokedexId", "pokedexEntryId") DO NOTHING;
-- Add comment to document this migration
COMMENT ON TABLE pokedex_entries_mapping IS 'Junction table defining which pokedex_entries belong to each pokedex. Used for accurate stats calculation.';
@@ -1,44 +0,0 @@
-- Create function to atomically recalculate pokedex entries mapping
-- This function replaces the two-step delete+insert process with a single atomic operation
-- to prevent orphaned pokedexes if repopulation fails or yields an empty list
CREATE OR REPLACE FUNCTION recalculate_pokedex_mappings(
p_pokedex_id UUID,
p_entry_ids BIGINT[]
)
RETURNS VOID
SECURITY DEFINER
SET search_path = public, pg_temp
LANGUAGE plpgsql
AS $$
DECLARE
v_user_id UUID;
BEGIN
-- Validate that entry_ids is not empty to avoid accidental deletion
IF p_entry_ids IS NULL OR array_length(p_entry_ids, 1) IS NULL THEN
RAISE EXCEPTION 'entry_ids cannot be null or empty';
END IF;
-- Verify that the caller owns the target pokedex
SELECT "userId" INTO v_user_id
FROM pokedexes
WHERE id = p_pokedex_id;
IF v_user_id IS NULL THEN
RAISE EXCEPTION 'pokedex not found';
END IF;
IF auth.uid() IS NULL OR v_user_id IS DISTINCT FROM auth.uid() THEN
RAISE EXCEPTION 'not authorized: you do not own this pokedex';
END IF;
-- Delete old mappings and insert new mappings in a single transaction
-- This ensures atomicity - either both operations succeed or both fail
DELETE FROM pokedex_entries_mapping
WHERE "pokedexId" = p_pokedex_id;
INSERT INTO pokedex_entries_mapping ("pokedexId", "pokedexEntryId")
SELECT p_pokedex_id, unnest(p_entry_ids)
ON CONFLICT ("pokedexId", "pokedexEntryId") DO NOTHING;
END;
$$;
@@ -1,16 +0,0 @@
-- Fix RLS SELECT policy on pokedex_entries_mapping to restrict reads to owning user
-- This prevents exposing all user mappings and ensures consistency with INSERT/DELETE policies
-- Drop the overly permissive public SELECT policy
DROP POLICY IF EXISTS "Anyone can view pokedex entries mapping" ON pokedex_entries_mapping;
-- Create a new SELECT policy that restricts reads to the owning user
CREATE POLICY "Users can view own pokedex entries mapping"
ON pokedex_entries_mapping
FOR SELECT USING (
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = pokedex_entries_mapping."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
@@ -0,0 +1,212 @@
-- Normalize reference data schema to match data/csvs/*.csv
--
-- Target tables (reference data):
-- - regions
-- - games (add regionId FK, keep existing columns)
-- - pokemon (1 row per pokedexNumber + form, matching data/csvs/pokemon.csv)
-- - pokemon_origin_games (join table)
-- - region_game_mappings (join table)
--
-- Also migrates user data table catch_records to reference pokemon instead of pokedex_entries.
-- 1) Drop tables that are being replaced
--
-- NOTE: We intentionally keep auth.users and user-owned pokedexes.
-- Any existing catch_records will be wiped because the foreign key target changes.
TRUNCATE TABLE catch_records;
-- Drop old mapping table (depends on pokedex_entries)
DROP TABLE IF EXISTS pokedex_entries_mapping;
-- Drop old regional dex table (per approved plan)
DROP TABLE IF EXISTS regional_dex_numbers;
-- Drop old region-game mappings (string-based)
DROP TABLE IF EXISTS region_game_mappings;
-- Drop old pokedex entries table (legacy, replaced by pokemon)
DROP TABLE IF EXISTS pokedex_entries;
-- 2) Regions
CREATE TABLE IF NOT EXISTS regions (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
"createdAt" TIMESTAMPTZ DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE regions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Regions are publicly readable" ON regions
FOR SELECT TO public USING (true);
CREATE TRIGGER update_regions_updated_at
BEFORE UPDATE ON regions
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 3) Games: keep existing id + displayName, add regionId FK
ALTER TABLE games
ADD COLUMN IF NOT EXISTS "regionId" BIGINT;
ALTER TABLE games
ADD CONSTRAINT games_region_id_fkey
FOREIGN KEY ("regionId") REFERENCES regions(id)
ON DELETE RESTRICT;
CREATE INDEX IF NOT EXISTS idx_games_region_id ON games("regionId");
-- 4) Region ↔ Game mapping (normalized)
CREATE TABLE IF NOT EXISTS region_game_mappings (
"regionId" BIGINT NOT NULL REFERENCES regions(id) ON DELETE CASCADE,
"gameId" TEXT NOT NULL REFERENCES games(id) ON DELETE CASCADE,
PRIMARY KEY ("regionId", "gameId")
);
ALTER TABLE region_game_mappings ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Region-game mappings are publicly readable" ON region_game_mappings
FOR SELECT TO public USING (true);
CREATE INDEX IF NOT EXISTS idx_region_game_mappings_game_id ON region_game_mappings("gameId");
-- 5) Pokemon (1 row per pokedexNumber + form)
CREATE TABLE IF NOT EXISTS pokemon (
id BIGSERIAL PRIMARY KEY,
"pokedexNumber" INTEGER NOT NULL,
pokemon TEXT NOT NULL,
form TEXT,
"originRegionId" BIGINT NOT NULL REFERENCES regions(id) ON DELETE RESTRICT,
"canGigantamax" BOOLEAN DEFAULT FALSE,
"createdAt" TIMESTAMPTZ DEFAULT NOW(),
"updatedAt" TIMESTAMPTZ DEFAULT NOW()
);
-- Uniqueness: exactly one "base" (form IS NULL) per pokedex number
CREATE UNIQUE INDEX IF NOT EXISTS uniq_pokemon_base_form
ON pokemon("pokedexNumber")
WHERE form IS NULL;
-- Uniqueness: for non-null forms, unique per (pokedexNumber, form)
CREATE UNIQUE INDEX IF NOT EXISTS uniq_pokemon_number_form
ON pokemon("pokedexNumber", form)
WHERE form IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_pokemon_pokedex_number ON pokemon("pokedexNumber");
CREATE INDEX IF NOT EXISTS idx_pokemon_name ON pokemon(pokemon);
CREATE INDEX IF NOT EXISTS idx_pokemon_origin_region_id ON pokemon("originRegionId");
ALTER TABLE pokemon ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Pokemon are publicly readable" ON pokemon
FOR SELECT TO public USING (true);
CREATE TRIGGER update_pokemon_updated_at
BEFORE UPDATE ON pokemon
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- 6) Pokemon ↔ origin games (normalized)
CREATE TABLE IF NOT EXISTS pokemon_origin_games (
"pokemonId" BIGINT NOT NULL REFERENCES pokemon(id) ON DELETE CASCADE,
"gameId" TEXT NOT NULL REFERENCES games(id) ON DELETE CASCADE,
PRIMARY KEY ("pokemonId", "gameId")
);
ALTER TABLE pokemon_origin_games ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Pokemon origin games are publicly readable" ON pokemon_origin_games
FOR SELECT TO public USING (true);
CREATE INDEX IF NOT EXISTS idx_pokemon_origin_games_game_id ON pokemon_origin_games("gameId");
-- 6.1) Compatibility view: keep the app working while refactoring
--
-- This view matches the old pokedex_entries shape used by the app:
-- - id (BIGINT)
-- - "pokedexNumber"
-- - pokemon
-- - form
-- - "canGigantamax"
-- - "regionToCatchIn" (region name)
-- - "gamesToCatchIn" (TEXT[] of game display names)
-- - "createdAt" / "updatedAt"
--
-- Note: read-only. Inserts/updates should target the new tables.
CREATE OR REPLACE VIEW pokedex_entries AS
SELECT
p.id,
p."pokedexNumber",
p.pokemon,
p.form,
p."canGigantamax",
r.name AS "regionToCatchIn",
COALESCE(
ARRAY_AGG(g."displayName" ORDER BY g."displayName") FILTER (WHERE g.id IS NOT NULL),
ARRAY[]::TEXT[]
) AS "gamesToCatchIn",
p."createdAt",
p."updatedAt"
FROM pokemon p
JOIN regions r ON r.id = p."originRegionId"
LEFT JOIN pokemon_origin_games pog ON pog."pokemonId" = p.id
LEFT JOIN games g ON g.id = pog."gameId"
GROUP BY p.id, r.name;
-- RLS: views respect underlying table policies; keep it explicitly public readable
GRANT SELECT ON pokedex_entries TO anon, authenticated;
-- 7) Recreate pokedex mapping table for expected entries
-- (same intent as old pokedex_entries_mapping, but points at pokemon.id)
CREATE TABLE IF NOT EXISTS pokedex_pokemon_mapping (
id BIGSERIAL PRIMARY KEY,
"pokedexId" UUID NOT NULL REFERENCES pokedexes(id) ON DELETE CASCADE,
"pokemonId" BIGINT NOT NULL REFERENCES pokemon(id) ON DELETE CASCADE,
UNIQUE("pokedexId", "pokemonId")
);
CREATE INDEX IF NOT EXISTS idx_pokedex_pokemon_mapping_pokedex_id
ON pokedex_pokemon_mapping("pokedexId");
CREATE INDEX IF NOT EXISTS idx_pokedex_pokemon_mapping_pokemon_id
ON pokedex_pokemon_mapping("pokemonId");
ALTER TABLE pokedex_pokemon_mapping ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Anyone can view pokedex pokemon mapping"
ON pokedex_pokemon_mapping
FOR SELECT USING (true);
CREATE POLICY "Users can insert own pokedex pokemon mappings"
ON pokedex_pokemon_mapping
FOR INSERT WITH CHECK (
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = pokedex_pokemon_mapping."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
CREATE POLICY "Users can delete own pokedex pokemon mappings"
ON pokedex_pokemon_mapping
FOR DELETE USING (
EXISTS (
SELECT 1 FROM pokedexes
WHERE pokedexes.id = pokedex_pokemon_mapping."pokedexId"
AND pokedexes."userId" = auth.uid()
)
);
-- 8) Update catch_records to reference pokemon instead of pokedex_entries
ALTER TABLE catch_records
DROP COLUMN IF EXISTS "pokedexEntryId";
ALTER TABLE catch_records
ADD COLUMN IF NOT EXISTS "pokemonId" BIGINT REFERENCES pokemon(id) ON DELETE CASCADE;
ALTER TABLE catch_records
ALTER COLUMN "pokemonId" SET NOT NULL;
-- Drop old unique constraint (name may vary across environments)
ALTER TABLE catch_records
DROP CONSTRAINT IF EXISTS catch_records_user_pokemon_pokedex_unique;
ALTER TABLE catch_records
ADD CONSTRAINT catch_records_user_pokemon_pokedex_unique
UNIQUE("userId", "pokemonId", "pokedexId");
CREATE INDEX IF NOT EXISTS idx_catch_records_pokemon_id ON catch_records("pokemonId");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,137 @@
-- Seed regions + games + region_game_mappings from CSVs
-- Auto-generated from data/csvs/regions.csv and data/csvs/games.csv
-- Regions
INSERT INTO regions (name) VALUES
('Kanto'),
('Johto'),
('Hoenn'),
('Sinnoh'),
('Unova'),
('Galar'),
('Alola'),
('Kalos'),
('Hisui'),
('Paldea'),
('Go'),
('Home'),
('Orre'),
('Ranch')
ON CONFLICT (name) DO UPDATE SET
"updatedAt" = NOW();
-- Games
INSERT INTO games (id, "displayName", region, generation, "releaseYear", "regionId") VALUES
('red', 'Red', 'Kanto', 1, 1996, (SELECT id FROM regions WHERE name = 'Kanto')),
('blue', 'Blue', 'Kanto', 1, 1996, (SELECT id FROM regions WHERE name = 'Kanto')),
('yellow', 'Yellow', 'Kanto', 1, 1998, (SELECT id FROM regions WHERE name = 'Kanto')),
('gold', 'Gold', 'Johto', 2, 1999, (SELECT id FROM regions WHERE name = 'Johto')),
('silver', 'Silver', 'Johto', 2, 1999, (SELECT id FROM regions WHERE name = 'Johto')),
('crystal', 'Crystal', 'Johto', 2, 2000, (SELECT id FROM regions WHERE name = 'Johto')),
('ruby', 'Ruby', 'Hoenn', 3, 2002, (SELECT id FROM regions WHERE name = 'Hoenn')),
('sapphire', 'Sapphire', 'Hoenn', 3, 2002, (SELECT id FROM regions WHERE name = 'Hoenn')),
('colosseum', 'Colosseum', 'Orre', 3, 2003, (SELECT id FROM regions WHERE name = 'Orre')),
('emerald', 'Emerald', 'Hoenn', 3, 2004, (SELECT id FROM regions WHERE name = 'Hoenn')),
('firered', 'FireRed', 'Kanto', 1, 2004, (SELECT id FROM regions WHERE name = 'Kanto')),
('leafgreen', 'LeafGreen', 'Kanto', 1, 2004, (SELECT id FROM regions WHERE name = 'Kanto')),
('xd-gale-of-darkness', 'XD: Gale of Darkness', 'Orre', 3, 2005, (SELECT id FROM regions WHERE name = 'Orre')),
('diamond', 'Diamond', 'Sinnoh', 4, 2006, (SELECT id FROM regions WHERE name = 'Sinnoh')),
('pearl', 'Pearl', 'Sinnoh', 4, 2006, (SELECT id FROM regions WHERE name = 'Sinnoh')),
('platinum', 'Platinum', 'Sinnoh', 4, 2008, (SELECT id FROM regions WHERE name = 'Sinnoh')),
('ranch', 'Ranch', 'Ranch', 4, 2008, (SELECT id FROM regions WHERE name = 'Ranch')),
('heart-gold', 'Heart Gold', 'Johto', 2, 2009, (SELECT id FROM regions WHERE name = 'Johto')),
('soul-silver', 'Soul Silver', 'Johto', 2, 2009, (SELECT id FROM regions WHERE name = 'Johto')),
('black', 'Black', 'Unova', 5, 2010, (SELECT id FROM regions WHERE name = 'Unova')),
('white', 'White', 'Unova', 5, 2010, (SELECT id FROM regions WHERE name = 'Unova')),
('black-2', 'Black 2', 'Unova', 5, 2012, (SELECT id FROM regions WHERE name = 'Unova')),
('white-2', 'White 2', 'Unova', 5, 2012, (SELECT id FROM regions WHERE name = 'Unova')),
('dream-radar', 'Dream Radar', 'Unova', 5, 2012, (SELECT id FROM regions WHERE name = 'Unova')),
('x', 'X', 'Kalos', 6, 2013, (SELECT id FROM regions WHERE name = 'Kalos')),
('y', 'Y', 'Kalos', 6, 2013, (SELECT id FROM regions WHERE name = 'Kalos')),
('omega-ruby', 'Omega Ruby', 'Hoenn', 3, 2014, (SELECT id FROM regions WHERE name = 'Hoenn')),
('alpha-sapphire', 'Alpha Sapphire', 'Hoenn', 3, 2014, (SELECT id FROM regions WHERE name = 'Hoenn')),
('sun', 'Sun', 'Alola', 7, 2016, (SELECT id FROM regions WHERE name = 'Alola')),
('moon', 'Moon', 'Alola', 7, 2016, (SELECT id FROM regions WHERE name = 'Alola')),
('moon-(demo)', 'Moon (demo)', 'Alola', 7, 2016, (SELECT id FROM regions WHERE name = 'Alola')),
('go', 'Go', 'Go', 7, 2016, (SELECT id FROM regions WHERE name = 'Go')),
('ultra-sun', 'Ultra Sun', 'Alola', 7, 2017, (SELECT id FROM regions WHERE name = 'Alola')),
('ultra-moon', 'Ultra Moon', 'Alola', 7, 2017, (SELECT id FROM regions WHERE name = 'Alola')),
('lg-pikachu', 'LG: Pikachu', 'Kanto', 1, 2018, (SELECT id FROM regions WHERE name = 'Kanto')),
('lg-eevee', 'LG: Eevee', 'Kanto', 1, 2018, (SELECT id FROM regions WHERE name = 'Kanto')),
('sword', 'Sword', 'Galar', 8, 2019, (SELECT id FROM regions WHERE name = 'Galar')),
('shield', 'Shield', 'Galar', 8, 2019, (SELECT id FROM regions WHERE name = 'Galar')),
('home', 'Home', 'Home', 8, 2020, (SELECT id FROM regions WHERE name = 'Home')),
('brilliant-diamond', 'Brilliant Diamond', 'Sinnoh', 4, 2021, (SELECT id FROM regions WHERE name = 'Sinnoh')),
('shining-pearl', 'Shining Pearl', 'Sinnoh', 4, 2021, (SELECT id FROM regions WHERE name = 'Sinnoh')),
('legends-arceus', 'Legends: Arceus', 'Hisui', 8, 2022, (SELECT id FROM regions WHERE name = 'Hisui')),
('scarlet', 'Scarlet', 'Paldea', 9, 2022, (SELECT id FROM regions WHERE name = 'Paldea')),
('violet', 'Violet', 'Paldea', 9, 2022, (SELECT id FROM regions WHERE name = 'Paldea')),
('legends-z-a', 'Legends: Z-A', 'Kalos', 6, 2025, (SELECT id FROM regions WHERE name = 'Kalos'))
ON CONFLICT (id) DO UPDATE SET
"displayName" = EXCLUDED."displayName",
region = EXCLUDED.region,
generation = EXCLUDED.generation,
"releaseYear" = EXCLUDED."releaseYear",
"regionId" = EXCLUDED."regionId",
"updatedAt" = NOW();
-- Region ↔ game mappings
INSERT INTO region_game_mappings ("regionId", "gameId") VALUES
((SELECT id FROM regions WHERE name = 'Kanto'), 'red'),
((SELECT id FROM regions WHERE name = 'Kanto'), 'blue'),
((SELECT id FROM regions WHERE name = 'Kanto'), 'yellow'),
((SELECT id FROM regions WHERE name = 'Johto'), 'gold'),
((SELECT id FROM regions WHERE name = 'Johto'), 'silver'),
((SELECT id FROM regions WHERE name = 'Johto'), 'crystal'),
((SELECT id FROM regions WHERE name = 'Hoenn'), 'ruby'),
((SELECT id FROM regions WHERE name = 'Hoenn'), 'sapphire'),
((SELECT id FROM regions WHERE name = 'Orre'), 'colosseum'),
((SELECT id FROM regions WHERE name = 'Hoenn'), 'emerald'),
((SELECT id FROM regions WHERE name = 'Kanto'), 'firered'),
((SELECT id FROM regions WHERE name = 'Kanto'), 'leafgreen'),
((SELECT id FROM regions WHERE name = 'Orre'), 'xd-gale-of-darkness'),
((SELECT id FROM regions WHERE name = 'Sinnoh'), 'diamond'),
((SELECT id FROM regions WHERE name = 'Sinnoh'), 'pearl'),
((SELECT id FROM regions WHERE name = 'Sinnoh'), 'platinum'),
((SELECT id FROM regions WHERE name = 'Ranch'), 'ranch'),
((SELECT id FROM regions WHERE name = 'Johto'), 'heart-gold'),
((SELECT id FROM regions WHERE name = 'Johto'), 'soul-silver'),
((SELECT id FROM regions WHERE name = 'Unova'), 'black'),
((SELECT id FROM regions WHERE name = 'Unova'), 'white'),
((SELECT id FROM regions WHERE name = 'Unova'), 'black-2'),
((SELECT id FROM regions WHERE name = 'Unova'), 'white-2'),
((SELECT id FROM regions WHERE name = 'Unova'), 'dream-radar'),
((SELECT id FROM regions WHERE name = 'Kalos'), 'x'),
((SELECT id FROM regions WHERE name = 'Kalos'), 'y'),
((SELECT id FROM regions WHERE name = 'Hoenn'), 'omega-ruby'),
((SELECT id FROM regions WHERE name = 'Hoenn'), 'alpha-sapphire'),
((SELECT id FROM regions WHERE name = 'Alola'), 'sun'),
((SELECT id FROM regions WHERE name = 'Alola'), 'moon'),
((SELECT id FROM regions WHERE name = 'Alola'), 'moon-(demo)'),
((SELECT id FROM regions WHERE name = 'Go'), 'go'),
((SELECT id FROM regions WHERE name = 'Alola'), 'ultra-sun'),
((SELECT id FROM regions WHERE name = 'Alola'), 'ultra-moon'),
((SELECT id FROM regions WHERE name = 'Kanto'), 'lg-pikachu'),
((SELECT id FROM regions WHERE name = 'Kanto'), 'lg-eevee'),
((SELECT id FROM regions WHERE name = 'Galar'), 'sword'),
((SELECT id FROM regions WHERE name = 'Galar'), 'shield'),
((SELECT id FROM regions WHERE name = 'Home'), 'home'),
((SELECT id FROM regions WHERE name = 'Sinnoh'), 'brilliant-diamond'),
((SELECT id FROM regions WHERE name = 'Sinnoh'), 'shining-pearl'),
((SELECT id FROM regions WHERE name = 'Hisui'), 'legends-arceus'),
((SELECT id FROM regions WHERE name = 'Paldea'), 'scarlet'),
((SELECT id FROM regions WHERE name = 'Paldea'), 'violet'),
((SELECT id FROM regions WHERE name = 'Kalos'), 'legends-z-a')
ON CONFLICT ("regionId", "gameId") DO NOTHING;
-- Add metadata
INSERT INTO metadata (key, value) VALUES
('regions_seeded', 'true'),
('regions_seed_date', NOW()::TEXT),
('regions_count', '14'),
('games_seeded', 'true'),
('games_seed_date', NOW()::TEXT),
('games_count', '45')
ON CONFLICT (key) DO UPDATE SET
value = EXCLUDED.value,
"updatedAt" = NOW();
@@ -1,9 +1,48 @@
-- Fix ambiguous column reference errors in stats functions.
-- Update stats + mapping functions to use the new normalized schema.
--
-- If earlier migrations have already been applied, editing them will not update
-- the deployed database. This migration re-defines the functions using
-- CREATE OR REPLACE so the corrected definitions take effect.
-- After [`supabase/migrations/20260116210000_normalize_csv_schema.sql`](supabase/migrations/20260116210000_normalize_csv_schema.sql:1):
-- - catch_records references "pokemonId" (not "pokedexEntryId")
-- - explicit expected entries table is pokedex_pokemon_mapping (not pokedex_entries_mapping)
-- 1) Recalculate function: same name/signature, but targets pokedex_pokemon_mapping.
CREATE OR REPLACE FUNCTION recalculate_pokedex_mappings(
p_pokedex_id UUID,
p_entry_ids BIGINT[]
)
RETURNS VOID
SECURITY DEFINER
SET search_path = public, pg_temp
LANGUAGE plpgsql
AS $$
DECLARE
v_user_id UUID;
BEGIN
IF p_entry_ids IS NULL OR array_length(p_entry_ids, 1) IS NULL THEN
RAISE EXCEPTION 'entry_ids cannot be null or empty';
END IF;
SELECT "userId" INTO v_user_id
FROM pokedexes
WHERE id = p_pokedex_id;
IF v_user_id IS NULL THEN
RAISE EXCEPTION 'pokedex not found';
END IF;
IF auth.uid() IS NULL OR v_user_id IS DISTINCT FROM auth.uid() THEN
RAISE EXCEPTION 'not authorized: you do not own this pokedex';
END IF;
DELETE FROM pokedex_pokemon_mapping
WHERE "pokedexId" = p_pokedex_id;
INSERT INTO pokedex_pokemon_mapping ("pokedexId", "pokemonId")
SELECT p_pokedex_id, unnest(p_entry_ids)
ON CONFLICT ("pokedexId", "pokemonId") DO NOTHING;
END;
$$;
-- 2) Stats functions: completed pokedexes are those where all expected pokemon are caught.
CREATE OR REPLACE FUNCTION update_and_get_stats()
RETURNS TABLE (
pokemon_caught BIGINT,
@@ -12,6 +51,7 @@ RETURNS TABLE (
updated_at TIMESTAMPTZ
)
SECURITY DEFINER
SET search_path = public, pg_temp
LANGUAGE plpgsql
AS $$
DECLARE
@@ -20,31 +60,29 @@ DECLARE
v_completed_pokedexes BIGINT;
v_updated_at TIMESTAMPTZ := NOW();
BEGIN
-- Calculate current stats: count of caught Pokémon
SELECT COUNT(*) INTO v_pokemon_caught
FROM catch_records
WHERE caught = true;
-- Calculate current stats: count of total users
SELECT COUNT(*) INTO v_total_users
FROM auth.users;
-- Calculate current stats: count of completed pokedexes
-- A pokedex is completed when all its expected entries have caught = true
-- Join catch_records to pokedex_entries_mapping to validate against explicit expected entries
SELECT COUNT(*) INTO v_completed_pokedexes
FROM (
SELECT cr."pokedexId"
FROM catch_records cr
INNER JOIN pokedex_entries_mapping pem
ON cr."pokedexId" = pem."pokedexId"
AND cr."pokedexEntryId" = pem."pokedexEntryId"
INNER JOIN pokedex_pokemon_mapping ppm
ON cr."pokedexId" = ppm."pokedexId"
AND cr."pokemonId" = ppm."pokemonId"
GROUP BY cr."pokedexId"
HAVING COUNT(*) = COUNT(*) FILTER (WHERE cr.caught = true)
AND COUNT(*) = (SELECT COUNT(*) FROM pokedex_entries_mapping WHERE "pokedexId" = cr."pokedexId")
AND COUNT(*) = (
SELECT COUNT(*)
FROM pokedex_pokemon_mapping
WHERE "pokedexId" = cr."pokedexId"
)
) completed;
-- Update cache using UPSERT pattern
INSERT INTO stats_cache (id, pokemon_caught, total_users, completed_pokedexes, updated_at)
VALUES (1, v_pokemon_caught, v_total_users, v_completed_pokedexes, v_updated_at)
ON CONFLICT (id) DO UPDATE
@@ -53,13 +91,11 @@ BEGIN
completed_pokedexes = EXCLUDED.completed_pokedexes,
updated_at = v_updated_at;
-- Return stats
RETURN QUERY
SELECT v_pokemon_caught, v_total_users, v_completed_pokedexes, v_updated_at AS updated_at;
END;
$$;
CREATE OR REPLACE FUNCTION get_public_stats()
RETURNS TABLE (
pokemon_caught BIGINT,
@@ -73,23 +109,21 @@ AS $$
DECLARE
v_cache_exists BOOLEAN;
BEGIN
-- Check if cache exists and is fresh (within 24 hours)
SELECT EXISTS(
SELECT 1 FROM stats_cache
WHERE stats_cache.updated_at > NOW() - INTERVAL '24 hours'
) INTO v_cache_exists;
IF v_cache_exists THEN
-- Return cached stats (qualify updated_at to avoid ambiguity with output parameter)
RETURN QUERY
SELECT sc.pokemon_caught, sc.total_users, sc.completed_pokedexes, sc.updated_at
FROM stats_cache sc
ORDER BY sc.updated_at DESC
LIMIT 1;
ELSE
-- Cache is stale or doesn't exist, update it
RETURN QUERY
SELECT * FROM update_and_get_stats();
END IF;
END;
$$;