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