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