Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: double prefixing with db_prefix array #52

Closed
dreaming-augustin opened this issue Jun 23, 2020 · 5 comments
Closed

Bug: double prefixing with db_prefix array #52

dreaming-augustin opened this issue Jun 23, 2020 · 5 comments

Comments

@dreaming-augustin
Copy link

Hello,

This commit:
078a90b
introduced a critical bug affecting sites where $db_prefix (set in settings.php) is an array.

For example:

    $db_prefix = array(
      'default'   => 'default_',
      'users'     => 'www_',
      'sessions'  => 'www_',
      'mytable'  => 'custom_',
    );     

In function db_prefix_tables($sql) (includes/database.inc):

    if (array_key_exists('default', $db_prefix)) {
      $tmp = $db_prefix;
      unset($tmp['default']);

      foreach ($tmp as $key => $val) {
        $sql = strtr($sql, array('{' . $key . '}' => '{' . $val . $key . '}'));
      }
      $sql = strtr($sql, array('{' => '{' . $db_prefix['default']));
    }

The above code will turn {mytable} to default_custom_mytable instead of the expected custom_mytable.

The critical line is this one:

$sql = strtr($sql, array('{' . $key . '}' => '{' . $val . $key . '}'));

which introduces back curly brackets, which triggers the next strstr() command to add the default prefix on top!

The fix is to revert that line to what it was before:

$sql = strtr($sql, array('{' . $key . '}' => $val . $key ));

I don't know what motivated changing that particular line, but looking at the whole commit, it does not seem necessary to change this line to achieve the intended overall purpose, which was to add backquotes for MySQL 8 compatibility purposes.

@ylp-ineris
Copy link
Contributor

Hello,

The same problem occured on a website I maintain during an update. Sharing the user table, the impact is important as nobody can authenticate after the update.

As this kind of settings (mutliple prefix) is not so often met, I understand it's just a regression introduced while making the CMS compatible with MySQL 8+ which ask to escape its keywords.

Here is a patch I developed and that works for the website I maintain:
double_table_name_prefixing_fix.zip

@millenniumtree
Copy link

millenniumtree commented Aug 26, 2021

Now it's removing pipes from some string arguments in a query of mine. Not the best way to solve this. ;D

I've replaced the lower part of the function with the following, which I believe fixes the original issue, while not breaking pipes in the rest of the query.

  // Add prefixes and transform curly braces into brace/pipes in order to mark already done operations
  // while preserving table name encapsulation for the next step.
  foreach ($db_prefix_local as $table_name => $prefix) {
    $sql = strtr($sql, array('{' . $table_name . '}' => '{|' . $prefix . $table_name . '|}'));
  }
  $sql = strtr($sql, array('{' => '{|' . $default_replacement, '}' => '|}'));

  // For MySQL 8, escape table names corresponding to reserved keywords.
  if (db_version_compare('mysql', '8.0.0')) {
    foreach ($db_mysql8_reserved_keywords as $keyword) {
      $sql = str_replace('{|' . $keyword . '|}', '`' . $keyword . '`', $sql);
    }
  }

  // Remove curly brace/pipes.
  return strtr($sql, array('{|' => '', '|}' => ''));

@ylp-ineris
Copy link
Contributor

ylp-ineris commented Aug 27, 2021

Sorry for the regression.

For the fix, something totally different from/not including curly braces must be used as a replacement. If no, the second strtr will also modify previously modified curly braces.

Maybe triple pipes instead of single pipes? A quick search let me think it's not used in SQL (while double pipes may).
Other possibilities:

  1. find another good character to replace the single pipes
  2. (use regexp to match well the strings to replace - too heavy for that treatment, isn't it?)
  3. introduce a first strtr to double curly braces and then, reduce them to single curly braces in the other calls to strtr

@ylp-ineris
Copy link
Contributor

ylp-ineris commented Aug 27, 2021

Fix tried with the third solution proposed:

  // Double curly braces in order to be able to mark already done operations
  // while preserving table name encapsulation for the next step,
  // adding prefixes and then, escaping MySQL 8 reserved keywords.
  $sql = strtr($sql, array('{' => '{{' , '}' => '}}'));

  // Add prefixes.
  foreach ($db_prefix_local as $table_name => $prefix) {
    $sql = strtr($sql, array('{{' . $table_name . '}}' => '{' . $prefix . $table_name . '}'));
  }
  $sql = strtr($sql, array('{{' => '{' . $default_replacement, '}}' => '}'));

  // For MySQL 8, escape table names corersponding to reserved keywords.
  if (db_version_compare('mysql', '8.0.0')) {
    foreach ($db_mysql8_reserved_keywords as $keyword) {
      $sql = str_replace('{' . $keyword . '}', '`' . $keyword . '`', $sql);
    }
  }

  // Remove curly braces.
  return str_replace(array('{', '}'), '', $sql);

And the corresponding patch:
db_prefix_tables_fix.zip

Haven't tried it but seems good...

@millenniumtree
Copy link

millenniumtree commented Aug 28, 2021 via email

omega8cc pushed a commit to omega8cc/pressflow6 that referenced this issue Sep 9, 2021
Trying to fix d6lts/drupal#52 : back to a single table name prefix while quoting all table names with backticks (and not just table names which are keywords in MySQL 8+)

Co-authored-by: yannick_le_pape_ineris <e-RT3ax{?KX'Hb]&dP*[KdS%-]%JVTCkxl}BJIf>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants