Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I am forced to port some code over to a PostgreSQL database that was once using a MySQL database. At first I thought that it would be a simple task of changing the db_driver for the DBD module. However, apparently PgSQL does not share some of the commands I was using on the MySQL database.
Specifically, major portions of the code pivot on a few lines that use something like:
… my $sth = $dbh->prepare("show tables") or &bail($DBI::errstr); my $rv = $sth->execute or &bail($DBI::errstr); while ( ( my $table_name ) = $sth->fetchrow_array ) { $sth2 = $dbh->prepare("describe $table_name") or die print "Can't do + select: $DBI::errstr"; $rv2 = $sth2->execute or die print "Can't execute the query: $DBI::e +rrstr"; while (($field_, $type_, $null_,$key_, $default_, $extra_) = $sth2-> +fetchrow_array) { #stuff with cols and then rows. } } …
Or… (credit to athomason's dump code)…
… # Get a list of the tables my @table_names; eval { @table_names = @{$dbh->selectcol_arrayref("SHOW TABLES")}; 1; } or die "Couldn't retrieve list of MySQL tables: $DBI::errstr"; # Get data from each table my (@tables, $table); eval { foreach $table (@table_names) { # Record how the table is layed out my $layout = $dbh->selectall_arrayref("DESCRIBE $table"); # Store the data in a 2D array reference my @fields = map ${$_}[0], @{$layout}; my $data = $dbh->selectall_arrayref("SELECT " . (join ',', @fields) . " FROM $table"); push @tables, [$layout, $data]; } 1; } or die "Failed while obtaining data from $table: $DBI::errstr"; …
DBD::Pg::db selectcol_arrayref failed: ERROR: Option 'tables' is not recognized.
DBD::Pg::st execute failed: ERROR: parser: parse error at or near "describe".

I guess that SHOW does something different in PgSQL than in MySQL and that DESCRIBE is non existent in PgSQL.
Any ideas on how I can get the same results from DBI or simple SQL commands so that I can get the same output from a Postgres database that I was getting from a MySQL database? So that I can just modify those lines which use SHOW and DESCRIBE and not have to touch the rest of the applications.

Thanks, -Jeff

Replies are listed 'Best First'.
Re: DBI - MySQL vs. PgSQL
by kschwab (Vicar) on Jan 17, 2002 at 06:58 UTC
    While I generally prefer PostgreSQL to MySql, this is one area where MySql did a better job.

    There isn't a general purpose DESCRIBE or SHOW TABLES in Pg's sql syntax if you aren't using the command line psql tool. ( try \dt and \d tablename if you are).

    You can peruse the source for psql (look at describe.c), or use the -E option to psql to dump it's internal SQL commands it uses for \dt and \d.

    For the lazier folks, here's postgres running \dt and \d on a table called fees, along with the SQL commands that \d and \dt use behind the scenes

Re: DBI - MySQL vs. PgSQL
by rdfield (Priest) on Jan 17, 2002 at 13:44 UTC
    I don't know MySQL nor PgSQL, but the following may work:

    $dbh->tables;
    returns a list of tables
    $sql = "select * from $table"
    returns the data
    and
    $sth->{NAME}->[0];
    is the name of the first column. etc

    rdfield

Re: DBI - MySQL vs. PgSQL
by quikwit (Beadle) on Jan 17, 2002 at 20:42 UTC
    Show and Describe are not in the SQL-92 standard , so you can't expect them to be supported.

    DBI does allows the user to get a list of tables through the $dbh->tables command.

    The way I get the column names is just run a bogus search: "select * from $table where 1 = 0". This should run very quickly and get me the information I want.

    Here's a sample program:

    #!/usr/bin/perl -w use strict; use DBI; my ($driver, $dbh, @tables); $driver="DBI:CSV:f_dir=Tables"; $dbh=DBI->connect($driver) or die "Can't connect: $DBI::errstr"; eval { @tables = $dbh->tables; @tables = grep { s/.*\.//o } @tables; } or die "Couldn't retrieve list of tables: $DBI::errstr\n"; for my $thisTable (@tables) { eval { my $sth=$dbh->prepare("select * from $thisTable where 1 = 0"); $sth->execute(); my $columnRef=$sth->{NAME_lc}; for my $thisColumn (@$columnRef) { print "Table: $thisTable, column is: $thisColumn\n"; # Do whatever else you want with the columns. } 1; } or die "Couldn't read table $_: $DBI::errstr\n"; } $dbh->disconnect();
    A few things to note:
    • My tables came out with names like "me".tablename, so I had to prune off the username part (I don't know how other DBDs will write this).
    • The $dbh->tables function may not be supported. The DBI documentation says "This method is experimental and may change or disappear".
    rick
      Actually, you should be able to just use:
      #!/usr/local/bin/perl -w use strict; use DBI; use DBD::Pg; my ($dbh, @tables); my $user = "username"; my $password = ""; my $db_name="database"; my $driver="Pg"; my $db_host="localhost"; my $dsn="DBI:$driver:dbname=$db_name;host=$db_host"; $dbh=DBI->connect($dsn,$user,$password) or die "Can't connect: $DBI:: +errstr"; @tables = $dbh->tables; for my $thisTable (@tables) { my $sth=$dbh->prepare("select * from $thisTable where 1 = 0"); $sth->execute(); my $columnRef=$sth->{NAME_lc}; for my $thisColumn (@$columnRef) { print "Table: $thisTable, column is: $thisColumn\n"; # Do whatever else you want with the columns. } 1; } $dbh->disconnect();
      -
      @tables = grep { s/.*\.//o } @tables;
      Would probably complain with use strict about cat’ing to an undef…. Right? I guess that is why you used evals though.
      And $dbh->tables with DBD:Pg will just return an array of table names anyway. Of course, as the DBI perldoc states: “Warning: This method is experimental and may change.”
      So, although I agree with quikwit that you should try to write things as portable as possibly… to some extent you will not be able to (tables() returning different things with CSV vs. Pg.) unless you have all possibly platforms to test/write on.
      I did not know that SHOW was not in the SQL92 specs. either. ++quikwit for pointing that out. I have been using SHOW TABLES myself.

      If you want to access fields in the columns as DESCRIBE may have done, you should check out my question on feeding fields into an array: Just another DBI question.
      Ciao, -xtype
Re: DBI - MySQL vs. PgSQL
by Anonymous Monk on Jan 18, 2002 at 16:06 UTC

    Postgres stores all its table names in a table, pg_tables.

    To get a list of all non system tables using SQL try:

    select * from pg_tables where tablename not like 'pg_%' 

    Ta ta

      > To get a list of all non system tables using SQL try:

      > select * from pg_tables where tablename not like 'pg_%' <CODE>

      Which is fine until you switch to another database in the future, at which time you have to modify the script again.

      In general, I choose to try to always use non-vendor specific solutions. Come to think of it, that's why DBI exists in the first place!

      rick