mirror of
https://github.com/jcreek/LivingDexTracker.git
synced 2026-07-12 18:43:45 +00:00
feat(*): Change how data is set up
This commit is contained in:
@@ -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
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
|||||||
|
region
|
||||||
|
Kanto
|
||||||
|
Johto
|
||||||
|
Hoenn
|
||||||
|
Sinnoh
|
||||||
|
Unova
|
||||||
|
Galar
|
||||||
|
Alola
|
||||||
|
Kalos
|
||||||
|
Hisui
|
||||||
|
Paldea
|
||||||
|
Go
|
||||||
|
Home
|
||||||
|
Orre
|
||||||
|
Ranch
|
||||||
|
@@ -2,8 +2,7 @@
|
|||||||
export interface CatchRecord {
|
export interface CatchRecord {
|
||||||
_id: string; // Maps to Supabase 'id' field for frontend compatibility
|
_id: string; // Maps to Supabase 'id' field for frontend compatibility
|
||||||
userId: string;
|
userId: string;
|
||||||
pokedexEntryId: string; // String representation of the foreign key
|
pokemonId: string;
|
||||||
pokedexId: string; // NEW: Foreign key to pokedexes table
|
|
||||||
haveToEvolve: boolean;
|
haveToEvolve: boolean;
|
||||||
caught: boolean;
|
caught: boolean;
|
||||||
inHome: boolean;
|
inHome: boolean;
|
||||||
@@ -15,7 +14,8 @@ export interface CatchRecord {
|
|||||||
export interface CatchRecordDB {
|
export interface CatchRecordDB {
|
||||||
id: string;
|
id: string;
|
||||||
userId: 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
|
pokedexId: string; // NEW: Foreign key to pokedexes table
|
||||||
haveToEvolve: boolean;
|
haveToEvolve: boolean;
|
||||||
caught: boolean;
|
caught: boolean;
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ class CatchRecordRepository {
|
|||||||
return {
|
return {
|
||||||
_id: record.id,
|
_id: record.id,
|
||||||
userId: record.userId,
|
userId: record.userId,
|
||||||
pokedexEntryId: record.pokedexEntryId.toString(),
|
pokemonId: record.pokemonId.toString(),
|
||||||
pokedexId: record.pokedexId,
|
|
||||||
haveToEvolve: record.haveToEvolve,
|
haveToEvolve: record.haveToEvolve,
|
||||||
caught: record.caught,
|
caught: record.caught,
|
||||||
inHome: record.inHome,
|
inHome: record.inHome,
|
||||||
@@ -28,12 +27,12 @@ class CatchRecordRepository {
|
|||||||
// Transform frontend data to database format (minimal transformation)
|
// Transform frontend data to database format (minimal transformation)
|
||||||
private transformToDatabase(data: Partial<CatchRecord>): Partial<CatchRecordDB> {
|
private transformToDatabase(data: Partial<CatchRecord>): Partial<CatchRecordDB> {
|
||||||
const dbData: Partial<CatchRecordDB> = {};
|
const dbData: Partial<CatchRecordDB> = {};
|
||||||
if (data.pokedexEntryId !== undefined && data.pokedexEntryId !== null) {
|
if (data.pokemonId !== undefined && data.pokemonId !== null) {
|
||||||
const numericId = Number(data.pokedexEntryId);
|
const numericId = Number(data.pokemonId);
|
||||||
if (isNaN(numericId)) {
|
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.pokedexId !== undefined) dbData.pokedexId = data.pokedexId;
|
||||||
if (data.haveToEvolve !== undefined) dbData.haveToEvolve = data.haveToEvolve;
|
if (data.haveToEvolve !== undefined) dbData.haveToEvolve = data.haveToEvolve;
|
||||||
@@ -53,14 +52,14 @@ class CatchRecordRepository {
|
|||||||
* catch the error and fall back to per-record upserts.
|
* catch the error and fall back to per-record upserts.
|
||||||
*/
|
*/
|
||||||
async bulkUpsert(
|
async bulkUpsert(
|
||||||
records: Array<PartialExcept<CatchRecord, 'pokedexEntryId'>>
|
records: Array<PartialExcept<CatchRecord, 'pokemonId'>>
|
||||||
): Promise<CatchRecord[]> {
|
): Promise<CatchRecord[]> {
|
||||||
if (records.length === 0) return [];
|
if (records.length === 0) return [];
|
||||||
|
|
||||||
const dbRows: Partial<CatchRecordDB>[] = records.map((r, index) => {
|
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(
|
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.userId = this.userId;
|
||||||
mapped.pokedexId = this.pokedexId;
|
mapped.pokedexId = this.pokedexId;
|
||||||
|
|
||||||
if (mapped.pokedexEntryId === undefined || mapped.pokedexEntryId === null) {
|
if (mapped.pokemonId === undefined || mapped.pokemonId === null) {
|
||||||
throw new Error(
|
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
|
const { data: result, error } = await this.supabase
|
||||||
.from('catch_records')
|
.from('catch_records')
|
||||||
.upsert(dbRows, {
|
.upsert(dbRows, {
|
||||||
onConflict: '"userId","pokedexId","pokedexEntryId"'
|
onConflict: '"userId","pokedexId","pokemonId"'
|
||||||
})
|
})
|
||||||
.select();
|
.select();
|
||||||
|
|
||||||
@@ -198,13 +197,9 @@ class CatchRecordRepository {
|
|||||||
async upsert(data: Partial<CatchRecord>): Promise<CatchRecord | null> {
|
async upsert(data: Partial<CatchRecord>): Promise<CatchRecord | null> {
|
||||||
if (data._id) {
|
if (data._id) {
|
||||||
return this.update(data._id, data);
|
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
|
// Try to find existing record for this user, pokemon, and pokedex
|
||||||
const existing = await this.findByUserAndPokemon(
|
const existing = await this.findByUserAndPokemon(this.userId, data.pokemonId, this.pokedexId);
|
||||||
this.userId,
|
|
||||||
data.pokedexEntryId,
|
|
||||||
this.pokedexId
|
|
||||||
);
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
return this.update(existing._id, data);
|
return this.update(existing._id, data);
|
||||||
} else {
|
} else {
|
||||||
@@ -216,10 +211,10 @@ class CatchRecordRepository {
|
|||||||
|
|
||||||
async findByUserAndPokemon(
|
async findByUserAndPokemon(
|
||||||
userId: string,
|
userId: string,
|
||||||
pokedexEntryId: string,
|
pokemonId: string,
|
||||||
pokedexId: string
|
pokedexId: string
|
||||||
): Promise<CatchRecord | null> {
|
): Promise<CatchRecord | null> {
|
||||||
const numericId = Number(pokedexEntryId);
|
const numericId = Number(pokemonId);
|
||||||
if (isNaN(numericId)) {
|
if (isNaN(numericId)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -227,7 +222,7 @@ class CatchRecordRepository {
|
|||||||
.from('catch_records')
|
.from('catch_records')
|
||||||
.select('*')
|
.select('*')
|
||||||
.eq('userId', userId)
|
.eq('userId', userId)
|
||||||
.eq('pokedexEntryId', numericId)
|
.eq('pokemonId', numericId)
|
||||||
.eq('pokedexId', pokedexId)
|
.eq('pokedexId', pokedexId)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ class CombinedDataRepository {
|
|||||||
return {
|
return {
|
||||||
_id: record.id,
|
_id: record.id,
|
||||||
userId: record.userId,
|
userId: record.userId,
|
||||||
pokedexEntryId: record.pokedexEntryId.toString(),
|
// Backwards-compatible field name; DB column is pokemonId
|
||||||
|
pokedexEntryId: record.pokemonId.toString(),
|
||||||
pokedexId: record.pokedexId,
|
pokedexId: record.pokedexId,
|
||||||
haveToEvolve: record.haveToEvolve,
|
haveToEvolve: record.haveToEvolve,
|
||||||
caught: record.caught,
|
caught: record.caught,
|
||||||
@@ -87,7 +88,7 @@ class CombinedDataRepository {
|
|||||||
.select('*')
|
.select('*')
|
||||||
.eq('userId', userId)
|
.eq('userId', userId)
|
||||||
.eq('pokedexId', this.pokedexId)
|
.eq('pokedexId', this.pokedexId)
|
||||||
.in('pokedexEntryId', entryIds);
|
.in('pokemonId', entryIds);
|
||||||
|
|
||||||
if (!recordsError && records) {
|
if (!recordsError && records) {
|
||||||
catchRecords = records;
|
catchRecords = records;
|
||||||
@@ -96,8 +97,7 @@ class CombinedDataRepository {
|
|||||||
|
|
||||||
// Combine the data exactly like master branch
|
// Combine the data exactly like master branch
|
||||||
const combinedData = entries.map((entry) => {
|
const combinedData = entries.map((entry) => {
|
||||||
const userCatchRecord =
|
const userCatchRecord = catchRecords.find((record) => record.pokemonId === entry.id) || null;
|
||||||
catchRecords.find((record) => record.pokedexEntryId === entry.id) || null;
|
|
||||||
|
|
||||||
const transformedEntry = this.transformPokedexEntry(entry);
|
const transformedEntry = this.transformPokedexEntry(entry);
|
||||||
const transformedCatchRecord = userCatchRecord
|
const transformedCatchRecord = userCatchRecord
|
||||||
@@ -165,7 +165,7 @@ class CombinedDataRepository {
|
|||||||
.select('*')
|
.select('*')
|
||||||
.eq('userId', userId)
|
.eq('userId', userId)
|
||||||
.eq('pokedexId', this.pokedexId)
|
.eq('pokedexId', this.pokedexId)
|
||||||
.in('pokedexEntryId', entryIds);
|
.in('pokemonId', entryIds);
|
||||||
|
|
||||||
if (!recordsError && records) {
|
if (!recordsError && records) {
|
||||||
catchRecords = records;
|
catchRecords = records;
|
||||||
@@ -174,8 +174,7 @@ class CombinedDataRepository {
|
|||||||
|
|
||||||
// Combine the data exactly like master branch
|
// Combine the data exactly like master branch
|
||||||
const combinedData = entries.map((entry) => {
|
const combinedData = entries.map((entry) => {
|
||||||
const userCatchRecord =
|
const userCatchRecord = catchRecords.find((record) => record.pokemonId === entry.id) || null;
|
||||||
catchRecords.find((record) => record.pokedexEntryId === entry.id) || null;
|
|
||||||
|
|
||||||
const transformedEntry = this.transformPokedexEntry(entry);
|
const transformedEntry = this.transformPokedexEntry(entry);
|
||||||
const transformedCatchRecord = userCatchRecord
|
const transformedCatchRecord = userCatchRecord
|
||||||
|
|||||||
@@ -20,11 +20,12 @@ export async function calculateExpectedEntries(
|
|||||||
|
|
||||||
// Apply region filter: if gameScope is specified, filter by region
|
// Apply region filter: if gameScope is specified, filter by region
|
||||||
if (pokedex.gameScope) {
|
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
|
const { data: regionData, error: regionError } = await supabase
|
||||||
.from('region_game_mappings')
|
.from('games')
|
||||||
.select('region')
|
.select('region')
|
||||||
.eq('game', pokedex.gameScope)
|
.eq('displayName', pokedex.gameScope)
|
||||||
.maybeSingle();
|
.maybeSingle();
|
||||||
|
|
||||||
if (regionError) {
|
if (regionError) {
|
||||||
@@ -71,17 +72,18 @@ export async function populatePokedexMappings(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mappings = expectedEntryIds.map((pokedexEntryId) => ({
|
// New schema writes to pokedex_pokemon_mapping; column is pokemonId
|
||||||
|
const mappings = expectedEntryIds.map((pokemonId) => ({
|
||||||
pokedexId,
|
pokedexId,
|
||||||
pokedexEntryId
|
pokemonId
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Process in chunks to avoid request size limits
|
// Process in chunks to avoid request size limits
|
||||||
const CHUNK_SIZE = 500;
|
const CHUNK_SIZE = 500;
|
||||||
for (let i = 0; i < mappings.length; i += CHUNK_SIZE) {
|
for (let i = 0; i < mappings.length; i += CHUNK_SIZE) {
|
||||||
const chunk = mappings.slice(i, i + CHUNK_SIZE);
|
const chunk = mappings.slice(i, i + CHUNK_SIZE);
|
||||||
const { error } = await supabase.from('pokedex_entries_mapping').upsert(chunk, {
|
const { error } = await supabase.from('pokedex_pokemon_mapping').upsert(chunk, {
|
||||||
onConflict: 'pokedexId,pokedexEntryId',
|
onConflict: 'pokedexId,pokemonId',
|
||||||
ignoreDuplicates: true
|
ignoreDuplicates: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export type FlushOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function keyFor(record: CatchRecord): string {
|
function keyFor(record: CatchRecord): string {
|
||||||
return `${record.userId}:${record.pokedexId}:${record.pokedexEntryId}`;
|
return `${record.userId}:${record.pokedexId}:${record.pokemonId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function backoffMs(attempts: number): number {
|
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, 'Farfetch’d', 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();
|
||||||
+53
-19
@@ -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
|
-- After [`supabase/migrations/20260116210000_normalize_csv_schema.sql`](supabase/migrations/20260116210000_normalize_csv_schema.sql:1):
|
||||||
-- the deployed database. This migration re-defines the functions using
|
-- - catch_records references "pokemonId" (not "pokedexEntryId")
|
||||||
-- CREATE OR REPLACE so the corrected definitions take effect.
|
-- - 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()
|
CREATE OR REPLACE FUNCTION update_and_get_stats()
|
||||||
RETURNS TABLE (
|
RETURNS TABLE (
|
||||||
pokemon_caught BIGINT,
|
pokemon_caught BIGINT,
|
||||||
@@ -12,6 +51,7 @@ RETURNS TABLE (
|
|||||||
updated_at TIMESTAMPTZ
|
updated_at TIMESTAMPTZ
|
||||||
)
|
)
|
||||||
SECURITY DEFINER
|
SECURITY DEFINER
|
||||||
|
SET search_path = public, pg_temp
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
@@ -20,31 +60,29 @@ DECLARE
|
|||||||
v_completed_pokedexes BIGINT;
|
v_completed_pokedexes BIGINT;
|
||||||
v_updated_at TIMESTAMPTZ := NOW();
|
v_updated_at TIMESTAMPTZ := NOW();
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Calculate current stats: count of caught Pokémon
|
|
||||||
SELECT COUNT(*) INTO v_pokemon_caught
|
SELECT COUNT(*) INTO v_pokemon_caught
|
||||||
FROM catch_records
|
FROM catch_records
|
||||||
WHERE caught = true;
|
WHERE caught = true;
|
||||||
|
|
||||||
-- Calculate current stats: count of total users
|
|
||||||
SELECT COUNT(*) INTO v_total_users
|
SELECT COUNT(*) INTO v_total_users
|
||||||
FROM auth.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
|
SELECT COUNT(*) INTO v_completed_pokedexes
|
||||||
FROM (
|
FROM (
|
||||||
SELECT cr."pokedexId"
|
SELECT cr."pokedexId"
|
||||||
FROM catch_records cr
|
FROM catch_records cr
|
||||||
INNER JOIN pokedex_entries_mapping pem
|
INNER JOIN pokedex_pokemon_mapping ppm
|
||||||
ON cr."pokedexId" = pem."pokedexId"
|
ON cr."pokedexId" = ppm."pokedexId"
|
||||||
AND cr."pokedexEntryId" = pem."pokedexEntryId"
|
AND cr."pokemonId" = ppm."pokemonId"
|
||||||
GROUP BY cr."pokedexId"
|
GROUP BY cr."pokedexId"
|
||||||
HAVING COUNT(*) = COUNT(*) FILTER (WHERE cr.caught = true)
|
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;
|
) completed;
|
||||||
|
|
||||||
-- Update cache using UPSERT pattern
|
|
||||||
INSERT INTO stats_cache (id, pokemon_caught, total_users, completed_pokedexes, updated_at)
|
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)
|
VALUES (1, v_pokemon_caught, v_total_users, v_completed_pokedexes, v_updated_at)
|
||||||
ON CONFLICT (id) DO UPDATE
|
ON CONFLICT (id) DO UPDATE
|
||||||
@@ -53,13 +91,11 @@ BEGIN
|
|||||||
completed_pokedexes = EXCLUDED.completed_pokedexes,
|
completed_pokedexes = EXCLUDED.completed_pokedexes,
|
||||||
updated_at = v_updated_at;
|
updated_at = v_updated_at;
|
||||||
|
|
||||||
-- Return stats
|
|
||||||
RETURN QUERY
|
RETURN QUERY
|
||||||
SELECT v_pokemon_caught, v_total_users, v_completed_pokedexes, v_updated_at AS updated_at;
|
SELECT v_pokemon_caught, v_total_users, v_completed_pokedexes, v_updated_at AS updated_at;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION get_public_stats()
|
CREATE OR REPLACE FUNCTION get_public_stats()
|
||||||
RETURNS TABLE (
|
RETURNS TABLE (
|
||||||
pokemon_caught BIGINT,
|
pokemon_caught BIGINT,
|
||||||
@@ -73,23 +109,21 @@ AS $$
|
|||||||
DECLARE
|
DECLARE
|
||||||
v_cache_exists BOOLEAN;
|
v_cache_exists BOOLEAN;
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Check if cache exists and is fresh (within 24 hours)
|
|
||||||
SELECT EXISTS(
|
SELECT EXISTS(
|
||||||
SELECT 1 FROM stats_cache
|
SELECT 1 FROM stats_cache
|
||||||
WHERE stats_cache.updated_at > NOW() - INTERVAL '24 hours'
|
WHERE stats_cache.updated_at > NOW() - INTERVAL '24 hours'
|
||||||
) INTO v_cache_exists;
|
) INTO v_cache_exists;
|
||||||
|
|
||||||
IF v_cache_exists THEN
|
IF v_cache_exists THEN
|
||||||
-- Return cached stats (qualify updated_at to avoid ambiguity with output parameter)
|
|
||||||
RETURN QUERY
|
RETURN QUERY
|
||||||
SELECT sc.pokemon_caught, sc.total_users, sc.completed_pokedexes, sc.updated_at
|
SELECT sc.pokemon_caught, sc.total_users, sc.completed_pokedexes, sc.updated_at
|
||||||
FROM stats_cache sc
|
FROM stats_cache sc
|
||||||
ORDER BY sc.updated_at DESC
|
ORDER BY sc.updated_at DESC
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
ELSE
|
ELSE
|
||||||
-- Cache is stale or doesn't exist, update it
|
|
||||||
RETURN QUERY
|
RETURN QUERY
|
||||||
SELECT * FROM update_and_get_stats();
|
SELECT * FROM update_and_get_stats();
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
Reference in New Issue
Block a user