1 package SL::DBUpgrade2;
11 my ($package, $form, $dbdriver) = @_;
12 my $self = { form => $form, dbdriver => $dbdriver };
13 return bless($self, $package);
18 $self->{dbcharset} = shift;
22 sub parse_dbupdate_controls {
23 $main::lxdebug->enter_sub();
27 my $form = $self->{form};
28 my $locale = $main::locale;
33 my $path = "sql/" . $self->{dbdriver} . "-upgrade2";
35 foreach my $file_name (<$path/*.sql>, <$path/*.pl>) {
36 next unless (open(IN, $file_name));
38 my $file = $file_name;
48 next unless (/^(--|\#)\s*\@/);
53 my @fields = split(/\s*:\s*/, $_, 2);
54 next unless (scalar(@fields) == 2);
56 if ($fields[0] eq "depends") {
57 push(@{$control->{"depends"}}, split(/\s+/, $fields[1]));
59 $control->{$fields[0]} = $fields[1];
63 next if ($control->{ignore});
65 $control->{charset} ||= Common::DEFAULT_CHARSET;
67 if (!$control->{"tag"}) {
68 _control_error($form, $file_name, $locale->text("Missing 'tag' field.")) ;
71 if ($control->{"tag"} =~ /[^a-zA-Z0-9_\(\)\-]/) {
72 _control_error($form, $file_name, $locale->text("The 'tag' field must only consist of alphanumeric characters or the carachters - _ ( )"))
75 if (defined($all_controls{$control->{"tag"}})) {
76 _control_error($form, $file_name, sprintf($locale->text("More than one control file with the tag '%s' exist."), $control->{"tag"}))
79 if (!$control->{"description"}) {
80 _control_error($form, $file_name, sprintf($locale->text("Missing 'description' field."))) ;
83 $control->{"priority"} *= 1;
84 $control->{"priority"} ||= 1000;
85 $control->{"file"} = $file;
87 delete @{$control}{qw(depth applied)};
89 $all_controls{$control->{"tag"}} = $control;
94 foreach my $control (values(%all_controls)) {
95 foreach my $dependency (@{$control->{"depends"}}) {
96 _control_error($form, $control->{"file"}, sprintf($locale->text("Unknown dependency '%s'."), $dependency)) if (!defined($all_controls{$dependency}));
99 map({ $_->{"loop"} = 0; } values(%all_controls));
100 _check_for_loops($form, $control->{"file"}, \%all_controls, $control->{"tag"});
103 map({ _dbupdate2_calculate_depth(\%all_controls, $_->{"tag"}) }
104 values(%all_controls));
106 $self->{all_controls} = \%all_controls;
108 $main::lxdebug->leave_sub();
110 return \%all_controls;
114 $main::lxdebug->enter_sub();
116 my ($self, $dbh, $filename, $version_or_control, $db_charset) = @_;
118 my $form = $self->{form};
119 my $fh = IO::File->new($filename, "r") or $form->error("$filename : $!\n");
124 my $file_charset = Common::DEFAULT_CHARSET;
127 next if !/^--\s*\@charset:\s*(.+)/;
131 $fh->seek(0, SEEK_SET);
133 $db_charset ||= Common::DEFAULT_CHARSET;
138 $_ = SL::Iconv::convert($file_charset, $db_charset, $_);
140 # Remove DOS and Unix style line endings.
146 for (my $i = 0; $i < length($_); $i++) {
147 my $char = substr($_, $i, 1);
149 # Are we inside a string?
151 if ($char eq $quote_chars[-1]) {
157 if (($char eq "'") || ($char eq "\"")) {
158 push(@quote_chars, $char);
160 } elsif ($char eq ";") {
162 # Query is complete. Send it.
164 $sth = $dbh->prepare($query);
165 if (!$sth->execute()) {
166 my $errstr = $dbh->errstr;
169 $form->dberror("The database update/creation did not succeed. " .
170 "The file ${filename} containing the following " .
171 "query failed:<br>${query}<br>" .
172 "The error message was: ${errstr}<br>" .
173 "All changes in that file have been reverted.");
185 # Insert a space at the end of each line so that queries split
186 # over multiple lines work properly.
188 $query .= @quote_chars ? "\n" : ' ';
192 if (ref($version_or_control) eq "HASH") {
193 $dbh->do("INSERT INTO schema_info (tag, login) VALUES (" .
194 $dbh->quote($version_or_control->{"tag"}) . ", " .
195 $dbh->quote($form->{"login"}) . ")");
196 } elsif ($version_or_control) {
197 $dbh->do("UPDATE defaults SET version = " .
198 $dbh->quote($version_or_control));
204 $main::lxdebug->leave_sub();
207 # Process a Perl script which updates the database.
208 # If the script returns 1 then the update was successful.
209 # Return code "2" means "needs more interaction; remove
210 # users/nologin and end current request".
211 # All other return codes are fatal errors.
212 sub process_perl_script {
213 $main::lxdebug->enter_sub();
215 my ($self, $dbh, $filename, $version_or_control, $db_charset) = @_;
217 my $form = $self->{form};
218 my $fh = IO::File->new($filename, "r") or $form->error("$filename : $!\n");
219 my $file_charset = Common::DEFAULT_CHARSET;
221 if (ref($version_or_control) eq "HASH") {
222 $file_charset = $version_or_control->{charset};
227 next if !/^--\s*\@charset:\s*(.+)/;
231 $fh->seek(0, SEEK_SET);
234 my $contents = join "", <$fh>;
237 $db_charset ||= Common::DEFAULT_CHARSET;
239 my $iconv = SL::Iconv::get_converter($file_charset, $db_charset);
243 # setup dbup_ export vars
244 my %dbup_myconfig = ();
245 map({ $dbup_myconfig{$_} = $form->{$_}; } qw(dbname dbuser dbpasswd dbhost dbport dbconnect));
247 my $dbup_locale = $::locale;
249 my $result = eval($contents);
256 if (!defined($result)) {
257 print $form->parse_html_template("dbupgrade/error",
258 { "file" => $filename,
261 } elsif (1 != $result) {
262 unlink("users/nologin") if (2 == $result);
266 if (ref($version_or_control) eq "HASH") {
267 $dbh->do("INSERT INTO schema_info (tag, login) VALUES (" .
268 $dbh->quote($version_or_control->{"tag"}) . ", " .
269 $dbh->quote($form->{"login"}) . ")");
270 } elsif ($version_or_control) {
271 $dbh->do("UPDATE defaults SET version = " .
272 $dbh->quote($version_or_control));
276 $main::lxdebug->leave_sub();
280 my ($self, $dbh, $filename, $version_or_control, $db_charset) = @_;
282 if ($filename =~ m/sql$/) {
283 $self->process_query($dbh, $filename, $version_or_control, $db_charset);
285 $self->process_perl_script($dbh, $filename, $version_or_control, $db_charset);
289 sub _check_for_loops {
290 my ($form, $file_name, $controls, $tag, @path) = @_;
294 my $ctrl = $controls->{$tag};
296 if ($ctrl->{"loop"} == 1) {
298 _control_error($form, $file_name, $main::locale->text("Dependency loop detected:") . " " . join(" -> ", @path))
300 } elsif ($ctrl->{"loop"} == 0) {
303 map({ _check_for_loops($form, $file_name, $controls, $_, @path); } @{ $ctrl->{"depends"} });
309 my ($form, $file_name, $message) = @_;
312 my $locale = $main::locale;
314 $form->error(sprintf($locale->text("Error in database control file '%s': %s"), $file_name, $message));
317 sub _dbupdate2_calculate_depth {
318 $main::lxdebug->enter_sub(2);
320 my ($tree, $tag) = @_;
322 my $node = $tree->{$tag};
324 return $main::lxdebug->leave_sub(2) if (defined($node->{"depth"}));
328 foreach $tag (@{$node->{"depends"}}) {
329 _dbupdate2_calculate_depth($tree, $tag);
330 my $value = $tree->{$tag}->{"depth"};
331 $max_depth = $value if ($value > $max_depth);
334 $node->{"depth"} = $max_depth + 1;
336 $main::lxdebug->leave_sub(2);
339 sub sort_dbupdate_controls {
342 return sort({ $a->{"depth"} != $b->{"depth"} ? $a->{"depth"} <=> $b->{"depth"}
343 : $a->{"priority"} != $b->{"priority"} ? $a->{"priority"} <=> $b->{"priority"}
344 : $a->{"tag"} cmp $b->{"tag"} } values(%{ $self->{all_controls} }));