Bug Report #1049

Database::list_fields returns incorrect results for multiple databases which share table names

Added by wvoq - about 3 years ago. Updated over 2 years ago.

Status:Closed Start date:
Priority:High Due date:
Assignee:Jeremy Bush % Done:

0%

Category:Libraries:Database
Target version:2.3.3
Resolution:fixed Points:

Description

If you have two databases which both use MySQL driver and both have a table named 'foo' then:

Database::instance('db1')->list_fields('foo');

returns correctly, but

Database::instance('db2')->list_fields('foo');

will always return the contents of the previous call, regardless of the structure of db2.foo.

This is because list_fields() uses the table name as the cache key in a static var $tables.

Mysql.php.patch (2 kB) wvoq -, 01/12/2009 02:32 pm

no_static.patch - Untested patch to stop using static within driver instance method (2.3 kB) Chris Bandy, 01/16/2009 05:26 pm

0001-Introspection-is-specific-to-a-config-group.-Cache-p.patch - This patch is against branch/2.4 and depends on #1064 (7.2 kB) Chris Bandy, 02/27/2009 03:51 pm


Related issues

related to Kohana v2.x - Bug Report #1236: Database introspection is cached when caching is disabled 2.3.3 Closed
related to Kohana v2.x - Bug Report #1220: Database caching breaks when using two databases with the... 2.3.3 Closed

History

Updated by wvoq - about 3 years ago

P.S. A similar problem occurs with list_tables()

Updated by wvoq - about 3 years ago

Suggested fix (using hostname and db name as part of the cache key):

public function list_tables(Database $db)
{
    static $tables;

    $key = $this->db_config['connection']['host'].'/'.$this->db_config['connection']['database'];

    if (empty($tables[$key]) AND $query = $db->query('SHOW TABLES FROM '.$this->escape_table($this->db_config['connection']['database'])))
    {
        $tables[$key] = array();

        foreach ($query->result(FALSE) as $row)
        {
            $tables[$key][] = current($row);
        }
    }

    return $tables[$key];
}
public function list_fields($table)
{
    static $tables;

    $key = $this->db_config['connection']['host'].'/'.$this->db_config['connection']['database'].'/'.$table;

    if (empty($tables[$key]))
    {
        foreach ($this->field_data($table) as $row)
        {
            // Make an associative array
            $tables[$key][$row->Field] = $this->sql_type($row->Type);

            if ($row->Key === 'PRI' AND $row->Extra === 'auto_increment')
            {
                // For sequenced (AUTO_INCREMENT) tables
                $tables[$key][$row->Field]['sequenced'] = TRUE;
            }

            if ($row->Null === 'YES')
            {
                // Set NULL status
                $tables[$key][$row->Field]['null'] = TRUE;
            }
        }
    }

    if (!isset($tables[$key]))
        throw new Kohana_Database_Exception('database.table_not_found', $table);

    return $tables[$key];
}

Updated by Jeremy Bush about 3 years ago

If you have a fix, please attach a patch containing the fix, not raw code. Please read ticket submission guidelines: [TicketSubmissionGuidelines]

Updated by wvoq - about 3 years ago

Sorry! Patch attached. (I just used "diff -u", please let me know if that's not right.)

Updated by Chris Bandy about 3 years ago

Wow. I just did some testing and it looks like static variables within class methods are shared with the class. Wow.

PHP 5.2.8-pl1-gentoo (cli) (built: Jan  9 2009 18:33:09)
Copyright (c) 1997-2008 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2008 Zend Technologies

<?php

class cb_Class {
        function increment() {
                static $x;
                $x += 1;
                return $x;
        }
}

$first = new cb_Class;
$second = new cb_Class;

echo $first->increment() ."\n";
echo $second->increment() ."\n";

?>

Results in

1
2

Updated by Chris Bandy about 3 years ago

Old, but PHP response is that this behaviour is correct...

http://bugs.php.net/bug.php?id=16245

Updated by Jeremy Bush about 3 years ago

Yes, it's well known and documented that static vars are shared between all instances. That's is expected behavior, but this ticket is still a valid bug.

Updated by Chris Bandy about 3 years ago

Regarding my above patch: Database_Driver instances are tied to Database instances. Information like table and field names should not be shared between Database_Driver instances.

Since static variables are actually class variables, table and field names are should be cached per instance in an instance variable.

Updated by John Heathco almost 3 years ago

Is this a bug in all database drivers?

Updated by wvoq - almost 3 years ago

The bug is in all drivers except Mysqli, for which list_fields() seems to work differently (it doesn't take a table name as argument).

Updated by Chris Bandy almost 3 years ago

#1062 has patches which specifically bypass the class attributes when implementing a method to clear the cache.

Updated by Bob - almost 3 years ago

I think #577 #1049 #1220 should be merged into one ticket, they all concern caching strategy on Database drivers.

Updated by Chris Bandy almost 3 years ago

This bug also relates directly to #1236.

Updated by Jeremy Bush over 2 years ago

  • Status changed from New to Closed
  • Resolution set to fixed

This was fixed with #1236 and #1220.

Also available in: Atom PDF