Currently when calling db_find_tables() it calls Database::getConnection() without any parameters, thus always queries the default database. The same is also true of the buildTableNameCondition function which is used by findTables(). Also, neither respect db_set_active().

The attached patch makes it possible to pass in a database target when calling db_find_tables(), allowing modules to get a table listing from the non-default database.

This is required by the dba module, which is trying to provide phpmyadmin like functionality to Drupal admins for any database defined in settings.php and would like to do so using the core schema api in a database agnostic way.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Jeremy’s picture

FileSize
2.89 KB

I killed the attachment with a preview. Trying again.

Jeremy’s picture

FileSize
2.87 KB

New version of patch with --no-prefix option set.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

Looks proper to me.

Crell’s picture

Status: Reviewed & tested by the community » Needs work

This is an API change during alpha. The procedural wrappers are very deliberately intended for accessing the default database, and possibly alternate target using the $options array.

If you want to access other databases than the currently active one, call Database::getConnection() with the properties you want and get back the raw connection object and do everything off of that. That is by design.

Actually, looking at the patch I sort of see what you're doing here. You're using the old procedural crappy wrappers for schema that we never got around to removing. :-) I'd encourage you to not use those. The schema objects are already per-connection when they're created. See the DatabaseSchema constructor which accepts the connection to which it is bound as a parameter. The target has already been selected at that point. Calling any of the utility functions or Database:: from within one of the database objects is a bug.

This looks like a "by design" to me.

Jeremy’s picture

Category: feature » support
Status: Needs work » Active

I've clearly not wrapped my head fully around the new database layer, then. Can you provide an example of code that perform a findTables() on a database other than the default? I have been unable to perform this operation on a secondary database using the schema api.

Crell’s picture

$conn = Database::getConnection('slave', 'default');
$schema = $conn->schema();
$tables = $schema->findTables('foo');
// .. do other stuff here.

Really, for anything interesting or esoteric you want to just bypass the function wrappers entirely. They're just there to simplify the common case. If you're not using the common case, don't bother with them.

Note that you can also chain most of the factory methods, so the above could all be done in a single line. I broke it up so you can see what you're getting back, and you can then keep $conn or $schema around to reuse or pass around or whatever.

Also note that it is possible to have multiple slave servers defined. In that case, you will only get one of them back, and a random one each PHP request. That's by design to support multiple slave servers. If their schemas are different, though, there's a bigger problem. :-)

Jeremy’s picture

Sorry, it's not the target I'm trying to change but the key. I had the terminology backwards. What I'm trying to do I had originally assumed would look something like this:

<?php
  $db = db_set_active('extra');
  $tables = Database::getConnection('default', 'extra')->schema()->findTables('%');
  db_set_active($db);
?>

However this returns the tables from my ('default', 'default') database rather than from my ('default', 'extra') database. I seem to be missing something?

Crell’s picture

Oh! Well, that's because the schema in core is still based only on hook_schema(), not on any introspection of the database. hook_schema() is only for the Drupal-local database. It doesn't help with anything else.

Unfortunately we never got around to dynamically detected schemas. I'd love to do that in D8, but right now it's not possible. For MySQL there's the schema module, but I don't know what it's upgrade plans are.

Jeremy’s picture

Category: support » feature
Status: Active » Needs review
FileSize
826 bytes

The attached patch provides what I'm looking for. It's not an API change, and it allows getConnectionInfo() to use the self::$activeKey if set rather than assuming 'default'. With this patch applied, my above code snippet in #7 works as intended.

Crell’s picture

Title: Allow db_find_tables() to search the non-default database » Schema object calls Database::getConnectionInfo(), which is dumb
Category: feature » bug
Status: Needs review » Active

Just talked to Jeremy in IRC. Turns out the *real* problem is that buildTableNameCondition() is calling Database::getConnectionInfo(), which is wrong.

I think the correct solution here is to save the driver options array in the connection object constructor, and provide a method to access it. Then update that schema method to use that rather than calling getConnctionInfo() at all, since that's wrong either way.

When that's fixed, the code I listed above should work as it is supposed to. (Right now it won't.)

Adjusting issue info as appropriate.

mikeryan’s picture

For MySQL there's the schema module, but I don't know what it's upgrade plans are.

I've begun the D7 port of schema module - can't say when it'll be truly functional though.

Jeremy’s picture

Status: Active » Needs review
FileSize
3.05 KB

The attached implements what I believe you are describing. Currently I only updated the MySQL driver, but I can update the patch for the rest if I'm headed in the right direction here.

Crell’s picture

Status: Needs review » Needs work

Yep, that's the approach I was talking about.

1) I'd prefer to put the $this->connectionOptions = $connection_options line further up, right before the $dsn is set. That's when it's "done", so let's save it there before we start executing setup queries.

In fact, I'm wondering if we can push that up into the parent constructor. It's not passed right now, but perhaps it should be? Hm.

2) The docblock for the variable is slightly wrong. It's a processed version of the array from settings.php. I'd say something like "the connection information for this connection object".

3) connectionOptions() needs a docblock as well.

Thanks for attacking this!

Jeremy’s picture

Status: Needs work » Needs review
FileSize
3.35 KB

I thought I was going to need to patch the other database drivers too, but neither of them defines buildTableNameCondition() and calls Database::getConnectionInfo() like MySQL so this patch still only affects the parent class and the MySQL class. I addressed your feedback above, though I kept the definition of $this->connectionOptions in the MySQL constructor. If you want it moved into the parent class, I'd prefer a little more guidance as it's not clear to me how this should work.

Jeremy’s picture

Bump. Is there any likelihood of this getting merged? Is there anything further that needs to be done first?

It's blocking me on this issue:
#714968: Ability to select all databases defined in settings.php

Dries’s picture

Quick thoughts on the patch:

- All the other get-functions seem to start with getXXX?

- The difference between getConnectionInfo() and getConnectionOptions() (or connectionOptions()) might be confusing. At least, it is not self-explanatory.

- We should probably have a test for this?

Jeremy’s picture

Assigned: Unassigned » Jeremy
FileSize
6.31 KB

I have updated the function name to getConnectionOptions(), and added comments to differentiate between getConnectionInfo() and getConnectionOptions().

I have also added a new database connection test.

Updated patch attached.

Crell’s picture

If the operation itself is in the parent class, then it needs to work in all cases even if we don't happen to be using it yet. Otherwise, the postgres and sqlite drivers are broken.

I've attached a new patch that sets the connection information for the other 2 drivers as well, and tweaks the docblock to be clearer.

If the bot approves then this is good to go, but I cannot now mark it RTBC myself. :-) (Jeremy can, though!)

Jeremy’s picture

Status: Needs review » Reviewed & tested by the community

Great, thanks for that! It passed the tests, moving to RTBC.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Looks like all of Dries's feedback was addressed. Committed to HEAD, with some minor comment clean-ups in the tests for code standards conformance.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.