tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

Omlouváme se, provoz fóra byl ukončen

Navrh implementace getTables pro DibiPostgreDriver

před 11 lety

jm
Člen | 10

Davide,

pouzivam Postgres databazi a narazil jsem na to, ze metoda getTables() neni momentalne implementovana v DibiPostgreDriver. Coz takhle nejak?

public function getTables()
{
    $this->query('SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema()');
    $res = array();
    while ($row = $this->fetch(FALSE)) {
        $res[] = array('name' => $row[0]);
    }
    $this->free();
    return $res;
}

Snazil jsem se to napsat stejne jako DibiMysqlDriver->getTables().

Honza

před 11 lety

David Grudl
Nette Core | 6806

Rád přidám. Lze ještě nějak detekovat a zjistit views?

před 11 lety

jm
Člen | 10

David Grudl napsal(a):

Rád přidám. Lze ještě nějak detekovat a zjistit views?

Jasne. Tabulka information_schema.tables ma sloupce table_catalog, table_schema, table_name, table_type, self_referencing_column_name, reference_generation, user_defined_type_catalog, user_defined_type_schema, user_defined_type_name, is_insertable_into, is_typed, commit_action. Takze lze nalezt views napriklad takhle

SELECT table_name from information_schema.tables WHERE table_schema = current_schema() and table_type = 'VIEW';

před 11 lety

David Grudl
Nette Core | 6806

Takže tohle by mělo vrátit dva sloupce: name s názvem a view s indikátorem tabulky, jo?

SELECT table_name as name, table_type = 'VIEW' as view FROM information_schema.tables WHERE table_schema = current_schema()

před 11 lety

jm
Člen | 10

Ano spravne. Vyzkousel jsem to a prikaz funguje:

             name             | view
------------------------------+------
 timex_tracks                 | f
 tracks                       | f
 sessions                     | f
 laps                         | f
 session_route_view           | t
 routes_condensed_view        | t
 session_full_view            | t
 session_short_view           | t

před 11 lety

David Grudl
Nette Core | 6806

Ještě by to chtělo sloupec view přetypovat na integer. Dá se to nějak? Nebo lze zapsat třeba table_type = 'VIEW' ? 1 : 0

před 11 lety

jm
Člen | 10

Zda se mi lepsi pretypovani pomoic CAST (jinac by asi bylo treba pouzit CASE construkci):

SELECT table_name as name, CAST(table_type = 'VIEW' AS INTEGER) as view FROM information_schema.tables WHERE table_schema = current_schema();

před 11 lety

David Grudl
Nette Core | 6806

Mělo by to fungovat.

Podle vzoru DibiMySqliDriver můžeš ještě zkusit doplnit getColumns() a getIndexes().

před 11 lety

edke
Člen | 198

@jm: akurat riesim s dibi a postgres zrejme nieco podobne :)

David Grudl wrote:
Podle vzoru DibiMySqliDriver můžeš ještě zkusit doplnit getColumns() a getIndexes().

Dovolil som si vyskusat spravit ukazku, ako by mohla vyzerat metoda getColumns():

/**
 * Returns metadata for all columns in a table.
 * @param  string
 * @return array
 */
public function getColumns($table)
{
    // position of primary key
    $this->query("
        SELECT indkey
        FROM pg_index, pg_class
        WHERE pg_class.relname = '$table'  AND pg_class.oid = pg_index.indrelid  and pg_index.indisprimary
    ");
    $primary= (int) pg_fetch_object($this->resultSet)->indkey;

    $this->query("
        SELECT *
        FROM information_schema.columns
        WHERE table_name = '$table'
        ORDER BY ordinal_position
    ");
    $res = array();
    while ($row = $this->fetch(TRUE)) {
        $position= (int) $row['ordinal_position'];

        $res[] = array(
            'name' => $row['column_name'],
            'table' => $table,
            'nativetype' => strtoupper($row['udt_name']),
            'size' => ( $size= max( $row['caracter_maximum_length'], $row['numeric_precision'])) ? (int) $size : NULL,
            'nullable' => $row['is_nullable'] === 'YES',
            'default' => $row['column_default'],
            'autoincrement' => (preg_match('/nextval/', $row['column_default']) === 1 && $position === $primary)
        );
    }
    $this->free();
    return $res;
}

Metoda getColumnsMeta() ale v driveri pre postgre, ale o poznanie chudobnejsia napriklad v porovnani s mysqli. Asi by tiez bolo dobre ju nasledne upravit.

před 11 lety

David Grudl
Nette Core | 6806

Díky, komitnul jsem to. Trošku jsem to upravil, můžeš to prosím vyzkoušet?

Jinak u reflexivních metod postačí, pokud poskytnou základní informace, možná i ten autoincrement je (v případě Postgre) zbytečný. Nechci zjišťovat vše, co lze zjistit, ale jen to, co se skutečné dá v praxi využít.

Obecně stačí u tabulek detekovat, jestli sloupec obsahuje string/float/int/date/bool (tohle není zatím zcela transparentní), jestli může obsahovat NULL a jakou má výchozí hodnotu. Hodí se i rozpoznání primárního klíče. U getColumnsMeta je potřeba informací ještě méně, nullable a default by se mohlo klidně vyhodit.

před 11 lety

edke
Člen | 198

K tej casti s primarnym klucom: v postgrese s autoincrementom je to trosku zlozitejsie ako v mysql, hladal som nejake info, podla coho bezpecne rozpoznat ze sa jedna naozaj o autoincrement. Nasiel som nejake info, podla neho za bezpecne povazuju, ak v default je next_val na sekvenciu a zaroven ak stlpec je primary key.

Editoval edke (28. 10. 2008 14:06)

před 11 lety

edke
Člen | 198

David Grudl wrote:

Díky, komitnul jsem to. Trošku jsem to upravil, můžeš to prosím vyzkoušet?

len dve veci:

1. eskapujes $table ako FIELD_TEXT a v query mas znovu apostrofy okolo $_table

$this->query(" .... WHERE pg_class.relname = '$_table' AND  ... ");

query potom vyhodi Exception a vyzera takto:

SELECT indkey
FROM pg_index, pg_class
WHERE pg_class.relname = ''clanok'' AND pg_class.oid = pg_index.indrelid AND pg_index.indisprimary

Nemalo by tam byt len $_table bez apostrofov ?

$this->query(" .... WHERE pg_class.relname = $_table AND  ..");

2.

Jinak u reflexivních metod postačí, pokud poskytnou základní informace, možná i ten autoincrement je (v případě Postgre) zbytečný. Nechci zjišťovat vše, co lze zjistit, ale jen to, co se skutečné dá v praxi využít.

Obecně stačí u tabulek detekovat, jestli sloupec obsahuje string/float/int/date/bool (tohle není zatím zcela transparentní), jestli může obsahovat NULL a jakou má výchozí hodnotu. Hodí se i rozpoznání primárního klíče. U getColumnsMeta je potřeba informací ještě méně, nullable a default by se mohlo klidně vyhodit.

'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
        ) + $row;

Pripajas vsetky stlpce z query, ja som tam naschval nechal vsetky, pre pripad, ze by bolo zaujimave pridat este nieco. Ale je tam toho naozaj hodne :)

private 'info' =>
  array
    'name' => string 'id' (length=2)
    'table' => string 'clanok' (length=6)
    'type' => null
    'nativetype' => string 'INT4' (length=4)
    'size' => int 32
    'nullable' => boolean false
    'default' => string 'nextval('clanok_id_seq1'::regclass)' (length=35)
    'autoincrement' => boolean true
    'table_catalog' => string 'deltabasket' (length=11)
    'table_schema' => string 'public' (length=6)
    'table_name' => string 'clanok' (length=6)
    'column_name' => string 'id' (length=2)
    'ordinal_position' => string '1' (length=1)
    'column_default' => string 'nextval('clanok_id_seq1'::regclass)' (length=35)
    'is_nullable' => string 'NO' (length=2)
    'data_type' => string 'integer' (length=7)
    'character_maximum_length' => null
    'character_octet_length' => null
    'numeric_precision' => string '32' (length=2)
    'numeric_precision_radix' => string '2' (length=1)
    'numeric_scale' => string '0' (length=1)
    'datetime_precision' => null
    'interval_type' => null
    'interval_precision' => null
    'character_set_catalog' => null
    'character_set_schema' => null
    'character_set_name' => null
    'collation_catalog' => null
    'collation_schema' => null
    'collation_name' => null
    'domain_catalog' => null
    'domain_schema' => null
    'domain_name' => null
    'udt_catalog' => string 'deltabasket' (length=11)
    'udt_schema' => string 'pg_catalog' (length=10)
    'udt_name' => string 'int4' (length=4)
    'scope_catalog' => null
    'scope_schema' => null
    'scope_name' => null
    'maximum_cardinality' => null
    'dtd_identifier' => string '1' (length=1)
    'is_self_referencing' => string 'NO' (length=2)
    'is_identity' => string 'NO' (length=2)
    'identity_generation' => null
    'identity_start' => null
    'identity_increment' => null
    'identity_maximum' => null
    'identity_minimum' => null
    'identity_cycle' => null
    'is_generated' => string 'NEVER' (length=5)
    'generation_expression' => null
    'is_updatable' => string 'YES' (length=3)

Asi by bolo lepsie vymenovat len tie zaujimave stlpce, resp. vobec nepridavat $row z query.

před 11 lety

David Grudl
Nette Core | 6806

Poslal jsem akualizaci.

Jinak ty informace navíc se teď dají získat přes getVendorInfo():

$res = dibi::query('...');

foreach ($res->getColumns() as $column) {
    echo "$column->name\n";
    echo "numeric_precision: ", $column->getVendorInfo('numeric_precision'), "\n";
}

před 11 lety

edke
Člen | 198

Este som upravil query v getColumns(), cez ten karteziansky sucin to nie je uplne najoptimajnejsie, preto:

drivers/postgre.php:

/**
 * Returns metadata for all columns in a table.
 * @param  string
 * @return array
 */
public function getColumns($table)
{
    $_table = $this->escape($table, dibi::FIELD_TEXT);

    $this->query("
        SELECT indkey
        FROM pg_class
        LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary
        WHERE pg_class.relname = $_table
    ");
    $primary = (int) pg_fetch_object($this->resultSet)->indkey;

a dalej tu je getIndexes() pre postgre driver, ladil som to pod 8.2.9.

drivers/postgre.php:

/**
 * Returns metadata for all indexes in a table.
 * @param  string
 * @return array
 */
public function getIndexes($table)
{
    $_table = $this->escape($table, dibi::FIELD_TEXT);

    $this->query("
        SELECT ordinal_position, column_name
        FROM information_schema.columns
        WHERE table_name = $_table AND table_schema = current_schema()
        ORDER BY ordinal_position
    ");

    $column= array();
    while ($row = pg_fetch_object($this->resultSet)  ) {
        $column[$row->ordinal_position]= $row->column_name;
    }

    $this->query("
        SELECT pg_class2.relname, indisunique, indisprimary, indkey
        FROM pg_class
        LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid
        INNER JOIN pg_class as pg_class2 on pg_class2.oid = pg_index.indexrelid
        WHERE pg_class.relname = $_table
    ");
    $res = array();
    while ($row = $this->fetch(TRUE))
    {
        $res[$row['relname']]['name'] = $row['relname'];
        $res[$row['relname']]['unique'] = $row['indisunique'] === 't';
        $res[$row['relname']]['primary'] = $row['indisprimary'] === 't';

        $index_columns= explode(' ', $row['indkey']);

        if( is_array($index_columns))
        {
            foreach($index_columns as $index)
            {
                $res[$row['relname']]['columns'][]= $column[$index];
            }
        }

    }
    $this->free();
    return array_values($res);
}

Editoval edke (28. 10. 2008 16:38)

před 11 lety

jm
Člen | 10

Diky Edke (a Davide),

Byl jsi rychlejsi :), dneska jsem se k programovani jeste nedostal, ale urcite vyzkousim.

Honza

Editoval jm (28. 10. 2008 17:03)

před 11 lety

David Grudl
Nette Core | 6806

Ještě jsem zjistil, že PostgreSQL 7.4.19 háže chybu cannot cast type boolean to integer kvůli tomu CAST(table_type = 'VIEW' AS INTEGER). Je to nějaká moc stará verze?

před 11 lety

edke
Člen | 198

David Grudl wrote:

Ještě jsem zjistil, že PostgreSQL 7.4.19 háže chybu cannot cast type boolean to integer kvůli tomu CAST(table_type = 'VIEW' AS INTEGER). Je to nějaká moc stará verze?

No dost :) viz. feature matrix

před 11 lety

edke
Člen | 198

David Grudl wrote:

Ještě jsem zjistil, že PostgreSQL 7.4.19

Este k tomu PostgreSQL 7.4.x … Bezia tie 2 metody getColumns() a getIndexes() aj na tej verzii ? Pretoze systemove tabulky ako pg_class, pg_index, information_schema a pod. sa zvykli v postgre v minulosti dost menit a nemam teraz nikde rychlo k dispozicii server so 7.4, aby som to otestoval.

před 11 lety

David Grudl
Nette Core | 6806

No, spíš tam dám výjimku pro PostgreSQL < 8.

před 11 lety

David Grudl
Nette Core | 6806

getIndexes je tam.

před 11 lety

jm
Člen | 10

David Grudl napsal(a):

getIndexes je tam.

Diky, pro me to vse funguje (postgresql je verze 8.3.4).