Table prefixes should be per database connection

David Stosik - November 26, 2007 - 14:57
Project:Drupal
Version:7.x-dev
Component:database system
Category:feature request
Priority:normal
Assigned:Unassigned
Status:needs work
Description

Hello,
I think this one can only go in 7.x, as 6.x code is frozen...

Currently, we can specify a prefix per table name, and a default one, in the settings.php file. For example, we can have

<?php

$db_url
= 'mysql://user:password@dbserver/db';

$db_prefix = 'drupal_';
?>

For a single prefix applied to all tables.
Or we can have
<?php

$db_url
= 'mysql://user:password@dbserver/db';

$db_prefix['users'] = '';
$db_prefix['default'] = 'drupal_';
?>

To prefix with 'drupal_' all tables, except users table.

The lack comes when one is using multiple databases, with one connection string for each. The prefix will then apply to all tables, whatever the current database is. For example, we can have the following:

<?php

$db_url
['default'] = 'mysql://user:password@dbserver/db';
$db_url['other'] = 'mysql://user2:password2@dbserver/other_db';

$db_prefix = 'drupal_';
?>
There, the prefix will apply to all tables of my two databases. There is not any mean to specify one prefix per database. And THIS is my problem, because, when using db_lock_table method on a table of my second database, its name gets prefixed by the prefix I am using on my Drupal database.
What I suggest is to put a new level in the db_prefix array, to become like this:
<?php

$db_url
['default'] = 'mysql://user:password@dbserver/db';
$db_url['other'] = 'mysql://user2:password2@dbserver/other_db';

$db_prefix['default']['default'] = 'drupal_'; // prefix all tables of my drupal database
$db_prefix['default']['users'] = ''; // don't prefix the users tables of my drupal database

$db_prefix['other']['default'] = 'other_';
?>

This would allow a better abstraction.

Please let me know what do you think of such a feature.

Thanks,
David

#1

David Stosik - November 30, 2007 - 08:55

Could anyone please give me his opinion on this subject?
Thanks,
David

#2

sambojambo - August 20, 2008 - 10:36

Hi David - I'm also having this issue. At this point I've only just started delving in to the problem, but sounds like a sensible feature request to me.

Sam

#3

Damien Tournoud - August 20, 2008 - 10:41

The multiple database connexion feature of Drupal is designed to allow module developers to query data outside of Drupal. In that case, the prefixing doesn't make much sense.

The best way to do this is for you to manually set $db_prefix after you db_set_active() call, and restore it afterwards.

#4

sambojambo - August 20, 2008 - 13:19

I'm posting this just as a simple example of how I hacked quickly round the problem. Might help someone else get on with the job.

<?php
function dbprefix_fix( $db_name )
{
  global
$db_prefix;

  if(
'other_db' == $db_name )
  {
   
$db_prefix = '';
   
db_set_active( 'other_db' );

  }else{
   
$db_prefix = array(
     
'default'         => 'surf_',
     
'users'           => 'shared_',
     
'sessions'        => 'shared_',
     
'role'            => 'shared_',
     
'authmap'         => 'shared_',
     
'sequences'       => 'shared_',
     
'profile_fields'  => 'shared_',
     
'profile_values'  => 'shared_',
     
'users_roles'     => 'shared_',
      );
   
db_set_active( 'default' );
  }
}
?>

#5

Damien Tournoud - August 20, 2008 - 13:50
Title:Table prefix per database URL» Table prefixes should be per database connection
Status:active» postponed

Agreed, table prefixes should be per database connection.

The DB:TNG patch #225450: Database Layer: The Next Generation is about to land, and will make this easy. Postponed until that.

#6

Damien Tournoud - August 20, 2008 - 14:06
Title:Table prefixes should be per database connection» [after DB:TNG] Table prefixes should be per database connection

#7

Crell - August 21, 2008 - 23:49
Title:[after DB:TNG] Table prefixes should be per database connection» Table prefixes should be per database connection
Status:postponed» active

DBTNG is in!

I am very much in favor of this. The way I'd suggest implementing it is to make 'prefix' another key in the $databases array:

<?php
$databases
['default']['default'] = array(
 
'type' => 'mysql',
 
'host' => 'locahost',
 
// ...
 
'prefix' => array('default' => 'foo_')
);
?>

The tricky part is that some modules do some really funky stuff with prefixing, including simpletest. So you'll have to be careful that simpletest doesn't break. Of course, simpletest may be able to work even better now if it can just establish its own database connection that defines its own prefixes... :-)

#8

Damien Tournoud - November 2, 2008 - 14:05
Status:active» needs review

Here is a full patch for this.

What this does:

- remove $db_prefix an re-implement it as a per connection 'prefix' key in the $databases array.
- add DatabaseConnection::(get|set)Prefix()
- add Database::renameConnection() and Database::removeConnection()
- change SimpleTest logic to take advantage of the new API:
- in setUp() : SimpleTest renames the default connection, then clones its parameters to create a new default connection with a prefix
- in tearDown() : SimpleTest removes the prefixed connection and rename the old default connection back
- moved simpletest prefix detection in conf_init()
- clean-up *a lot* install.php, by removing several unneeded references to $db_url (!!) and $db_prefix

This allowed me to find a bug in the database layer (#329080: Statics in objects are not per-instance). The fix in that other issue is required to make this work.

AttachmentSize
195410-prefix-per-connection.patch 25.76 KB
Testbed results
195410-prefix-per-connection.patchfailedFailed: Failed to apply patch. Detailed results

#9

Crell - November 3, 2008 - 05:43
Status:needs review» needs work

Visual inspection only, as I just reviewed the linked patch and CNW'ed it. :-)

- I'm not sure we need to give prefix its own array/string. Why not tell each connection its entire DB info structure and let it use that?

- Hooray for removing the evil regex hack from openConnection()! :-)

- if (preg_match("/^simpletest\d+/", $prefix = Database::getActiveConnection()->getPrefix())) { If the prefix is an array, that's going to break in exciting ways, isn't it?

- For performance, since we're pre-processing the info array anyway, how about we pre-process a string prefix into an array with one key, default? That should save us some is_array() calls later, and an array_key_exists(), probably. (Just assume that default always exists, and pre-set it to '' if not specified.)

- DatabaseTestCase::setUp() has some error logging code in it. Probably OK for now, but let's remember to remove it before committing.

And looking at this patch, I realized that with addConnection() we can now remove references to the global $databases array from the database API entirely and just assign the connections from $databases in conf_init(). Sweet! Probably a separate patch, though. :-)

#10

Damien Tournoud - February 15, 2009 - 18:34
Status:needs work» needs review

Rerolled and revamped version. Should take care of every remark from #9. Needs a complete review.

AttachmentSize
195416-prefix-per-connection.patch 24.72 KB
Testbed results
195416-prefix-per-connection.patchfailedFailed: Failed to install HEAD. Detailed results

#11

System Message - February 15, 2009 - 18:55
Status:needs review» needs work

The last submitted patch failed testing.

#12

Dave Reid - February 15, 2009 - 19:30
Status:needs work» needs review

Hmm...it appears that automatic testing of patches that change the db installer is a big fail. I've run across the same situation in #346494: DB drivers need to be able to change the configure database form during install.

#13

Crell - February 16, 2009 - 05:04
Status:needs review» needs work

Visual inspection of #10:

- There's a lot of git flotsam at the top of the file. Not sure if that's going to cause problems when applying or not.

- DatabaseConnection: "The default prefix used by that database connection". "This", not "That", since it's describing the self-same object. Ibid for non-default prefixes.

- Er, I don't get why we have default and non-default prefixes as separate properties? OK, I get it when I read down as far as setPrefix, but it should probably be a bit better documented.

- Ooo, prefixTables() gets so much shorter! :-)

<?php
$this
->setPrefix($connection_options['prefix'] ? $connection_options['prefix'] : '');
?>

That will throw a notice if $connection_options['prefix'] is not set at all. It needs an isset() or empty().

- There's a lot of changes in DrupalWebTestCase::tearDown() that don't look related to the DB prefixing at all. Are those really necessary here?

- The code removed from tearDown() includes the code that removes all of the simpletest-specific tables. I don't see that code replaced anywhere else, though. Simply removing the connection doesn't delete the tables (I would hope :-) ). So won't that leave a lot of testing tables lying about?

- The examples in default.settings.php don't quite make it clear that they're talking about the prefix key of a $databases entry. Perhaps expand the default samples of that, too?

Exciting!

#14

Damien Tournoud - February 16, 2009 - 06:47
Status:needs work» needs review

Thanks for the review, Crell.

- There's a lot of git flotsam at the top of the file. Not sure if that's going to cause problems when applying or not.

It's perfectly innocuous, and quite cool ;) This is really not the first GIT patch I submit here ;)

- DatabaseConnection: "The default prefix used by that database connection". "This", not "That", since it's describing the self-same object. Ibid for non-default prefixes.
- Er, I don't get why we have default and non-default prefixes as separate properties? OK, I get it when I read down as far as setPrefix, but it should probably be a bit better documented.

Changed that to this, and added a bit of documentation to defaultPrefix.

- Ooo, prefixTables() gets so much shorter! :-)

<?php
$this
->setPrefix($connection_options['prefix'] ? $connection_options['prefix'] : '');
?>

That will throw a notice if $connection_options['prefix'] is not set at all. It needs an isset() or empty().

Oups.

- There's a lot of changes in DrupalWebTestCase::tearDown() that don't look related to the DB prefixing at all. Are those really necessary here?
- The code removed from tearDown() includes the code that removes all of the simpletest-specific tables. I don't see that code replaced anywhere else, though. Simply removing the connection doesn't delete the tables (I would hope :-) ). So won't that leave a lot of testing tables lying about?

This is an artifact of trying to reroll a three-months old patch ;) I fixed those. When ignoring whitespace changes, the only differences are:

<?php
@@ -899,8 +907,8 @@ class DrupalWebTestCase {
    * and
reset the database prefix.
    */
   protected function
tearDown() {
-    global
$db_prefix, $user;
-    if (
preg_match('/simpletest\d+/', $db_prefix)) {
+    global
$user;
+
      
// Delete temporary files directory and reset files directory path.
      
file_unmanaged_delete_recursive(file_directory_path());
      
variable_set('file_directory_path', $this->originalFileDirectory);
@@ -
912,8 +920,9 @@ class DrupalWebTestCase {
        
db_drop_table($ret, $name);
       }

-     
// Return the database prefix to the original.
-      $db_prefix = $this->originalPrefix;
+   
// Get back to the original connection.
+    Database::removeConnection('default');
+   
Database::renameConnection('simpletest_original_default', 'default');

      
// Return the user to the original one.
      
$user = $this->originalUser;
@@ -
937,7 +946,6 @@ class DrupalWebTestCase {
      
// Close the CURL handler.
      
$this->curlClose();
-    }
   }
?>

- The examples in default.settings.php don't quite make it clear that they're talking about the prefix key of a $databases entry. Perhaps expand the default samples of that, too?

I extended them a bit and added a 'prefix' to most of the example $databases.

AttachmentSize
195416-prefix-per-connection.patch 25.86 KB
Testbed results
195416-prefix-per-connection.patchfailedFailed: Failed to install HEAD. Detailed results

#15

System Message - February 16, 2009 - 06:55
Status:needs review» needs work

The last submitted patch failed testing.

#16

Damien Tournoud - March 6, 2009 - 18:35
Status:needs work» needs review

Rerolled (some conflicts since chx' update.php patch went in).

AttachmentSize
195416-prefix-per-connection.patch 28.12 KB
Testbed results
195416-prefix-per-connection.patchfailedFailed: Failed to install HEAD. Detailed results

#17

System Message - March 6, 2009 - 18:45
Status:needs review» needs work

The last submitted patch failed testing.

#18

Josh Waihi - March 11, 2009 - 22:14

subscribing. #140860: Consistent table names and database handling of table names will be directly related to this

 
 

Drupal is a registered trademark of Dries Buytaert.