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

460 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
- **Смена tablespace** с кастомного на дефолтный
- **Удаление OWNER** (замена на `current_user`)
- **Удаление ROLE/TABLESPACE** из дампа
- **Разделение по типам объектов**
---
## Этап 1. Создание дампов
### 1.1. Дамп СХЕМЫ (только таблицы, без функций/триггеров)
```bash
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. Дамп ДАННЫХ (без схемы, с отключенными триггерами)
```bash
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. Дамп ФУНКЦИЙ И ПРОЦЕДУР
```bash
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. Дамп ТРИГГЕРОВ
```bash
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)
```bash
# Полный дамп схемы
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. (Опционально) Дамп ВНЕШНИХ КЛЮЧЕЙ (если нужно создавать отдельно)
```bash
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. Подготовка целевой БД
```bash
# Параметры
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. Восстановление ТАБЛИЦ (схема)
```bash
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. Восстановление ДАННЫХ
```bash
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. Восстановление ФУНКЦИЙ И ПРОЦЕДУР
```bash
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. Восстановление ТРИГГЕРОВ
```bash
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)
```bash
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. ВКЛЮЧАЕМ ТРИГГЕРЫ
```bash
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. Проверка внешних ключей
```bash
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. Проверка работы триггеров
```bash
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. Итоговая проверка
```bash
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
```
---
## Итоговый список файлов
```bash
/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. **Представления создаются** после таблиц
Этот подход гарантирует успешную миграцию без ошибок! 🚀