Initial commit.

This commit is contained in:
2026-01-28 22:05:35 +00:00
parent c4a0928edd
commit d72d9c705b
45 changed files with 3517 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
env_tcg/
env_tcg/*

BIN
backup_20251020.dump Normal file

Binary file not shown.

2
docker/.env Normal file
View File

@@ -0,0 +1,2 @@
PGADMIN_EMAIL=teddy@shuffleandskirmish.co.uk
PGADMIN_PASSWORD=k9a8f4jucJaFw3

36
docker/docker-compose.yml Normal file
View File

@@ -0,0 +1,36 @@
version: '3.8'
services:
trading_card_games_pgadmin:
image: dpage/pgadmin4:latest
container_name: trading_card_games_pgadmin
restart: unless-stopped
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_EMAIL:-admin@example.com}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_PASSWORD:-changeme}
PGADMIN_CONFIG_SERVER_MODE: 'True'
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
volumes:
- trading_card_games_pgadmin_data:/var/lib/pgadmin
networks:
- traefik-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.trading-card-games-pgadmin.rule=Host(`pgadmin.mtg.teddy.org.uk`)"
- "traefik.http.routers.trading-card-games-pgadmin.entrypoints=https"
- "traefik.http.routers.trading-card-games-pgadmin.tls=true"
- "traefik.http.routers.trading-card-games-pgadmin.tls.certresolver=le"
- "traefik.http.services.trading-card-games-pgadmin.loadbalancer.server.port=80"
- "traefik.http.routers.trading-card-games-pgadmin-http.rule=Host(`pgadmin.mtg.teddy.org.uk`)"
- "traefik.http.routers.trading-card-games-pgadmin-http.entrypoints=http"
- "traefik.http.routers.trading-card-games-pgadmin-http.middlewares=https-redirect@docker"
expose:
- "80"
volumes:
trading_card_games_pgadmin_data:
name: trading_card_games_pgadmin_data
networks:
traefik-public:
external: true

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,316 @@
import pandas as pd
import psycopg2
from psycopg2 import sql
from datetime import datetime
import numpy as np
import sys
# Database connection configuration
DB_CONFIG = {
'dbname': 'tcg',
'user': 'postgres',
'password': 'lick_pubes',
'host': 'localhost',
'port': '5432'
}
# Version history configuration
DEFAULT_USER_ID = 3
DEFAULT_CHANGE_SET_ID = 1
def clean_numeric(value):
"""Clean numeric values, handling various formats"""
if pd.isna(value) or value == '':
return None
if isinstance(value, str):
# Remove currency symbols, commas, and spaces
value = value.replace('£', '').replace('$', '').replace(',', '').strip()
try:
return float(value) if value else None
except:
return None
def clean_boolean(value):
"""Convert various boolean representations to Python boolean"""
if pd.isna(value):
return False
if isinstance(value, bool):
return value
if isinstance(value, str):
return value.lower() in ['yes', 'true', '1', 'y']
return bool(value)
def clean_datetime(value):
"""Convert various date formats to datetime"""
if pd.isna(value) or value == '':
return None
try:
return pd.to_datetime(value) # , dayfirst=True)
except:
return None
def clean_string(value, max_length=None):
"""Clean string values"""
if pd.isna(value) or value == '':
return None
value = str(value).strip()
if max_length and len(value) > max_length:
value = value[:max_length]
return value
def process_spreadsheet(file_path, connection):
"""Process the spreadsheet and insert records into database"""
# Read the Excel file
df = pd.read_excel(file_path, sheet_name='MTG Card Dictionary')
"""
pd.set_option('display.max_columns', None)
print('dataframe')
print(df)
"""
cursor = connection.cursor()
inserted_count = 0
skipped_count = 0
for index, row in df.iterrows():
try:
# print(index, row)
# Prepare data for insertion
data = {
'all_parts': clean_string(row['all_parts']),
'arena_id': clean_numeric(row['arena_id']),
'artist': clean_string(row['artist']),
'artist_ids': clean_string(row['artist_ids'], 250),
'booster': clean_boolean(row['booster']),
'border_color': clean_string(row['border_color']),
'card_back_id': clean_string(row['card_back_id'], 250),
'card_faces': clean_string(row['card_faces']),
'cardmarket_id': clean_numeric(row['cardmarket_id']),
'cmc': clean_numeric(row['cmc']),
'collector_number': clean_string(row['collector_number']),
'color_identity': clean_string(row['color_identity']),
'colors': clean_string(row['colors']),
'content_warning': clean_boolean(row['content_warning']),
'digital': clean_boolean(row['digital']),
'edhrec_rank': clean_numeric(row['edhrec_rank']),
'finishes': clean_string(row['finishes']),
'flavor_name': clean_string(row['flavor_name']),
'flavor_text': clean_string(row['flavor_text']),
'foil': clean_boolean(row['foil']),
'frame': clean_string(row['frame']),
'full_art': clean_boolean(row['full_art']),
'game_changer': clean_boolean(row['game_changer']),
'games': clean_string(row['games']),
'hand_modifier': clean_string(row['hand_modifier']),
'highres_image': clean_boolean(row['highres_image']),
'id': clean_string(row['id'], 100),
'image_status': clean_string(row['image_status']),
'image_uri_art_crop': clean_string(row['image_uri_art_crop']),
'image_uri_border_crop': clean_string(row['image_uri_border_crop']),
'image_uri_large': clean_string(row['image_uri_large']),
'image_uri_normal': clean_string(row['image_uri_normal']),
'image_uri_png': clean_string(row['image_uri_png']),
'image_uri_small': clean_string(row['image_uri_small']),
'keywords': clean_string(row['keywords']),
'lang': clean_string(row['lang']),
'layout': clean_string(row['layout']),
'legal_alchemy': clean_boolean(row['legal_alchemy']),
'legal_brawl': clean_boolean(row['legal_brawl']),
'legal_commander': clean_boolean(row['legal_commander']),
'legal_duel': clean_boolean(row['legal_duel']),
'legal_future': clean_boolean(row['legal_future']),
'legal_gladiator': clean_boolean(row['legal_gladiator']),
'legal_historic': clean_boolean(row['legal_historic']),
'legal_legacy': clean_boolean(row['legal_legacy']),
'legal_modern': clean_boolean(row['legal_modern']),
'legal_oathbreaker': clean_boolean(row['legal_oathbreaker']),
'legal_oldschool': clean_boolean(row['legal_oldschool']),
'legal_pauper': clean_boolean(row['legal_pauper']),
'legal_paupercommander': clean_boolean(row['legal_paupercommander']),
'legal_penny': clean_boolean(row['legal_penny']),
'legal_pioneer': clean_boolean(row['legal_pioneer']),
'legal_predh': clean_boolean(row['legal_predh']),
'legal_premodern': clean_boolean(row['legal_premodern']),
'legal_standard': clean_boolean(row['legal_standard']),
'legal_standardbrawl': clean_boolean(row['legal_standardbrawl']),
'legal_timeless': clean_boolean(row['legal_timeless']),
'legal_vintage': clean_boolean(row['legal_vintage']),
'life_modifier': clean_string(row['life_modifier']),
'loyalty': clean_string(row['loyalty']),
'mana_cost': clean_string(row['mana_cost']),
'mtgo_id': clean_numeric(row['mtgo_id']),
'multiverse_ids': clean_string(row['multiverse_ids'], 250),
'name': clean_string(row['name']),
'nonfoil': clean_boolean(row['nonfoil']),
'oracle_id': clean_string(row['oracle_id'], 100),
'oracle_text': clean_string(row['oracle_text']),
'oversized': clean_boolean(row['oversized']),
'penny_rank': clean_numeric(row['penny_rank']),
'power': clean_string(row['power']),
'preview_date': clean_datetime(row['preview_date']),
'preview_source': clean_string(row['preview_source']),
'preview_source_uri': clean_string(row['preview_source_uri']),
'price_eur': clean_numeric(row['price_eur']),
'price_eur_foil': clean_numeric(row['price_eur_foil']),
'price_tix': clean_numeric(row['price_tix']),
'price_usd': clean_numeric(row['price_usd']),
'price_usd_etched': clean_numeric(row['price_usd_etched']),
'price_usd_foil': clean_numeric(row['price_usd_foil']),
'printed_name': clean_string(row['printed_name']),
'printed_text': clean_string(row['printed_text']),
'printed_type_line': clean_string(row['printed_type_line']),
'prints_search_uri': clean_string(row['prints_search_uri']),
'produced_mana': clean_string(row['produced_mana']),
'promo': clean_boolean(row['promo']),
'purchase_cardhoarder': clean_string(row['purchase_cardhoarder']),
'purchase_cardmarket': clean_string(row['purchase_cardmarket']),
'purchase_tcgplayer': clean_string(row['purchase_tcgplayer']),
'rarity': clean_string(row['rarity']),
'released_at': clean_datetime(row['released_at']),
'reprint': clean_boolean(row['reprint']),
'reserved': clean_boolean(row['reserved']),
'rulings_uri': clean_string(row['rulings_uri']),
'scryfall_set_uri': clean_string(row['scryfall_set_uri']),
'scryfall_uri': clean_string(row['scryfall_uri']),
'security_stamp': clean_string(row['security_stamp']),
'set': clean_string(row['set']),
'set_id': clean_string(row['set_id'], 100),
'set_name': clean_string(row['set_name']),
'set_search_uri': clean_string(row['set_search_uri']),
'set_type': clean_string(row['set_type']),
'set_uri': clean_string(row['set_uri']),
'story_spotlight': clean_boolean(row['story_spotlight']),
'tcgplayer_id': clean_numeric(row['tcgplayer_id']),
'textless': clean_boolean(row['textless']),
'toughness': clean_string(row['toughness']),
'type_line': clean_string(row['type_line']),
'uri': clean_string(row['uri']),
'uri_edhrec': clean_string(row['uri_edhrec']),
'uri_gatherer': clean_string(row['uri_gatherer']),
'uri_tcgplayer_infinite_articles': clean_string(row['uri_tcgplayer_infinite_articles']),
'uri_tcgplayer_infinite_decks': clean_string(row['uri_tcgplayer_infinite_decks']),
'variation': clean_boolean(row['variation']),
'active': True,
'created_on': 'NOW()', # You might want to use database function or Python datetime
'created_by_user_id': DEFAULT_USER_ID,
'updated_last_on': 'NOW()',
'updated_last_by_user_id': DEFAULT_USER_ID,
'change_set_id': DEFAULT_CHANGE_SET_ID
}
# Build the insert query
insert_query = """
INSERT INTO tcg.public.TCG_MTG_Card (
all_parts, arena_id, artist, artist_ids, booster, border_color, card_back_id,
card_faces, cardmarket_id, cmc, collector_number, color_identity, colors,
content_warning, digital, edhrec_rank, finishes, flavor_name, flavor_text,
foil, frame, full_art, game_changer, games, hand_modifier, highres_image,
id, image_status, image_uri_art_crop, image_uri_border_crop, image_uri_large,
image_uri_normal, image_uri_png, image_uri_small, keywords, lang, layout,
legal_alchemy, legal_brawl, legal_commander, legal_duel, legal_future,
legal_gladiator, legal_historic, legal_legacy, legal_modern, legal_oathbreaker,
legal_oldschool, legal_pauper, legal_paupercommander, legal_penny, legal_pioneer,
legal_predh, legal_premodern, legal_standard, legal_standardbrawl, legal_timeless,
legal_vintage, life_modifier, loyalty, mana_cost, mtgo_id, multiverse_ids,
name, nonfoil, oracle_id, oracle_text, oversized, penny_rank, power,
preview_date, preview_source, preview_source_uri, price_eur, price_eur_foil,
price_tix, price_usd, price_usd_etched, price_usd_foil, printed_name,
printed_text, printed_type_line, prints_search_uri, produced_mana, promo,
purchase_cardhoarder, purchase_cardmarket, purchase_tcgplayer, rarity,
released_at, reprint, reserved, rulings_uri, scryfall_set_uri, scryfall_uri,
security_stamp, set, set_id, set_name, set_search_uri, set_type, set_uri,
story_spotlight, tcgplayer_id, textless, toughness, type_line, uri,
uri_edhrec, uri_gatherer, uri_tcgplayer_infinite_articles,
uri_tcgplayer_infinite_decks, variation, active, created_on, created_by_user_id,
updated_last_on, updated_last_by_user_id, change_set_id
) VALUES (
%(all_parts)s, %(arena_id)s, %(artist)s, %(artist_ids)s, %(booster)s, %(border_color)s, %(card_back_id)s,
%(card_faces)s, %(cardmarket_id)s, %(cmc)s, %(collector_number)s, %(color_identity)s, %(colors)s,
%(content_warning)s, %(digital)s, %(edhrec_rank)s, %(finishes)s, %(flavor_name)s, %(flavor_text)s,
%(foil)s, %(frame)s, %(full_art)s, %(game_changer)s, %(games)s, %(hand_modifier)s, %(highres_image)s,
%(id)s, %(image_status)s, %(image_uri_art_crop)s, %(image_uri_border_crop)s, %(image_uri_large)s,
%(image_uri_normal)s, %(image_uri_png)s, %(image_uri_small)s, %(keywords)s, %(lang)s, %(layout)s,
%(legal_alchemy)s, %(legal_brawl)s, %(legal_commander)s, %(legal_duel)s, %(legal_future)s,
%(legal_gladiator)s, %(legal_historic)s, %(legal_legacy)s, %(legal_modern)s, %(legal_oathbreaker)s,
%(legal_oldschool)s, %(legal_pauper)s, %(legal_paupercommander)s, %(legal_penny)s, %(legal_pioneer)s,
%(legal_predh)s, %(legal_premodern)s, %(legal_standard)s, %(legal_standardbrawl)s, %(legal_timeless)s,
%(legal_vintage)s, %(life_modifier)s, %(loyalty)s, %(mana_cost)s, %(mtgo_id)s, %(multiverse_ids)s,
%(name)s, %(nonfoil)s, %(oracle_id)s, %(oracle_text)s, %(oversized)s, %(penny_rank)s, %(power)s,
%(preview_date)s, %(preview_source)s, %(preview_source_uri)s, %(price_eur)s, %(price_eur_foil)s,
%(price_tix)s, %(price_usd)s, %(price_usd_etched)s, %(price_usd_foil)s, %(printed_name)s,
%(printed_text)s, %(printed_type_line)s, %(prints_search_uri)s, %(produced_mana)s, %(promo)s,
%(purchase_cardhoarder)s, %(purchase_cardmarket)s, %(purchase_tcgplayer)s, %(rarity)s,
%(released_at)s, %(reprint)s, %(reserved)s, %(rulings_uri)s, %(scryfall_set_uri)s, %(scryfall_uri)s,
%(security_stamp)s, %(set)s, %(set_id)s, %(set_name)s, %(set_search_uri)s, %(set_type)s, %(set_uri)s,
%(story_spotlight)s, %(tcgplayer_id)s, %(textless)s, %(toughness)s, %(type_line)s, %(uri)s,
%(uri_edhrec)s, %(uri_gatherer)s, %(uri_tcgplayer_infinite_articles)s,
%(uri_tcgplayer_infinite_decks)s, %(variation)s, %(active)s, %(created_on)s, %(created_by_user_id)s,
%(updated_last_on)s, %(updated_last_by_user_id)s, %(change_set_id)s
)
"""
cursor.execute(insert_query, data)
inserted_count += 1
if (index + 1) % 500 == 0:
print(f"Processed {index + 1} rows...")
try:
connection.commit()
except psycopg2.Error as e:
print(f'error: {e}')
connection.rollback()
except Exception as e:
print(f"Error processing row {index + 2}: {str(e)}")
skipped_count += 1
connection.rollback()
continue
# Final commit
try:
connection.commit()
except psycopg2.Error as e:
print(f'error: {e}')
connection.rollback()
print(f"\nImport completed:")
print(f"- Records inserted: {inserted_count}")
print(f"- Records skipped: {skipped_count}")
cursor.close()
def main():
"""Main function to run the import"""
file_path = '/home/teddy/Downloads/Trading Cards.xlsx'
# Connect to database
try:
print("Connecting to database...")
connection = psycopg2.connect(**DB_CONFIG)
print("Connected successfully!")
# Process the spreadsheet
print(f"\nProcessing {file_path}...")
process_spreadsheet(file_path, connection)
except Exception as e:
print(f"Error: {str(e)}")
sys.exit(1)
finally:
if 'connection' in locals() and connection:
connection.close()
print("\nDatabase connection closed.")
if __name__ == "__main__":
# Configuration notes:
# 1. Update DB_CONFIG with your actual database credentials
# 2. Set DEFAULT_USER_ID to your user_id in the system
# 3. Ensure TCG_MTG_Card table has records with matching Key IDs
# 4. Ensure TCG_MTG_Finish and TCG_Condition tables have the referenced IDs
main()

View File

@@ -0,0 +1,235 @@
import pandas as pd
import psycopg2
from psycopg2 import sql
from datetime import datetime
import numpy as np
import sys
# Database connection configuration
DB_CONFIG = {
'dbname': 'tcg',
'user': 'postgres',
'password': 'lick_pubes',
'host': 'localhost',
'port': '5432'
}
# Version history configuration
DEFAULT_USER_ID = 3
DEFAULT_CHANGE_SET_ID = 1
def clean_numeric(value):
"""Clean numeric values, handling various formats"""
if pd.isna(value) or value == '':
return None
if isinstance(value, str):
# Remove currency symbols, commas, and spaces
value = value.replace('£', '').replace('$', '').replace(',', '').strip()
try:
return float(value) if value else None
except:
return None
def clean_boolean(value):
"""Convert various boolean representations to Python boolean"""
if pd.isna(value):
return False
if isinstance(value, bool):
return value
if isinstance(value, str):
return value.lower() in ['yes', 'true', '1', 'y']
return bool(value)
def clean_datetime(value):
"""Convert various date formats to datetime"""
if pd.isna(value) or value == '':
return None
try:
return pd.to_datetime(value, dayfirst=True)
except:
return None
def clean_string(value, max_length=None):
"""Clean string values"""
if pd.isna(value) or value == '':
return None
value = str(value).strip()
if max_length and len(value) > max_length:
value = value[:max_length]
return value
"""
def get_or_create_card_id(cursor, key_id):
"" "Get card_id from TCG_MTG_Card table based on key_id, or create if needed"" "
# First check if card exists
cursor.execute("" "
SELECT card_id FROM tcg.public.TCG_MTG_Card
WHERE key_id = %s AND active = TRUE
"" ", (key_id,))
result = cursor.fetchone()
if result:
return result[0]
else:
# You might need to adjust this based on your card creation logic
print(f"Warning: No card found for key_id {key_id}. You may need to create the card record first.")
return None
"""
def process_spreadsheet(file_path, connection, minimum_row_number = 1):
"""Process the spreadsheet and insert records into database"""
# Read the Excel file
df = pd.read_excel(file_path, sheet_name='MTG Cards')
pd.set_option('display.max_columns', None)
print('dataframe')
print(df)
cursor = connection.cursor()
inserted_count = 0
skipped_count = 0
for index, row in df.iterrows():
if (index + 1 < minimum_row_number):
continue
if (clean_string(row['Name']) == 'Double-Faced Substitute Card'):
continue
try:
# print(index, row)
# Prepare data for insertion
data = {
'finish_id': int(row['Finish Id']) # if pd.notna(row['Finish Id']) else 1, # Default to 1 if missing
, 'condition_id': int(row['Condition Id']) # if pd.notna(row['Condition Id']) else 1, # Default to 1 if missing
, 'sleeve_colour_name': clean_string(row['Sleeve'], 250)
, 'location_name': clean_string(row['Location'], 250)
, 'acquired_from': clean_string(row['Acquired From'], 500)
, 'acquired_on': clean_datetime(row['Acquired On'])
, 'cost_gbp': clean_numeric(row['Cost']) # or 0.00, # Default to 0 if missing
, 'sale_price_gbp': clean_numeric(row['Sale Price'])
, 'is_sold': clean_boolean(row['Sold?'])
, 'is_destroyed': clean_boolean(row['Destroyed?'])
, 'notes': clean_string(row['Notes'])
, 'alterations_customisations': clean_string(row['Alterations / Customisations'], 250)
, 'grading_company_name': clean_string(row['Grading Company'], 250)
, 'grading_score': clean_string(row['Grading Score'], 250)
, 'subgrades': clean_string(row['Subgrades'], 250)
, 'misprint_errors': clean_string(row['Misprint Errors'], 250)
, 'miscut_errors': clean_string(row['Miscut Status'], 250)
, 'playability': clean_string(row['Playability'], 250)
, 'owner_user_id': DEFAULT_USER_ID # Using configured user ID
, 'ownership_status_name': clean_string(row['Ownership Status'], 250)
, 'trading_status_name': clean_string(row['Trading Status'], 250)
, 'loaned_to_user_id': None # You'll need to map "Loaned To" to user_id if needed
, 'loan_start_on': clean_datetime(row['Loan Start Date'])
, 'loan_end_on': clean_datetime(row['Loan End Date'])
, 'provenance': clean_string(row['Provenance - e.g. special history, tournaments used in'])
, 'signed_by_names': clean_string(row['Signed By'])
, 'signature_condition_name': clean_string(row['Signature Condition'], 250)
, 'active': True
, 'created_by_user_id': DEFAULT_USER_ID
, 'updated_last_by_user_id': DEFAULT_USER_ID
, 'change_set_id': DEFAULT_CHANGE_SET_ID
, 'set_code': clean_string(row['Set Code'])
, 'collector_number': clean_string(row['Collector Number'])
, 'display_order': int(row['ID'])
, 'name': clean_string(row['Name'])
, 'is_token': clean_boolean(row['Is Token?'])
, 'token_rear_side_name': clean_string(row['Token Rear Side Card Name'])
, 'token_rear_side_set_code': clean_string(row['Token Rear Side Card Set'])
, 'token_rear_side_collector_number': clean_string(row['Token Rear Side Card Collector Number'])
}
# Build the insert query
insert_query = """
INSERT INTO tcg.public.TCG_MTG_Inventory_Temp (
finish_id, condition_id, sleeve_colour_name, location_name,
acquired_from, acquired_on, cost_gbp, sale_price_gbp, is_sold,
is_destroyed, notes, alterations_customisations, grading_company_name,
grading_score, subgrades, misprint_errors, miscut_errors, playability,
owner_user_id, ownership_status_name, trading_status_name, loaned_to_user_id,
loan_start_on, loan_end_on, provenance, signed_by_names,
signature_condition_name, active, created_by_user_id,
updated_last_by_user_id, change_set_id,
set_code, collector_number, display_order, name,
is_token, token_rear_side_name, token_rear_side_set_code, token_rear_side_collector_number
) VALUES (
%(finish_id)s, %(condition_id)s, %(sleeve_colour_name)s, %(location_name)s,
%(acquired_from)s, %(acquired_on)s, %(cost_gbp)s, %(sale_price_gbp)s, %(is_sold)s,
%(is_destroyed)s, %(notes)s, %(alterations_customisations)s, %(grading_company_name)s,
%(grading_score)s, %(subgrades)s, %(misprint_errors)s, %(miscut_errors)s, %(playability)s,
%(owner_user_id)s, %(ownership_status_name)s, %(trading_status_name)s, %(loaned_to_user_id)s,
%(loan_start_on)s, %(loan_end_on)s, %(provenance)s, %(signed_by_names)s,
%(signature_condition_name)s, %(active)s, %(created_by_user_id)s,
%(updated_last_by_user_id)s, %(change_set_id)s,
%(set_code)s, %(collector_number)s, %(display_order)s, %(name)s,
%(is_token)s, %(token_rear_side_name)s, %(token_rear_side_set_code)s, %(token_rear_side_collector_number)s
)
"""
cursor.execute(insert_query, data)
inserted_count += 1
if (index + 1) % 100 == 0:
print(f"Processed {index + 1} rows...")
try:
connection.commit()
except psycopg2.Error as e:
print(f'error: {e}')
connection.rollback()
except Exception as e:
print(f"Error processing row {index + 2}: {str(e)}")
skipped_count += 1
connection.rollback()
continue
# Final commit
try:
connection.commit()
except psycopg2.Error as e:
print(f'error: {e}')
connection.rollback()
print(f"\nImport completed:")
print(f"- Records inserted: {inserted_count}")
print(f"- Records skipped: {skipped_count}")
cursor.close()
def main():
"""Main function to run the import"""
# file_path = '/home/teddy/Documents/Lifestyle/Trading Cards/Trading Cards upload.xlsx'
file_path = '/media/teddy/3_6GB-SSD-Storage/Documents/Lifestyle/Trading Cards/Trading Cards upload.xlsx'
minimum_row_number = 394 # ROW NUMBER - 1, or Inventory ID
# Connect to database
try:
print("Connecting to database...")
connection = psycopg2.connect(**DB_CONFIG)
print("Connected successfully!")
# Process the spreadsheet
print(f"\nProcessing {file_path}...")
process_spreadsheet(file_path, connection, minimum_row_number)
except Exception as e:
print(f"Error: {str(e)}")
sys.exit(1)
finally:
if 'connection' in locals() and connection:
connection.close()
print("\nDatabase connection closed.")
if __name__ == "__main__":
# Configuration notes:
# 1. Update DB_CONFIG with your actual database credentials
# 2. Set DEFAULT_USER_ID to your user_id in the system
# 3. Ensure TCG_MTG_Card table has records with matching Key IDs
# 4. Ensure TCG_MTG_Finish and TCG_Condition tables have the referenced IDs
main()

View File

@@ -0,0 +1,234 @@
import pandas as pd
from openpyxl import load_workbook
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import re
import time
ITEM_SHIPPING_COST_IN = 8
def get_eur_to_gbp_rate():
"""Fetch current EUR to GBP conversion rate"""
try:
response = requests.get('https://api.exchangerate-api.com/v4/latest/EUR', timeout=10)
data = response.json()
return data['rates']['GBP']
except Exception as e:
print(f"Error fetching exchange rate: {e}")
print("Using fallback rate: 0.85")
return 0.85
def parse_cardmarket_price(price_text):
"""Convert '141,30 €' format to float in EUR"""
if not price_text:
return None
price_clean = re.sub(r'[^\d,]', '', price_text)
price_clean = price_clean.replace(',', '.')
try:
return float(price_clean)
except ValueError:
return None
def setup_driver():
"""Setup Chrome driver with visible window"""
chrome_options = Options()
# Remove headless mode to see the browser
# chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--disable-blink-features=AutomationControlled')
chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36')
chrome_options.add_argument('--window-size=1920,1080')
try:
driver = webdriver.Chrome(options=chrome_options)
return driver
except Exception as e:
print(f"Error setting up Chrome driver: {e}")
print("Make sure Chrome and chromedriver are installed")
return None
def scrape_cardmarket_price_selenium(driver, url):
"""Scrape price from Card Market URL using Selenium"""
try:
print(f" Loading page...")
driver.get(url)
# Wait for page to load
time.sleep(3)
print(f" Page title: {driver.title}")
# Try multiple selector strategies
selectors = [
# Original selector
'#table > div:nth-child(1) > div.table-body > .row.article-row.g-0:nth-child(1) > div.col-offer > div.price-container > div > div:nth-child(1) > span:nth-child(1)',
# Simpler selectors
#'.article-row .price-container span',
#'span.price',
#'div.price span',
#'.font-weight-bold.color-primary',
]
for i, selector in enumerate(selectors):
try:
elements = driver.find_elements(By.CSS_SELECTOR, selector)
print(f" Selector {i+1}: Found {len(elements)} elements")
for elem in elements[:3]: # Check first 3
text = elem.text
print(f" Text: '{text}'")
if '' in text and re.search(r'\d', text):
print(f" ✓ Found price with selector {i+1}: {text}")
# input("Confirm")
return text
except Exception as e:
print(f" Selector {i+1} failed: {e}")
"""
# Try finding by text pattern
print(f" Trying to find price by text pattern...")
all_elements = driver.find_elements(By.XPATH, "//*[contains(text(), '')]")
print(f" Found {len(all_elements)} elements containing ''")
for elem in all_elements[:10]: # Check first 10
text = elem.text
if re.search(r'\d+[,\.]\d+\s*€', text):
print(f" ✓ Found price by pattern: {text}")
return text
"""
print(f" ✗ No price found")
input("Press Enter to continue to next URL...")
return None
except Exception as e:
print(f" Error: {e}")
return None
def main():
workbook_name = 'TCG Sole Trader Copy.xlsx'
sheet_name = 'Sourcing'
print("Loading workbook...")
wb = load_workbook(workbook_name)
if sheet_name not in wb.sheetnames:
print(f"Error: Sheet '{sheet_name}' not found")
return
sheet = wb[sheet_name]
# Find table boundaries
table_found = False
start_row = None
for row in range(1, sheet.max_row + 1):
if sheet.cell(row, 1).value == 'tbl_Sourcing' or 'Source Name' in str(sheet.cell(row, 3).value):
start_row = row + 1
table_found = True
break
if not table_found or not start_row:
for row in range(1, min(20, sheet.max_row + 1)):
if 'Source Name' in str(sheet.cell(row, 3).value):
start_row = row + 1
table_found = True
break
if not table_found:
print("Error: Could not find table 'tbl_Sourcing' or 'Source Name' column")
return
# Find column indices
header_row = start_row - 1
source_name_col = None
source_link_col = None
source_unit_cost_col = None
for col in range(1, sheet.max_column + 1):
header = str(sheet.cell(header_row, col).value).strip()
if 'Source Name' in header:
source_name_col = col
elif 'Source Link' in header:
source_link_col = col
elif 'Source Unit Cost' in header:
source_unit_cost_col = col
if not source_name_col:
source_name_col = 3
print(f"Starting from row {start_row}")
print(f"Columns - Source Name: {source_name_col}, Source Link: {source_link_col}, Source Unit Cost: {source_unit_cost_col}")
if not all([source_link_col, source_unit_cost_col]):
print("Error: Could not find required columns")
return
# Get EUR to GBP rate
eur_to_gbp = get_eur_to_gbp_rate()
print(f"Using EUR to GBP rate: {eur_to_gbp}")
# Setup Selenium driver
print("Setting up browser automation (browser will be visible)...")
driver = setup_driver()
if not driver:
return
try:
# Process rows
processed_count = 0
updated_count = 0
for row in range(start_row, sheet.max_row + 1):
source_name = sheet.cell(row, source_name_col).value
source_link = sheet.cell(row, source_link_col).value
# Check if row is empty
if not source_name and not source_link:
break
# Check conditions
if source_name == "Card Market" and source_link and str(source_link).strip():
processed_count += 1
print(f"\n{'='*60}")
print(f"Processing row {row}: {source_link}")
print(f"{'='*60}")
# Scrape price
price_text = scrape_cardmarket_price_selenium(driver, source_link)
if price_text:
print(f" Found price: {price_text}")
# Parse and convert
eur_price = parse_cardmarket_price(price_text)
if eur_price:
gbp_price = eur_price * eur_to_gbp
print(f" Converted: €{eur_price:.2f} → £{gbp_price:.2f}")
# Update cell
sheet.cell(row, source_unit_cost_col).value = gbp_price + ITEM_SHIPPING_COST_IN
updated_count += 1
else:
print(f" Error: Could not parse price")
else:
print(f" Error: Could not find price on page")
# Save workbook
print(f"\n{'='*60}")
print(f"Saving workbook...")
wb.save(workbook_name)
print(f"\nComplete!")
print(f"Processed: {processed_count} Card Market entries")
print(f"Updated: {updated_count} prices")
finally:
driver.quit()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,248 @@
import pandas as pd
from openpyxl import load_workbook
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options
import re
import time
def setup_driver(headless=True):
"""Setup Chrome driver"""
chrome_options = Options()
if headless:
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-dev-shm-usage')
chrome_options.add_argument('--disable-blink-features=AutomationControlled')
chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36')
chrome_options.add_argument('--window-size=1920,1080')
try:
driver = webdriver.Chrome(options=chrome_options)
return driver
except Exception as e:
print(f"Error setting up Chrome driver: {e}")
print("Make sure Chrome and chromedriver are installed")
return None
def parse_price_value(text):
"""Extract numeric value from price string like '$5.50' or '€5,50'"""
if not text:
return None
# Remove currency symbols and extract numbers
cleaned = re.sub(r'[^\d,.\-]', '', text)
# Replace comma with period for decimal
cleaned = cleaned.replace(',', '.')
try:
return float(cleaned)
except ValueError:
return None
def scrape_mtg_stocks_values(driver, url):
"""Scrape expected value and market value from MTG Stocks"""
try:
print(f" Loading page...")
driver.get(url)
# Wait for table to load
time.sleep(3)
# Valid booster types to match
valid_booster_types = [
'Play Booster',
'Set Booster',
'Booster',
'Play Booster Pack',
'Set Booster Pack',
'Booster Pack'
]
# Find all rows in the table
row_selector = 'mtg-sets-expected-value > mtg-product-tree > .table-responsive > table > tbody:nth-child(2) > tr'
rows = driver.find_elements(By.CSS_SELECTOR, row_selector)
print(f" Found {len(rows)} rows in table")
for row in rows:
try:
# Get the booster type from first column
booster_type_elem = row.find_element(By.CSS_SELECTOR, 'td:nth-child(1) > div.d-flex.align-items-center:nth-child(1) > a:nth-child(2)')
booster_type = booster_type_elem.text.strip()
print(f" Checking row: '{booster_type}'")
# Check if this matches our valid types
if booster_type in valid_booster_types:
print(f" ✓ Match found: '{booster_type}'")
# Get expected value (3rd column)
expected_value_elem = row.find_element(By.CSS_SELECTOR, 'td:nth-child(3)')
expected_value_text = expected_value_elem.text.strip()
# Get market value (5th column)
market_value_elem = row.find_element(By.CSS_SELECTOR, 'td:nth-child(5)')
market_value_text = market_value_elem.text.strip()
print(f" Expected Value: '{expected_value_text}'")
print(f" Market Value: '{market_value_text}'")
# Parse values
expected_value = parse_price_value(expected_value_text)
market_value = parse_price_value(market_value_text)
return {
'expected_value': expected_value,
'market_value': market_value,
'found': True
}
except Exception as e:
# Row doesn't match structure, continue to next
continue
print(f" ✗ No matching booster type found")
return {
'expected_value': None,
'market_value': None,
'found': False
}
except Exception as e:
print(f" Error: {e}")
return {
'expected_value': None,
'market_value': None,
'found': False
}
def main():
workbook_name = 'TCG Sole Trader Copy.xlsx'
sheet_name = 'MTG Set'
print("Loading workbook...")
wb = load_workbook(workbook_name)
if sheet_name not in wb.sheetnames:
print(f"Error: Sheet '{sheet_name}' not found")
return
sheet = wb[sheet_name]
# Find table boundaries and columns
table_found = False
start_row = None
header_row = None
# Search for table header
print("max sheet column: ", str(sheet.max_column))
for row in range(2, max(50, sheet.max_row + 1)):
cell_value = str(sheet.cell(row, 1).value)
# Check multiple columns for table indicators
for col in range(1, max(10, sheet.max_column + 1)):
cell_value = str(sheet.cell(row, col).value)
if 'EV MTG Stocks Link' in cell_value:
header_row = row
start_row = row + 1
table_found = True
break
if table_found:
break
if not table_found:
print("Error: Could not find 'EV MTG Stocks Link' column")
return
print(f"Found table header at row {header_row}")
print(f"Starting from row {start_row}")
# Find column indices
ev_link_col = None
expected_value_col = None
market_value_col = None
for col in range(1, sheet.max_column + 1):
header = str(sheet.cell(header_row, col).value).strip()
if 'EV MTG Stocks Link' in header:
ev_link_col = col
elif 'Play Booster Expected Market Value' in header:
expected_value_col = col
elif 'Play Boost Sealed Market Value' in header:
market_value_col = col
print(f"Columns - EV Link: {ev_link_col}, Expected Value: {expected_value_col}, Market Value: {market_value_col}")
if not all([ev_link_col, expected_value_col, market_value_col]):
print("Error: Could not find all required columns")
print(f" EV MTG Stocks Link: {'Found' if ev_link_col else 'NOT FOUND'}")
print(f" Play Booster Expected Market Value: {'Found' if expected_value_col else 'NOT FOUND'}")
print(f" Play Boost Sealed Market Value: {'Found' if market_value_col else 'NOT FOUND'}")
return
# Setup Selenium driver
print("Setting up browser automation...")
driver = setup_driver(headless=False) # Set to False to see browser
if not driver:
return
try:
# Process rows
processed_count = 0
updated_count = 0
cleared_count = 0
for row in range(start_row, sheet.max_row + 1):
ev_link = sheet.cell(row, ev_link_col).value
# Check if row is empty
if not ev_link:
# Check if we've passed the end of the table
empty_count = 0
for check_col in range(1, min(10, sheet.max_column + 1)):
if not sheet.cell(row, check_col).value:
empty_count += 1
if empty_count >= 5: # If most columns are empty, assume end of table
break
continue
processed_count += 1
print(f"\n{'='*80}")
print(f"Processing row {row}: {ev_link}")
print(f"{'='*80}")
# Scrape values
result = scrape_mtg_stocks_values(driver, ev_link)
if result['found']:
# Update cells with found values
sheet.cell(row, expected_value_col).value = result['expected_value']
sheet.cell(row, market_value_col).value = result['market_value']
updated_count += 1
print(f" ✓ Updated - Expected: {result['expected_value']}, Market: {result['market_value']}")
else:
# Clear cells - no matching booster type found
sheet.cell(row, expected_value_col).value = ''
sheet.cell(row, market_value_col).value = ''
cleared_count += 1
print(f" ✗ Cleared values - no matching booster type found")
# Small delay between requests
time.sleep(2)
# Save workbook
print(f"\n{'='*80}")
print(f"Saving workbook...")
wb.save(workbook_name)
print(f"\nComplete!")
print(f"Processed: {processed_count} entries")
print(f"Updated: {updated_count} entries")
print(f"Cleared: {cleared_count} entries (no matching data)")
finally:
driver.quit()
if __name__ == "__main__":
main()

11
python/requirements.txt Normal file
View File

@@ -0,0 +1,11 @@
# MTG Card Import
pandas
psycopg2
numpy
openpyxl
# MTG Booster Box Price CardMarket
requests
beautifulsoup4
selenium

View File

@@ -0,0 +1,7 @@
CREATE TABLE tcg.public.TCG_Change_Set (
change_set_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, comment VARCHAR(500)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
);

View File

@@ -0,0 +1,25 @@
CREATE TABLE tcg.public.TCG_User (
user_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, user_auth0_id VARCHAR(200)
, firstname VARCHAR(250)
, surname VARCHAR(250)
, email VARCHAR(256)
, is_email_verified BOOLEAN NOT NULL DEFAULT FALSE
, is_super_user BOOLEAN NOT NULL DEFAULT FALSE
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_User_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_User_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_Change_Set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_User_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, user_id INT NOT NULL
, CONSTRAINT FK_TCG_User_Audit_user_id
FOREIGN KEY (user_id)
REFERENCES tcg.public.TCG_User(user_id)
, name_field VARCHAR(255) NOT NULL
, value_prev VARCHAR(500)
, value_new VARCHAR(500)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_User_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,25 @@
CREATE TABLE tcg.public.TCG_Condition (
condition_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, name VARCHAR(250) NOT NULL
, code VARCHAR(250) NOT NULL
, description TEXT NOT NULL
, summary VARCHAR(250) NOT NULL
, us_name VARCHAR(250) NOT NULL
, price_ratio_min DECIMAL(7, 1)
, price_ratio_max DECIMAL(7, 1)
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_Condition_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_Condition_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_Condition_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_Condition_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, condition_id INT NOT NULL
, CONSTRAINT FK_TCG_Condition_Audit_condition_id
FOREIGN KEY (condition_id)
REFERENCES tcg.public.TCG_Condition(condition_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_Condition_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,22 @@
CREATE TABLE tcg.public.TCG_MTG_Finish (
finish_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, name VARCHAR(250) NOT NULL
, description TEXT NOT NULL
, price_ratio_min DECIMAL(6, 1)
, price_ratio_max DECIMAL(6, 1)
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Finish_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Finish_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Finish_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_MTG_Finish_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, finish_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Finish_Audit_finish_id
FOREIGN KEY (finish_id)
REFERENCES tcg.public.TCG_MTG_Finish(finish_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Finish_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,131 @@
CREATE TABLE tcg.public.TCG_MTG_Card (
card_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, all_parts TEXT
, arena_id INTEGER
, artist TEXT
, artist_ids VARCHAR(250)
, booster BOOLEAN
, border_color TEXT
, card_back_id VARCHAR(250)
, card_faces TEXT
, cardmarket_id INTEGER
, cmc DECIMAL(8, 1)
, collector_number TEXT
, color_identity TEXT
, colors TEXT
, content_warning BOOLEAN
, digital BOOLEAN
, edhrec_rank INTEGER
, finishes TEXT
, flavor_name TEXT
, flavor_text TEXT
, foil BOOLEAN
, frame TEXT
, full_art BOOLEAN
, game_changer BOOLEAN
, games TEXT
, hand_modifier TEXT
, highres_image BOOLEAN
, id VARCHAR(100)
, image_status TEXT
, image_uri_art_crop TEXT
, image_uri_border_crop TEXT
, image_uri_large TEXT
, image_uri_normal TEXT
, image_uri_png TEXT
, image_uri_small TEXT
, keywords TEXT
, lang TEXT
, layout TEXT
, legal_alchemy BOOLEAN
, legal_brawl BOOLEAN
, legal_commander BOOLEAN
, legal_duel BOOLEAN
, legal_future BOOLEAN
, legal_gladiator BOOLEAN
, legal_historic BOOLEAN
, legal_legacy BOOLEAN
, legal_modern BOOLEAN
, legal_oathbreaker BOOLEAN
, legal_oldschool BOOLEAN
, legal_pauper BOOLEAN
, legal_paupercommander BOOLEAN
, legal_penny BOOLEAN
, legal_pioneer BOOLEAN
, legal_predh BOOLEAN
, legal_premodern BOOLEAN
, legal_standard BOOLEAN
, legal_standardbrawl BOOLEAN
, legal_timeless BOOLEAN
, legal_vintage BOOLEAN
, life_modifier TEXT
, loyalty TEXT
, mana_cost TEXT
, mtgo_id INTEGER
, multiverse_ids VARCHAR(250)
, name TEXT NOT NULL
, nonfoil BOOLEAN
, oracle_id VARCHAR(100)
, oracle_text TEXT
, oversized BOOLEAN
, penny_rank INTEGER
, power TEXT
, preview_date DATE
, preview_source TEXT
, preview_source_uri TEXT
, price_eur DECIMAL(10, 3)
, price_eur_foil DECIMAL(10, 3)
, price_tix DECIMAL(10, 3)
, price_usd DECIMAL(10, 3)
, price_usd_etched DECIMAL(10, 3)
, price_usd_foil DECIMAL(10, 3)
, printed_name TEXT
, printed_text TEXT
, printed_type_line TEXT
, prints_search_uri TEXT
, produced_mana TEXT
, promo BOOLEAN
, purchase_cardhoarder TEXT
, purchase_cardmarket TEXT
, purchase_tcgplayer TEXT
, rarity TEXT
, released_at DATE
, reprint BOOLEAN
, reserved BOOLEAN
, rulings_uri TEXT
, scryfall_set_uri TEXT
, scryfall_uri TEXT
, security_stamp TEXT
, set TEXT
, set_id VARCHAR(100)
, set_name TEXT
, set_search_uri TEXT
, set_type TEXT
, set_uri TEXT
, story_spotlight BOOLEAN
, tcgplayer_id INTEGER
, textless BOOLEAN
, toughness TEXT
, type_line TEXT
, uri TEXT
, uri_edhrec TEXT
, uri_gatherer TEXT
, uri_tcgplayer_infinite_articles TEXT
, uri_tcgplayer_infinite_decks TEXT
, variation BOOLEAN
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Card_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Card_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Card_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_MTG_Card_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, card_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Card_Audit_card_id
FOREIGN KEY (card_id)
REFERENCES tcg.public.TCG_MTG_Card(card_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Card_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,66 @@
CREATE TABLE tcg.public.TCG_MTG_Inventory (
inventory_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, card_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_card_id
FOREIGN KEY (card_id)
REFERENCES tcg.public.TCG_MTG_Card(card_id)
, finish_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_finish_id
FOREIGN KEY (finish_id)
REFERENCES tcg.public.TCG_MTG_Finish(finish_id)
, condition_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_condition_id
FOREIGN KEY (condition_id)
REFERENCES tcg.public.TCG_Condition(condition_id)
, sleeve_colour_name VARCHAR(250)
, location_name VARCHAR(250)
, acquired_from VARCHAR(500)
, acquired_on TIMESTAMP
, cost_gbp DECIMAL(6, 2) NOT NULL
, sale_price_gbp DECIMAL(6, 2)
, is_sold BOOLEAN NOT NULL DEFAULT FALSE
, is_destroyed BOOLEAN NOT NULL DEFAULT FALSE
, notes TEXT
, alterations_customisations VARCHAR(250)
, grading_company_name VARCHAR(250)
, grading_score VARCHAR(250)
, subgrades VARCHAR(250)
, misprint_errors VARCHAR(250)
, miscut_errors VARCHAR(250)
, playability VARCHAR(250)
, owner_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_owner_user_id
FOREIGN KEY (owner_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, ownership_status_name VARCHAR(250)
, trading_status_name VARCHAR(250)
, loaned_to_user_id INT
, CONSTRAINT FK_TCG_MTG_Inventory_loaned_to_user_id
FOREIGN KEY (loaned_to_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, loan_start_on TIMESTAMP
, loan_end_on TIMESTAMP
, provenance TEXT
, signed_by_names TEXT
, signature_condition_name VARCHAR(250)
, token_rear_side_card_id INT
, CONSTRAINT FK_TCG_MTG_Inventory_token_rear_side_card_id
FOREIGN KEY (token_rear_side_card_id)
REFERENCES tcg.public.TCG_MTG_Card(card_id)
, display_order INT NOT NULL
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_MTG_Inventory_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, inventory_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_Audit_inventory_id
FOREIGN KEY (inventory_id)
REFERENCES tcg.public.TCG_MTG_Inventory(inventory_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,77 @@
CREATE TABLE tcg.public.TCG_MTG_Inventory_Temp (
temp_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, inventory_id INT
, card_id INT
, CONSTRAINT FK_TCG_MTG_Inventory_Temp_card_id
FOREIGN KEY (card_id)
REFERENCES tcg.public.TCG_MTG_Card(card_id)
, finish_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_Temp_finish_id
FOREIGN KEY (finish_id)
REFERENCES tcg.public.TCG_MTG_Finish(finish_id)
, condition_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_Temp_condition_id
FOREIGN KEY (condition_id)
REFERENCES tcg.public.TCG_Condition(condition_id)
, sleeve_colour_name VARCHAR(250)
, location_name VARCHAR(250)
, acquired_from VARCHAR(500)
, acquired_on TIMESTAMP
, cost_gbp DECIMAL(6, 2) NOT NULL
, sale_price_gbp DECIMAL(6, 2)
, is_sold BOOLEAN NOT NULL DEFAULT FALSE
, is_destroyed BOOLEAN NOT NULL DEFAULT FALSE
, notes TEXT
, alterations_customisations VARCHAR(250)
, grading_company_name VARCHAR(250)
, grading_score VARCHAR(250)
, subgrades VARCHAR(250)
, misprint_errors VARCHAR(250)
, miscut_errors VARCHAR(250)
, playability VARCHAR(250)
, owner_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_Temp_owner_user_id
FOREIGN KEY (owner_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, ownership_status_name VARCHAR(250)
, trading_status_name VARCHAR(250)
, loaned_to_user_id INT
, CONSTRAINT FK_TCG_MTG_Inventory_Temp_loaned_to_user_id
FOREIGN KEY (loaned_to_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, loan_start_on TIMESTAMP
, loan_end_on TIMESTAMP
, provenance TEXT
, signed_by_names TEXT
, signature_condition_name VARCHAR(250)
, token_rear_side_card_id INT
, CONSTRAINT FK_TCG_MTG_Inventory_token_rear_side_card_id
FOREIGN KEY (token_rear_side_card_id)
REFERENCES tcg.public.TCG_MTG_Card(card_id)
, display_order INT NOT NULL
, active BOOLEAN NOT NULL DEFAULT TRUE
-- , created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_Temp_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
-- , updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_Temp_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Inventory_Temp_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
, set_code TEXT
, collector_number TEXT
, display_order INT
, name VARCHAR(250)
, is_token BOOLEAN
, token_rear_side_name VARCHAR(250)
, token_rear_side_set_code TEXT
, token_rear_side_collector_number TEXT
);

View File

@@ -0,0 +1,21 @@
CREATE TABLE tcg.public.TCG_MTG_Deck_Commander_Bracket (
commander_bracket_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, name VARCHAR(250) NOT NULL
, description TEXT
, display_order INT NOT NULL
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_MTG_Deck_Commander_Bracket_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, commander_bracket_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_Audit_commander_bracket_id
FOREIGN KEY (commander_bracket_id)
REFERENCES tcg.public.TCG_MTG_Deck_Commander_Bracket(commander_bracket_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Commander_Bracket_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,24 @@
CREATE TABLE tcg.public.TCG_MTG_Deck (
deck_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, name VARCHAR(250) NOT NULL
, is_commander BOOLEAN NOT NULL DEFAULT TRUE
, commander_bracket_id INT
, CONSTRAINT FK_TCG_MTG_Deck_commander_bracket_id
FOREIGN KEY (commander_bracket_id)
REFERENCES tcg.public.TCG_MTG_Deck_Commander_Bracket(commander_bracket_id)
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_MTG_Deck_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, deck_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Audit_deck_id
FOREIGN KEY (deck_id)
REFERENCES tcg.public.TCG_MTG_Deck(deck_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,27 @@
CREATE TABLE tcg.public.TCG_MTG_Deck_Inventory_Link (
link_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, deck_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_deck_id
FOREIGN KEY (deck_id)
REFERENCES tcg.public.TCG_MTG_Deck(deck_id)
, inventory_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_inventory_id
FOREIGN KEY (inventory_id)
REFERENCES tcg.public.TCG_MTG_Inventory(inventory_id)
, display_order INT NOT NULL
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP NOT NULL
, updated_last_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_updated_last_by_user_id
FOREIGN KEY (updated_last_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_MTG_Deck_Inventory_Link_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, link_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_Audit_link_id
FOREIGN KEY (link_id)
REFERENCES tcg.public.TCG_MTG_Deck_Inventory_Link(link_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Deck_Inventory_Link_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,20 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_Change_Set()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_Change_Set
BEFORE INSERT ON tcg.public.TCG_Change_Set
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_Change_Set()
;
CREATE TRIGGER TRI_before_update_TCG_Change_Set
BEFORE UPDATE ON tcg.public.TCG_Change_Set
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_Change_Set()
;

View File

@@ -0,0 +1,76 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_User()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_User()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_User_Audit (
user_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed user_auth0_id
SELECT NEW.user_id, 'user_auth0_id', OLD.user_auth0_id, NEW.user_auth0_id, NEW.change_set_id
WHERE OLD.user_auth0_id IS NOT DISTINCT FROM NEW.user_auth0_id
UNION
-- Changed firstname
SELECT NEW.user_id, 'firstname', OLD.firstname, NEW.firstname, NEW.change_set_id
WHERE OLD.firstname IS NOT DISTINCT FROM NEW.firstname
UNION
-- Changed surname
SELECT NEW.user_id, 'surname', OLD.surname, NEW.surname, NEW.change_set_id
WHERE OLD.surname IS NOT DISTINCT FROM NEW.surname
UNION
-- Changed email
SELECT NEW.user_id, 'email', OLD.email, NEW.email, NEW.change_set_id
WHERE OLD.email IS NOT DISTINCT FROM NEW.email
UNION
-- Changed is_email_verified
SELECT NEW.user_id, 'is_email_verified', OLD.is_email_verified::TEXT, NEW.is_email_verified::TEXT, NEW.change_set_id
WHERE OLD.is_email_verified IS NOT DISTINCT FROM NEW.is_email_verified
UNION
-- Changed is_super_user
SELECT NEW.user_id, 'is_super_user', OLD.is_super_user::TEXT, NEW.is_super_user::TEXT, NEW.change_set_id
WHERE OLD.is_super_user IS NOT DISTINCT FROM NEW.is_super_user
UNION
-- Changed active
SELECT NEW.user_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_User
BEFORE INSERT ON tcg.public.TCG_User
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_User()
;
CREATE TRIGGER TRI_before_update_TCG_User
BEFORE UPDATE ON tcg.public.TCG_User
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_User()
;

View File

@@ -0,0 +1,80 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_Condition()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_Condition()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_Condition_Audit (
condition_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed name
SELECT NEW.condition_id, 'name', OLD.name, NEW.name, NEW.change_set_id
WHERE OLD.name IS NOT DISTINCT FROM NEW.name
UNION
-- Changed code
SELECT NEW.condition_id, 'code', OLD.code, NEW.code, NEW.change_set_id
WHERE OLD.code IS NOT DISTINCT FROM NEW.code
UNION
-- Changed description
SELECT NEW.condition_id, 'description', OLD.description, NEW.description, NEW.change_set_id
WHERE OLD.description IS NOT DISTINCT FROM NEW.description
UNION
-- Changed summary
SELECT NEW.condition_id, 'summary', OLD.summary, NEW.summary, NEW.change_set_id
WHERE OLD.summary IS NOT DISTINCT FROM NEW.summary
UNION
-- Changed us_name
SELECT NEW.condition_id, 'us_name', OLD.us_name, NEW.us_name, NEW.change_set_id
WHERE OLD.us_name IS NOT DISTINCT FROM NEW.us_name
UNION
-- Changed price_ratio_min
SELECT NEW.condition_id, 'price_ratio_min', CAST(OLD.price_ratio_min AS VARCHAR), CAST(NEW.price_ratio_min AS VARCHAR), NEW.change_set_id
WHERE OLD.price_ratio_min IS NOT DISTINCT FROM NEW.price_ratio_min
UNION
-- Changed price_ratio_max
SELECT NEW.condition_id, 'price_ratio_max', CAST(OLD.price_ratio_max AS VARCHAR), CAST(NEW.price_ratio_max AS VARCHAR), NEW.change_set_id
WHERE OLD.price_ratio_max IS NOT DISTINCT FROM NEW.price_ratio_max
UNION
-- Changed active
SELECT NEW.condition_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_Condition
BEFORE INSERT ON tcg.public.TCG_Condition
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_Condition()
;
CREATE TRIGGER TRI_before_update_TCG_Condition
BEFORE UPDATE ON tcg.public.TCG_Condition
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_Condition()
;

View File

@@ -0,0 +1,68 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Finish()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Finish()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_MTG_Finish_Audit (
finish_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed name
SELECT NEW.finish_id, 'name', OLD.name, NEW.name, NEW.change_set_id
WHERE OLD.name IS NOT DISTINCT FROM NEW.name
UNION
-- Changed description
SELECT NEW.finish_id, 'description', OLD.description, NEW.description, NEW.change_set_id
WHERE OLD.description IS NOT DISTINCT FROM NEW.description
UNION
-- Changed price_ratio_min
SELECT NEW.finish_id, 'price_ratio_min', CAST(OLD.price_ratio_min AS VARCHAR), CAST(NEW.price_ratio_min AS VARCHAR), NEW.change_set_id
WHERE OLD.price_ratio_min IS NOT DISTINCT FROM NEW.price_ratio_min
UNION
-- Changed price_ratio_max
SELECT NEW.finish_id, 'price_ratio_max', CAST(OLD.price_ratio_max AS VARCHAR), CAST(NEW.price_ratio_max AS VARCHAR), NEW.change_set_id
WHERE OLD.price_ratio_max IS NOT DISTINCT FROM NEW.price_ratio_max
UNION
-- Changed active
SELECT NEW.finish_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_MTG_Finish
BEFORE INSERT ON tcg.public.TCG_MTG_Finish
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Finish()
;
CREATE TRIGGER TRI_before_update_TCG_MTG_Finish
BEFORE UPDATE ON tcg.public.TCG_MTG_Finish
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Finish()
;

View File

@@ -0,0 +1,496 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Card()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Card()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_MTG_Card_Audit (
card_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed all_parts
SELECT NEW.card_id, 'all_parts', OLD.all_parts, NEW.all_parts, NEW.change_set_id
WHERE OLD.all_parts IS NOT DISTINCT FROM NEW.all_parts
UNION
-- Changed arena_id
SELECT NEW.card_id, 'arena_id', CAST(OLD.arena_id AS VARCHAR), CAST(NEW.arena_id AS VARCHAR), NEW.change_set_id
WHERE OLD.arena_id IS NOT DISTINCT FROM NEW.arena_id
UNION
-- Changed artist
SELECT NEW.card_id, 'artist', OLD.artist, NEW.artist, NEW.change_set_id
WHERE OLD.artist IS NOT DISTINCT FROM NEW.artist
UNION
-- Changed artist_ids
SELECT NEW.card_id, 'artist_ids', OLD.artist_ids, NEW.artist_ids, NEW.change_set_id
WHERE OLD.artist_ids IS NOT DISTINCT FROM NEW.artist_ids
UNION
-- Changed booster
SELECT NEW.card_id, 'booster', OLD.booster::TEXT, NEW.booster::TEXT, NEW.change_set_id
WHERE OLD.booster IS NOT DISTINCT FROM NEW.booster
UNION
-- Changed border_color
SELECT NEW.card_id, 'border_color', OLD.border_color, NEW.border_color, NEW.change_set_id
WHERE OLD.border_color IS NOT DISTINCT FROM NEW.border_color
UNION
-- Changed card_back_id
SELECT NEW.card_id, 'card_back_id', OLD.card_back_id, NEW.card_back_id, NEW.change_set_id
WHERE OLD.card_back_id IS NOT DISTINCT FROM NEW.card_back_id
UNION
-- Changed cmc
SELECT NEW.card_id, 'cmc', CAST(OLD.cmc AS VARCHAR), CAST(NEW.cmc AS VARCHAR), NEW.change_set_id
WHERE OLD.cmc IS NOT DISTINCT FROM NEW.cmc
UNION
-- Changed collector_number
SELECT NEW.card_id, 'collector_number', OLD.collector_number, NEW.collector_number, NEW.change_set_id
WHERE OLD.collector_number IS NOT DISTINCT FROM NEW.collector_number
UNION
-- Changed color_identity
SELECT NEW.card_id, 'color_identity', OLD.color_identity, NEW.color_identity, NEW.change_set_id
WHERE OLD.color_identity IS NOT DISTINCT FROM NEW.color_identity
UNION
-- Changed colors
SELECT NEW.card_id, 'colors', OLD.colors, NEW.colors, NEW.change_set_id
WHERE OLD.colors IS NOT DISTINCT FROM NEW.colors
UNION
-- Changed content_warning
SELECT NEW.card_id, 'content_warning', OLD.content_warning::TEXT, NEW.content_warning::TEXT, NEW.change_set_id
WHERE OLD.content_warning IS NOT DISTINCT FROM NEW.content_warning
UNION
-- Changed digital
SELECT NEW.card_id, 'digital', OLD.digital::TEXT, NEW.digital::TEXT, NEW.change_set_id
WHERE OLD.digital IS NOT DISTINCT FROM NEW.digital
UNION
-- Changed edhrec_rank
SELECT NEW.card_id, 'edhrec_rank', CAST(OLD.edhrec_rank AS VARCHAR), CAST(NEW.edhrec_rank AS VARCHAR), NEW.change_set_id
WHERE OLD.edhrec_rank IS NOT DISTINCT FROM NEW.edhrec_rank
UNION
-- Changed finishes
SELECT NEW.card_id, 'finishes', OLD.finishes, NEW.finishes, NEW.change_set_id
WHERE OLD.finishes IS NOT DISTINCT FROM NEW.finishes
UNION
-- Changed flavor_name
SELECT NEW.card_id, 'flavor_name', OLD.flavor_name, NEW.flavor_name, NEW.change_set_id
WHERE OLD.flavor_name IS NOT DISTINCT FROM NEW.flavor_name
UNION
-- Changed flavor_text
SELECT NEW.card_id, 'flavor_text', OLD.flavor_text, NEW.flavor_text, NEW.change_set_id
WHERE OLD.flavor_text IS NOT DISTINCT FROM NEW.flavor_text
UNION
-- Changed foil
SELECT NEW.card_id, 'foil', OLD.foil::TEXT, NEW.foil::TEXT, NEW.change_set_id
WHERE OLD.foil IS NOT DISTINCT FROM NEW.foil
UNION
-- Changed frame
SELECT NEW.card_id, 'frame', OLD.frame, NEW.frame, NEW.change_set_id
WHERE OLD.frame IS NOT DISTINCT FROM NEW.frame
UNION
-- Changed full_art
SELECT NEW.card_id, 'full_art', OLD.full_art::TEXT, NEW.full_art::TEXT, NEW.change_set_id
WHERE OLD.full_art IS NOT DISTINCT FROM NEW.full_art
UNION
-- Changed game_changer
SELECT NEW.card_id, 'game_changer', OLD.game_changer::TEXT, NEW.game_changer::TEXT, NEW.change_set_id
WHERE OLD.game_changer IS NOT DISTINCT FROM NEW.game_changer
UNION
-- Changed games
SELECT NEW.card_id, 'games', OLD.games, NEW.games, NEW.change_set_id
WHERE OLD.games IS NOT DISTINCT FROM NEW.games
UNION
-- Changed hand_modifier
SELECT NEW.card_id, 'hand_modifier', OLD.hand_modifier, NEW.hand_modifier, NEW.change_set_id
WHERE OLD.hand_modifier IS NOT DISTINCT FROM NEW.hand_modifier
UNION
-- Changed highres_image
SELECT NEW.card_id, 'highres_image', OLD.highres_image::TEXT, NEW.highres_image::TEXT, NEW.change_set_id
WHERE OLD.highres_image IS NOT DISTINCT FROM NEW.highres_image
UNION
-- Changed id
SELECT NEW.card_id, 'id', CAST(OLD.id AS VARCHAR), CAST(NEW.id AS VARCHAR), NEW.change_set_id
WHERE OLD.id IS NOT DISTINCT FROM NEW.id
UNION
-- Changed image_status
SELECT NEW.card_id, 'image_status', OLD.image_status, NEW.image_status, NEW.change_set_id
WHERE OLD.image_status IS NOT DISTINCT FROM NEW.image_status
UNION
-- Changed image_uri_art_crop
SELECT NEW.card_id, 'image_uri_art_crop', OLD.image_uri_art_crop, NEW.image_uri_art_crop, NEW.change_set_id
WHERE OLD.image_uri_art_crop IS NOT DISTINCT FROM NEW.image_uri_art_crop
UNION
-- Changed image_uri_border_crop
SELECT NEW.card_id, 'image_uri_border_crop', OLD.image_uri_border_crop, NEW.image_uri_border_crop, NEW.change_set_id
WHERE OLD.image_uri_border_crop IS NOT DISTINCT FROM NEW.image_uri_border_crop
UNION
-- Changed image_uri_large
SELECT NEW.card_id, 'image_uri_large', OLD.image_uri_large, NEW.image_uri_large, NEW.change_set_id
WHERE OLD.image_uri_large IS NOT DISTINCT FROM NEW.image_uri_large
UNION
-- Changed image_uri_normal
SELECT NEW.card_id, 'image_uri_normal', OLD.image_uri_normal, NEW.image_uri_normal, NEW.change_set_id
WHERE OLD.image_uri_normal IS NOT DISTINCT FROM NEW.image_uri_normal
UNION
-- Changed image_uri_png
SELECT NEW.card_id, 'image_uri_png', OLD.image_uri_png, NEW.image_uri_png, NEW.change_set_id
WHERE OLD.image_uri_png IS NOT DISTINCT FROM NEW.image_uri_png
UNION
-- Changed image_uri_small
SELECT NEW.card_id, 'image_uri_small', OLD.image_uri_small, NEW.image_uri_small, NEW.change_set_id
WHERE OLD.image_uri_small IS NOT DISTINCT FROM NEW.image_uri_small
UNION
-- Changed keywords
SELECT NEW.card_id, 'keywords', OLD.keywords, NEW.keywords, NEW.change_set_id
WHERE OLD.keywords IS NOT DISTINCT FROM NEW.keywords
UNION
-- Changed lang
SELECT NEW.card_id, 'lang', OLD.lang, NEW.lang, NEW.change_set_id
WHERE OLD.lang IS NOT DISTINCT FROM NEW.lang
UNION
-- Changed layout
SELECT NEW.card_id, 'layout', OLD.layout, NEW.layout, NEW.change_set_id
WHERE OLD.layout IS NOT DISTINCT FROM NEW.layout
UNION
-- Changed legal_alchemy
SELECT NEW.card_id, 'legal_alchemy', OLD.legal_alchemy::TEXT, NEW.legal_alchemy::TEXT, NEW.change_set_id
WHERE OLD.legal_alchemy IS NOT DISTINCT FROM NEW.legal_alchemy
UNION
-- Changed legal_brawl
SELECT NEW.card_id, 'legal_brawl', OLD.legal_brawl::TEXT, NEW.legal_brawl::TEXT, NEW.change_set_id
WHERE OLD.legal_brawl IS NOT DISTINCT FROM NEW.legal_brawl
UNION
-- Changed legal_commander
SELECT NEW.card_id, 'legal_commander', OLD.legal_commander::TEXT, NEW.legal_commander::TEXT, NEW.change_set_id
WHERE OLD.legal_commander IS NOT DISTINCT FROM NEW.legal_commander
UNION
-- Changed legal_duel
SELECT NEW.card_id, 'legal_duel', OLD.legal_duel::TEXT, NEW.legal_duel::TEXT, NEW.change_set_id
WHERE OLD.legal_duel IS NOT DISTINCT FROM NEW.legal_duel
UNION
-- Changed legal_future
SELECT NEW.card_id, 'legal_future', OLD.legal_future::TEXT, NEW.legal_future::TEXT, NEW.change_set_id
WHERE OLD.legal_future IS NOT DISTINCT FROM NEW.legal_future
UNION
-- Changed legal_gladiator
SELECT NEW.card_id, 'legal_gladiator', OLD.legal_gladiator::TEXT, NEW.legal_gladiator::TEXT, NEW.change_set_id
WHERE OLD.legal_gladiator IS NOT DISTINCT FROM NEW.legal_gladiator
UNION
-- Changed legal_historic
SELECT NEW.card_id, 'legal_historic', OLD.legal_historic::TEXT, NEW.legal_historic::TEXT, NEW.change_set_id
WHERE OLD.legal_historic IS NOT DISTINCT FROM NEW.legal_historic
UNION
-- Changed legal_legacy
SELECT NEW.card_id, 'legal_legacy', OLD.legal_legacy::TEXT, NEW.legal_legacy::TEXT, NEW.change_set_id
WHERE OLD.legal_legacy IS NOT DISTINCT FROM NEW.legal_legacy
UNION
-- Changed legal_modern
SELECT NEW.card_id, 'legal_modern', OLD.legal_modern::TEXT, NEW.legal_modern::TEXT, NEW.change_set_id
WHERE OLD.legal_modern IS NOT DISTINCT FROM NEW.legal_modern
UNION
-- Changed legal_oathbreaker
SELECT NEW.card_id, 'legal_oathbreaker', OLD.legal_oathbreaker::TEXT, NEW.legal_oathbreaker::TEXT, NEW.change_set_id
WHERE OLD.legal_oathbreaker IS NOT DISTINCT FROM NEW.legal_oathbreaker
UNION
-- Changed legal_oldschool
SELECT NEW.card_id, 'legal_oldschool', OLD.legal_oldschool::TEXT, NEW.legal_oldschool::TEXT, NEW.change_set_id
WHERE OLD.legal_oldschool IS NOT DISTINCT FROM NEW.legal_oldschool
UNION
-- Changed legal_pauper
SELECT NEW.card_id, 'legal_pauper', OLD.legal_pauper::TEXT, NEW.legal_pauper::TEXT, NEW.change_set_id
WHERE OLD.legal_pauper IS NOT DISTINCT FROM NEW.legal_pauper
UNION
-- Changed legal_paupercommander
SELECT NEW.card_id, 'legal_paupercommander', OLD.legal_paupercommander::TEXT, NEW.legal_paupercommander::TEXT, NEW.change_set_id
WHERE OLD.legal_paupercommander IS NOT DISTINCT FROM NEW.legal_paupercommander
UNION
-- Changed legal_penny
SELECT NEW.card_id, 'legal_penny', OLD.legal_penny::TEXT, NEW.legal_penny::TEXT, NEW.change_set_id
WHERE OLD.legal_penny IS NOT DISTINCT FROM NEW.legal_penny
UNION
-- Changed legal_pioneer
SELECT NEW.card_id, 'legal_pioneer', OLD.legal_pioneer::TEXT, NEW.legal_pioneer::TEXT, NEW.change_set_id
WHERE OLD.legal_pioneer IS NOT DISTINCT FROM NEW.legal_pioneer
UNION
-- Changed legal_predh
SELECT NEW.card_id, 'legal_predh', OLD.legal_predh::TEXT, NEW.legal_predh::TEXT, NEW.change_set_id
WHERE OLD.legal_predh IS NOT DISTINCT FROM NEW.legal_predh
UNION
-- Changed legal_premodern
SELECT NEW.card_id, 'legal_premodern', OLD.legal_premodern::TEXT, NEW.legal_premodern::TEXT, NEW.change_set_id
WHERE OLD.legal_premodern IS NOT DISTINCT FROM NEW.legal_premodern
UNION
-- Changed legal_standard
SELECT NEW.card_id, 'legal_standard', OLD.legal_standard::TEXT, NEW.legal_standard::TEXT, NEW.change_set_id
WHERE OLD.legal_standard IS NOT DISTINCT FROM NEW.legal_standard
UNION
-- Changed legal_standardbrawl
SELECT NEW.card_id, 'legal_standardbrawl', OLD.legal_standardbrawl::TEXT, NEW.legal_standardbrawl::TEXT, NEW.change_set_id
WHERE OLD.legal_standardbrawl IS NOT DISTINCT FROM NEW.legal_standardbrawl
UNION
-- Changed legal_timeless
SELECT NEW.card_id, 'legal_timeless', OLD.legal_timeless::TEXT, NEW.legal_timeless::TEXT, NEW.change_set_id
WHERE OLD.legal_timeless IS NOT DISTINCT FROM NEW.legal_timeless
UNION
-- Changed legal_vintage
SELECT NEW.card_id, 'legal_vintage', OLD.legal_vintage::TEXT, NEW.legal_vintage::TEXT, NEW.change_set_id
WHERE OLD.legal_vintage IS NOT DISTINCT FROM NEW.legal_vintage
UNION
-- Changed life_modifier
SELECT NEW.card_id, 'life_modifier', OLD.life_modifier, NEW.life_modifier, NEW.change_set_id
WHERE OLD.life_modifier IS NOT DISTINCT FROM NEW.life_modifier
UNION
-- Changed loyalty
SELECT NEW.card_id, 'loyalty', OLD.loyalty, NEW.loyalty, NEW.change_set_id
WHERE OLD.loyalty IS NOT DISTINCT FROM NEW.loyalty
UNION
-- Changed mana_cost
SELECT NEW.card_id, 'mana_cost', OLD.mana_cost, NEW.mana_cost, NEW.change_set_id
WHERE OLD.mana_cost IS NOT DISTINCT FROM NEW.mana_cost
UNION
-- Changed mtgo_id
SELECT NEW.card_id, 'mtgo_id', CAST(OLD.mtgo_id AS VARCHAR), CAST(NEW.mtgo_id AS VARCHAR), NEW.change_set_id
WHERE OLD.mtgo_id IS NOT DISTINCT FROM NEW.mtgo_id
UNION
-- Changed multiverse_ids
SELECT NEW.card_id, 'multiverse_ids', OLD.multiverse_ids, NEW.multiverse_ids, NEW.change_set_id
WHERE OLD.multiverse_ids IS NOT DISTINCT FROM NEW.multiverse_ids
UNION
-- Changed name
SELECT NEW.card_id, 'name', OLD.name, NEW.name, NEW.change_set_id
WHERE OLD.name IS NOT DISTINCT FROM NEW.name
UNION
-- Changed nonfoil
SELECT NEW.card_id, 'nonfoil', OLD.nonfoil::TEXT, NEW.nonfoil::TEXT, NEW.change_set_id
WHERE OLD.nonfoil IS NOT DISTINCT FROM NEW.nonfoil
UNION
-- Changed oracle_id
SELECT NEW.card_id, 'oracle_id', CAST(OLD.oracle_id AS VARCHAR), CAST(NEW.oracle_id AS VARCHAR), NEW.change_set_id
WHERE OLD.oracle_id IS NOT DISTINCT FROM NEW.oracle_id
UNION
-- Changed oracle_text
SELECT NEW.card_id, 'oracle_text', OLD.oracle_text, NEW.oracle_text, NEW.change_set_id
WHERE OLD.oracle_text IS NOT DISTINCT FROM NEW.oracle_text
UNION
-- Changed oversized
SELECT NEW.card_id, 'oversized', OLD.oversized::TEXT, NEW.oversized::TEXT, NEW.change_set_id
WHERE OLD.oversized IS NOT DISTINCT FROM NEW.oversized
UNION
-- Changed penny_rank
SELECT NEW.card_id, 'penny_rank', CAST(OLD.penny_rank AS VARCHAR), CAST(NEW.penny_rank AS VARCHAR), NEW.change_set_id
WHERE OLD.penny_rank IS NOT DISTINCT FROM NEW.penny_rank
UNION
-- Changed power
SELECT NEW.card_id, 'power', OLD.power, NEW.power, NEW.change_set_id
WHERE OLD.power IS NOT DISTINCT FROM NEW.power
UNION
-- Changed preview_date
SELECT NEW.card_id, 'preview_date', TO_CHAR(OLD.preview_date, 'YYYY-MM-DD'), TO_CHAR(NEW.preview_date, 'YYYY-MM-DD'), NEW.change_set_id
WHERE OLD.preview_date IS NOT DISTINCT FROM NEW.preview_date
UNION
-- Changed preview_source
SELECT NEW.card_id, 'preview_source', OLD.preview_source, NEW.preview_source, NEW.change_set_id
WHERE OLD.preview_source IS NOT DISTINCT FROM NEW.preview_source
UNION
-- Changed preview_source_uri
SELECT NEW.card_id, 'preview_source_uri', OLD.preview_source_uri, NEW.preview_source_uri, NEW.change_set_id
WHERE OLD.preview_source_uri IS NOT DISTINCT FROM NEW.preview_source_uri
UNION
-- Changed price_eur
SELECT NEW.card_id, 'price_eur', CAST(OLD.price_eur AS VARCHAR), CAST(NEW.price_eur AS VARCHAR), NEW.change_set_id
WHERE OLD.price_eur IS NOT DISTINCT FROM NEW.price_eur
UNION
-- Changed price_eur_foil
SELECT NEW.card_id, 'price_eur_foil', CAST(OLD.price_eur_foil AS VARCHAR), CAST(NEW.price_eur_foil AS VARCHAR), NEW.change_set_id
WHERE OLD.price_eur_foil IS NOT DISTINCT FROM NEW.price_eur_foil
UNION
-- Changed price_tix
SELECT NEW.card_id, 'price_tix', CAST(OLD.price_tix AS VARCHAR), CAST(NEW.price_tix AS VARCHAR), NEW.change_set_id
WHERE OLD.price_tix IS NOT DISTINCT FROM NEW.price_tix
UNION
-- Changed price_usd
SELECT NEW.card_id, 'price_usd', CAST(OLD.price_usd AS VARCHAR), CAST(NEW.price_usd AS VARCHAR), NEW.change_set_id
WHERE OLD.price_usd IS NOT DISTINCT FROM NEW.price_usd
UNION
-- Changed price_usd_etched
SELECT NEW.card_id, 'price_usd_etched', CAST(OLD.price_usd_etched AS VARCHAR), CAST(NEW.price_usd_etched AS VARCHAR), NEW.change_set_id
WHERE OLD.price_usd_etched IS NOT DISTINCT FROM NEW.price_usd_etched
UNION
-- Changed price_usd_foil
SELECT NEW.card_id, 'price_usd_foil', CAST(OLD.price_usd_foil AS VARCHAR), CAST(NEW.price_usd_foil AS VARCHAR), NEW.change_set_id
WHERE OLD.price_usd_foil IS NOT DISTINCT FROM NEW.price_usd_foil
UNION
-- Changed printed_name
SELECT NEW.card_id, 'printed_name', OLD.printed_name, NEW.printed_name, NEW.change_set_id
WHERE OLD.printed_name IS NOT DISTINCT FROM NEW.printed_name
UNION
-- Changed printed_text
SELECT NEW.card_id, 'printed_text', OLD.printed_text, NEW.printed_text, NEW.change_set_id
WHERE OLD.printed_text IS NOT DISTINCT FROM NEW.printed_text
UNION
-- Changed printed_type_line
SELECT NEW.card_id, 'printed_type_line', OLD.printed_type_line, NEW.printed_type_line, NEW.change_set_id
WHERE OLD.printed_type_line IS NOT DISTINCT FROM NEW.printed_type_line
UNION
-- Changed prints_search_uri
SELECT NEW.card_id, 'printed_search_uri', OLD.printed_search_uri, NEW.printed_search_uri, NEW.change_set_id
WHERE OLD.printed_search_uri IS NOT DISTINCT FROM NEW.printed_search_uri
UNION
-- Changed produced_mana
SELECT NEW.card_id, 'produced_mana', OLD.produced_mana, NEW.produced_mana, NEW.change_set_id
WHERE OLD.produced_mana IS NOT DISTINCT FROM NEW.produced_mana
UNION
-- Changed promo
SELECT NEW.card_id, 'promo', OLD.promo::TEXT, NEW.promo::TEXT, NEW.change_set_id
WHERE OLD.promo IS NOT DISTINCT FROM NEW.promo
UNION
-- Changed purchase_cardhoarder
SELECT NEW.card_id, 'purchase_cardhoarder', OLD.purchase_cardhoarder, NEW.purchase_cardhoarder, NEW.change_set_id
WHERE OLD.purchase_cardhoarder IS NOT DISTINCT FROM NEW.purchase_cardhoarder
UNION
-- Changed purchase_cardmarket
SELECT NEW.card_id, 'purchase_cardmarket', OLD.purchase_cardmarket, NEW.purchase_cardmarket, NEW.change_set_id
WHERE OLD.purchase_cardmarket IS NOT DISTINCT FROM NEW.purchase_cardmarket
UNION
-- Changed purchase_tcgplayer
SELECT NEW.card_id, 'purchase_tcgplayer', OLD.purchase_tcgplayer, NEW.purchase_tcgplayer, NEW.change_set_id
WHERE OLD.purchase_tcgplayer IS NOT DISTINCT FROM NEW.purchase_tcgplayer
UNION
-- Changed rarity
SELECT NEW.card_id, 'rarity', OLD.rarity, NEW.rarity, NEW.change_set_id
WHERE OLD.rarity IS NOT DISTINCT FROM NEW.rarity
UNION
-- Changed released_at
SELECT NEW.card_id, 'released_at', TO_CHAR(OLD.released_at, 'YYYY-MM-DD'), TO_CHAR(NEW.released_at, 'YYYY-MM-DD'), NEW.change_set_id
WHERE OLD.released_at IS NOT DISTINCT FROM NEW.released_at
UNION
-- Changed reprint
SELECT NEW.card_id, 'reprint', OLD.reprint::TEXT, NEW.reprint::TEXT, NEW.change_set_id
WHERE OLD.reprint IS NOT DISTINCT FROM NEW.reprint
UNION
-- Changed reserved
SELECT NEW.card_id, 'reserved', OLD.reserved::TEXT, NEW.reserved::TEXT, NEW.change_set_id
WHERE OLD.reserved IS NOT DISTINCT FROM NEW.reserved
UNION
-- Changed rulings_uri
SELECT NEW.card_id, 'rulings_uri', OLD.rulings_uri, NEW.rulings_uri, NEW.change_set_id
WHERE OLD.rulings_uri IS NOT DISTINCT FROM NEW.rulings_uri
UNION
-- Changed scryfall_set_uri
SELECT NEW.card_id, 'scryfall_set_uri', OLD.scryfall_set_uri, NEW.scryfall_set_uri, NEW.change_set_id
WHERE OLD.scryfall_set_uri IS NOT DISTINCT FROM NEW.scryfall_set_uri
UNION
-- Changed scryfall_uri
SELECT NEW.card_id, 'scryfall_uri', OLD.scryfall_uri, NEW.scryfall_uri, NEW.change_set_id
WHERE OLD.scryfall_uri IS NOT DISTINCT FROM NEW.scryfall_uri
UNION
-- Changed security_stamp
SELECT NEW.card_id, 'security_stamp', OLD.security_stamp, NEW.security_stamp, NEW.change_set_id
WHERE OLD.security_stamp IS NOT DISTINCT FROM NEW.security_stamp
UNION
-- Changed set
SELECT NEW.card_id, 'set', OLD.set, NEW.set, NEW.change_set_id
WHERE OLD.set IS NOT DISTINCT FROM NEW.set
UNION
-- Changed set_id
SELECT NEW.card_id, 'set_id', CAST(OLD.set_id AS VARCHAR), CAST(NEW.set_id AS VARCHAR), NEW.change_set_id
WHERE OLD.set_id IS NOT DISTINCT FROM NEW.set_id
UNION
-- Changed set_name
SELECT NEW.card_id, 'set_name', OLD.set_name, NEW.set_name, NEW.change_set_id
WHERE OLD.set_name IS NOT DISTINCT FROM NEW.set_name
UNION
-- Changed set_search_uri
SELECT NEW.card_id, 'set_search_uri', OLD.set_search_uri, NEW.set_search_uri, NEW.change_set_id
WHERE OLD.set_search_uri IS NOT DISTINCT FROM NEW.set_search_uri
UNION
-- Changed set_type
SELECT NEW.card_id, 'set_type', OLD.set_type, NEW.set_type, NEW.change_set_id
WHERE OLD.set_type IS NOT DISTINCT FROM NEW.set_type
UNION
-- Changed set_uri
SELECT NEW.card_id, 'set_uri', OLD.set_uri, NEW.set_uri, NEW.change_set_id
WHERE OLD.set_uri IS NOT DISTINCT FROM NEW.set_uri
UNION
-- Changed story_spotlight
SELECT NEW.card_id, 'story_spotlight', OLD.story_spotlight::TEXT, NEW.story_spotlight::TEXT, NEW.change_set_id
WHERE OLD.story_spotlight IS NOT DISTINCT FROM NEW.story_spotlight
UNION
-- Changed tcgplayer_id
SELECT NEW.card_id, 'tcgplayer_id', CAST(OLD.tcgplayer_id AS VARCHAR), CAST(NEW.tcgplayer_id AS VARCHAR), NEW.change_set_id
WHERE OLD.tcgplayer_id IS NOT DISTINCT FROM NEW.tcgplayer_id
UNION
-- Changed textless
SELECT NEW.card_id, 'textless', OLD.textless::TEXT, NEW.textless::TEXT, NEW.change_set_id
WHERE OLD.textless IS NOT DISTINCT FROM NEW.textless
UNION
-- Changed toughness
SELECT NEW.card_id, 'toughness', OLD.toughness, NEW.toughness, NEW.change_set_id
WHERE OLD.toughness IS NOT DISTINCT FROM NEW.toughness
UNION
-- Changed type_line
SELECT NEW.card_id, 'type_line', OLD.type_line, NEW.type_line, NEW.change_set_id
WHERE OLD.type_line IS NOT DISTINCT FROM NEW.type_line
UNION
-- Changed uri
SELECT NEW.card_id, 'uri', OLD.uri, NEW.uri, NEW.change_set_id
WHERE OLD.uri IS NOT DISTINCT FROM NEW.uri
UNION
-- Changed uri_edhrec
SELECT NEW.card_id, 'uri_edhrec', OLD.uri_edhrec, NEW.uri_edhrec, NEW.change_set_id
WHERE OLD.uri_edhrec IS NOT DISTINCT FROM NEW.uri_edhrec
UNION
-- Changed uri_gatherer
SELECT NEW.card_id, 'uri_gatherer', OLD.uri_gatherer, NEW.uri_gatherer, NEW.change_set_id
WHERE OLD.uri_gatherer IS NOT DISTINCT FROM NEW.uri_gatherer
UNION
-- Changed uri_tcgplayer_infinite_articles
SELECT NEW.card_id, 'uri_tcgplayer_infinite_articles', OLD.uri_tcgplayer_infinite_articles, NEW.uri_tcgplayer_infinite_articles, NEW.change_set_id
WHERE OLD.uri_tcgplayer_infinite_articles IS NOT DISTINCT FROM NEW.uri_tcgplayer_infinite_articles
UNION
-- Changed uri_tcgplayer_infinite_decks
SELECT NEW.card_id, 'uri_tcgplayer_infinite_decks', OLD.uri_tcgplayer_infinite_decks, NEW.uri_tcgplayer_infinite_decks, NEW.change_set_id
WHERE OLD.uri_tcgplayer_infinite_decks IS NOT DISTINCT FROM NEW.uri_tcgplayer_infinite_decks
UNION
-- Changed variation
SELECT NEW.card_id, 'variation', OLD.variation::TEXT, NEW.variation::TEXT, NEW.change_set_id
WHERE OLD.variation IS NOT DISTINCT FROM NEW.variation
UNION
-- Changed active
SELECT NEW.card_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_MTG_Card
BEFORE INSERT ON tcg.public.TCG_MTG_Card
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Card()
;
CREATE TRIGGER TRI_before_update_TCG_MTG_Card
BEFORE UPDATE ON tcg.public.TCG_MTG_Card
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Card()
;

View File

@@ -0,0 +1,164 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Inventory()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Inventory()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_MTG_Inventory_Audit (
inventory_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed card_id
SELECT NEW.inventory_id, 'card_id', CAST(OLD.card_id AS VARCHAR), CAST(NEW.card_id AS VARCHAR), NEW.change_set_id
WHERE OLD.card_id IS NOT DISTINCT FROM NEW.card_id
UNION
-- Changed finish_id
SELECT NEW.inventory_id, 'finish_id', CAST(OLD.finish_id AS VARCHAR), CAST(NEW.finish_id AS VARCHAR), NEW.change_set_id
WHERE OLD.finish_id IS NOT DISTINCT FROM NEW.finish_id
UNION
-- Changed condition_id
SELECT NEW.inventory_id, 'condition_id', CAST(OLD.condition_id AS VARCHAR), CAST(NEW.condition_id AS VARCHAR), NEW.change_set_id
WHERE OLD.condition_id IS NOT DISTINCT FROM NEW.condition_id
UNION
-- Changed sleeve_colour_name
SELECT NEW.inventory_id, 'sleeve_colour_name', OLD.sleeve_colour_name, NEW.sleeve_colour_name, NEW.change_set_id
WHERE OLD.sleeve_colour_name IS NOT DISTINCT FROM NEW.sleeve_colour_name
UNION
-- Changed location_name
SELECT NEW.inventory_id, 'location_name', OLD.location_name, NEW.location_name, NEW.change_set_id
WHERE OLD.location_name IS NOT DISTINCT FROM NEW.location_name
UNION
-- Changed acquired_from
SELECT NEW.inventory_id, 'acquired_from', OLD.acquired_from, NEW.acquired_from, NEW.change_set_id
WHERE OLD.acquired_from IS NOT DISTINCT FROM NEW.acquired_from
UNION
-- Changed acquired_on
SELECT NEW.inventory_id, 'acquired_on', TO_CHAR(OLD.acquired_on, 'YYYY-MM-DD'), TO_CHAR(NEW.acquired_on, 'YYYY-MM-DD'), NEW.change_set_id
WHERE OLD.acquired_on IS NOT DISTINCT FROM NEW.acquired_on
UNION
-- Changed cost_gbp
SELECT NEW.inventory_id, 'cost_gbp', CAST(OLD.cost_gbp AS VARCHAR), CAST(NEW.cost_gbp AS VARCHAR), NEW.change_set_id
WHERE OLD.cost_gbp IS NOT DISTINCT FROM NEW.cost_gbp
UNION
-- Changed sale_price_gbp
SELECT NEW.inventory_id, 'sale_price_gbp', CAST(OLD.sale_price_gbp AS VARCHAR), CAST(NEW.sale_price_gbp AS VARCHAR), NEW.change_set_id
WHERE OLD.sale_price_gbp IS NOT DISTINCT FROM NEW.sale_price_gbp
UNION
-- Changed is_sold
SELECT NEW.inventory_id, 'is_sold', OLD.is_sold::TEXT, NEW.is_sold::TEXT, NEW.change_set_id
WHERE OLD.is_sold IS NOT DISTINCT FROM NEW.is_sold
UNION
-- Changed is_destroyed
SELECT NEW.inventory_id, 'is_destroyed', OLD.is_destroyed::TEXT, NEW.is_destroyed::TEXT, NEW.change_set_id
WHERE OLD.is_destroyed IS NOT DISTINCT FROM NEW.is_destroyed
UNION
-- Changed notes
SELECT NEW.inventory_id, 'notes', OLD.notes, NEW.notes, NEW.change_set_id
WHERE OLD.notes IS NOT DISTINCT FROM NEW.notes
UNION
-- Changed alterations_customisations
SELECT NEW.inventory_id, 'alterations_customisations', OLD.alterations_customisations, NEW.alterations_customisations, NEW.change_set_id
WHERE OLD.alterations_customisations IS NOT DISTINCT FROM NEW.alterations_customisations
UNION
-- Changed grading_company_name
SELECT NEW.inventory_id, 'grading_company_name', OLD.grading_company_name, NEW.grading_company_name, NEW.change_set_id
WHERE OLD.grading_company_name IS NOT DISTINCT FROM NEW.grading_company_name
UNION
-- Changed grading_score
SELECT NEW.inventory_id, 'grading_score', OLD.grading_score, NEW.grading_score, NEW.change_set_id
WHERE OLD.grading_score IS NOT DISTINCT FROM NEW.grading_score
UNION
-- Changed subgrades
SELECT NEW.inventory_id, 'subgrades', OLD.subgrades, NEW.subgrades, NEW.change_set_id
WHERE OLD.subgrades IS NOT DISTINCT FROM NEW.subgrades
UNION
-- Changed misprint_errors
SELECT NEW.inventory_id, 'misprint_errors', OLD.misprint_errors, NEW.misprint_errors, NEW.change_set_id
WHERE OLD.misprint_errors IS NOT DISTINCT FROM NEW.misprint_errors
UNION
-- Changed miscut_errors
SELECT NEW.inventory_id, 'miscut_errors', OLD.miscut_errors, NEW.miscut_errors, NEW.change_set_id
WHERE OLD.miscut_errors IS NOT DISTINCT FROM NEW.miscut_errors
UNION
-- Changed playability
SELECT NEW.inventory_id, 'playability', OLD.playability, NEW.playability, NEW.change_set_id
WHERE OLD.playability IS NOT DISTINCT FROM NEW.playability
UNION
-- Changed owner_user_id
SELECT NEW.inventory_id, 'owner_user_id', CAST(OLD.owner_user_id AS VARCHAR), CAST(NEW.owner_user_id AS VARCHAR), NEW.change_set_id
WHERE OLD.owner_user_id IS NOT DISTINCT FROM NEW.owner_user_id
UNION
-- Changed ownership_status_name
SELECT NEW.inventory_id, 'ownership_status_name', OLD.ownership_status_name, NEW.ownership_status_name, NEW.change_set_id
WHERE OLD.ownership_status_name IS NOT DISTINCT FROM NEW.ownership_status_name
UNION
-- Changed trading_status_name
SELECT NEW.inventory_id, 'trading_status_name', OLD.trading_status_name, NEW.trading_status_name, NEW.change_set_id
WHERE OLD.trading_status_name IS NOT DISTINCT FROM NEW.trading_status_name
UNION
-- Changed loaned_to_user_id
SELECT NEW.inventory_id, 'loaned_to_user_id', CAST(OLD.loaned_to_user_id AS VARCHAR), CAST(NEW.loaned_to_user_id AS VARCHAR), NEW.change_set_id
WHERE OLD.loaned_to_user_id IS NOT DISTINCT FROM NEW.loaned_to_user_id
UNION
-- Changed acquired_on
SELECT NEW.inventory_id, 'acquired_on', TO_CHAR(OLD.acquired_on, 'YYYY-MM-DD'), TO_CHAR(NEW.acquired_on, 'YYYY-MM-DD'), NEW.change_set_id
WHERE OLD.acquired_on IS NOT DISTINCT FROM NEW.acquired_on
UNION
-- Changed acquired_on
SELECT NEW.inventory_id, 'acquired_on', TO_CHAR(OLD.acquired_on, 'YYYY-MM-DD'), TO_CHAR(NEW.acquired_on, 'YYYY-MM-DD'), NEW.change_set_id
WHERE OLD.acquired_on IS NOT DISTINCT FROM NEW.acquired_on
UNION
-- Changed provenance
SELECT NEW.inventory_id, 'provenance', OLD.provenance, NEW.provenance, NEW.change_set_id
WHERE OLD.provenance IS NOT DISTINCT FROM NEW.provenance
UNION
-- Changed signed_by_names
SELECT NEW.inventory_id, 'signed_by_names', OLD.signed_by_names, NEW.signed_by_names, NEW.change_set_id
WHERE OLD.signed_by_names IS NOT DISTINCT FROM NEW.signed_by_names
UNION
-- Changed signature_condition_name
SELECT NEW.inventory_id, 'signature_condition_name', OLD.signature_condition_name, NEW.signature_condition_name, NEW.change_set_id
WHERE OLD.signature_condition_name IS NOT DISTINCT FROM NEW.signature_condition_name
UNION
-- Changed active
SELECT NEW.inventory_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_MTG_Inventory
BEFORE INSERT ON tcg.public.TCG_MTG_Inventory
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Inventory()
;
CREATE TRIGGER TRI_before_update_TCG_MTG_Inventory
BEFORE UPDATE ON tcg.public.TCG_MTG_Inventory
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Inventory()
;

View File

@@ -0,0 +1,64 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck_Commander_Bracket()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck_Commander_Bracket()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_MTG_Deck_Commander_Bracket_Audit (
commander_bracket_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed name
SELECT NEW.commander_bracket_id, 'name', CAST(OLD.name AS VARCHAR), CAST(NEW.name AS VARCHAR), NEW.change_set_id
WHERE OLD.name IS NOT DISTINCT FROM NEW.name
UNION
-- Changed description
SELECT NEW.commander_bracket_id, 'description', CAST(OLD.description AS VARCHAR), CAST(NEW.description AS VARCHAR), NEW.change_set_id
WHERE OLD.description IS NOT DISTINCT FROM NEW.description
UNION
-- Changed display_order
SELECT NEW.commander_bracket_id, 'display_order', CAST(OLD.display_order AS VARCHAR), CAST(NEW.display_order AS VARCHAR), NEW.change_set_id
WHERE OLD.display_order IS NOT DISTINCT FROM NEW.display_order
UNION
-- Changed active
SELECT NEW.commander_bracket_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_MTG_Deck_Commander_Bracket
BEFORE INSERT ON tcg.public.TCG_MTG_Deck_Commander_Bracket
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck_Commander_Bracket()
;
CREATE TRIGGER TRI_before_update_TCG_MTG_Deck_Commander_Bracket
BEFORE UPDATE ON tcg.public.TCG_MTG_Deck_Commander_Bracket
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck_Commander_Bracket()
;

View File

@@ -0,0 +1,64 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_MTG_Deck_Audit (
deck_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed name
SELECT NEW.deck_id, 'name', CAST(OLD.name AS VARCHAR), CAST(NEW.name AS VARCHAR), NEW.change_set_id
WHERE OLD.name IS NOT DISTINCT FROM NEW.name
UNION
-- Changed is_commander
SELECT NEW.deck_id, 'is_commander', OLD.is_commander::TEXT, NEW.is_commander::TEXT, NEW.change_set_id
WHERE OLD.is_commander IS NOT DISTINCT FROM NEW.is_commander
UNION
-- Changed commander_bracket_id
SELECT NEW.deck_id, 'commander_bracket_id', CAST(OLD.commander_bracket_id AS VARCHAR), CAST(NEW.commander_bracket_id AS VARCHAR), NEW.change_set_id
WHERE OLD.commander_bracket_id IS NOT DISTINCT FROM NEW.commander_bracket_id
UNION
-- Changed active
SELECT NEW.deck_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_MTG_Deck
BEFORE INSERT ON tcg.public.TCG_MTG_Deck
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck()
;
CREATE TRIGGER TRI_before_update_TCG_MTG_Deck
BEFORE UPDATE ON tcg.public.TCG_MTG_Deck
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck()
;

View File

@@ -0,0 +1,64 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck_Inventory_Link()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck_Inventory_Link()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_MTG_Deck_Inventory_Link_Audit (
link_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed deck_id
SELECT NEW.link_id, 'deck_id', CAST(OLD.deck_id AS VARCHAR), CAST(NEW.deck_id AS VARCHAR), NEW.change_set_id
WHERE OLD.deck_id IS NOT DISTINCT FROM NEW.deck_id
UNION
-- Changed inventory_id
SELECT NEW.link_id, 'inventory_id', CAST(OLD.inventory_id AS VARCHAR), CAST(NEW.inventory_id AS VARCHAR), NEW.change_set_id
WHERE OLD.inventory_id IS NOT DISTINCT FROM NEW.inventory_id
UNION
-- Changed display_order
SELECT NEW.link_id, 'display_order', CAST(OLD.display_order AS VARCHAR), CAST(NEW.display_order AS VARCHAR), NEW.change_set_id
WHERE OLD.display_order IS NOT DISTINCT FROM NEW.display_order
UNION
-- Changed active
SELECT NEW.link_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_MTG_Deck_Inventory_Link
BEFORE INSERT ON tcg.public.TCG_MTG_Deck_Inventory_Link
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Deck_Inventory_Link()
;
CREATE TRIGGER TRI_before_update_TCG_MTG_Deck_Inventory_Link
BEFORE UPDATE ON tcg.public.TCG_MTG_Deck_Inventory_Link
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Deck_Inventory_Link()
;

566
sql/90000_usp_populate.sql Normal file
View File

@@ -0,0 +1,566 @@
-- Change Set
INSERT INTO tcg.public.TCG_Change_Set (
comment
, updated_last_by_user_id
)
VALUES (
'Initial population of lookup tables.'
, 3
);
-- User
INSERT INTO tcg.public.TCG_User (
firstname
, surname
, email
, is_super_user
, change_set_id
)
VALUES (
'Teddy'
, 'Smith'
, 'edward.middletonsmith@gmail.com'
, TRUE
, 1
);
INSERT INTO tcg.public.TCG_Change_Set (
comment
, updated_last_by_user_id
)
VALUES (
'Alter created by user id in User table from initial upload.'
, 3
);
UPDATE tcg.public.TCG_User
SET
created_by_user_id = 3
, updated_last_by_user_id = 3
, change_set_id = 4
;
-- Condition
INSERT INTO tcg.public.TCG_Condition (
name
, code
, description
, summary
, us_name
, change_set_id
, created_by_user_id
, updated_last_by_user_id
)
VALUES
(
'Mint' -- name
, 'M' -- code
, $$A mint card is in perfect condition; no excuses. This means, that the front is in perfect condition, there are no scratches on the surface, and the surface is perfectly clean. For the back it means, that the card is indistinguishable from cards of a newly openend booster. If a card has a signature or a Grand Prix stamp it can never be graded Mint, even if the card stock is otherwise in Mint condition.
In most cases it doesn't make sense to grade a card as Mint. For newer cards, the value of a Mint card is basically the same as a Near Mint card. Older cards (1993-96), however, may command a high premium if they are in actual Mint condition - usually professionally graded. Thus 'Mint' is mainly a grading for cards where there is a high collectors' interest or a high professional grade. For cards that are sold mostly for playing purpose, a Near Mint grade is a safer choice.$$ -- description
, 'Perfect' -- summary
, 'Mint' -- us_name
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Near Mint' -- name
, 'NM' -- code
, $$A Near Mint card looks like it has never been played without sleeves. Small allowances can be made, but the card generally shows no wear.
The border of NM card can have small white spots, but they must be very few and very small. When the card is inspected under bright daylight, the surface must generally appear clean. It can have a few minor spots, but scratches can never be allowed for NM cards.
Generally a Near Mint card is in a condition that would make it considered unmarked if played in an unsleeved deck. (Not recommended!)
As the Mint grade is often not used for cards of newer expansions, Near Mint usually means Near Mint or better (equivalent to the American NM/M grade).$$ -- description
, 'Booster-Fresh' -- summary
, 'Near Mint' -- us_name
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Excellent' -- name
, 'EX' -- code
, $$An Excellent cards look like it was used for a few games without sleeves. For Excellent cards it is almost always clearly visible upon first inspection that the card is not in perfect condition. However, although the damage is clearly visible it is only of minor severity.
Excellent cards usually have a couple of white spots at the corners or around the border. The surface may have minor scratches, that are visible upon closer inspection. However, the card cannot be graded Excellent if the creases are so deep that they are visible upon first sight.
An Excellent card is usually in a condition where it is not quite clear if the card would be considered marked or unmarked if it would be played in a tournament without sleeves.
The American equivalent usually is Slightly Played or Lightly Played (not to be confused with the European Light Played).$$ -- description
, 'Minor-Wear' -- summary
, 'Slightly Played' -- us_name
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Good' -- name
, 'GD' -- code
, $$A Good card looks like it might have been used for a long tournament without sleeves.
Cards in Good condition usually show strong wear all around the card. The edges and corners have many white spots, the surface usually has scratches and the card usually has accumulated some dirt on its surface. However, the card still only has damage that stems from regular play. The card has no water damage or bends whatsoever.
A Good card (and all cards in worse condition) are clearly in a condition that would make them ineligible for play without sleeves as they would be considered marked.
The American equivalent to this is usually 'Moderately Played' or 'Very Good'. Note that 'Good' is a bit of a misnomer. A Good card doesn't really look good. In fact it looks pretty beat up, making the American Very Good even more of a misnomer.$$ -- description
, 'Visible-Wear' -- summary
, 'Moderately Played' -- us_name
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Light Played' -- name
, 'LP' -- code
, $$A Light Played card looks as if it has been used without sleeves for an extended period of time.
A Light Played card is clearly legal for play in a sleeved deck. It has also not been tampered with (inked border, random scribblings on the card etc.). If both of these criteria apply the card may look very bad, but it can be graded Light Played.
The American equivalent usually is 'Played' or 'Good'.$$ -- description
, 'Severe-Wear' -- summary
, 'Played' -- us_name
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Played' -- name
, 'PL' -- code
, $$A Played card looks as bad as you can get a card through regular use without sleeves.
A Played card looks extremely bad, and it is doubtful if the card is tournament legal even in a sleeved deck. However, the card has not been tampered with otherwise (inked border, random scribblings on the card etc.).
The American equivalent usually is Heavily Played or Good.$$ -- description
, 'Damaged' -- summary
, 'Heavily Played' -- us_name
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Poor' -- name
, 'PO' -- code
, $$A Poor card has damage that cannot normally have stemmed from regular use of the card.
A card in Poor condition is literally destroyed. It is either obviously illegal for tournament play or has been tampered with in ways that destroy its worth almost completely (inked border, random scribblings on the card etc.).
Americans usually use Poor in the same way.$$ -- description
, 'Destroyed' -- summary
, 'Poor' -- us_name
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
;
'
-- MTG Finish
INSERT INTO tcg.public.TCG_MTG_Finish (
name
, description
, change_set_id
, created_by_user_id
, updated_last_by_user_id
)
VALUES
(
'Standard' -- name
, '' -- description
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Foil' -- name
, '' -- description
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Etched Foil' -- name
, '' -- description
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Borderless' -- name
, '' -- description
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Extended Art' -- name
, '' -- description
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
, (
'Showcase' -- name
, '' -- description
, 1 -- change_set_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
)
;
-- Inventory
SELECT I_T.temp_id -- COUNT(*)
, I_T.name
, MAIN_CARD.name
, I_T.set_code
, MAIN_CARD.SET
, I_T.collector_number
, MAIN_CARD.collector_number
, MAIN_CARD.set_type
, I_T.display_order
, I_T.token_rear_side_name
, REAR_CARD.name AS rear_name
, I_T.token_rear_side_set_code
, REAR_CARD.SET AS rear_set
, I_T.token_rear_side_collector_number
, REAR_CARD.collector_number AS rear_collector_number
, REAR_CARD.set_type AS rear_set_type
FROM tcg.public.TCG_MTG_Inventory_Temp I_T
LEFT JOIN tcg.public.TCG_MTG_Card MAIN_CARD
ON (
(
I_T.is_token = FALSE
AND LOWER(I_T.set_code) = MAIN_CARD.SET
)
OR (
I_T.is_token = TRUE
AND MAIN_CARD.set_type = 'token'
AND 't' || LOWER(I_T.set_code) = MAIN_CARD.set
)
/*
OR (
I_T.set_code LIKE 'FIN%'
AND C.set = 'fin'
)
*/
)
AND I_T.collector_number = MAIN_CARD.collector_number
LEFT JOIN tcg.public.TCG_MTG_Card REAR_CARD
ON (
(
I_T.is_token = FALSE
AND LOWER(I_T.set_code) = REAR_CARD.SET
)
OR (
I_T.is_token = TRUE
AND REAR_CARD.set_type = 'token'
AND 't' || LOWER(I_T.set_code) = REAR_CARD.set
)
)
AND I_T.collector_number = REAR_CARD.collector_number
WHERE -- 1=1 OR
MAIN_CARD.card_id IS NULL
-- OR C.name <> I_T.name
-- OR C.set_type = 'token'
OR (
I_T.is_token = TRUE
AND REAR_CARD.card_id IS NULL
)
-- I_T.name = 'Idyllic Beachfront'
-- GROUP BY I_T.temp_id
ORDER BY I_T.display_order
;
SELECT -- *
C.name
, C.set
, C.collector_number
, *
FROM tcg.public.TCG_MTG_Card C
WHERE
-- C.name LIKE 'Dragon Illusion'
C.set = 'tdm'
AND C.collector_number = '294'
ORDER BY C.set, C.collector_number
;
INSERT INTO tcg.public.TCG_MTG_Inventory (
card_id
, finish_id
, condition_id
, sleeve_colour_name
, location_name
, acquired_from
, acquired_on
, cost_gbp
, sale_price_gbp
, is_sold
, is_destroyed
, notes
, alterations_customisations
, grading_company_name
, grading_score
, subgrades
, misprint_errors
, miscut_errors
, playability
, owner_user_id
, ownership_status_name
, trading_status_name
, loaned_to_user_id
, loan_start_on
, loan_end_on
, provenance
, signed_by_names
, signature_condition_name
, active
, created_by_user_id
, updated_last_by_user_id
, change_set_id
, token_rear_side_card_id
)
SELECT
MAIN_CARD.card_id
, I_T.finish_id
, I_T.condition_id
, I_T.sleeve_colour_name
, I_T.location_name
, I_T.acquired_from
, I_T.acquired_on
, I_T.cost_gbp
, I_T.sale_price_gbp
, I_T.is_sold
, I_T.is_destroyed
, I_T.notes
, I_T.alterations_customisations
, I_T.grading_company_name
, I_T.grading_score
, I_T.subgrades
, I_T.misprint_errors
, I_T.miscut_errors
, I_T.playability
, I_T.owner_user_id
, I_T.ownership_status_name
, I_T.trading_status_name
, I_T.loaned_to_user_id
, I_T.loan_start_on
, I_T.loan_end_on
, I_T.provenance
, I_T.signed_by_names
, I_T.signature_condition_name
, I_T.active
, I_T.created_by_user_id
, I_T.updated_last_by_user_id
, I_T.change_set_id
, REAR_CARD.card_id
FROM tcg.public.TCG_MTG_Inventory_Temp I_T
INNER JOIN tcg.public.TCG_MTG_Card MAIN_CARD
ON (
(
I_T.is_token = FALSE
AND LOWER(I_T.set_code) = MAIN_CARD.SET
)
OR (
I_T.is_token = TRUE
AND MAIN_CARD.set_type = 'token'
AND 't' || LOWER(I_T.set_code) = MAIN_CARD.set
)
/*
OR (
I_T.set_code LIKE 'FIN%'
AND C.set = 'fin'
)
*/
)
AND I_T.collector_number = MAIN_CARD.collector_number
LEFT JOIN tcg.public.TCG_MTG_Card REAR_CARD
ON (
(
I_T.is_token = FALSE
AND LOWER(I_T.set_code) = REAR_CARD.SET
)
OR (
I_T.is_token = TRUE
AND REAR_CARD.set_type = 'token'
AND 't' || LOWER(I_T.set_code) = REAR_CARD.set
)
)
AND I_T.collector_number = REAR_CARD.collector_number
ORDER BY I_T.display_order
;
-- SELECT * FROM tcg.public.TCG_MTG_Inventory_Temp;
SELECT
-- I.inventory_id
C.card_id
, C.name
, C.card_faces
FROM /* tcg.public.TCG_MTG_Inventory I
INNER JOIN*/ tcg.public.TCG_MTG_Card C -- ON I.card_id = C.card_id
WHERE
-- C.name LIKE '%Yuna%'
C.set_type = 'token'
-- AND C.card_faces LIKE '%name'': ''Treasure'
AND C.name IN ('Treasure', 'Clue')
ORDER BY C.card_id -- I.inventory_id
;
SELECT
C.set
, C.collector_number
, COUNT(*)
FROM tcg.public.TCG_MTG_Card C
GROUP BY
C.set
, C.collector_number
HAVING COUNT(*) > 1
;
-- Deck Lookup Tables
INSERT INTO tcg.public.TCG_MTG_Deck_Commander_Bracket (
name
, description
, display_order
, created_by_user_id
, updated_last_by_user_id
, change_set_id
)
VALUES
(
'Exhibition' -- name
, $$Throw down with your ultra-casual Commander deck!
Winning is not the primary goal here, as it's more about showing off something unusual you've made. Villains yelling in the art? Everything has the number four? Oops, all Horses? Those are all fair game! The games here are likely to go long and end slowly.
Just focus on having fun and enjoying what the table has brought!
Deck Building: No cards from the Game Changers list. No intentional two-card infinite combos, mass land denial, or extra-turn cards. Tutors should be sparse.$$ -- description
, 1
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
)
, (
'Core' -- name
, $$The easiest reference point is that the average current preconstructed deck is at a Core (Bracket 2) level.
While Bracket 2 decks may not have every perfect card, they have the potential for big, splashy turns, strong engines, and are built in a way that works toward winning the game. While the game is unlikely to end out of nowhere and generally goes nine or more turns, you can expect big swings. The deck usually has some cards that aren't perfect from a gameplay perspective but are there for flavor reasons, or just because they bring a smile to your face.
Deck Building: No cards from the Game Changers list. No intentional two-card infinite combos or mass land denial. Extra-turn cards should only appear in low quantities and are not intended to be chained in succession or looped. Tutors should be sparse.$$ -- description
, 2
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
)
, (
'Upgraded' -- name
, $$These decks are souped up and ready to play beyond the strength of an average preconstructed deck.
They are full of carefully selected cards, with work having gone into figuring out the best card for each slot. The games tend to be a little faster as well, ending a turn or two sooner than your Core (Bracket 2) decks. This also is where players can begin playing up to three cards from the Game Changers list, amping up the decks further. Of course, it doesn't have to have any Game Changers to be a Bracket 3 deck: many decks are more powerful than a preconstructed deck, even without them!
These decks should generally not have any two-card infinite combos that can happen cheaply and in about the first six or so turns of the game, but it's possible the long game could end with one being deployed, even out of nowhere.
Deck Building: Up to three cards from the Game Changers list. No intentional early-game two-card infinite combos. Extra-turn cards should only appear in low quantities and are not intended to be chained in succession or looped. No mass land denial.$$ -- description
, 3
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
)
, (
'Optimized' -- name
, $$It's time to go wild!
Bring out your strongest decks and cards. You can expect to see explosive starts, strong tutors, cheap combos that end games, mass land destruction, or a deck full of cards off the Game Changers list. This is high-powered Commander, and games have the potential to end quickly.
The focus here is on bringing the best version of the deck you want to play, but not one built around a tournament metagame. It's about shuffling up your strong and fully optimized deck, whatever it may be, and seeing how it fares. For most Commander players, these are the highest-power Commander decks you will interact with.
Deck Building: There are no restrictions (other than the banned list).$$ -- description
, 4
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
)
, (
'cEDh' -- name
, $$This is high power with a very competitive and metagame-focused mindset.
"Mindset" is a key part of that description: Much of it is in how you approach the format and deck building. It's not just no holds barred, where you play your most powerful cards like in Bracket 4. It requires careful planning: There is care paid into following and paying attention to a metagame and tournament structure, and no sacrifices are made in deck building as you try to be the one to win the pod. Additionally, there is special care and attention paid to behavior and tableside negotiation (such as not making spite plays or concessions) that play into the tournament structure.
cEDH, or "competitive Commander" and similar names, is where winning matters more than self-expression. You might not be playing your favorite cards or commanders, as pet cards are usually replaced with cards needed in the meta, but you're playing what you think will be most likely to win.
Deck Building: There are no restrictions (other than the banned list).
$$ -- description
, 5
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
)
;
'
INSERT INTO tcg.public.TCG_MTG_Deck (
name
, commander_bracket_id
, created_by_user_id
, updated_last_by_user_id
, change_set_id
)
VALUES
(
'Final Fantasy: Counter Blitz' -- name
, 2 -- commander_bracket_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
)
, (
'Tarkir Dragonstorm: Jeskai Striker' -- name
, 2 -- commander_bracket_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
)
, (
'The Lost Caverns of Ixalan: Blood Rites' -- name
, 2 -- commander_bracket_id
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
)
;
SELECT *
FROM tcg.public.TCG_MTG_Deck D
;
INSERT INTO tcg.public.TCG_MTG_Deck_Inventory_Link (
deck_id
, inventory_id
, display_order
, created_by_user_id
, updated_last_by_user_id
, change_set_id
)
SELECT
CASE WHEN C.set LIKE '%fic' OR C.set LIKE '%fin' THEN 1 ELSE 2 END AS deck_id
, I.inventory_id
, CASE WHEN C.set LIKE '%fic' OR C.set LIKE '%fin' THEN I.inventory_id ELSE I.inventory_id - 106 END AS display_order
, 3 -- created_by_user_id
, 3 -- updated_last_by_user_id
, 1 -- change_set_id
FROM tcg.public.TCG_MTG_Inventory I
INNER JOIN tcg.public.TCG_MTG_Card C ON I.card_id = C.card_id
ORDER BY I.inventory_id
;
INSERT INTO tcg.public.TCG_Change_Set (
comment
, updated_last_by_user_id
)
VALUES (
'Remove extra cards from Tarkir Dragonstorm deck.'
, 3
);
UPDATE tcg.public.TCG_MTG_Deck_Inventory_Link DIL
SET
active = FALSE
, change_set_id = 6
WHERE DIL.inventory_id IN (217, 218)
;

View File

@@ -0,0 +1,93 @@
-- Unowned Land
WITH
Cleaned_Card AS (
SELECT
C.card_id
, C.name
, C.type_line
, C.oracle_text
, C.color_identity
, C.produced_mana
, COALESCE(C.price_eur, C.price_usd, C.price_tix) AS price
FROM tcg.public.TCG_MTG_Card C
)
, Owned_Card AS (
SELECT C.name
FROM tcg.public.TCG_MTG_Card C
INNER JOIN tcg.public.TCG_MTG_Inventory I ON C.card_id = I.card_id
)
, Unowned_Land AS (
SELECT
CC.card_id
, CC.name
, ROW_NUMBER() OVER (
PARTITION BY
CC.name
, CC.oracle_text
ORDER BY
CC.price NULLS LAST
, CC.type_line
, CC.card_id
) AS index_card_price_in_card_reprints
FROM Cleaned_Card CC
LEFT JOIN Owned_Card OC ON CC.name = OC.name
WHERE
OC.name IS NULL
AND CC.type_line LIKE '%Land%'
)
SELECT
-- COUNT(*)
-- MAX(COALESCE(CC.price, 0.0))
CC.name
, CC.type_line
, REPLACE(CC.name, ' ', '+')
, CC.oracle_text
, CONCAT(
'https://magicmadhouse.co.uk/search.php?search_query='
, REPLACE(CC.name, ' ', '+')
, '&Filters=brand%3AMagic%253A%2520The%2520Gathering'
) AS url_mm
, CONCAT(
'https://www.chaoscards.co.uk/search/'
, REPLACE(CC.name, ' ', '+')
, '?sort=price_asc&in_stock=In+stock&brand=Magic+the+Gathering'
) AS url_cc
, CONCAT(
'https://www.bigorbitcards.co.uk/magic-the-gathering/search/'
, REPLACE(CC.name, ' ', '+')
, '/?order=product.price.asc'
) AS url_boc
, CC.color_identity
, CC.produced_mana
, CC.price
FROM Cleaned_Card CC
INNER JOIN Unowned_Land UL ON CC.card_id = UL.card_id
WHERE
UL.index_card_price_in_card_reprints = 1
AND ( -- Invalid colours
CC.color_identity NOT IN ('G, R', 'B, G', 'B, R', 'B, U')
)
AND CC.oracle_text NOT LIKE '%This land enters tapped unless you control a Mount or Vehicle.%'
ORDER BY
CC.price NULLS LAST
, CC.type_line
, CC.name
, CC.card_id
-- LIMIT 250
OFFSET 200
;
/*
SELECT
C.name
, C.set
FROM tcg.public.TCG_MTG_Card C
INNER JOIN tcg.public.TCG_MTG_Inventory I ON C.card_id = I.card_id
WHERE UPPER(C.type_line) LIKE '%SOLDIER%'
ORDER BY
C.set
, C.name
;
*/

View File

@@ -0,0 +1,16 @@
CREATE TABLE tcg.public.TCG_Language (
language_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, name VARCHAR(250) NOT NULL
, description VARCHAR(250) NOT NULL
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_Language_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP
, updated_last_by_user_id INT
, change_set_id INT
, CONSTRAINT FK_TCG_Language_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_Language_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, language_id INT NOT NULL
, CONSTRAINT FK_TCG_Language_Audit_language_id
FOREIGN KEY (language_id)
REFERENCES tcg.public.TCG_Language(language_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_Language_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,19 @@
CREATE TABLE tcg.public.TCG_MTG_Rarity (
rarity_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, name VARCHAR(250) NOT NULL
, colour_name VARCHAR(250) NOT NULL
, price_ratio_min DECIMAL(6, 1)
, price_ratio_max DECIMAL(6, 1)
, active BOOLEAN NOT NULL DEFAULT TRUE
, created_on TIMESTAMP NOT NULL
, created_by_user_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Rarity_created_by_user_id
FOREIGN KEY (created_by_user_id)
REFERENCES tcg.public.TCG_User(user_id)
, updated_last_on TIMESTAMP
, updated_last_by_user_id INT
, change_set_id INT
, CONSTRAINT FK_TCG_MTG_Rarity_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,15 @@
CREATE TABLE tcg.public.TCG_MTG_Rarity_Audit (
audit_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, rarity_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Rarity_Audit_rarity_id
FOREIGN KEY (rarity_id)
REFERENCES tcg.public.TCG_MTG_Rarity(rarity_id)
, name_field VARCHAR(255) NOT NULL
, value_prev TEXT
, value_new TEXT
, change_set_id INT NOT NULL
, CONSTRAINT FK_TCG_MTG_Rarity_Audit_change_set_id
FOREIGN KEY (change_set_id)
REFERENCES tcg.public.TCG_Change_Set(change_set_id)
);

View File

@@ -0,0 +1,68 @@
CREATE OR REPLACE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Rarity()
RETURNS TRIGGER AS $$
BEGIN
NEW.created_on = CURRENT_TIMESTAMP;
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF NEW.change_set_id IS NULL THEN
RAISE EXCEPTION 'Change Set ID must be provided.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE OR REPLACE FUNCTION tcg.public.FN_before_update_TCG_MTG_Rarity()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_last_on = CURRENT_TIMESTAMP;
IF OLD.change_set_id IS NOT DISTINCT FROM NEW.change_set_id THEN
RAISE EXCEPTION 'New Change Set ID must be provided.';
END IF;
INSERT INTO tcg.public.TCG_MTG_Rarity_Audit (
rarity_id
, name_field
, value_prev
, value_new
, change_set_id
)
-- Changed name
SELECT NEW.rarity_id, 'name', OLD.name, NEW.name, NEW.change_set_id
WHERE OLD.name IS NOT DISTINCT FROM NEW.name
UNION
-- Changed colour_name
SELECT NEW.rarity_id, 'colour_name', OLD.colour_name, NEW.colour_name, NEW.change_set_id
WHERE OLD.colour_name IS NOT DISTINCT FROM NEW.colour_name
UNION
-- Changed price_ratio_min
SELECT NEW.rarity_id, 'price_ratio_min', CAST(OLD.price_ratio_min AS VARCHAR), CAST(NEW.price_ratio_min AS VARCHAR), NEW.change_set_id
WHERE OLD.price_ratio_min IS NOT DISTINCT FROM NEW.price_ratio_min
UNION
-- Changed price_ratio_max
SELECT NEW.rarity_id, 'price_ratio_max', CAST(OLD.price_ratio_max AS VARCHAR), CAST(NEW.price_ratio_max AS VARCHAR), NEW.change_set_id
WHERE OLD.price_ratio_max IS NOT DISTINCT FROM NEW.price_ratio_max
UNION
-- Changed active
SELECT NEW.rarity_id, 'active', OLD.active::TEXT, NEW.active::TEXT, NEW.change_set_id
WHERE OLD.active IS NOT DISTINCT FROM NEW.active
;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
;
CREATE TRIGGER TRI_before_insert_TCG_MTG_Rarity
BEFORE INSERT ON tcg.public.TCG_MTG_Rarity
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_insert_TCG_MTG_Rarity()
;
CREATE TRIGGER TRI_before_update_TCG_MTG_Rarity
BEFORE UPDATE ON tcg.public.TCG_MTG_Rarity
FOR EACH ROW
EXECUTE FUNCTION tcg.public.FN_before_update_TCG_MTG_Rarity()
;