DDL schemas for Trading Platform: - User management - Authentication - Payments - Education - ML predictions - Trading data Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
323 lines
9.1 KiB
Bash
Executable File
323 lines
9.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# ============================================================================
|
|
# CREATE DATABASE SCRIPT - Trading Platform
|
|
# ============================================================================
|
|
#
|
|
# Script de carga limpia para crear la base de datos desde DDL.
|
|
# Cumple con DIRECTIVA-POLITICA-CARGA-LIMPIA.md
|
|
#
|
|
# USO:
|
|
# ./create-database.sh # Crear BD (si no existe)
|
|
# ./create-database.sh --drop-first # Drop y recrear
|
|
# ./create-database.sh --seeds-only # Solo ejecutar seeds
|
|
#
|
|
# ============================================================================
|
|
|
|
set -e
|
|
|
|
# Colores para output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuración
|
|
# Valores según DEVENV-PORTS-INVENTORY.yml (instancia nativa compartida)
|
|
# IMPORTANTE: Puerto 5432 = instancia nativa PostgreSQL (NO Docker)
|
|
# Nombres homologados: trading_platform / trading_user (2026-01-07)
|
|
DB_NAME="${DB_NAME:-trading_platform}"
|
|
DB_USER="${DB_USER:-trading_user}"
|
|
DB_PASSWORD="${DB_PASSWORD:-trading_dev_2025}"
|
|
DB_HOST="${DB_HOST:-localhost}"
|
|
DB_PORT="${DB_PORT:-5432}"
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DDL_DIR="$SCRIPT_DIR/../ddl"
|
|
SEEDS_DIR="$SCRIPT_DIR/../seeds"
|
|
|
|
# Función de logging
|
|
log() {
|
|
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[✓]${NC} $1"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[!]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[✗]${NC} $1"
|
|
}
|
|
|
|
# Función para ejecutar SQL
|
|
run_sql() {
|
|
local file=$1
|
|
local description=$2
|
|
|
|
if [ -f "$file" ]; then
|
|
log "Ejecutando: $description"
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -f "$file" -q
|
|
log_success "$description"
|
|
else
|
|
log_warning "Archivo no encontrado: $file"
|
|
fi
|
|
}
|
|
|
|
# Función para ejecutar SQL en orden
|
|
run_sql_dir() {
|
|
local dir=$1
|
|
local description=$2
|
|
|
|
if [ -d "$dir" ]; then
|
|
log "Procesando directorio: $description"
|
|
for file in "$dir"/*.sql; do
|
|
if [ -f "$file" ]; then
|
|
local filename=$(basename "$file")
|
|
run_sql "$file" "$filename"
|
|
fi
|
|
done
|
|
else
|
|
log_warning "Directorio no encontrado: $dir"
|
|
fi
|
|
}
|
|
|
|
# Verificar conexión
|
|
check_connection() {
|
|
log "Verificando conexión a PostgreSQL..."
|
|
if PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres -c '\q' 2>/dev/null; then
|
|
log_success "Conexión exitosa"
|
|
else
|
|
log_error "No se puede conectar a PostgreSQL"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Drop database si existe
|
|
drop_database() {
|
|
log "Eliminando base de datos $DB_NAME si existe..."
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres -c "DROP DATABASE IF EXISTS $DB_NAME;" -q
|
|
log_success "Base de datos eliminada"
|
|
}
|
|
|
|
# Crear database
|
|
create_database() {
|
|
log "Creando base de datos $DB_NAME..."
|
|
# Intentar con locale explícito, si falla usar default
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres -c "CREATE DATABASE $DB_NAME WITH ENCODING 'UTF8' LC_COLLATE='en_US.UTF-8' LC_CTYPE='en_US.UTF-8' TEMPLATE=template0;" -q 2>/dev/null || \
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres -c "CREATE DATABASE $DB_NAME WITH ENCODING 'UTF8';" -q 2>/dev/null || true
|
|
log_success "Base de datos creada"
|
|
}
|
|
|
|
# Crear schemas
|
|
create_schemas() {
|
|
log "Creando schemas..."
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -q << EOF
|
|
-- Schemas principales
|
|
CREATE SCHEMA IF NOT EXISTS auth;
|
|
CREATE SCHEMA IF NOT EXISTS education;
|
|
CREATE SCHEMA IF NOT EXISTS trading;
|
|
CREATE SCHEMA IF NOT EXISTS investment;
|
|
CREATE SCHEMA IF NOT EXISTS financial;
|
|
CREATE SCHEMA IF NOT EXISTS ml;
|
|
CREATE SCHEMA IF NOT EXISTS llm;
|
|
CREATE SCHEMA IF NOT EXISTS audit;
|
|
CREATE SCHEMA IF NOT EXISTS market_data;
|
|
|
|
-- Comentarios
|
|
COMMENT ON SCHEMA auth IS 'Autenticación y usuarios';
|
|
COMMENT ON SCHEMA education IS 'Plataforma educativa';
|
|
COMMENT ON SCHEMA trading IS 'Trading y paper engine';
|
|
COMMENT ON SCHEMA investment IS 'Cuentas PAMM';
|
|
COMMENT ON SCHEMA financial IS 'Pagos, suscripciones, wallets';
|
|
COMMENT ON SCHEMA ml IS 'Machine Learning signals';
|
|
COMMENT ON SCHEMA llm IS 'LLM Agent';
|
|
COMMENT ON SCHEMA audit IS 'Auditoría y logs';
|
|
COMMENT ON SCHEMA market_data IS 'Datos de mercado OHLCV (5m, 15m)';
|
|
|
|
-- Search path
|
|
ALTER DATABASE $DB_NAME SET search_path TO auth, public;
|
|
EOF
|
|
log_success "Schemas creados"
|
|
}
|
|
|
|
# Cargar extensiones
|
|
load_extensions() {
|
|
log "Cargando extensiones..."
|
|
PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -q << EOF
|
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
|
CREATE EXTENSION IF NOT EXISTS "pg_trgm";
|
|
CREATE EXTENSION IF NOT EXISTS "btree_gin";
|
|
CREATE EXTENSION IF NOT EXISTS "vector"; -- pgvector para embeddings LLM
|
|
EOF
|
|
log_success "Extensiones cargadas"
|
|
}
|
|
|
|
# Cargar DDL por schema
|
|
load_ddl() {
|
|
local schema=$1
|
|
local schema_dir="$DDL_DIR/schemas/$schema"
|
|
|
|
log "Cargando DDL para schema: $schema"
|
|
|
|
# 0. Extensiones primero (si existen)
|
|
if [ -f "$schema_dir/00-extensions.sql" ]; then
|
|
run_sql "$schema_dir/00-extensions.sql" "$schema: Extensions"
|
|
fi
|
|
|
|
# 1. Enums (busca 00-enums.sql o 01-enums.sql)
|
|
if [ -f "$schema_dir/00-enums.sql" ]; then
|
|
run_sql "$schema_dir/00-enums.sql" "$schema: ENUMs"
|
|
elif [ -f "$schema_dir/01-enums.sql" ]; then
|
|
run_sql "$schema_dir/01-enums.sql" "$schema: ENUMs"
|
|
fi
|
|
|
|
# 2. Tablas en orden
|
|
run_sql_dir "$schema_dir/tables" "$schema: Tables"
|
|
|
|
# 3. Funciones
|
|
run_sql_dir "$schema_dir/functions" "$schema: Functions"
|
|
|
|
# 4. Triggers
|
|
run_sql_dir "$schema_dir/triggers" "$schema: Triggers"
|
|
|
|
# 5. Views
|
|
run_sql_dir "$schema_dir/views" "$schema: Views"
|
|
|
|
# 6. Indexes adicionales
|
|
if [ -f "$schema_dir/99-indexes.sql" ]; then
|
|
run_sql "$schema_dir/99-indexes.sql" "$schema: Indexes"
|
|
fi
|
|
|
|
log_success "Schema $schema cargado"
|
|
}
|
|
|
|
# Cargar todos los DDL
|
|
load_all_ddl() {
|
|
log "Cargando todos los DDL..."
|
|
|
|
# Orden de carga (respeta dependencias)
|
|
local schemas=("auth" "education" "financial" "trading" "investment" "ml" "llm" "audit" "market_data")
|
|
|
|
for schema in "${schemas[@]}"; do
|
|
if [ -d "$DDL_DIR/schemas/$schema" ]; then
|
|
load_ddl "$schema"
|
|
else
|
|
log_warning "Schema $schema no tiene DDL definido"
|
|
fi
|
|
done
|
|
|
|
log_success "Todos los DDL cargados"
|
|
}
|
|
|
|
# Cargar seeds
|
|
load_seeds() {
|
|
local env=${1:-prod}
|
|
local seeds_path="$SEEDS_DIR/$env"
|
|
|
|
log "Cargando seeds ($env)..."
|
|
|
|
if [ -d "$seeds_path" ]; then
|
|
for schema_dir in "$seeds_path"/*/; do
|
|
if [ -d "$schema_dir" ]; then
|
|
local schema=$(basename "$schema_dir")
|
|
run_sql_dir "$schema_dir" "Seeds: $schema"
|
|
fi
|
|
done
|
|
log_success "Seeds cargados"
|
|
else
|
|
log_warning "No hay seeds para ambiente: $env"
|
|
fi
|
|
}
|
|
|
|
# Validar integridad
|
|
validate_database() {
|
|
log "Validando integridad de la base de datos..."
|
|
|
|
# Contar tablas por schema
|
|
local result=$(PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -t -c "
|
|
SELECT schemaname, COUNT(*)
|
|
FROM pg_tables
|
|
WHERE schemaname IN ('auth', 'education', 'trading', 'investment', 'financial', 'ml', 'llm', 'audit', 'market_data')
|
|
GROUP BY schemaname
|
|
ORDER BY schemaname;
|
|
")
|
|
|
|
echo ""
|
|
echo "=== Resumen de Tablas por Schema ==="
|
|
echo "$result"
|
|
echo ""
|
|
|
|
# Verificar FKs
|
|
local fk_count=$(PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -t -c "
|
|
SELECT COUNT(*) FROM information_schema.table_constraints WHERE constraint_type = 'FOREIGN KEY';
|
|
")
|
|
log_success "Foreign Keys: $fk_count"
|
|
|
|
log_success "Validación completada"
|
|
}
|
|
|
|
# Main
|
|
main() {
|
|
echo ""
|
|
echo "=============================================="
|
|
echo " Trading Platform - Database Setup"
|
|
echo " Política de Carga Limpia (DDL-First)"
|
|
echo "=============================================="
|
|
echo ""
|
|
|
|
local drop_first=false
|
|
local seeds_only=false
|
|
local seed_env="prod"
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--drop-first)
|
|
drop_first=true
|
|
shift
|
|
;;
|
|
--seeds-only)
|
|
seeds_only=true
|
|
shift
|
|
;;
|
|
--env)
|
|
seed_env=$2
|
|
shift 2
|
|
;;
|
|
*)
|
|
log_error "Argumento desconocido: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
check_connection
|
|
|
|
if [ "$seeds_only" = true ]; then
|
|
load_seeds "$seed_env"
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$drop_first" = true ]; then
|
|
drop_database
|
|
fi
|
|
|
|
create_database
|
|
create_schemas
|
|
load_extensions
|
|
load_all_ddl
|
|
load_seeds "$seed_env"
|
|
validate_database
|
|
|
|
echo ""
|
|
log_success "Base de datos creada exitosamente!"
|
|
echo ""
|
|
}
|
|
|
|
main "$@"
|