1 package SL::DBUpgrade2;
13 return bless({}, $package)->init(@_);
17 my ($self, %params) = @_;
19 map { $self->{$_} = $params{$_} } keys %params;
24 sub parse_dbupdate_controls {
25 $main::lxdebug->enter_sub();
29 my $form = $self->{form};
30 my $locale = $main::locale;
35 my $path = "sql/" . $self->{dbdriver} . "-upgrade2";
37 foreach my $file_name (<$path/*.sql>, <$path/*.pl>) {
38 next unless (open(IN, $file_name));
40 my $file = $file_name;
50 next unless (/^(--|\#)\s*\@/);
55 my @fields = split(/\s*:\s*/, $_, 2);
56 next unless (scalar(@fields) == 2);
58 if ($fields[0] eq "depends") {
59 push(@{$control->{"depends"}}, split(/\s+/, $fields[1]));
61 $control->{$fields[0]} = $fields[1];
65 next if ($control->{ignore});
67 $control->{charset} ||= Common::DEFAULT_CHARSET;
69 if (!$control->{"tag"}) {
70 _control_error($form, $file_name, $locale->text("Missing 'tag' field.")) ;
73 if ($control->{"tag"} =~ /[^a-zA-Z0-9_\(\)\-]/) {
74 _control_error($form, $file_name, $locale->text("The 'tag' field must only consist of alphanumeric characters or the carachters - _ ( )"))
77 if (defined($all_controls{$control->{"tag"}})) {
78 _control_error($form, $file_name, sprintf($locale->text("More than one control file with the tag '%s' exist."), $control->{"tag"}))
81 if (!$control->{"description"}) {
82 _control_error($form, $file_name, sprintf($locale->text("Missing 'description' field."))) ;
85 $control->{"priority"} *= 1;
86 $control->{"priority"} ||= 1000;
87 $control->{"file"} = $file;
89 delete @{$control}{qw(depth applied)};
91 $all_controls{$control->{"tag"}} = $control;
96 foreach my $control (values(%all_controls)) {
97 foreach my $dependency (@{$control->{"depends"}}) {
98 _control_error($form, $control->{"file"}, sprintf($locale->text("Unknown dependency '%s'."), $dependency)) if (!defined($all_controls{$dependency}));
101 map({ $_->{"loop"} = 0; } values(%all_controls));
102 _check_for_loops($form, $control->{"file"}, \%all_controls, $control->{"tag"});
105 map({ _dbupdate2_calculate_depth(\%all_controls, $_->{"tag"}) }
106 values(%all_controls));
108 $self->{all_controls} = \%all_controls;
110 $main::lxdebug->leave_sub();
112 return \%all_controls;
116 $main::lxdebug->enter_sub();
118 my ($self, $dbh, $filename, $version_or_control, $db_charset) = @_;
120 my $form = $self->{form};
121 my $fh = IO::File->new($filename, "r") or $form->error("$filename : $!\n");
126 my $file_charset = Common::DEFAULT_CHARSET;
129 next if !/^--\s*\@charset:\s*(.+)/;
133 $fh->seek(0, SEEK_SET);
135 $db_charset ||= Common::DEFAULT_CHARSET;
140 $_ = SL::Iconv::convert($file_charset, $db_charset, $_);
142 # Remove DOS and Unix style line endings.
148 for (my $i = 0; $i < length($_); $i++) {
149 my $char = substr($_, $i, 1);
151 # Are we inside a string?
153 if ($char eq $quote_chars[-1]) {
159 if (($char eq "'") || ($char eq "\"")) {
160 push(@quote_chars, $char);
162 } elsif ($char eq ";") {
164 # Query is complete. Send it.
166 $sth = $dbh->prepare($query);
167 if (!$sth->execute()) {
168 my $errstr = $dbh->errstr;
171 $form->dberror("The database update/creation did not succeed. " .
172 "The file ${filename} containing the following " .
173 "query failed:<br>${query}<br>" .
174 "The error message was: ${errstr}<br>" .
175 "All changes in that file have been reverted.");
187 # Insert a space at the end of each line so that queries split
188 # over multiple lines work properly.
190 $query .= @quote_chars ? "\n" : ' ';
194 if (ref($version_or_control) eq "HASH") {
195 $dbh->do("INSERT INTO schema_info (tag, login) VALUES (" .
196 $dbh->quote($version_or_control->{"tag"}) . ", " .
197 $dbh->quote($form->{"login"}) . ")");
198 } elsif ($version_or_control) {
199 $dbh->do("UPDATE defaults SET version = " .
200 $dbh->quote($version_or_control));
206 $main::lxdebug->leave_sub();
209 # Process a Perl script which updates the database.
210 # If the script returns 1 then the update was successful.
211 # Return code "2" means "needs more interaction; remove
212 # users/nologin and end current request".
213 # All other return codes are fatal errors.
214 sub process_perl_script {
215 $main::lxdebug->enter_sub();
217 my ($self, $dbh, $filename, $version_or_control, $db_charset) = @_;
219 my $form = $self->{form};
220 my $fh = IO::File->new($filename, "r") or $form->error("$filename : $!\n");
221 my $file_charset = Common::DEFAULT_CHARSET;
223 if (ref($version_or_control) eq "HASH") {
224 $file_charset = $version_or_control->{charset};
229 next if !/^--\s*\@charset:\s*(.+)/;
233 $fh->seek(0, SEEK_SET);
236 my $contents = join "", <$fh>;
239 $db_charset ||= Common::DEFAULT_CHARSET;
241 my $iconv = SL::Iconv::get_converter($file_charset, $db_charset);
245 # setup dbup_ export vars
246 my %dbup_myconfig = ();
247 map({ $dbup_myconfig{$_} = $form->{$_}; } qw(dbname dbuser dbpasswd dbhost dbport dbconnect));
249 my $dbup_locale = $::locale;
251 my $result = eval($contents);
258 if (!defined($result)) {
259 print $form->parse_html_template("dbupgrade/error",
260 { "file" => $filename,
263 } elsif (1 != $result) {
264 unlink("users/nologin") if (2 == $result);
268 if (ref($version_or_control) eq "HASH") {
269 $dbh->do("INSERT INTO schema_info (tag, login) VALUES (" .
270 $dbh->quote($version_or_control->{"tag"}) . ", " .
271 $dbh->quote($form->{"login"}) . ")");
272 } elsif ($version_or_control) {
273 $dbh->do("UPDATE defaults SET version = " .
274 $dbh->quote($version_or_control));
278 $main::lxdebug->leave_sub();
282 my ($self, $dbh, $filename, $version_or_control, $db_charset) = @_;
284 if ($filename =~ m/sql$/) {
285 $self->process_query($dbh, $filename, $version_or_control, $db_charset);
287 $self->process_perl_script($dbh, $filename, $version_or_control, $db_charset);
291 sub _check_for_loops {
292 my ($form, $file_name, $controls, $tag, @path) = @_;
296 my $ctrl = $controls->{$tag};
298 if ($ctrl->{"loop"} == 1) {
300 _control_error($form, $file_name, $main::locale->text("Dependency loop detected:") . " " . join(" -> ", @path))
302 } elsif ($ctrl->{"loop"} == 0) {
305 map({ _check_for_loops($form, $file_name, $controls, $_, @path); } @{ $ctrl->{"depends"} });
311 my ($form, $file_name, $message) = @_;
314 my $locale = $main::locale;
316 $form->error(sprintf($locale->text("Error in database control file '%s': %s"), $file_name, $message));
319 sub _dbupdate2_calculate_depth {
320 $main::lxdebug->enter_sub(2);
322 my ($tree, $tag) = @_;
324 my $node = $tree->{$tag};
326 return $main::lxdebug->leave_sub(2) if (defined($node->{"depth"}));
330 foreach $tag (@{$node->{"depends"}}) {
331 _dbupdate2_calculate_depth($tree, $tag);
332 my $value = $tree->{$tag}->{"depth"};
333 $max_depth = $value if ($value > $max_depth);
336 $node->{"depth"} = $max_depth + 1;
338 $main::lxdebug->leave_sub(2);
341 sub sort_dbupdate_controls {
344 return sort({ $a->{"depth"} != $b->{"depth"} ? $a->{"depth"} <=> $b->{"depth"}
345 : $a->{"priority"} != $b->{"priority"} ? $a->{"priority"} <=> $b->{"priority"}
346 : $a->{"tag"} cmp $b->{"tag"} } values(%{ $self->{all_controls} }));