]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1885709: Support for MySQL 8 (#106)
authorDave Miller <justdave@bugzilla.org>
Sun, 17 Mar 2024 18:08:48 +0000 (14:08 -0400)
committerGitHub <noreply@github.com>
Sun, 17 Mar 2024 18:08:48 +0000 (14:08 -0400)
* Cherry-pick BMO MySQL 8 fix into Harmony

* update docker image to use mysql8 libraries

---------

Co-authored-by: dklawren <dklawren@users.noreply.github.com>
35 files changed:
Bugzilla/Bug.pm
Bugzilla/DB.pm
Bugzilla/DB/Mysql.pm
Bugzilla/DB/Schema.pm
Bugzilla/DB/Schema/Mysql.pm
Bugzilla/DB/Schema/Sqlite.pm
Bugzilla/Group.pm
Bugzilla/Install/DB.pm
Bugzilla/Migrate.pm
Bugzilla/Object.pm
Bugzilla/Product.pm
Bugzilla/Search.pm
Bugzilla/User.pm
Dockerfile
Dockerfile.bmo-slim
Dockerfile.cpanfile
Makefile.PL
cpanfile
docker-compose.test.yml
docker-compose.yml
editgroups.cgi
editproducts.cgi
editusers.cgi
extensions/BMO/Extension.pm
extensions/BMO/lib/Reports/Groups.pm
extensions/SecureMail/Extension.pm
qa/config/generate_test_data.pl
sanitycheck.cgi
scripts/convert_datetime.pl
scripts/generate_bmo_data.pl
scripts/movecomponent-1565636.pl
scripts/remove-non-public-data.pl
scripts/sanitizeme.pl
userprefs.cgi
whine.pl

index 15b10f3fa548978f935487aeb7fa94b776421c27..1a70e04627684773857dcddb2be3dd8fcd4db277 100644 (file)
@@ -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 = ?
index 417659b2032eb752e5e2ded2bf65ffde431fd36e..f5407f9f57bda75ca5655fc3c42690ca1d12585a 100644 (file)
@@ -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
       );
index 484c1355e4527cddab8210662b5d3a8ee8d3546a..5d744b0bd8d43d620ae78be433dea1f9f2ad1f52 100644 (file)
@@ -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);
   }
 }
index bc14d18f4b17a16277f9bf1cd09fffa0810cb0a6..dc824107df608f2e074f50f631e7dd31ccdeb83c 100644 (file)
@@ -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<get_drop_table_ddl($table)>
@@ -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<delete_table($name)>
index 39c1fc45ee902eafdbbd95aad51c7517f3fba217..02526887df9e5bb2501646eca16c560a98de496f 100644 (file)
@@ -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 {
index 81cd8c2ea8cb85bc2bbb19c3c1ec868d2f5c17fa..f5935a2fd768888fa213b32426c7325ce6b3e886 100644 (file)
@@ -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",
index 362192f82ec7470aac14317b17626e55c8ff9353..a68939aeba4fe05c299dc839144dbdbd1b1b8c8e 100644 (file)
@@ -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) {
index dedadd279242a47d500333b06b373136f63c3740..dd5de66883926b686ae61f74022503eafc0c9b63 100644 (file)
@@ -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 _<n> 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 {
index a84bc5789dbced2afbead62e24b979f51cf40ca8..57472d8212b9dab80556340e8c9a883e9a5f1a95 100644 (file)
@@ -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
+  );
 }
 
 ######################
index 0132682c6580989cc4fb8bda34e13f8f430a1c81..12debf40bbeaf97e0f560f6cb1078cc70222c6c6 100644 (file)
@@ -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);
index 7e3105ad2da615db957aaf8865804234a8f2b02b..e7438d73c88201b83e415a706e560d403d07c539 100644 (file)
@@ -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
   );
index 7afcaf77bf7a1e4bfd51be6af6ccfbde3055f6e6..2d36b9440c2730655480296bbe0c400e7defdb78 100644 (file)
@@ -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') {
index 91400de57a8b58d7687bc5e5bbf15b4944801993..d98c3f3fb9efe721f59d7785fc4b220f3ec68b96 100644 (file)
@@ -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 = ?
index 653c48fe5a2eef7d77fd9ccb0b6355cf6ef0ea58..9a39eacc714ec9f0198b56ec428b112a825bfbf6 100644 (file)
@@ -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
 
index 15c93afdd04800776a1979d53dd9d1e9eabe7f12..b62ad7d1638be4fa85f37539ce3b95a4e9be3270 100644 (file)
@@ -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/*
index c5c0ad842cac0df34f37717c176ba4867ec464e4..66a93aab0a0591fed0ec44835fd38b789c3d5a6d 100644 (file)
@@ -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
index b8f953b6933a07cabfe92c784393065d45df10e3..6a0ca232084702ddd1cd679206209a21fb45afa5 100755 (executable)
@@ -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)
 
index 37e6c1bb750b70eef6eab6a6775fedc34c5381d1..3b47b5846e996d581fbd8ca330a99a2ace455e8e 100644 (file)
--- 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';
index de60c9589485be86a54f7dc4561384aa6d551baa..923d1b096933bd7ff6280d2e59e51a6aea0cd22d 100644 (file)
@@ -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:
index 2bfedb8955a553213f44f0eacdf2c61c16f8b4e1..5acb0cd194ab09700ba0d5097348df0203649a4b 100644 (file)
@@ -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
index 91bf2d9ef94893f6dffa949a3b1769c75dc89abe..28f0ad2da2cd15f077ae3dab6b8f618b20bb761e 100755 (executable)
@@ -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");
index 97254489249d2699c228aa1cc555262216b7dfb0..c2dc8893b19eb56849ec8574eef7710b3faefc63 100755 (executable)
@@ -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
       );
index 00cfb4bb40cd03f9228e6d96ce2fd8e7141ec993..1a5b24979c5111fae0e659dbc68fc7082c86dfd6 100755 (executable)
@@ -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.
index d30d83317309946de2a74aca553e1a447c5dfcf9..7350fbd7978a91708206214a10703b9fa11a7acf 100644 (file)
@@ -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
index 52fdffa94829a3fc9505219764dab5b9413abe6f..135a99ae6a096655e2ae6ad8f36777707261fe55 100644 (file)
@@ -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
index f6ea127fcfc4db2d73d5ed7ee3967ebbb5cf735f..42700dec15d389c9c60d9c36808336af114f3496 100644 (file)
@@ -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.
index a452a7a74675475c48f90deba905ffb04b54672e..c5ce33034660fdb8bb4cebcd6ede8133c57bd7ae 100644 (file)
@@ -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)
   );
 }
index c46578a30bf64b36c8d1733c74d18546bf922286..ddab63bf9126fad39aec15ad75aa2175ed02bdca 100755 (executable)
@@ -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"
   );
index 4da0659747a9c684bca43bbcd3041915087777b3..5cbcdc6c1f6265b5cf034c00d693b2eb0dea6a67 100755 (executable)
@@ -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')");
 
index 886f6680b5222ffe184c4f3dd88fbe81823da431..f4d748b2466b886d8373cc2962bd3c90e94288f5 100755 (executable)
@@ -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})
       );
index 637a4dedd58013fdefeb8c5fe1a66922bd21df17..4f305c316a702705af4add86c65e828a4aa82b0c 100755 (executable)
@@ -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)
+    );
   }
 }
index d6d0ee93f2bd6190c0281437794b76041ea1a879..fcda7652a72c635abfbc83bc64649ec10eafac82 100755 (executable)
@@ -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}");
     }
   }
 
index fa68f0a8e2d04c9cf366eb53d833c563ddf5c6af..60cd9559ac3cadfd734a1cdd7b55b1491cd413af 100755 (executable)
@@ -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");
   }
 }
 
index 3f9a3600aca65f40bf012c97b9c551b7d07582cf..d7a5eb146ac812718cba6565c2a12e1f7b1c5d76 100755 (executable)
@@ -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;
index 0d65d7b4a6e77cedf6528403d5ddcf9be550c244..14c77b8690c3005e8ba129f50bc3f66708f957de 100755 (executable)
--- 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);