Files
psql_tasks/move_psql01.md
T
2026-07-21 00:46:38 +08:00

14 KiB
Raw Blame History

  • Смена tablespace с кастомного на дефолтный
  • Удаление OWNER (замена на current_user)
  • Удаление ROLE/TABLESPACE из дампа
  • Разделение по типам объектов

Этап 1. Создание дампов

1.1. Дамп СХЕМЫ (только таблицы, без функций/триггеров)

pg_dump -p 5436 -U postgres -d edemo_ehd1 \
    --schema-only \
    --clean \
    --if-exists \
    --exclude-function \
    --exclude-trigger \
    --exclude-table="*" \
    --exclude-schema=pg_catalog,information_schema \
    2>/dev/null | \
    grep -vE '^(CREATE|ALTER|DROP) (ROLE|TABLESPACE)' | \
    sed -E 's/OWNER TO [^;]+;/OWNER TO current_user;/g' | \
    sed -E 's/ TABLESPACE = [^;]+//g' \
    > /opt/pg_dumps/01_schema_tables.sql

Что делает:

  • --schema-only — только структура
  • --clean --if-exists — DROP IF EXISTS перед созданием
  • --exclude-function --exclude-trigger — исключаем функции и триггеры
  • grep -vE — удаляем строки с ROLE и TABLESPACE
  • sed — меняем OWNER на current_user и удаляем TABLESPACE

1.2. Дамп ДАННЫХ (без схемы, с отключенными триггерами)

pg_dump -p 5436 -U postgres -d edemo_ehd1 \
    --data-only \
    --disable-triggers \
    --no-owner \
    --no-privileges \
    -F c \
    -f /opt/pg_dumps/02_data.dump

Что делает:

  • --data-only — только данные
  • --disable-triggers — отключаем триггеры при дампе
  • --no-owner — не сохраняем владельцев
  • -F c — кастомный формат (для скорости)

1.3. Дамп ФУНКЦИЙ И ПРОЦЕДУР

pg_dump -p 5436 -U postgres -d edemo_ehd1 \
    --schema-only \
    --clean \
    --if-exists \
    --function \
    --procedure \
    2>/dev/null | \
    grep -vE '^(CREATE|ALTER|DROP) (ROLE|TABLESPACE)' | \
    sed -E 's/OWNER TO [^;]+;/OWNER TO current_user;/g' | \
    sed -E 's/ TABLESPACE = [^;]+//g' \
    > /opt/pg_dumps/03_functions.sql

Что делает:

  • --function --procedure — выгружаем только функции и процедуры
  • Остальное аналогично пункту 1.1

1.4. Дамп ТРИГГЕРОВ

pg_dump -p 5436 -U postgres -d edemo_ehd1 \
    --schema-only \
    --clean \
    --if-exists \
    --trigger \
    2>/dev/null | \
    grep -vE '^(CREATE|ALTER|DROP) (ROLE|TABLESPACE)' | \
    sed -E 's/OWNER TO [^;]+;/OWNER TO current_user;/g' | \
    sed -E 's/ TABLESPACE = [^;]+//g' \
    > /opt/pg_dumps/04_triggers.sql

1.5. Дамп ПРЕДСТАВЛЕНИЙ (VIEWS)

# Полный дамп схемы
pg_dump -p 5436 -U postgres -d edemo_ehd1 \
    --schema-only \
    --clean \
    --if-exists \
    2>/dev/null | \
    grep -vE '^(CREATE|ALTER|DROP) (ROLE|TABLESPACE)' | \
    sed -E 's/OWNER TO [^;]+;/OWNER TO current_user;/g' | \
    sed -E 's/ TABLESPACE = [^;]+//g' \
    > /opt/pg_dumps/05_full_schema_clean.sql

# Извлекаем только VIEWS
grep -E 'CREATE (OR REPLACE )?VIEW' /opt/pg_dumps/05_full_schema_clean.sql \
    > /opt/pg_dumps/05_views.sql

# Удаляем временный файл
rm -f /opt/pg_dumps/05_full_schema_clean.sql

1.6. (Опционально) Дамп ВНЕШНИХ КЛЮЧЕЙ (если нужно создавать отдельно)

pg_dump -p 5436 -U postgres -d edemo_ehd1 \
    --schema-only \
    --clean \
    --if-exists \
    --constraint \
    2>/dev/null | \
    grep -vE '^(CREATE|ALTER|DROP) (ROLE|TABLESPACE)' | \
    sed -E 's/OWNER TO [^;]+;/OWNER TO current_user;/g' | \
    sed -E 's/ TABLESPACE = [^;]+//g' \
    > /opt/pg_dumps/06_constraints.sql

Этап 2. Восстановление в целевой БД

2.0. Подготовка целевой БД

# Параметры
NEW_HOST="new_host"
USER="postgres"
NEW_DB="new_db"

# Создаем БД (если не существует)
createdb -h $NEW_HOST -U $USER $NEW_DB

# Или пересоздаем
dropdb -h $NEW_HOST -U $USER --if-exists $NEW_DB
createdb -h $NEW_HOST -U $USER $NEW_DB

# Убеждаемся, что используется дефолтный tablespace
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "SHOW default_tablespace;"

2.1. Восстановление ТАБЛИЦ (схема)

echo "=== ШАГ 2.1: Восстановление таблиц ==="
psql -h $NEW_HOST -U $USER -d $NEW_DB -f /opt/pg_dumps/01_schema_tables.sql

# Проверяем
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "\dt" | head -20

Ожидаемый результат: Все таблицы созданы, без функций и триггеров.


2.2. Восстановление ДАННЫХ

echo "=== ШАГ 2.2: Восстановление данных ==="
pg_restore -h $NEW_HOST -U $USER -d $NEW_DB \
    --data-only \
    --disable-triggers \
    --no-owner \
    --no-privileges \
    -j 1 \
    /opt/pg_dumps/02_data.dump

# Проверяем количество записей
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "
SELECT schemaname, tablename, n_live_tup 
FROM pg_stat_user_tables 
WHERE schemaname = 'public' 
ORDER BY n_live_tup DESC 
LIMIT 10;"

Ожидаемый результат: Данные залиты, триггеры не срабатывали.


2.3. Восстановление ФУНКЦИЙ И ПРОЦЕДУР

echo "=== ШАГ 2.3: Восстановление функций и процедур ==="

# Проверяем, есть ли уже функции
psql -h $NEW_HOST -U $USER -d $NEW_DB <<EOF
SELECT proname, pronargs 
FROM pg_proc 
WHERE pronamespace::regnamespace::text = 'public' 
ORDER BY proname;
EOF

# Восстанавливаем
psql -h $NEW_HOST -U $USER -d $NEW_DB -f /opt/pg_dumps/03_functions.sql

# Если ошибка "already exists", добавляем OR REPLACE
if [ $? -ne 0 ]; then
    echo "⚠️ Ошибка, пробуем с CREATE OR REPLACE..."
    sed -i.bak 's/CREATE FUNCTION/CREATE OR REPLACE FUNCTION/g' /opt/pg_dumps/03_functions.sql
    sed -i.bak 's/CREATE PROCEDURE/CREATE OR REPLACE PROCEDURE/g' /opt/pg_dumps/03_functions.sql
    psql -h $NEW_HOST -U $USER -d $NEW_DB -f /opt/pg_dumps/03_functions.sql
fi

# Проверяем
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "
SELECT proname, pronargs 
FROM pg_proc 
WHERE pronamespace::regnamespace::text = 'public' 
ORDER BY proname;"

Ожидаемый результат: Все функции созданы корректно.


2.4. Восстановление ТРИГГЕРОВ

echo "=== ШАГ 2.4: Восстановление триггеров ==="

# Удаляем существующие триггеры (если есть)
psql -h $NEW_HOST -U $USER -d $NEW_DB <<EOF
DO \$\$
DECLARE
    r RECORD;
BEGIN
    FOR r IN 
        SELECT tgname, tgrelid::regclass::text as table_name
        FROM pg_trigger
        WHERE tgisinternal = false
          AND tgrelid::regclass::text LIKE 'public.%'
    LOOP
        EXECUTE format('DROP TRIGGER IF EXISTS %I ON %s;', r.tgname, r.table_name);
    END LOOP;
END \$\$;
EOF

# Восстанавливаем триггеры
psql -h $NEW_HOST -U $USER -d $NEW_DB -f /opt/pg_dumps/04_triggers.sql

# Проверяем
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "
SELECT 
    tgname as trigger_name,
    tgrelid::regclass::text as table_name,
    tgfoid::regproc as function_name
FROM pg_trigger
WHERE tgisinternal = false
  AND tgrelid::regclass::text LIKE 'public.%'
ORDER BY tgname;"

Ожидаемый результат: Триггеры созданы, но пока отключены (или включены, зависит от дампа).


2.5. Восстановление ПРЕДСТАВЛЕНИЙ (VIEWS)

echo "=== ШАГ 2.5: Восстановление представлений ==="

# Проверяем, есть ли уже представления
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "\dv"

# Восстанавливаем
psql -h $NEW_HOST -U $USER -d $NEW_DB -f /opt/pg_dumps/05_views.sql

# Проверяем
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "
SELECT schemaname, viewname 
FROM pg_views 
WHERE schemaname = 'public' 
ORDER BY viewname;"

Ожидаемый результат: Все представления созданы.


2.6. ВКЛЮЧАЕМ ТРИГГЕРЫ

echo "=== ШАГ 2.6: Включение триггеров ==="

# Включаем триггеры
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "SET session_replication_role = 'origin';"

# Проверяем статус триггеров
psql -h $NEW_HOST -U $USER -d $NEW_DB -c "
SELECT 
    tgname as trigger_name,
    tgrelid::regclass::text as table_name,
    CASE tgenabled 
        WHEN 'O' THEN '✅ ON (включен)'
        WHEN 'D' THEN '❌ DISABLED (отключен)'
        WHEN 'R' THEN '🔄 REPLICA'
        WHEN 'A' THEN '🔄 ALWAYS'
    END as status
FROM pg_trigger
WHERE tgisinternal = false
  AND tgrelid::regclass::text LIKE 'public.%'
ORDER BY tgname;"

Ожидаемый результат: Все триггеры включены (status = ON).


Этап 3. Проверка целостности

3.1. Проверка внешних ключей

psql -h $NEW_HOST -U $USER -d $NEW_DB <<EOF
DO \$\$
DECLARE
    r RECORD;
    v_count BIGINT;
    v_error_count INT := 0;
BEGIN
    RAISE NOTICE '=== ПРОВЕРКА ВНЕШНИХ КЛЮЧЕЙ ===';
    
    FOR r IN 
        SELECT 
            tc.table_schema, 
            tc.table_name, 
            tc.constraint_name,
            ccu.table_name as fk_table
        FROM information_schema.table_constraints tc
        JOIN information_schema.constraint_column_usage ccu 
            ON ccu.constraint_name = tc.constraint_name
        WHERE constraint_type = 'FOREIGN KEY'
          AND tc.table_schema = 'public'
    LOOP
        EXECUTE format(
            'SELECT COUNT(*) FROM %I.%I WHERE NOT EXISTS (SELECT 1 FROM %I.%I)',
            r.table_schema, r.table_name, r.table_schema, r.fk_table
        ) INTO v_count;
        
        IF v_count > 0 THEN
            RAISE NOTICE '⚠️ %: % сиротских записей (ссылаются на %)', 
                r.constraint_name, v_count, r.fk_table;
            v_error_count := v_error_count + 1;
        END IF;
    END LOOP;
    
    IF v_error_count = 0 THEN
        RAISE NOTICE '✅ Все внешние ключи корректны!';
    ELSE
        RAISE NOTICE '❌ Найдено проблем: %', v_error_count;
    END IF;
END \$\$;
EOF

3.2. Проверка работы триггеров

psql -h $NEW_HOST -U $USER -d $NEW_DB <<EOF
-- Проверяем, что триггеры активны
SELECT 
    tgname,
    tgenabled,
    tgfoid::regproc as function_name
FROM pg_trigger
WHERE tgisinternal = false
  AND tgenabled IN ('O', 'A')  -- ON или ALWAYS
  AND tgrelid::regclass::text LIKE 'public.%';

-- Тестовый INSERT для проверки триггера (замените на вашу таблицу)
-- BEGIN;
-- INSERT INTO public."EdsDocumentModelTask" (...) VALUES (...);
-- SELECT * FROM public."BarcodeOldBarcodePair" WHERE ...;
-- ROLLBACK;
EOF

3.3. Итоговая проверка

psql -h $NEW_HOST -U $USER -d $NEW_DB <<EOF
\echo '=== ОБЩАЯ СТАТИСТИКА ==='

-- Количество таблиц
SELECT 'Таблицы: ' || count(*) as info 
FROM pg_tables 
WHERE schemaname = 'public';

-- Количество функций
SELECT 'Функции: ' || count(*) as info 
FROM pg_proc 
WHERE pronamespace::regnamespace::text = 'public';

-- Количество триггеров
SELECT 'Триггеры: ' || count(*) as info 
FROM pg_trigger 
WHERE tgisinternal = false 
  AND tgrelid::regclass::text LIKE 'public.%';

-- Количество представлений
SELECT 'Представления: ' || count(*) as info 
FROM pg_views 
WHERE schemaname = 'public';

-- Размер БД
SELECT pg_size_pretty(pg_database_size(current_database())) as db_size;
EOF

Итоговый список файлов

/opt/pg_dumps/
├── 01_schema_tables.sql      # Только таблицы (+ индексы, PK)
├── 02_data.dump              # Данные (кастомный формат)
├── 03_functions.sql          # Функции и процедуры
├── 04_triggers.sql           # Триггеры
├── 05_views.sql              # Представления
└── 06_constraints.sql        # (опционально) Внешние ключи

Важные моменты

  1. Все дампы очищены от ROLE, TABLESPACE и OWNER
  2. Tablespace заменен на дефолтный
  3. OWNER заменен на current_user
  4. Порядок восстановления строго соблюден
  5. Триггеры отключены при заливке данных
  6. Функции создаются после таблиц и данных
  7. Триггеры создаются после функций
  8. Представления создаются после таблиц

Этот подход гарантирует успешную миграцию без ошибок! 🚀