| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- #!/usr/bin/perl
- #
- # Copyright (C) Roman Dmitriev, rnd@rajven.ru
- #
- use utf8;
- use open ":encoding(utf8)";
- use open ':std', ':encoding(UTF-8)';
- use Encode;
- no warnings 'utf8';
- use English;
- use FindBin '$Bin';
- use lib "/opt/Eye/scripts";
- use Getopt::Long qw(GetOptions);
- use Data::Dumper;
- use eyelib::config;
- use eyelib::main;
- use eyelib::database;
- use eyelib::common;
- use eyelib::net_utils;
- use strict;
- use warnings;
- # === Получаем имя чистой БД ===
- my $clear_db_name = $DBNAME . "_test";
- # === Подключаемся к рабочей БД ===
- my $work_db = init_db();
- # === Подключаемся к чистой БД ===
- my $clear_db;
- if ($config_ref{DBTYPE} eq 'mysql') {
- my $dsn = "dbi:mysql:database=$clear_db_name;host=$DBHOST;port=3306;mysql_local_infile=1";
- $clear_db = DBI->connect($dsn, $DBUSER, $DBPASS, {
- RaiseError => 0,
- AutoCommit => 1,
- mysql_enable_utf8 => 1
- });
- if (!defined $clear_db) {
- die "Cannot connect to MySQL database '$clear_db_name': $DBI::errstr\n";
- }
- $clear_db->do('SET NAMES utf8mb4');
- } else {
- my $dsn = "dbi:Pg:dbname=$clear_db_name;host=$DBHOST;port=5432;";
- $clear_db = DBI->connect($dsn, $DBUSER, $DBPASS, {
- RaiseError => 0,
- AutoCommit => 1,
- pg_enable_utf8 => 1,
- pg_server_prepare => 0
- });
- if (!defined $clear_db) {
- die "Cannot connect to PostgreSQL database '$clear_db_name': $DBI::errstr\n";
- }
- }
- # === Функция нормализации значения по умолчанию ===
- sub normalize_default {
- my ($default, $db_type) = @_;
- return undef unless defined $default;
- if ($db_type eq 'mysql') {
- # Убираем кавычки, если строка
- $default =~ s/^'(.*)'$/$1/;
- # NULL → undef
- return undef if lc($default) eq 'null';
- } else {
- # PostgreSQL: уже в нормальном виде
- return undef if lc($default) eq 'null';
- }
- return $default;
- }
- # === Сбор схемы для БД ===
- sub get_schema {
- my ($db, $db_type, $db_name) = @_;
- my %schema;
- my @tables;
- if ($db_type eq 'mysql') {
- my @rows = get_records_sql($db, 'SHOW TABLES');
- my $idx = 'Tables_in_' . $db_name;
- @tables = map { $_->{$idx} } grep { $_ && exists $_->{$idx} } @rows;
- } else {
- my @rows = get_records_sql($db, "SELECT tablename FROM pg_tables WHERE schemaname = 'public'");
- @tables = map { $_->{tablename} } @rows;
- }
- for my $table (@tables) {
- my %cols = get_table_columns($db, $table);
- # get_table_columns уже возвращает lowercase имена
- for my $col (keys %cols) {
- my $info = $cols{$col};
- $schema{$table}{$col} = {
- type => $info->{type} // '',
- nullable => $info->{nullable} // 1,
- default => normalize_default($info->{default}, $db_type),
- };
- }
- }
- return %schema;
- }
- # === Сбор схем ===
- print "Fetching schema from working database '$DBNAME'...\n";
- my %work_schema = get_schema($work_db, $config_ref{DBTYPE}, $DBNAME);
- print "Fetching schema from clean database '$clear_db_name'...\n";
- my %clear_schema = get_schema($clear_db, $config_ref{DBTYPE}, $clear_db_name);
- print "\n=== Comparing DB schemas ===\n\n";
- my $has_critical_error = 0;
- # === 1. Проверка: всё ли из чистой БД есть в рабочей? ===
- for my $table (keys %clear_schema) {
- if (!exists $work_schema{$table}) {
- print "❗ ERROR: Table '$table' exists in clean DB but not in working DB!\n";
- $has_critical_error = 1;
- next;
- }
- for my $col (keys %{ $clear_schema{$table} }) {
- if (!exists $work_schema{$table}{$col}) {
- print "❗ ERROR: Column '$col' in table '$table' exists in clean DB but not in working DB!\n";
- $has_critical_error = 1;
- next;
- }
- # === Сравнение типов ===
- my $clean_type = $clear_schema{$table}{$col}{type} // '';
- my $work_type = $work_schema{$table}{$col}{type} // '';
- # Нормализуем типы для сравнения (MySQL vs PG)
- # if ($config_ref{DBTYPE} eq 'mysql') {
- # Пример: TINYINT(1) → boolean-like, но у нас SMALLINT
- # Для простоты сравниваем как строки
- # }
- if ($clean_type ne $work_type) {
- print "❗ ERROR: Column '$col' in table '$table' has different type:\n";
- print " Clean: '$clean_type', Working: '$work_type'\n";
- $has_critical_error = 1;
- }
- # === Сравнение NULL ===
- my $clean_null = $clear_schema{$table}{$col}{nullable} // 1;
- my $work_null = $work_schema{$table}{$col}{nullable} // 1;
- if ($clean_null != $work_null) {
- my $clean_str = $clean_null ? "NULL" : "NOT NULL";
- my $work_str = $work_null ? "NULL" : "NOT NULL";
- print "❗ ERROR: Column '$col' in table '$table' has different NULL setting:\n";
- print " Clean: $clean_str, Working: $work_str\n";
- $has_critical_error = 1;
- }
- # === Сравнение DEFAULT ===
- my $clean_def = $clear_schema{$table}{$col}{default};
- my $work_def = $work_schema{$table}{$col}{default};
- if (!defined $clean_def && !defined $work_def) {
- # ok
- } elsif (!defined $clean_def || !defined $work_def) {
- print "❗ ERROR: Column '$col' in table '$table' has different DEFAULT (one is NULL):\n";
- print " Clean: ", defined $clean_def ? "'$clean_def'" : "NULL", "\n";
- print " Working: ", defined $work_def ? "'$work_def'" : "NULL", "\n";
- $has_critical_error = 1;
- } elsif ($clean_def ne $work_def) {
- print "❗ ERROR: Column '$col' in table '$table' has different DEFAULT:\n";
- print " Clean: '$clean_def', Working: '$work_def'\n";
- $has_critical_error = 1;
- }
- }
- }
- # === 2. Проверка: есть ли лишнее в рабочей БД? ===
- for my $table (keys %work_schema) {
- if (!exists $clear_schema{$table}) {
- print "⚠ WARNING: Table '$table' exists in working DB but not in clean DB — will be skipped.\n";
- next;
- }
- for my $col (keys %{ $work_schema{$table} }) {
- if (!exists $clear_schema{$table}{$col}) {
- print "⚠ WARNING: Column '$col' in table '$table' exists in working DB but not in clean DB — will be ignored.\n";
- }
- }
- }
- if ($has_critical_error) {
- print "\nSchema validation failed: structural differences found.\n";
- exit 103;
- }
- print "✅ Schema validation passed.\n\n";
- exit 0;
|