From: Dave Miller Date: Sun, 17 Mar 2024 18:08:48 +0000 (-0400) Subject: Bug 1885709: Support for MySQL 8 (#106) X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=62493442cf45cd2122a6565a46de9365a5becfa5;p=thirdparty%2Fbugzilla.git Bug 1885709: Support for MySQL 8 (#106) * Cherry-pick BMO MySQL 8 fix into Harmony * update docker image to use mysql8 libraries --------- Co-authored-by: dklawren --- diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 15b10f3fa..1a70e0462 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -4136,7 +4136,7 @@ sub groups { . " THEN 1 ELSE 0 END," . " CASE WHEN groups.id IN($grouplist) THEN 1 ELSE 0 END," . " isactive, membercontrol, othercontrol" - . " FROM groups" + . " FROM " . $dbh->quote_identifier('groups') . " LEFT JOIN bug_group_map" . " ON bug_group_map.group_id = groups.id" . " AND bug_id = ?" @@ -4201,7 +4201,7 @@ sub groups { # only show the group if it's visible to normal members my ($member_control) = $dbh->selectrow_array( "SELECT membercontrol - FROM groups + FROM " . $dbh->quote_identifier('groups') . " LEFT JOIN group_control_map ON group_control_map.group_id = groups.id AND group_control_map.product_id = ? diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index 417659b20..f5407f9f5 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -1559,7 +1559,11 @@ sub _check_references { # reserved words. my $bad_values = $self->selectcol_arrayref( "SELECT DISTINCT tabl.$column - FROM $table AS tabl LEFT JOIN $foreign_table AS forn + FROM " + . $self->quote_identifier($table) + . " AS tabl LEFT JOIN " + . $self->quote_identifier($foreign_table) + . " AS forn ON tabl.$column = forn.$foreign_column WHERE forn.$foreign_column IS NULL AND tabl.$column IS NOT NULL" @@ -1569,7 +1573,10 @@ sub _check_references { my $delete_action = $fk->{DELETE} || ''; if ($delete_action eq 'CASCADE') { $self->do( - "DELETE FROM $table WHERE $column IN (" . join(',', ('?') x @$bad_values) . ")", + "DELETE FROM " + . $self->quote_identifier($table) + . " WHERE $column IN (" + . join(',', ('?') x @$bad_values) . ")", undef, @$bad_values ); if (Bugzilla->usage_mode == USAGE_MODE_CMDLINE) { @@ -1590,7 +1597,9 @@ sub _check_references { } elsif ($delete_action eq 'SET NULL') { $self->do( - "UPDATE $table SET $column = NULL + "UPDATE " + . $self->quote_identifier($table) + . " SET $column = NULL WHERE $column IN (" . join(',', ('?') x @$bad_values) . ")", undef, @$bad_values ); diff --git a/Bugzilla/DB/Mysql.pm b/Bugzilla/DB/Mysql.pm index 484c1355e..5d744b0bd 100644 --- a/Bugzilla/DB/Mysql.pm +++ b/Bugzilla/DB/Mysql.pm @@ -345,7 +345,17 @@ sub bz_setup_database { if ($self->utf8_charset eq 'utf8mb4') { my %global = map {@$_} @{$self->selectall_arrayref(q(SHOW GLOBAL VARIABLES LIKE 'innodb_%'))}; - my $utf8mb4_supported = 1; + + # In versions of MySQL > 8, the default value for innodb_file_format is Barracuda + # and the setting was deprecated. Also innodb_file_per_table also now defaults + # to ON. innodb_large_prefix has also been removed in newer MySQL versions. + my $utf8mb4_supported + = (!exists $global{innodb_file_format} + || $global{innodb_file_format} eq 'Barracuda') + && (!exists $global{innodb_file_per_table} + || $global{innodb_file_per_table} eq 'ON') + && (!exists $global{innodb_large_prefix} + || $global{innodb_large_prefix} eq 'ON'); die install_string('mysql_innodb_settings') unless $utf8mb4_supported; @@ -359,7 +369,11 @@ sub bz_setup_database { 'mysql_row_format_conversion', {table => $table, format => $new_row_format} ), "\n"; - $self->do(sprintf 'ALTER TABLE %s ROW_FORMAT=%s', $table, $new_row_format); + $self->do( + sprintf 'ALTER TABLE %s ROW_FORMAT=%s', + $self->quote_identifier($table), + $new_row_format + ); } } } @@ -402,7 +416,7 @@ sub bz_setup_database { " most tables.\nConverting tables to InnoDB:\n"; foreach my $table (@$myisam_tables) { print "Converting table $table... "; - $self->do("ALTER TABLE $table ENGINE = InnoDB"); + $self->do('ALTER TABLE ' . $self->quote_identifier($table) . ' ENGINE = InnoDB'); print "done.\n"; } } @@ -697,8 +711,13 @@ sub bz_setup_database { } print "Converting the $table table to UTF-8...\n"; - my $bin = "ALTER TABLE $table " . join(', ', @binary_sql); - my $utf = "ALTER TABLE $table " + my $bin + = 'ALTER TABLE ' + . $self->quote_identifier($table) . ' ' + . join(', ', @binary_sql); + my $utf + = 'ALTER TABLE ' + . $self->quote_identifier($table) . ' ' . join(', ', @utf8_sql, "DEFAULT CHARACTER SET $charset COLLATE $collate"); $self->do($bin); $self->do($utf); @@ -709,7 +728,9 @@ sub bz_setup_database { } } else { - $self->do("ALTER TABLE $table DEFAULT CHARACTER SET $charset COLLATE $collate"); + $self->do('ALTER TABLE ' + . $self->quote_identifier($table) + . " DEFAULT CHARACTER SET $charset COLLATE $collate"); } } # foreach my $table (@tables) @@ -809,7 +830,8 @@ sub _fix_defaults { print "Fixing defaults...\n"; foreach my $table (reverse sort keys %fix_columns) { my @alters = map("ALTER COLUMN $_ DROP DEFAULT", @{$fix_columns{$table}}); - my $sql = "ALTER TABLE $table " . join(',', @alters); + my $sql + = 'ALTER TABLE ' . $self->quote_identifier($table) . ' ' . join(',', @alters); $self->do($sql); } } diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index bc14d18f4..dc824107d 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -2206,7 +2206,9 @@ is undefined. return "\n CONSTRAINT $fk_name FOREIGN KEY ($column)\n" - . " REFERENCES $to_table($to_column)\n" + . " REFERENCES " + . Bugzilla->dbh->quote_identifier($to_table) + . "($to_column)\n" . " ON UPDATE $update ON DELETE $delete"; } @@ -2241,13 +2243,18 @@ sub get_add_fks_sql { my @add = $self->_column_fks_to_ddl($table, $column_fks); my @sql; + my $dbh = Bugzilla->dbh; if ($self->MULTIPLE_FKS_IN_ALTER) { - my $alter = "ALTER TABLE $table ADD " . join(', ADD ', @add); + my $alter + = "ALTER TABLE " + . $dbh->quote_identifier($table) . " ADD " + . join(', ADD ', @add); push(@sql, $alter); } else { foreach my $fk_string (@add) { - push(@sql, "ALTER TABLE $table ADD $fk_string"); + push(@sql, + "ALTER TABLE " . $dbh->quote_identifier($table) . " ADD $fk_string"); } } return @sql; @@ -2268,7 +2275,8 @@ sub get_drop_fk_sql { my ($self, $table, $column, $references) = @_; my $fk_name = $self->_get_fk_name($table, $column, $references); - return ("ALTER TABLE $table DROP CONSTRAINT $fk_name"); + return ( + "ALTER TABLE " . Bugzilla->dbh->quote_identifier($table) . " DROP CONSTRAINT $fk_name"); } sub convert_type { @@ -2433,7 +2441,9 @@ sub _get_create_table_ddl { } my $sql - = "CREATE TABLE $table (\n" . join(",\n", @col_lines, @fk_lines) . "\n)"; + = "CREATE TABLE " + . Bugzilla->dbh->quote_identifier($table) . " (\n" + . join(",\n", @col_lines, @fk_lines) . "\n)"; return $sql; } @@ -2457,7 +2467,9 @@ sub _get_create_index_ddl { my $sql = "CREATE "; $sql .= "$index_type " if ($index_type && $index_type eq 'UNIQUE'); $sql - .= "INDEX $index_name ON $table_name \(" . join(", ", @$index_fields) . "\)"; + .= "INDEX $index_name ON " + . Bugzilla->dbh->quote_identifier($table_name) . ' (' + . join(', ', @$index_fields) . ')'; return ($sql); @@ -2483,16 +2495,20 @@ sub get_add_column_ddl { my ($self, $table, $column, $definition, $init_value) = @_; my @statements; + my $dbh = Bugzilla->dbh; push(@statements, - "ALTER TABLE $table " + 'ALTER TABLE ' + . $dbh->quote_identifier($table) . ' ' . $self->ADD_COLUMN . " $column " . $self->get_type_ddl($definition)); # XXX - Note that although this works for MySQL, most databases will fail # before this point, if we haven't set a default. - (push(@statements, "UPDATE $table SET $column = $init_value")) - if defined $init_value; + ( + push(@statements, + 'UPDATE ' . $dbh->quote_identifier($table) . " SET $column = $init_value") + ) if defined $init_value; if (defined $definition->{REFERENCES}) { push(@statements, @@ -2559,6 +2575,7 @@ sub get_alter_column_ddl { my $self = shift; my ($table, $column, $new_def, $set_nulls_to) = @_; + my $dbh = Bugzilla->dbh; my @statements; my $old_def = $self->get_column_abstract($table, $column); @@ -2585,7 +2602,10 @@ sub get_alter_column_ddl { # If we went from having a default to not having one elsif (!defined $default && defined $default_old) { - push(@statements, "ALTER TABLE $table ALTER COLUMN $column" . " DROP DEFAULT"); + push(@statements, + "ALTER TABLE " + . $dbh->quote_identifier($table) + . " ALTER COLUMN $column DROP DEFAULT"); } # If we went from no default to a default, or we changed the default. @@ -2593,28 +2613,40 @@ sub get_alter_column_ddl { || ($default ne $default_old)) { push(@statements, - "ALTER TABLE $table ALTER COLUMN $column " . " SET DEFAULT $default"); + "ALTER TABLE " + . $dbh->quote_identifier($table) + . " ALTER COLUMN $column SET DEFAULT $default"); } # If we went from NULL to NOT NULL. if (!$old_def->{NOTNULL} && $new_def->{NOTNULL}) { push(@statements, $self->_set_nulls_sql(@_)); - push(@statements, "ALTER TABLE $table ALTER COLUMN $column" . " SET NOT NULL"); + push(@statements, + "ALTER TABLE " + . $dbh->quote_identifier($table) + . " ALTER COLUMN $column SET NOT NULL"); } # If we went from NOT NULL to NULL elsif ($old_def->{NOTNULL} && !$new_def->{NOTNULL}) { - push(@statements, "ALTER TABLE $table ALTER COLUMN $column" . " DROP NOT NULL"); + push(@statements, + "ALTER TABLE " + . $dbh->quote_identifier($table) + . " ALTER COLUMN $column DROP NOT NULL"); } # If we went from not being a PRIMARY KEY to being a PRIMARY KEY. if (!$old_def->{PRIMARYKEY} && $new_def->{PRIMARYKEY}) { - push(@statements, "ALTER TABLE $table ADD PRIMARY KEY ($column)"); + push(@statements, + "ALTER TABLE " + . $dbh->quote_identifier($table) + . " ADD PRIMARY KEY ($column)"); } # If we went from being a PK to not being a PK elsif ($old_def->{PRIMARYKEY} && !$new_def->{PRIMARYKEY}) { - push(@statements, "ALTER TABLE $table DROP PRIMARY KEY"); + push(@statements, + "ALTER TABLE " . $dbh->quote_identifier($table) . " DROP PRIMARY KEY"); } return @statements; @@ -2636,7 +2668,10 @@ sub _set_nulls_sql { } my @sql; if (defined $default) { - push(@sql, "UPDATE $table SET $column = $default" . " WHERE $column IS NULL"); + push(@sql, + "UPDATE " + . Bugzilla->dbh->quote_identifier($table) + . " SET $column = $default WHERE $column IS NULL"); } return @sql; } @@ -2671,7 +2706,9 @@ sub get_drop_column_ddl { =cut my ($self, $table, $column) = @_; - return ("ALTER TABLE $table DROP COLUMN $column"); + return ("ALTER TABLE " + . Bugzilla->dbh->quote_identifier($table) + . " DROP COLUMN $column"); } =item C @@ -2684,7 +2721,7 @@ sub get_drop_column_ddl { sub get_drop_table_ddl { my ($self, $table) = @_; - return ("DROP TABLE $table"); + return ('DROP TABLE ' . Bugzilla->dbh->quote_identifier($table)); } sub get_rename_column_ddl { @@ -2734,7 +2771,11 @@ Gets SQL to rename a table in the database. =cut my ($self, $old_name, $new_name) = @_; - return ("ALTER TABLE $old_name RENAME TO $new_name"); + my $dbh = Bugzilla->dbh; + return ('ALTER TABLE ' + . $dbh->quote_identifier($old_name) + . ' RENAME TO ' + . $dbh->quote_identifier($new_name)); } =item C diff --git a/Bugzilla/DB/Schema/Mysql.pm b/Bugzilla/DB/Schema/Mysql.pm index 39c1fc45e..02526887d 100644 --- a/Bugzilla/DB/Schema/Mysql.pm +++ b/Bugzilla/DB/Schema/Mysql.pm @@ -151,11 +151,15 @@ sub _get_create_index_ddl { # Returns a "create index" SQL statement. my ($self, $table_name, $index_name, $index_fields, $index_type) = @_; + my $dbh = Bugzilla->dbh; my $sql = "CREATE "; $sql .= "$index_type " if ($index_type eq 'UNIQUE' || $index_type eq 'FULLTEXT'); - $sql .= "INDEX \`$index_name\` ON $table_name \(" + $sql + .= "INDEX " + . $dbh->quote_identifier($index_name) . " ON " + . $dbh->quote_identifier($table_name) . " \(" . join(", ", @$index_fields) . "\)"; return ($sql); @@ -189,10 +193,12 @@ sub get_alter_column_ddl { my @statements; - push( - @statements, "UPDATE $table SET $column = $set_nulls_to - WHERE $column IS NULL" - ) if defined $set_nulls_to; + my $dbh = Bugzilla->dbh; + push(@statements, + "UPDATE " + . $dbh->quote_identifier($table) + . " SET $column = $set_nulls_to WHERE $column IS NULL") + if defined $set_nulls_to; # Calling SET DEFAULT or DROP DEFAULT is *way* faster than calling # CHANGE COLUMN, so just do that if we're just changing the default. @@ -204,27 +210,32 @@ sub get_alter_column_ddl { && $self->columns_equal(\%new_defaultless, \%old_defaultless)) { if (!defined $new_def->{DEFAULT}) { - push(@statements, "ALTER TABLE $table ALTER COLUMN $column DROP DEFAULT"); + push(@statements, + "ALTER TABLE " + . $dbh->quote_identifier($table) + . " ALTER COLUMN $column DROP DEFAULT"); } else { - push( - @statements, "ALTER TABLE $table ALTER COLUMN $column - SET DEFAULT " . $new_def->{DEFAULT} - ); + push(@statements, + "ALTER TABLE " + . $dbh->quote_identifier($table) + . " ALTER COLUMN $column SET DEFAULT " + . $new_def->{DEFAULT}); } } else { my $new_ddl = $self->get_type_ddl(\%new_def_copy); - push( - @statements, "ALTER TABLE $table CHANGE COLUMN - $column $column $new_ddl" - ); + push(@statements, + "ALTER TABLE " + . $dbh->quote_identifier($table) + . " CHANGE COLUMN $column $column $new_ddl"); } if ($old_def->{PRIMARYKEY} && !$new_def->{PRIMARYKEY}) { # Dropping a PRIMARY KEY needs an explicit DROP PRIMARY KEY - push(@statements, "ALTER TABLE $table DROP PRIMARY KEY"); + push(@statements, + 'ALTER TABLE ' . $dbh->quote_identifier($table) . ' DROP PRIMARY KEY'); } return @statements; @@ -233,8 +244,9 @@ sub get_alter_column_ddl { sub get_drop_fk_sql { my ($self, $table, $column, $references) = @_; my $fk_name = $self->_get_fk_name($table, $column, $references); - my @sql = ("ALTER TABLE $table DROP FOREIGN KEY $fk_name"); my $dbh = Bugzilla->dbh; + my @sql = ( + "ALTER TABLE " . $dbh->quote_identifier($table) . " DROP FOREIGN KEY $fk_name"); # MySQL requires, and will create, an index on any column with # an FK. It will name it after the fk, which we never do. @@ -261,7 +273,7 @@ sub get_rename_indexes_ddl { my ($self, $table, %indexes) = @_; my @keys = keys %indexes or return (); - my $sql = "ALTER TABLE $table "; + my $sql = 'ALTER TABLE' . Bugzilla->dbh->quote_identifier($table) . ' '; foreach my $old_name (@keys) { my $name = $indexes{$old_name}->{NAME}; @@ -282,7 +294,9 @@ sub get_rename_indexes_ddl { sub get_set_serial_sql { my ($self, $table, $column, $value) = @_; - return ("ALTER TABLE $table AUTO_INCREMENT = $value"); + return ("ALTER TABLE " + . Bugzilla->dbh->quote_identifier($table) + . " AUTO_INCREMENT = $value"); } # Converts a DBI column_info output to an abstract column definition. @@ -420,7 +434,9 @@ sub get_rename_column_ddl { # MySQL doesn't like having the PRIMARY KEY statement in a rename. $def =~ s/PRIMARY KEY//i; - return ("ALTER TABLE $table CHANGE COLUMN $old_name $new_name $def"); + return ("ALTER TABLE " + . Bugzilla->dbh->quote_identifier($table) + . " CHANGE COLUMN $old_name $new_name $def"); } sub get_type_ddl { diff --git a/Bugzilla/DB/Schema/Sqlite.pm b/Bugzilla/DB/Schema/Sqlite.pm index 81cd8c2ea..f5935a2fd 100644 --- a/Bugzilla/DB/Schema/Sqlite.pm +++ b/Bugzilla/DB/Schema/Sqlite.pm @@ -112,7 +112,7 @@ sub _sqlite_alter_schema { my $insert_str = join(',', @insert_cols); my $select_str = join(',', @select_cols); my $copy_sql - = "INSERT INTO $table ($insert_str)" . " SELECT $select_str FROM $rename_to"; + = "INSERT INTO " . $dbh->quote_identifier($table) . " ($insert_str)" . " SELECT $select_str FROM " . $dbh->quote_identifier($rename_to); # We have to turn FKs off before doing this. Otherwise, when we rename # the table, all of the FKs in the other tables will be automatically @@ -125,7 +125,10 @@ sub _sqlite_alter_schema { 'PRAGMA foreign_keys = OFF', 'BEGIN EXCLUSIVE TRANSACTION', @{$options->{pre_sql} || []}, - "ALTER TABLE $table RENAME TO $rename_to", + 'ALTER TABLE ' + . Bugzilla->dbh->quote_identifier($table) + . ' RENAME TO ' + . Bugzilla->dbh->quote_identifier($rename_to), $create_table, $copy_sql, "DROP TABLE $rename_to", diff --git a/Bugzilla/Group.pm b/Bugzilla/Group.pm index 362192f82..a68939aeb 100644 --- a/Bugzilla/Group.pm +++ b/Bugzilla/Group.pm @@ -485,7 +485,8 @@ sub create { sub ValidateGroupName { my ($name, @users) = (@_); my $dbh = Bugzilla->dbh; - my $query = "SELECT id FROM groups " . "WHERE name = ?"; + my $query + = 'SELECT id FROM ' . $dbh->quote_identifier('groups') . ' WHERE name = ?'; if (Bugzilla->params->{'usevisibilitygroups'}) { my @visible = (-1); foreach my $user (@users) { diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index dedadd279..dd5de6688 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -1703,7 +1703,8 @@ sub _convert_groups_system_from_groupset { $dbh->bz_drop_index('groups', 'groups_name_idx'); my @primary_key = $dbh->primary_key(undef, undef, 'groups'); if (@primary_key) { - $dbh->do("ALTER TABLE groups DROP PRIMARY KEY"); + $dbh->do( + 'ALTER TABLE ' . $dbh->quote_identifier('groups') . ' DROP PRIMARY KEY'); } $dbh->bz_add_column('groups', 'id', @@ -1714,7 +1715,8 @@ sub _convert_groups_system_from_groupset { # Convert all existing groupset records to map entries before removing # groupset fields or removing "bit" from groups. - my $sth = $dbh->prepare("SELECT bit, id FROM groups WHERE bit > 0"); + my $sth = $dbh->prepare( + 'SELECT bit, id FROM ' . $dbh->quote_identifier('groups') . ' WHERE bit > 0'); $sth->execute(); while (my ($bit, $gid) = $sth->fetchrow_array) { @@ -1809,7 +1811,7 @@ sub _convert_groups_system_from_groupset { # Get names of groups added. my $sth2 = $dbh->prepare( - "SELECT name FROM groups + "SELECT name FROM " . $dbh->quote_identifier('groups') . " WHERE (bit & $added) != 0 AND (bit & $removed) = 0" ); @@ -1821,7 +1823,7 @@ sub _convert_groups_system_from_groupset { # Get names of groups removed. $sth2 = $dbh->prepare( - "SELECT name FROM groups + "SELECT name FROM " . $dbh->quote_identifier('groups') . " WHERE (bit & $removed) != 0 AND (bit & $added) = 0" ); @@ -1834,9 +1836,7 @@ sub _convert_groups_system_from_groupset { # Get list of group bits added that correspond to # missing groups. $sth2 = $dbh->prepare( - "SELECT ($added & ~BIT_OR(bit)) - FROM groups" - ); + "SELECT ($added & ~BIT_OR(bit)) FROM " . $dbh->quote_identifier('groups')); $sth2->execute(); my ($miss) = $sth2->fetchrow_array; if ($miss) { @@ -1848,9 +1848,7 @@ sub _convert_groups_system_from_groupset { # Get list of group bits deleted that correspond to # missing groups. $sth2 = $dbh->prepare( - "SELECT ($removed & ~BIT_OR(bit)) - FROM groups" - ); + "SELECT ($removed & ~BIT_OR(bit)) FROM " . $dbh->quote_identifier('groups')); $sth2->execute(); ($miss) = $sth2->fetchrow_array; if ($miss) { @@ -1886,7 +1884,7 @@ sub _convert_groups_system_from_groupset { # Get names of groups added. my $sth2 = $dbh->prepare( - "SELECT name FROM groups + "SELECT name FROM " . $dbh->quote_identifier('groups') . " WHERE (bit & $added) != 0 AND (bit & $removed) = 0" ); @@ -1898,7 +1896,7 @@ sub _convert_groups_system_from_groupset { # Get names of groups removed. $sth2 = $dbh->prepare( - "SELECT name FROM groups + "SELECT name FROM " . $dbh->quote_identifier('groups') . " WHERE (bit & $removed) != 0 AND (bit & $added) = 0" ); @@ -1927,12 +1925,12 @@ sub _convert_groups_system_from_groupset { # Identify admin group. my ($admin_gid) - = $dbh->selectrow_array("SELECT id FROM groups WHERE name = 'admin'"); + = $dbh->selectrow_array( + "SELECT id FROM " . $dbh->quote_identifier('groups') . " WHERE name = 'admin'"); if (!$admin_gid) { - $dbh->do( - q{INSERT INTO groups (name, description) - VALUES ('admin', 'Administrators')} - ); + $dbh->do("INSERT INTO " + . $dbh->quote_identifier('groups') + . " (name, description) VALUES ('admin', 'Administrators')"); $admin_gid = $dbh->bz_last_key('groups', 'id'); } @@ -2559,7 +2557,8 @@ sub _fix_group_with_empty_name { # Note that there can be at most one such group (because of # the SQL index on the name column). my ($emptygroupid) - = $dbh->selectrow_array("SELECT id FROM groups where name = ''"); + = $dbh->selectrow_array( + "SELECT id FROM " . $dbh->quote_identifier('groups') . " where name = ''"); if ($emptygroupid) { # There is a group with an empty name; find a name to rename it @@ -2567,7 +2566,8 @@ sub _fix_group_with_empty_name { # group_$gid and add _ if necessary. my $trycount = 0; my $trygroupname; - my $sth = $dbh->prepare("SELECT 1 FROM groups where name = ?"); + my $sth = $dbh->prepare( + 'SELECT 1 FROM ' . $dbh->quote_identifier('groups') . ' where name = ?'); my $name_exists = 1; while ($name_exists) { @@ -2578,7 +2578,8 @@ sub _fix_group_with_empty_name { $name_exists = $dbh->selectrow_array($sth, undef, $trygroupname); $trycount++; } - $dbh->do("UPDATE groups SET name = ? WHERE id = ?", + $dbh->do( + 'UPDATE ' . $dbh->quote_identifier('groups') . ' SET name = ? WHERE id = ?', undef, $trygroupname, $emptygroupid); print "Group $emptygroupid had an empty name; renamed as", " '$trygroupname'.\n"; @@ -2749,7 +2750,8 @@ sub _change_all_mysql_booleans_to_tinyint { my $quip_info_sth = $dbh->column_info(undef, undef, 'quips', '%'); my $quips_cols = $quip_info_sth->fetchall_hashref("COLUMN_NAME"); my $approved_col = $quips_cols->{'approved'}; - if ( $approved_col->{TYPE_NAME} eq 'TINYINT' + if ($approved_col->{TYPE_NAME} eq 'TINYINT' + and defined $approved_col->{COLUMN_SIZE} and $approved_col->{COLUMN_SIZE} == 1) { # series.public could have been renamed to series.is_public, @@ -3011,8 +3013,11 @@ EOT sub _rederive_regex_groups { my $dbh = Bugzilla->dbh; - my $regex_groups_exist = $dbh->selectrow_array( - "SELECT 1 FROM groups WHERE userregexp = '' " . $dbh->sql_limit(1)); + my $regex_groups_exist + = $dbh->selectrow_array("SELECT 1 FROM " + . $dbh->quote_identifier('groups') + . " WHERE userregexp = '' " + . $dbh->sql_limit(1)); return if !$regex_groups_exist; my $regex_derivations @@ -3027,7 +3032,7 @@ sub _rederive_regex_groups { my $sth = $dbh->prepare( "SELECT profiles.userid, profiles.login_name, groups.id, groups.userregexp, user_group_map.group_id - FROM (profiles CROSS JOIN groups) + FROM (profiles CROSS JOIN " . $dbh->quote_identifier('groups') . ") LEFT JOIN user_group_map ON user_group_map.user_id = profiles.userid AND user_group_map.group_id = groups.id @@ -4228,7 +4233,9 @@ sub _migrate_group_owners { return if $dbh->bz_column_info('groups', 'owner_user_id'); $dbh->bz_add_column('groups', 'owner_user_id', {TYPE => 'INT3'}); my $nobody = Bugzilla::User->check(Bugzilla->localconfig->nobody_user); - $dbh->do('UPDATE groups SET owner_user_id = ?', undef, $nobody->id); + $dbh->do( + 'UPDATE ' . $dbh->quote_identifier('groups') . ' SET owner_user_id = ?', + undef, $nobody->id); } sub _migrate_nicknames { diff --git a/Bugzilla/Migrate.pm b/Bugzilla/Migrate.pm index a84bc5789..57472d821 100644 --- a/Bugzilla/Migrate.pm +++ b/Bugzilla/Migrate.pm @@ -891,8 +891,13 @@ sub _do_table_insert { my @values = map { $hash->{$_} } @fields; my $field_sql = join(',', @fields); my $question_sql = join(',', @questions); - Bugzilla->dbh->do("INSERT INTO $table ($field_sql) VALUES ($question_sql)", - undef, @values); + my $dbh = Bugzilla->dbh; + $dbh->do( + "INSERT INTO " + . $dbh->quote_identifier($table) + . " ($field_sql) VALUES ($question_sql)", + undef, @values + ); } ###################### diff --git a/Bugzilla/Object.pm b/Bugzilla/Object.pm index 0132682c6..12debf40b 100644 --- a/Bugzilla/Object.pm +++ b/Bugzilla/Object.pm @@ -112,6 +112,7 @@ sub _load_from_db { my $table = $class->DB_TABLE; my $name_field = $class->NAME_FIELD; my $id_field = $class->ID_FIELD; + my $sql_table = $dbh->quote_identifier($table); my $id = $param; if (ref $param eq 'HASH') { @@ -130,11 +131,10 @@ sub _load_from_db { # Too large integers make PostgreSQL crash. return if $id > MAX_INT_32; - $object_data = $dbh->selectrow_hashref( - qq{ - SELECT $columns FROM $table - WHERE $id_field = ?}, undef, $id - ); + $object_data + = $dbh->selectrow_hashref( + "SELECT $columns FROM $sql_table WHERE $id_field = ?", + undef, $id); } else { unless (defined $param->{name} @@ -162,7 +162,7 @@ sub _load_from_db { } $object_data - = $dbh->selectrow_hashref("SELECT $columns FROM $table WHERE $condition", + = $dbh->selectrow_hashref("SELECT $columns FROM $sql_table WHERE $condition", undef, @values); } return $object_data; @@ -411,15 +411,16 @@ sub _do_list_select { } if (!$objects) { - my $sql = "SELECT $cols FROM $table"; + my $dbh = Bugzilla->dbh; + + my $sql_table = $dbh->quote_identifier($table); + my $sql = "SELECT $cols FROM $sql_table"; if (defined $where) { $sql .= " WHERE $where "; } $sql .= " ORDER BY $order"; $sql .= " $postamble" if $postamble; - my $dbh = Bugzilla->dbh; - # Sometimes the values are tainted, but we don't want to untaint them # for the caller. So we copy the array. It's safe to untaint because # they're only used in placeholders here. @@ -559,7 +560,8 @@ sub update { my $columns = join(', ', map {"$_ = ?"} @update_columns); - $dbh->do("UPDATE $table SET $columns WHERE $id_field = ?", + my $sql_table = $dbh->quote_identifier($table); + $dbh->do("UPDATE $sql_table SET $columns WHERE $id_field = ?", undef, @values, $self->id) if @values; @@ -591,7 +593,8 @@ sub remove_from_db { my $dbh = Bugzilla->dbh; $dbh->bz_start_transaction(); $self->audit_log(AUDIT_REMOVE) if $self->AUDIT_REMOVES; - $dbh->do("DELETE FROM $table WHERE $id_field = ?", undef, $self->id); + my $sql_table = $dbh->quote_identifier($table); + $dbh->do("DELETE FROM $sql_table WHERE $id_field = ?", undef, $self->id); $dbh->bz_commit_transaction(); if ($self->USE_MEMCACHED) { @@ -652,8 +655,9 @@ sub any_exist { my $class = shift; my $table = $class->DB_TABLE; my $dbh = Bugzilla->dbh; - my $any_exist - = $dbh->selectrow_array("SELECT 1 FROM $table " . $dbh->sql_limit(1)); + my $sql_table = $dbh->quote_identifier($table); + my $any_exist = $dbh->selectrow_array( + "SELECT 1 FROM $sql_table " . $dbh->sql_limit(1)); return $any_exist ? 1 : 0; } @@ -750,9 +754,13 @@ sub insert_create_data { my $qmarks = '?,' x @field_names; chop($qmarks); my $table = $class->DB_TABLE; + my $sql_table = $dbh->quote_identifier($table); $dbh->do( - "INSERT INTO $table (" . join(', ', @field_names) . ") VALUES ($qmarks)", - undef, @values); + "INSERT INTO $sql_table (" + . join(', ', @field_names) + . ") VALUES ($qmarks)", + undef, @values + ); my $id = $dbh->bz_last_key($table, $class->ID_FIELD); my $object = $class->new($id); diff --git a/Bugzilla/Product.pm b/Bugzilla/Product.pm index 7e3105ad2..e7438d73c 100644 --- a/Bugzilla/Product.pm +++ b/Bugzilla/Product.pm @@ -642,13 +642,13 @@ sub group_controls { if (!defined $self->{group_controls} || $full_data) { # Include name to the list, to allow us sorting data more easily. - my $query = qq{SELECT id, name, entry, membercontrol, othercontrol, + my $query = "SELECT id, name, entry, membercontrol, othercontrol, canedit, editcomponents, editbugs, canconfirm - FROM groups + FROM " . $dbh->quote_identifier('groups') . " LEFT JOIN group_control_map ON id = group_id $where_or_and product_id = ? - $and_or_where isbuggroup = 1}; + $and_or_where isbuggroup = 1"; $self->{group_controls} = $dbh->selectall_hashref($query, 'id', undef, $self->id); @@ -685,7 +685,9 @@ sub groups_available { $dbh->selectcol_arrayref( "SELECT group_id, membercontrol FROM group_control_map - INNER JOIN groups ON group_control_map.group_id = groups.id + INNER JOIN " + . $dbh->quote_identifier('groups') + . " ON group_control_map.group_id = groups.id WHERE isbuggroup = 1 AND isactive = 1 AND product_id = ? AND (membercontrol = $shown OR membercontrol = $default) AND " . Bugzilla->user->groups_in_sql(), {Columns => [1, 2]}, @@ -700,7 +702,9 @@ sub groups_available { $dbh->selectcol_arrayref( "SELECT group_id, othercontrol FROM group_control_map - INNER JOIN groups ON group_control_map.group_id = groups.id + INNER JOIN " + . $dbh->quote_identifier('groups') + . " ON group_control_map.group_id = groups.id WHERE isbuggroup = 1 AND isactive = 1 AND product_id = ? AND (othercontrol = $shown OR othercontrol = $default)", {Columns => [1, 2]}, $self->id @@ -734,10 +738,13 @@ sub groups_mandatory { # For membercontrol we don't check group_id IN, because if membercontrol # is Mandatory, the group is Mandatory for everybody, regardless of their # group membership. - my $ids = Bugzilla->dbh->selectcol_arrayref( + my $dbh = Bugzilla->dbh; + my $ids = $dbh->selectcol_arrayref( "SELECT group_id FROM group_control_map - INNER JOIN groups ON group_control_map.group_id = groups.id + INNER JOIN " + . $dbh->quote_identifier('groups') + . " ON group_control_map.group_id = groups.id WHERE product_id = ? AND isactive = 1 AND (membercontrol = $mandatory OR (othercontrol = $mandatory @@ -770,10 +777,13 @@ sub groups_valid { # Note that we don't check OtherControl below, because there is no # valid NA/* combination. - my $ids = Bugzilla->dbh->selectcol_arrayref( + my $dbh = Bugzilla->dbh; + my $ids = $dbh->selectcol_arrayref( "SELECT DISTINCT group_id FROM group_control_map AS gcm - INNER JOIN groups ON gcm.group_id = groups.id + INNER JOIN " + . $dbh->quote_identifier('groups') + . " ON gcm.group_id = groups.id WHERE product_id = ? AND isbuggroup = 1 AND membercontrol != " . CONTROLMAPNA, undef, $self->id ); diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 7afcaf77b..2d36b9440 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -1431,8 +1431,9 @@ sub _translate_join { if ($extra_condition) { $extra_condition = " AND $extra_condition"; } - - my @join_sql = "$join JOIN $table AS $name" + my $sql_table + = $table eq 'groups' ? Bugzilla->dbh->quote_identifier($table) : $table; + my @join_sql = "$join JOIN $sql_table AS $name " . " ON $from_table.$from = $name.$to$extra_condition"; return @join_sql; } @@ -3256,7 +3257,7 @@ sub _multiselect_table { } elsif ($field eq 'bug_group') { $args->{full_field} = 'groups.name'; - return "bug_group_map INNER JOIN groups + return "bug_group_map INNER JOIN " . $dbh->quote_identifier('groups') . " ON bug_group_map.group_id = groups.id"; } elsif ($field eq 'blocked' or $field eq 'dependson') { diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 91400de57..d98c3f3fb 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -1822,7 +1822,7 @@ sub visible_groups_direct { } else { # All groups are visible if usevisibilitygroups is off. - $sth = $dbh->prepare('SELECT id FROM groups'); + $sth = $dbh->prepare('SELECT id FROM ' . $dbh->quote_identifier('groups')); } $sth->execute(); @@ -1886,7 +1886,7 @@ sub derive_regexp_groups { $sth = $dbh->prepare( "SELECT id, userregexp, user_group_map.group_id - FROM groups + FROM " . $dbh->quote_identifier('groups') . " LEFT JOIN user_group_map ON groups.id = user_group_map.group_id AND user_group_map.user_id = ? diff --git a/Dockerfile b/Dockerfile index 653c48fe5..9a39eacc7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mozillabteam/bmo-perl-slim:20200505.1 +FROM bugzilla/bugzilla-perl-slim:20240316.1 ENV DEBIAN_FRONTEND noninteractive @@ -23,7 +23,7 @@ WORKDIR /app COPY . /app -RUN chown -R app.app /app && \ +RUN chown -R app:app /app && \ perl -I/app -I/app/local/lib/perl5 -c -E 'use Bugzilla; BEGIN { Bugzilla->extensions }' && \ perl -c /app/scripts/entrypoint.pl diff --git a/Dockerfile.bmo-slim b/Dockerfile.bmo-slim index 15c93afdd..b62ad7d16 100644 --- a/Dockerfile.bmo-slim +++ b/Dockerfile.bmo-slim @@ -1,12 +1,12 @@ -FROM perl:5.30.2-slim AS builder +FROM perl:5.38.0-slim AS builder RUN apt-get update +RUN apt-get -y dist-upgrade RUN apt-get install -y \ apt-file \ build-essential \ cmake \ curl \ - default-libmysqlclient-dev \ git \ libcairo-dev \ libexpat-dev \ @@ -15,6 +15,16 @@ RUN apt-get install -y \ openssl \ zlib1g-dev +# The Perl image is based on Debian, which doesn't have MySQL 8, and the +# current DBD::mysql requires MySQL 8 libraries to build, so we have +# to get the client libraries from mysql.com +RUN gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 5072E1F5 \ + && gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3A79BD29 \ + && gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys B7B3B788A8D3785C \ + && echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/mysql-8.0.gpg] http://repo.mysql.com/apt/debian/ bookworm mysql-8.0' > /etc/apt/sources.list.d/mysql-8.0.list +RUN apt-get update \ + && apt-get install -y libmysqlclient-dev + RUN cpanm --notest --quiet App::cpm Module::CPANfile Carton::Snapshot WORKDIR /app @@ -33,19 +43,27 @@ RUN find local -name '*.so' -exec ldd {} \; \ | xargs -IFILE apt-file search -l FILE \ | sort -u > PACKAGES -FROM perl:5.30.2-slim +FROM perl:5.38.0-slim ENV DEBIAN_FRONTEND noninteractive COPY --from=builder /app/local /app/local COPY --from=builder /app/PACKAGES /app/PACKAGES +RUN apt-get update \ + && apt-get install -y gnupg +RUN gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 5072E1F5 \ + && gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3A79BD29 \ + && gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys B7B3B788A8D3785C \ + && echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/mysql-8.0.gpg] http://repo.mysql.com/apt/debian/ bookworm mysql-8.0' > /etc/apt/sources.list.d/mysql-8.0.list + RUN apt-get update \ && apt-get install -y \ curl \ git \ graphviz \ libcap2-bin \ + libmysqlclient21 \ rsync \ $(cat /app/PACKAGES) \ && rm -rf /var/cache/apt/* /var/lib/apt/lists/* diff --git a/Dockerfile.cpanfile b/Dockerfile.cpanfile index c5c0ad842..66a93aab0 100644 --- a/Dockerfile.cpanfile +++ b/Dockerfile.cpanfile @@ -1,10 +1,22 @@ -FROM perl:5.30.2-slim +FROM perl:5.38.0-slim -RUN apt-get update -RUN apt-get install -y \ +RUN apt-get update \ + && apt-get dist-upgrade -y \ + && apt-get install -y \ build-essential curl libssl-dev zlib1g-dev openssl \ libexpat-dev cmake git libcairo-dev libgd-dev \ - default-libmysqlclient-dev unzip wget + unzip wget + +# The Perl image is based on Debian, which doesn't have MySQL 8, and the +# current DBD::mysql requires MySQL 8 libraries to build, so we have +# to get the client libraries from mysql.com +RUN gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 5072E1F5 \ + && gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3A79BD29 \ + && gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mysql-8.0.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys B7B3B788A8D3785C \ + && echo 'deb [arch=amd64 signed-by=/usr/share/keyrings/mysql-8.0.gpg] http://repo.mysql.com/apt/debian/ bookworm mysql-8.0' > /etc/apt/sources.list.d/mysql-8.0.list +RUN apt-get update \ + && apt-get install -y libmysqlclient-dev + RUN cpanm --notest --quiet App::cpm Module::CPANfile Carton::Snapshot WORKDIR /app diff --git a/Makefile.PL b/Makefile.PL index b8f953b69..6a0ca2320 100755 --- a/Makefile.PL +++ b/Makefile.PL @@ -236,7 +236,7 @@ my %optional_features = ( description => 'MySQL database support', prereqs => { runtime => - {requires => {'DBD::mysql' => '4.037', 'DateTime::Format::MySQL' => '0.06'}} + {requires => {'DBD::mysql' => '5.003', 'DateTime::Format::MySQL' => '0.06'}} } }, new_charts => { @@ -386,7 +386,7 @@ WriteMakefile( sub MY::postamble { return <<"MAKE"; -GEN_CPANFILE_ARGS = -D better_xff -D mysql -D jsonrpc -D xmlrpc +GEN_CPANFILE_ARGS = -D better_xff -D mysql -D jsonrpc -D xmlrpc -D documentation cpanfile: MYMETA.json \t\$(PERLRUN) gen-cpanfile.pl \$(GEN_CPANFILE_ARGS) diff --git a/cpanfile b/cpanfile index 37e6c1bb7..3b47b5846 100644 --- a/cpanfile +++ b/cpanfile @@ -1,5 +1,4 @@ requires 'Algorithm::BloomFilter', '0.02'; -requires 'Bytes::Random::Secure'; requires 'CGI', '4.31'; requires 'CGI::Compile'; requires 'CGI::Emulate::PSGI'; @@ -9,9 +8,7 @@ requires 'Class::XSAccessor', '1.18'; requires 'Crypt::CBC'; requires 'Crypt::DES'; requires 'Crypt::DES_EDE3'; -requires 'Crypt::OpenPGP', '1.12'; -requires 'Crypt::SMIME'; -requires 'DBD::mysql', '4.037'; +requires 'DBD::mysql', '5.003'; requires 'DBI', '1.614'; requires 'DBIx::Class'; requires 'DBIx::Class::Helpers', '2.034002'; @@ -29,7 +26,9 @@ requires 'Email::Address'; requires 'Email::MIME', '1.904'; requires 'Email::Send', '1.911'; requires 'FFI::Platypus'; +requires 'File::Copy::Recursive'; requires 'File::MimeInfo::Magic'; +requires 'File::Which'; requires 'Future', '0.34'; requires 'HTML::Escape', '1.10'; requires 'HTML::Tree'; @@ -69,7 +68,7 @@ requires 'Scope::Guard', '0.21'; requires 'Sereal', '4.004'; requires 'Sub::Quote', '2.005000'; requires 'Sys::Syslog'; -requires 'Template', '2.24'; +requires 'Template', '3.008'; requires 'Test::CPAN::Meta'; requires 'Test::Pod'; requires 'Test::Pod::Coverage'; diff --git a/docker-compose.test.yml b/docker-compose.test.yml index de60c9589..923d1b096 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -2,11 +2,13 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -version: '3' +version: '3.6' services: bmo.test: - image: bmo + build: &bmo_build + context: . + dockerfile: Dockerfile command: dev_httpd tmpfs: - /tmp @@ -17,6 +19,7 @@ services: - BMO_db_name=bugs - BMO_db_pass=bugs - BMO_db_user=bugs + - BMO_db_mysql_ssl_get_pubkey=1 - BMO_memcached_namespace=bugzilla - BMO_memcached_servers=memcached:11211 - BMO_ses_username=ses@mozilla.bugs @@ -43,7 +46,7 @@ services: - selenium bmo.db: - image: mysql:5.7 + image: mysql:8 tmpfs: - /tmp logging: diff --git a/docker-compose.yml b/docker-compose.yml index 2bfedb895..5acb0cd19 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -version: '3.4' +version: '3.6' services: bmo.test: @@ -92,7 +92,7 @@ services: - memcached bmo.db: - image: mysql:5.7 + image: mysql:8 volumes: - bmo-mysql-db:/var/lib/mysql - ./docker/mysql:/etc/mysql/conf.d diff --git a/editgroups.cgi b/editgroups.cgi index 91bf2d9ef..28f0ad2da 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -48,10 +48,11 @@ my $token = $cgi->param('token'); sub CheckGroupID { my ($group_id) = @_; $group_id = trim($group_id || 0); + my $dbh = Bugzilla->dbh; ThrowUserError("group_not_specified") unless $group_id; ( - detaint_natural($group_id) && Bugzilla->dbh->selectrow_array( - "SELECT id FROM groups WHERE id = ?", + detaint_natural($group_id) && $dbh->selectrow_array( + 'SELECT id FROM ' . $dbh->quote_identifier('groups') . ' WHERE id = ?', undef, $group_id ) ) || ThrowUserError("invalid_group_ID"); diff --git a/editproducts.cgi b/editproducts.cgi index 972544892..c2dc8893b 100755 --- a/editproducts.cgi +++ b/editproducts.cgi @@ -349,7 +349,7 @@ if ($action eq 'updategroupcontrols') { FROM bugs INNER JOIN bug_group_map ON bug_group_map.bug_id = bugs.bug_id - INNER JOIN groups + INNER JOIN ' . $dbh->quote_identifier('groups') . ' ON bug_group_map.group_id = groups.id WHERE groups.id IN (' . join(', ', @now_na) . ') AND bugs.product_id = ? ' . $dbh->sql_group_by('groups.name'), @@ -371,7 +371,7 @@ if ($action eq 'updategroupcontrols') { (SELECT bug_group_map.bug_id FROM bug_group_map WHERE bug_group_map.group_id = groups.id)) AS count - FROM groups + FROM ' . $dbh->quote_identifier('groups') . ' WHERE groups.id IN (' . join(', ', @now_mandatory) . ') ORDER BY groups.name', {'Slice' => {}}, $product->id ); diff --git a/editusers.cgi b/editusers.cgi index 00cfb4bb4..1a5b24979 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -843,16 +843,16 @@ sub userDataToVars { $vars->{'groups'} = $user->bless_groups(); $vars->{'permissions'} = $dbh->selectall_hashref( - qq{SELECT id, + 'SELECT id, COUNT(directmember.group_id) AS directmember, COUNT(regexpmember.group_id) AS regexpmember, - (CASE WHEN (groups.id IN ($grouplist) + (CASE WHEN (groups.id IN (' . $grouplist . ') AND COUNT(directmember.group_id) = 0 AND COUNT(regexpmember.group_id) = 0 ) THEN 1 ELSE 0 END) AS derivedmember, COUNT(directbless.group_id) AS directbless - FROM groups + FROM ' . $dbh->quote_identifier('groups') . ' LEFT JOIN user_group_map AS directmember ON directmember.group_id = id AND directmember.user_id = ? @@ -868,7 +868,7 @@ sub userDataToVars { AND directbless.user_id = ? AND directbless.isbless = 1 AND directbless.grant_type = ? - } . $dbh->sql_group_by('id'), + ' . $dbh->sql_group_by('id'), 'id', undef, ( $otheruserid, GRANT_DIRECT, $otheruserid, GRANT_REGEXP, @@ -877,12 +877,14 @@ sub userDataToVars { ); # Find indirect bless permission. - $query = qq{SELECT groups.id - FROM groups, group_group_map AS ggm + $query = 'SELECT groups.id + FROM ' + . $dbh->quote_identifier('groups') + . ', group_group_map AS ggm WHERE groups.id = ggm.grantor_id - AND ggm.member_id IN ($grouplist) + AND ggm.member_id IN (' . $grouplist . ') AND ggm.grant_type = ? - } . $dbh->sql_group_by('id'); + ' . $dbh->sql_group_by('id'); foreach (@{$dbh->selectall_arrayref($query, undef, (GROUP_BLESS))}) { # Merge indirect bless permissions into permission variable. diff --git a/extensions/BMO/Extension.pm b/extensions/BMO/Extension.pm index d30d83317..7350fbd79 100644 --- a/extensions/BMO/Extension.pm +++ b/extensions/BMO/Extension.pm @@ -1477,7 +1477,9 @@ sub install_update_db { # if there are no groups, then we're creating a database from scratch # and there's nothing to migrate - my ($group_count) = $dbh->selectrow_array("SELECT COUNT(*) FROM groups"); + my ($group_count) + = $dbh->selectrow_array( + "SELECT COUNT(*) FROM " . $dbh->quote_identifier('groups')); if ($group_count) { # Migrate old product_sec_group mappings from the time this change was made diff --git a/extensions/BMO/lib/Reports/Groups.pm b/extensions/BMO/lib/Reports/Groups.pm index 52fdffa94..135a99ae6 100644 --- a/extensions/BMO/lib/Reports/Groups.pm +++ b/extensions/BMO/lib/Reports/Groups.pm @@ -37,7 +37,7 @@ sub admins_report { my $query = " SELECT groups.id, " . $dbh->sql_group_concat('profiles.userid', "','", 1) . " - FROM groups + FROM " . $dbh->quote_identifier('groups') . " LEFT JOIN user_group_map ON user_group_map.group_id = groups.id AND user_group_map.isbless = 1 diff --git a/extensions/SecureMail/Extension.pm b/extensions/SecureMail/Extension.pm index f6ea127fc..42700dec1 100644 --- a/extensions/SecureMail/Extension.pm +++ b/extensions/SecureMail/Extension.pm @@ -77,8 +77,10 @@ BEGIN { sub _group_secure_mail { return $_[0]->{'secure_mail'}; } sub _securemail_groups { - return Bugzilla->dbh->selectcol_arrayref( - "SELECT name FROM groups WHERE secure_mail = 1") // []; + my $dbh = Bugzilla->dbh; + return $dbh->selectcol_arrayref('SELECT name FROM ' + . $dbh->quote_identifier('groups') + . ' WHERE secure_mail = 1') // []; } # We want to lazy-load the public_key. diff --git a/qa/config/generate_test_data.pl b/qa/config/generate_test_data.pl index a452a7a74..c5ce33034 100644 --- a/qa/config/generate_test_data.pl +++ b/qa/config/generate_test_data.pl @@ -612,7 +612,9 @@ if (!Bugzilla::Group->new({name => $group_name})) { if (!Bugzilla::Group->new({name => $group_name})) { $dbh->do( - 'INSERT INTO groups (name, description, isbuggroup, isactive) + 'INSERT INTO ' + . $dbh->quote_identifier('groups') + . ' (name, description, isbuggroup, isactive) VALUES (?, ?, 1, 1)', undef, ($group_name, $group_desc) ); } diff --git a/sanitycheck.cgi b/sanitycheck.cgi index c46578a30..ddab63bf9 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -124,7 +124,7 @@ if ($cgi->param('createmissinggroupcontrolmapentries')) { # Find all group/product combinations used for bugs but not set up # correctly in group_control_map my $invalid_combinations = $dbh->selectall_arrayref( - qq{ SELECT bugs.product_id, + "SELECT bugs.product_id, bgm.group_id, gcm.membercontrol, groups.name, @@ -132,15 +132,14 @@ if ($cgi->param('createmissinggroupcontrolmapentries')) { FROM bugs INNER JOIN bug_group_map AS bgm ON bugs.bug_id = bgm.bug_id - INNER JOIN groups + INNER JOIN " . $dbh->quote_identifier('groups') . " ON bgm.group_id = groups.id INNER JOIN products ON bugs.product_id = products.id LEFT JOIN group_control_map AS gcm ON bugs.product_id = gcm.product_id AND bgm.group_id = gcm.group_id - WHERE COALESCE(gcm.membercontrol, $na) = $na - } + WHERE COALESCE(gcm.membercontrol, $na) = $na " . $dbh->sql_group_by( 'bugs.product_id, bgm.group_id', 'gcm.membercontrol, groups.name, products.name' @@ -373,7 +372,7 @@ if ($cgi->param('remove_old_whine_targets')) { my $old_ids = $dbh->selectcol_arrayref( "SELECT DISTINCT mailto FROM whine_schedules - LEFT JOIN $table + LEFT JOIN " . $dbh->quote_identifier($table) . " ON $table.$col = whine_schedules.mailto WHERE mailto_type = $type AND $table.$col IS NULL" ); @@ -448,13 +447,14 @@ sub CrossCheck { Status('cross_check_from', {table => $refertable, field => $referfield}); my $query - = qq{SELECT DISTINCT $refertable.$referfield} - . ($keyname ? qq{, $refertable.$keyname } : q{}) - . qq{ FROM $refertable - LEFT JOIN $table + = "SELECT DISTINCT $refertable.$referfield" + . ($keyname ? ", $refertable.$keyname " : "") + . " FROM " + . $dbh->quote_identifier($refertable) . " + LEFT JOIN " . $dbh->quote_identifier($table) . " ON $refertable.$referfield = $table.$field WHERE $table.$field IS NULL - AND $refertable.$referfield IS NOT NULL}; + AND $refertable.$referfield IS NOT NULL"; my $sth = $dbh->prepare($query); $sth->execute; @@ -675,14 +675,14 @@ sub DoubleCrossCheck { qq{ SELECT DISTINCT $refertable.$referfield1, $refertable.$referfield2 } - . ($keyname ? qq{, $refertable.$keyname } : q{}) . qq{ FROM $refertable - LEFT JOIN $table + . ($keyname ? qq{, $refertable.$keyname } : q{}) . " FROM $refertable + LEFT JOIN " . $dbh->quote_identifier($table) . " ON $refertable.$referfield1 = $table.$field1 AND $refertable.$referfield2 = $table.$field2 WHERE $table.$field1 IS NULL AND $table.$field2 IS NULL AND $refertable.$referfield1 IS NOT NULL - AND $refertable.$referfield2 IS NOT NULL} + AND $refertable.$referfield2 IS NOT NULL" ); foreach my $check (@$d_cross_check) { @@ -969,7 +969,7 @@ BugCheck( "bugs INNER JOIN group_control_map ON bugs.product_id = group_control_map.product_id - INNER JOIN groups + INNER JOIN " . $dbh->quote_identifier('groups') . " ON group_control_map.group_id = groups.id LEFT JOIN bug_group_map ON bugs.bug_id = bug_group_map.bug_id @@ -1015,7 +1015,7 @@ foreach my $target (['groups', 'id', MAILTO_GROUP], my $old = $dbh->selectall_arrayref( "SELECT whine_schedules.id, mailto FROM whine_schedules - LEFT JOIN $table + LEFT JOIN " . $dbh->quote_identifier($table) . " ON $table.$col = whine_schedules.mailto WHERE mailto_type = $type AND $table.$col IS NULL" ); diff --git a/scripts/convert_datetime.pl b/scripts/convert_datetime.pl index 4da065974..5cbcdc6c1 100755 --- a/scripts/convert_datetime.pl +++ b/scripts/convert_datetime.pl @@ -112,7 +112,9 @@ foreach my $row (@$rows) { $bugs_dbh->bz_add_column($table, $column, $column_info); # Migrate the PST value to UTC - $bugs_dbh->do("UPDATE $table SET $column = CONVERT_TZ(" + $bugs_dbh->do("UPDATE " + . $bugs_dbh->quote_identifier($table) + . " SET $column = CONVERT_TZ(" . $column . '_pst' . ", 'America/Los_Angeles', 'UTC')"); diff --git a/scripts/generate_bmo_data.pl b/scripts/generate_bmo_data.pl index 886f6680b..f4d748b24 100755 --- a/scripts/generate_bmo_data.pl +++ b/scripts/generate_bmo_data.pl @@ -400,7 +400,9 @@ foreach my $group (@groups) { my $new_group; if (exists $group->{no_admin} && $group->{no_admin}) { $dbh->do( - 'INSERT INTO groups (name, description, isbuggroup, isactive) + 'INSERT INTO ' + . $dbh->quote_identifier('groups') + . ' (name, description, isbuggroup, isactive) VALUES (?, ?, 1, 1)', undef, ($group->{name}, $group->{description}) ); diff --git a/scripts/movecomponent-1565636.pl b/scripts/movecomponent-1565636.pl index 637a4dedd..4f305c316 100755 --- a/scripts/movecomponent-1565636.pl +++ b/scripts/movecomponent-1565636.pl @@ -284,10 +284,16 @@ sub fix_flags { = $dbh->selectcol_arrayref( "SELECT DISTINCT type_id FROM $table WHERE component_id = ?", undef, $component_obj->id); - $dbh->do("DELETE FROM $table WHERE component_id = ?", undef, $component_obj->id); + $dbh->do( + 'DELETE FROM ' . $dbh->quote_identifier($table) . ' WHERE component_id = ?', + undef, $component_obj->id); foreach my $type_id (@$type_ids) { $dbh->do( - "INSERT INTO $table (type_id, product_id, component_id) VALUES (?, ?, ?)", - undef, ($type_id, $new_product_obj->id, $component_obj->id)); + 'INSERT INTO ' + . $dbh->quote_identifier($table) + . ' (type_id, product_id, component_id) VALUES (?, ?, ?)', + undef, + ($type_id, $new_product_obj->id, $component_obj->id) + ); } } diff --git a/scripts/remove-non-public-data.pl b/scripts/remove-non-public-data.pl index d6d0ee93f..fcda7652a 100755 --- a/scripts/remove-non-public-data.pl +++ b/scripts/remove-non-public-data.pl @@ -234,7 +234,9 @@ foreach my $table (@tables) { } if (@drop_columns) { print "dropping columns from $table\n"; - $dbh->do("ALTER TABLE $table " . join(", ", @drop_columns)); + $dbh->do('ALTER TABLE ' + . $dbh->quote_identifier($table) . ' ' + . join(", ", @drop_columns)); } } else { @@ -280,7 +282,9 @@ sub drop_referencing { } foreach my $fk (@{$dbh->selectall_arrayref($sql, {Slice => {}}, @values)}) { print " dropping fk $fk->{table}.$fk->{name}\n"; - $dbh->do("ALTER TABLE $fk->{table} DROP FOREIGN KEY $fk->{name}"); + $dbh->do("ALTER TABLE " + . $dbh->quote_identifier($fk->{table}) + . " DROP FOREIGN KEY $fk->{name}"); } # drop indexes @@ -296,11 +300,15 @@ sub drop_referencing { foreach my $fk (@{$dbh->selectall_arrayref($sql, {Slice => {}}, @values)}) { if ($fk->{ref}) { print " dropping fk $fk->{table}.$fk->{name}\n"; - $dbh->do("ALTER TABLE $fk->{table} DROP FOREIGN KEY $fk->{name}"); + $dbh->do("ALTER TABLE " + . $dbh->quote_identifier($fk->{table}) + . " DROP FOREIGN KEY $fk->{name}"); } else { print " dropping index $fk->{table}.$fk->{name}\n"; - $dbh->do("ALTER TABLE $fk->{table} DROP INDEX $fk->{name}"); + $dbh->do("ALTER TABLE " + . $dbh->quote_identifier($fk->{table}) + . " DROP INDEX $fk->{name}"); } } @@ -310,7 +318,9 @@ sub drop_referencing { {Slice => {}}, $column); foreach my $fk (@$rows) { print " dropping index $fk->{Table}.$fk->{Key_name}\n"; - $dbh->do("ALTER TABLE $fk->{Table} DROP INDEX $fk->{Key_name}"); + $dbh->do("ALTER TABLE " + . $dbh->quote_identifier($fk->{table}) + . " DROP INDEX $fk->{Key_name}"); } } diff --git a/scripts/sanitizeme.pl b/scripts/sanitizeme.pl index fa68f0a8e..60cd9559a 100755 --- a/scripts/sanitizeme.pl +++ b/scripts/sanitizeme.pl @@ -197,26 +197,34 @@ sub delete_security_groups { # Delete all security groups. print "Deleting " . ($keep_group_bugs ? 'non-' : '') . "security groups...\n"; - $dbh->do( - "DELETE user_group_map FROM groups JOIN user_group_map ON groups.id = user_group_map.group_id WHERE groups.isbuggroup = 1" + $dbh->do("DELETE user_group_map FROM " + . $dbh->quote_identifier('groups') + . " JOIN user_group_map ON groups.id = user_group_map.group_id WHERE groups.isbuggroup = 1" ); - $dbh->do( - "DELETE group_group_map FROM groups JOIN group_group_map ON (groups.id = group_group_map.member_id OR groups.id = group_group_map.grantor_id) WHERE groups.isbuggroup = 1" + $dbh->do("DELETE group_group_map FROM " + . $dbh->quote_identifier('groups') + . " JOIN group_group_map ON (groups.id = group_group_map.member_id OR groups.id = group_group_map.grantor_id) WHERE groups.isbuggroup = 1" ); - $dbh->do( - "DELETE group_control_map FROM groups JOIN group_control_map ON groups.id = group_control_map.group_id WHERE groups.isbuggroup = 1" + $dbh->do("DELETE group_control_map FROM " + . $dbh->quote_identifier('groups') + . " JOIN group_control_map ON groups.id = group_control_map.group_id WHERE groups.isbuggroup = 1" ); - $dbh->do( - "UPDATE flagtypes LEFT JOIN groups ON flagtypes.grant_group_id = groups.id SET grant_group_id = NULL WHERE groups.isbuggroup = 1" + $dbh->do("UPDATE flagtypes LEFT JOIN " + . $dbh->quote_identifier('groups') + . " ON flagtypes.grant_group_id = groups.id SET grant_group_id = NULL WHERE groups.isbuggroup = 1" ); - $dbh->do( - "UPDATE flagtypes LEFT JOIN groups ON flagtypes.request_group_id = groups.id SET request_group_id = NULL WHERE groups.isbuggroup = 1" + $dbh->do("UPDATE flagtypes LEFT JOIN " + . $dbh->quote_identifier('groups') + . " ON flagtypes.request_group_id = groups.id SET request_group_id = NULL WHERE groups.isbuggroup = 1" ); if ($keep_group_bugs) { - $dbh->do("DELETE FROM groups WHERE isbuggroup = 1 AND id $keep_group_bugs_sql"); + $dbh->do("DELETE FROM " + . $dbh->quote_identifier('groups') + . " WHERE isbuggroup = 1 AND id $keep_group_bugs_sql"); } else { - $dbh->do("DELETE FROM groups WHERE isbuggroup = 1"); + $dbh->do( + "DELETE FROM " . $dbh->quote_identifier('groups') . " WHERE isbuggroup = 1"); } } diff --git a/userprefs.cgi b/userprefs.cgi index 3f9a3600a..d7a5eb146 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -533,18 +533,19 @@ sub DoPermissions { my (@has_bits, @set_bits); my $groups - = $dbh->selectall_arrayref( - "SELECT DISTINCT name, description FROM groups WHERE id IN (" + = $dbh->selectall_arrayref('SELECT DISTINCT name, description FROM ' + . $dbh->quote_identifier('groups') + . ' WHERE id IN (' . $user->groups_as_string - . ") ORDER BY name"); + . ') ORDER BY name'); foreach my $group (@$groups) { my ($nam, $desc) = @$group; push(@has_bits, {"desc" => $desc, "name" => $nam}); } $groups = $dbh->selectall_arrayref( 'SELECT DISTINCT id, name, description - FROM groups - ORDER BY name' + FROM ' . $dbh->quote_identifier('groups') . ' + ORDER BY name' ); foreach my $group (@$groups) { my ($group_id, $nam, $desc) = @$group; diff --git a/whine.pl b/whine.pl index 0d65d7b4a..14c77b869 100755 --- a/whine.pl +++ b/whine.pl @@ -250,7 +250,8 @@ sub get_next_event { } } elsif ($mailto_type == MAILTO_GROUP) { - my $sth = $dbh->prepare("SELECT name FROM groups " . "WHERE id=?"); + my $sth = $dbh->prepare( + 'SELECT name FROM ' . $dbh->quote_identifier('groups') . ' WHERE id = ?'); $sth->execute($mailto); my $groupname = $sth->fetch->[0]; my $group_id = Bugzilla::Group::ValidateGroupName($groupname, $owner);