Files
telegram-bot-workers/migrations/001_schema_enhancements.sql
kappa e3314e301a fix(migration): PRAGMA foreign_keys 처리 추가
- PRAGMA foreign_keys = OFF (마이그레이션 시작)
- PRAGMA foreign_keys = ON (마이그레이션 완료 후)
- 프로덕션 배포 완료 (25 queries, 3.55ms)
2026-01-19 16:09:28 +09:00

161 lines
6.0 KiB
SQL

-- Migration 001: Schema Enhancements
-- Purpose: Add data integrity constraints and audit logging
-- Date: 2026-01-19
-- Author: Claude Code
-- =============================================================================
-- IMPORTANT: Disable FOREIGN KEY constraints during migration
-- =============================================================================
PRAGMA foreign_keys = OFF;
-- =============================================================================
-- STEP 1: user_deposits - Add CHECK constraint for balance >= 0
-- =============================================================================
-- SQLite requires table recreation to add CHECK constraints
-- 1.1 Create backup table
CREATE TABLE user_deposits_backup AS SELECT * FROM user_deposits;
-- 1.2 Drop existing table
DROP TABLE user_deposits;
-- 1.3 Create new table with CHECK constraint
CREATE TABLE user_deposits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL UNIQUE,
balance INTEGER NOT NULL DEFAULT 0 CHECK (balance >= 0),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 1.4 Restore data (negative balances will be rejected)
INSERT INTO user_deposits (id, user_id, balance, created_at, updated_at)
SELECT id, user_id, balance, created_at, updated_at
FROM user_deposits_backup
WHERE balance >= 0;
-- 1.5 Log rejected records (if any)
-- Note: In production, manually review these records before migration
SELECT 'REJECTED NEGATIVE BALANCE:' as warning, *
FROM user_deposits_backup
WHERE balance < 0;
-- 1.6 Drop backup table
DROP TABLE user_deposits_backup;
-- 1.7 Recreate index
CREATE INDEX IF NOT EXISTS idx_deposits_user ON user_deposits(user_id);
-- =============================================================================
-- STEP 2: deposit_transactions - Add length constraint for depositor_name
-- =============================================================================
-- 2.1 Create backup table
CREATE TABLE deposit_transactions_backup AS SELECT * FROM deposit_transactions;
-- 2.2 Drop existing table
DROP TABLE deposit_transactions;
-- 2.3 Create new table with length constraint
CREATE TABLE deposit_transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
type TEXT NOT NULL CHECK(type IN ('deposit', 'withdrawal', 'refund')),
amount INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'confirmed', 'rejected', 'cancelled')),
depositor_name TEXT CHECK (length(depositor_name) <= 15),
description TEXT,
confirmed_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 2.4 Restore data (truncate long depositor names)
INSERT INTO deposit_transactions (
id, user_id, type, amount, status, depositor_name, description, confirmed_at, created_at
)
SELECT
id,
user_id,
type,
amount,
status,
CASE
WHEN depositor_name IS NULL THEN NULL
WHEN length(depositor_name) > 15 THEN substr(depositor_name, 1, 15)
ELSE depositor_name
END as depositor_name,
description,
confirmed_at,
created_at
FROM deposit_transactions_backup;
-- 2.5 Log truncated records (if any)
SELECT 'TRUNCATED DEPOSITOR NAME:' as warning, id, depositor_name, length(depositor_name) as original_length
FROM deposit_transactions_backup
WHERE depositor_name IS NOT NULL AND length(depositor_name) > 15;
-- 2.6 Drop backup table
DROP TABLE deposit_transactions_backup;
-- 2.7 Recreate indexes
CREATE INDEX IF NOT EXISTS idx_transactions_user ON deposit_transactions(user_id);
CREATE INDEX IF NOT EXISTS idx_transactions_status ON deposit_transactions(status, created_at DESC);
-- =============================================================================
-- STEP 3: Audit Log Table
-- =============================================================================
-- 3.1 Create audit_logs table
CREATE TABLE IF NOT EXISTS audit_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
telegram_id TEXT,
action TEXT NOT NULL,
resource_type TEXT,
resource_id INTEGER,
details TEXT,
ip_address TEXT,
user_agent TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 3.2 Create indexes for query performance
CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_telegram_id ON audit_logs(telegram_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action);
CREATE INDEX IF NOT EXISTS idx_audit_logs_resource ON audit_logs(resource_type, resource_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at DESC);
-- =============================================================================
-- VERIFICATION QUERIES
-- =============================================================================
-- Count records in each table
SELECT 'users' as table_name, COUNT(*) as count FROM users
UNION ALL
SELECT 'user_deposits', COUNT(*) FROM user_deposits
UNION ALL
SELECT 'deposit_transactions', COUNT(*) FROM deposit_transactions
UNION ALL
SELECT 'audit_logs', COUNT(*) FROM audit_logs;
-- Verify CHECK constraints
-- Attempt to insert invalid data (should fail)
-- Uncomment to test:
-- INSERT INTO user_deposits (user_id, balance) VALUES (999999, -1000);
-- INSERT INTO deposit_transactions (user_id, type, amount, depositor_name) VALUES (999999, 'deposit', 1000, 'ThisIsAVeryLongNameThatExceedsFiftyCharactersAndShouldBeTruncatedOrRejected');
-- =============================================================================
-- Re-enable FOREIGN KEY constraints
-- =============================================================================
PRAGMA foreign_keys = ON;
-- =============================================================================
-- MIGRATION COMPLETE
-- =============================================================================
SELECT 'Migration 001 completed successfully' as status, datetime('now') as timestamp;