Umstellung DBUpgrade2-Modul auf Objekt-Aufrufsyntax
[kivitendo-erp.git] / SL / DBUpgrade2.pm
1 package SL::DBUpgrade2;
2
3 use SL::Common;
4
5 use strict;
6
7 sub new {
8   my ($package, $form, $dbdriver) = @_;
9   my $self                        = { form => $form, dbdriver => $dbdriver };
10   return bless($self, $package);
11 }
12
13 sub parse_dbupdate_controls {
14   $main::lxdebug->enter_sub();
15
16   my ($self) = @_;
17
18   my $form   = $self->{form};
19   my $locale = $main::locale;
20
21   local *IN;
22   my %all_controls;
23
24   my $path = "sql/" . $self->{dbdriver} . "-upgrade2";
25
26   foreach my $file_name (<$path/*.sql>, <$path/*.pl>) {
27     next unless (open(IN, $file_name));
28
29     my $file = $file_name;
30     $file =~ s|.*/||;
31
32     my $control = {
33       "priority" => 1000,
34       "depends"  => [],
35     };
36
37     while (<IN>) {
38       chomp();
39       next unless (/^(--|\#)\s*\@/);
40       s/^(--|\#)\s*\@//;
41       s/\s*$//;
42       next if ($_ eq "");
43
44       my @fields = split(/\s*:\s*/, $_, 2);
45       next unless (scalar(@fields) == 2);
46
47       if ($fields[0] eq "depends") {
48         push(@{$control->{"depends"}}, split(/\s+/, $fields[1]));
49       } else {
50         $control->{$fields[0]} = $fields[1];
51       }
52     }
53
54     next if ($control->{ignore});
55
56     $control->{charset} ||= Common::DEFAULT_CHARSET;
57
58     if (!$control->{"tag"}) {
59       _control_error($form, $file_name, $locale->text("Missing 'tag' field.")) ;
60     }
61
62     if ($control->{"tag"} =~ /[^a-zA-Z0-9_\(\)\-]/) {
63       _control_error($form, $file_name, $locale->text("The 'tag' field must only consist of alphanumeric characters or the carachters - _ ( )"))
64     }
65
66     if (defined($all_controls{$control->{"tag"}})) {
67       _control_error($form, $file_name, sprintf($locale->text("More than one control file with the tag '%s' exist."), $control->{"tag"}))
68     }
69
70     if (!$control->{"description"}) {
71       _control_error($form, $file_name, sprintf($locale->text("Missing 'description' field."))) ;
72     }
73
74     $control->{"priority"}  *= 1;
75     $control->{"priority"} ||= 1000;
76     $control->{"file"}       = $file;
77
78     delete @{$control}{qw(depth applied)};
79
80     $all_controls{$control->{"tag"}} = $control;
81
82     close(IN);
83   }
84
85   foreach my $control (values(%all_controls)) {
86     foreach my $dependency (@{$control->{"depends"}}) {
87       _control_error($form, $control->{"file"}, sprintf($locale->text("Unknown dependency '%s'."), $dependency)) if (!defined($all_controls{$dependency}));
88     }
89
90     map({ $_->{"loop"} = 0; } values(%all_controls));
91     _check_for_loops($form, $control->{"file"}, \%all_controls, $control->{"tag"});
92   }
93
94   map({ _dbupdate2_calculate_depth(\%all_controls, $_->{"tag"}) }
95       values(%all_controls));
96
97   $self->{all_controls} = \%all_controls;
98
99   $main::lxdebug->leave_sub();
100
101   return \%all_controls;
102 }
103
104 sub _check_for_loops {
105   my ($form, $file_name, $controls, $tag, @path) = @_;
106
107   push(@path, $tag);
108
109   my $ctrl = $controls->{$tag};
110
111   if ($ctrl->{"loop"} == 1) {
112     # Not done yet.
113     _control_error($form, $file_name, $main::locale->text("Dependency loop detected:") . " " . join(" -> ", @path))
114
115   } elsif ($ctrl->{"loop"} == 0) {
116     # Not checked yet.
117     $ctrl->{"loop"} = 1;
118     map({ _check_for_loops($form, $file_name, $controls, $_, @path); } @{ $ctrl->{"depends"} });
119     $ctrl->{"loop"} = 2;
120   }
121 }
122
123 sub _control_error {
124   my ($form, $file_name, $message) = @_;
125
126   $form = $main::form;
127   my $locale = $main::locale;
128
129   $form->error(sprintf($locale->text("Error in database control file '%s': %s"), $file_name, $message));
130 }
131
132 sub _dbupdate2_calculate_depth {
133   $main::lxdebug->enter_sub(2);
134
135   my ($tree, $tag) = @_;
136
137   my $node = $tree->{$tag};
138
139   return $main::lxdebug->leave_sub(2) if (defined($node->{"depth"}));
140
141   my $max_depth = 0;
142
143   foreach $tag (@{$node->{"depends"}}) {
144     _dbupdate2_calculate_depth($tree, $tag);
145     my $value = $tree->{$tag}->{"depth"};
146     $max_depth = $value if ($value > $max_depth);
147   }
148
149   $node->{"depth"} = $max_depth + 1;
150
151   $main::lxdebug->leave_sub(2);
152 }
153
154 sub sort_dbupdate_controls {
155   my $self = shift;
156
157   return sort({   $a->{"depth"}    !=  $b->{"depth"}    ? $a->{"depth"}    <=> $b->{"depth"}
158                 : $a->{"priority"} !=  $b->{"priority"} ? $a->{"priority"} <=> $b->{"priority"}
159                 :                                         $a->{"tag"}      cmp $b->{"tag"}      } values(%{ $self->{all_controls} }));
160 }
161
162 1;