# -*- Mode: perl; tab-width: 4; indent-tabs-mode: nil; -*- # # This file is MPL/GPL dual-licensed under the following terms: # # The contents of this file are subject to the Mozilla Public License # Version 1.1 (the "License"); you may not use this file except in # compliance with the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and # limitations under the License. # # The Original Code is PLIF 1.0. # The Initial Developer of the Original Code is Ian Hickson. # # Alternatively, the contents of this file may be used under the terms # of the GNU General Public License Version 2 or later (the "GPL"), in # which case the provisions of the GPL are applicable instead of those # above. If you wish to allow use of your version of this file only # under the terms of the GPL and not to allow others to use your # version of this file under the MPL, indicate your decision by # deleting the provisions above and replace them with the notice and # other provisions required by the GPL. If you do not delete the # provisions above, a recipient may use your version of this file # under either the MPL or the GPL. package PLIF::DatabaseHelper::DBI; use strict; use vars qw(@ISA); use PLIF::DatabaseHelper; @ISA = qw(PLIF::DatabaseHelper); 1; sub databaseType { return qw(mysql); } sub tableExists { my $self = shift; my($app, $database, $table) = @_; return defined($database->execute('SHOW TABLES LIKE ?', $table)->row); } sub columnExists { my $self = shift; my($app, $database, $table, $columnName) = @_; $self->assert($table !~ /`/o, 1, 'Internal error: An invalid table name was passed to columnExists (it contained a \'`\' character)'); #'); # reset font-lock my $columns = $database->execute("SHOW COLUMNS FROM `$table`"); while (my @columnDefinition = $columns->row) { if ($columnDefinition[0] eq $columnName) { return @columnDefinition; } } return undef; } # some routines used during setup to actually add the database sub setupDatabaseName { # XXX MySQL specific -- should we create a MySQL specific DatabaseHelper? return 'mysql'; } sub setupVerifyVersion { my $self = shift; my($app, $database) = @_; # check version and call $self->error(1, ''); if it is too low. # XXX currently we do not check version numbers (do nothing) # # my $version = $database->execute("SELECT VERSION()")->row; # $version will have the form major.minor.revision-log, where # major, minor and revision are decimal numbers, and '-log' is # present if logging in enabled. # # For MySQL, we should probably require 3.23.41... because that's # what I'm using to develop with. # # GRANT is only available since 3.22.11. } sub setupCreateUser { my $self = shift; my($app, $database, $username, $password, $hostname, $name) = @_; # all done below in the setupSetRights() method } sub setupCreateDatabase { my $self = shift; my($app, $database, $name) = @_; $self->assert($name !~ /`/o, 1, 'Internal error: An invalid database name was passed to setupCreateDatabase (it contained a \'`\' character)'); #'); # reset font-lock $database->execute("CREATE DATABASE IF NOT EXISTS `$name`"); } sub setupSetRights { my $self = shift; my($app, $database, $username, $password, $hostname, $name) = @_; # XXX MySQL specific -- should we create a MySQL specific DatabaseHelper? # XXX are these really internal errors? (yes, if we say the caller must sanitise the input) $self->assert($name !~ /\`/o, 1, 'Internal error: An invalid database name was passed to setupSetRights (it contained a \'`\' character)'); $self->assert($username !~ /\`/o, 1, 'Internal error: An invalid username was passed to setupSetRights (it contained a \'`\' character)'); $self->assert($hostname !~ /\`/o, 1, 'Internal error: An invalid hostname was passed to setupSetRights (it contained a \'`\' character)'); my $result; if ($password ne '') { $result = $database->execute("GRANT ALTER, INDEX, SELECT, CREATE, INSERT, DELETE, UPDATE, DROP, REFERENCES ON `$name`.* TO `$username`\@`$hostname` IDENTIFIED BY ?", $password); } else { $result = $database->execute("GRANT ALTER, INDEX, SELECT, CREATE, INSERT, DELETE, UPDATE, DROP, REFERENCES ON `$name`.* TO `$username`\@`$hostname`"); } if ($result->err) { $self->error(1, 'Failed to set user rights: ' . $result->errstr); } } =over time i would expect the following to be implemented: ********************* **** WARNING!!!! **** ********************* The following section is only licensed as MPL and not MPL/GPL. It is copied from the Bugzilla 2.x codebase for our information only. ########################################################################### # Detect changed local settings ########################################################################### sub GetIndexDef ($$) { my ($table, $field) = @_; my $sth = $dbh->prepare("SHOW INDEX FROM $table"); $sth->execute; while (my $ref = $sth->fetchrow_arrayref) { next if $$ref[2] ne $field; return $ref; } } sub CountIndexes ($) { my ($table) = @_; my $sth = $dbh->prepare("SHOW INDEX FROM $table"); $sth->execute; if ( $sth->rows == -1 ) { die ("Unexpected response while counting indexes in $table:" . " \$sth->rows == -1"); } return ($sth->rows); } sub DropIndexes ($) { my ($table) = @_; my %SEEN; # get the list of indexes # my $sth = $dbh->prepare("SHOW INDEX FROM $table"); $sth->execute; # drop each index # while ( my $ref = $sth->fetchrow_arrayref) { # note that some indexes are described by multiple rows in the # index table, so we may have already dropped the index described # in the current row. # next if exists $SEEN{$$ref[2]}; my $dropSth = $dbh->prepare("ALTER TABLE $table DROP INDEX $$ref[2]"); $dropSth->execute; $dropSth->finish; $SEEN{$$ref[2]} = 1; } } # # Check if the enums in the bugs table return the same values that are defined # in the various locally changeable variables. If this is true, then alter the # table definition. # sub CheckEnumField ($$@) { my ($table, $field, @against) = @_; my $ref = GetFieldDef($table, $field); #print "0: $$ref[0] 1: $$ref[1] 2: $$ref[2] 3: $$ref[3] 4: $$ref[4]\n"; $_ = "enum('" . join("','", @against) . "')"; if ($$ref[1] ne $_) { print "Updating field $field in table $table ...\n"; $_ .= " NOT NULL" if $$ref[3]; $dbh->do("ALTER TABLE $table CHANGE $field $field $_"); $::regenerateshadow = 1; } } ########################################################################### # Update the tables to the current definition ########################################################################### # # As time passes, fields in tables get deleted, added, changed and so on. # So we need some helper subroutines to make this possible: # sub ChangeFieldType ($$$) { my ($table, $field, $newtype) = @_; my $ref = GetFieldDef($table, $field); #print "0: $$ref[0] 1: $$ref[1] 2: $$ref[2] 3: $$ref[3] 4: $$ref[4]\n"; my $oldtype = $ref->[1]; if ($ref->[4]) { $oldtype .= qq{ default "$ref->[4]"}; } if ($oldtype ne $newtype) { print "Updating field type $field in table $table ...\n"; print "old: $oldtype\n"; print "new: $newtype\n"; $newtype .= " NOT NULL" if $$ref[3]; $dbh->do("ALTER TABLE $table CHANGE $field $field $newtype"); } } sub RenameField ($$$) { my ($table, $field, $newname) = @_; my $ref = GetFieldDef($table, $field); return unless $ref; # already fixed? #print "0: $$ref[0] 1: $$ref[1] 2: $$ref[2] 3: $$ref[3] 4: $$ref[4]\n"; if ($$ref[1] ne $newname) { print "Updating field $field in table $table ...\n"; my $type = $$ref[1]; $type .= " NOT NULL" if $$ref[3]; $dbh->do("ALTER TABLE $table CHANGE $field $newname $type"); } } sub AddField ($$$) { my ($table, $field, $definition) = @_; my $ref = GetFieldDef($table, $field); return if $ref; # already added? print "Adding new field $field to table $table ...\n"; $dbh->do("ALTER TABLE $table ADD COLUMN $field $definition"); } sub DropField ($$) { my ($table, $field) = @_; my $ref = GetFieldDef($table, $field); return unless $ref; # already dropped? print "Deleting unused field $field from table $table ...\n"; $dbh->do("ALTER TABLE $table DROP COLUMN $field"); } =cut