severe jdbc metadata problem - Mailing list pgsql-bugs

From Tim Holloway
Subject severe jdbc metadata problem
Date
Msg-id 389A2263.AF1E791B@southeast.net
Whole thread Raw
List pgsql-bugs
If PostgreSQL failed to compile on your computer or you found a bug that
is likely to be specific to one platform then please fill out this form
and e-mail it to pgsql-ports@postgresql.org.

To report any other bug, fill out the form below and e-mail it to
pgsql-bugs@postgresql.org.

If you not only found the problem but solved it and generated a patch
then e-mail it to pgsql-patches@postgresql.org instead.  Please use the
command "diff -c" to generate the patch.

You may also enter a bug report at http://www.postgresql.org/ instead of
e-mail-ing this form.

============================================================================
                        POSTGRESQL BUG REPORT TEMPLATE
============================================================================


Your name        :    Tim Holloway
Your email address    : mtsinc@leading.nete


System Configuration
---------------------
  Architecture (example: Intel Pentium)      : Intel Pentium

  Operating System (example: Linux 2.0.26 ELF)     : Linux 2.2

  PostgreSQL version (example: PostgreSQL-6.5)  :   PostgreSQL-6.5.2

  Compiler used (example:  gcc 2.8.0)        : N/A


Please enter a FULL description of your problem:
------------------------------------------------

jdbc: postgresql.Driver - Invalid column width returned from
 resultsetmetadata getColumnDisplaySize() method.




Please describe a way to repeat the problem.   Please try to provide a
concise reproducible example, if at all possible:
----------------------------------------------------------------------

Sun, in their infinite wisdom, did not provide a way to get abstract
metadata from a resultset without actually issuing a query. Symantec's
Visual Cafe finesses this issue by modifying the model query with "WHERE 1 > 2"
to return an empty resultset (I can appreciate this - one of my tables would
return over 5 MB everytime the metadata changed if data was actually retrieved
wholesale).

As near as I can read from the driver source, however, the postgresql jdbc driver
doesn't return abstract metadata - it returns metadata based on the
actual results OF THAT QUERY. The algorithm appears to compute the widest display(?)
value of each column counting down through the rows and then taking either that
number OR the column's name length in characters, whichever is greater. For an
mpty resultset, this means that the "column display widths" are always returned as
the widths of the column names.

While the above mentioned algorithm has its uses - e.g.: it works well for \dt-type
displays - I believe it is not the appropriate one for jdbc. The metadata is "impure"
in that it varies with the results of the query. The column name's length is not
neccesarily going to be displayed in the same location as the colum data in a Java
app. Scanning each otem to determine display width is potentially expensive,

Most importantly, one of the most popular Internet database application
generators in the industry is rendered almost completey useless.

Model code:
========== tear here

    // The following code can be used as a template.  Simply
    // substitute the appropriate url, login, and password, and then substitute the
    // SQL statement you want to send to the database.



    //----------------------------------------------------------------------------
    //
    // Module:      SimpleSelect.java
    //
    // Description: Test program for ODBC API interface.  This java application
    // will connect to a JDBC driver, issue a select statement
    // and display all result columns and rows
    //
    // Product:     JDBC to ODBC Bridge
    //
    // Author:      Karl Moss
    //
    // Date:        February, 1996
    //
    // Copyright:   1990-1996 INTERSOLV, Inc.
    // This software contains confidential and proprietary
    // information of INTERSOLV, Inc.
    //----------------------------------------------------------------------------

// NOTE: Adapted for postgresql jdbc Driver - tfh

import java.applet.*;
import java.net.URL;
import java.sql.*;

public class SimpleSelect extends java.applet.Applet  {


    public static void main (String args[]) {
    //public void init() {
        //super.init();

            String url   = "jdbc:postgresql://master/RandyScott";
            String query = "SELECT * FROM ARTISTS";
            try {
                Class.forName("postgresql.Driver");
            } catch ( ClassNotFoundException e ) {
                System.out.println("ERROR: postgresql.Driver not found!\n");
                return;
            }
System.out.println("Got class.forName");
                try {
System.out.println("Trying for connection A" );
            Connection con;
                  DriverManager.setLogStream(System.out);
System.out.println("Trying for connection B");
                    con = DriverManager.getConnection (
                        url, "apache", "HSD9511");
System.out.println("Didn't fault on connection.");



                    // Attempt to connect to a driver.  Each one
                    // of the registered drivers will be loaded until
                    // one is found that can process this URL

                    // If we were unable to connect, an exception
                    // would have been thrown.  So, if we get here,
                    // we are successfully connected to the URL

                    // Check for, and display and warnings generated
                    // by the connect.

                    checkForWarning (con.getWarnings ());

                    // Get the DatabaseMetaData object and display
                    // some information about the connection

                    DatabaseMetaData dma = con.getMetaData ();

                    System.out.println("\nConnected to " + dma.getURL());
                    System.out.println("Driver       " +
                            dma.getDriverName());
                    System.out.println("Version      " +
                            dma.getDriverVersion());
                    System.out.println("");

                    // Create a Statement object so we can submit
                    // SQL statements to the driver

                    Statement stmt = con.createStatement ();

                    // Submit a query, creating a ResultSet object

                    ResultSet rs = stmt.executeQuery (query);

                    // Display all columns and rows from the result set

                    dispResultSet (rs);

                    // Close the result set

                    rs.close();

                    // Close the statement

                    stmt.close();

                    // Close the connection

                    con.close();
            }
            catch (SQLException ex) {

                    // A SQLException was generated.  Catch it and
                    // display the error information.  Note that there
                    // could be multiple error objects chained
                    // together

            System.out.println ("\n*** SQLException caught ***\n");

            while (ex != null) {
                    System.out.println ("SQLState: " +
                                    ex.getSQLState ());
                    System.out.println ("Message:  " + ex.getMessage ());
                    System.out.println ("Vendor:   " +
                                    ex.getErrorCode ());
                    ex = ex.getNextException ();
                    System.out.println ("");
                    }
            }
            catch (java.lang.Exception ex) {

                    // Got some other type of exception.  Dump it.

                    ex.printStackTrace ();
            }
    }

    //-------------------------------------------------------------------
    // checkForWarning
    // Checks for and displays warnings.  Returns true if a warning
    // existed
    //-------------------------------------------------------------------

    private static boolean checkForWarning (SQLWarning warn)
                    throws SQLException  {
            boolean rc = false;

            // If a SQLWarning object was given, display the
            // warning messages.  Note that there could be
            // multiple warnings chained together

            if (warn != null) {
                    System.out.println ("\n *** Warning ***\n");
                    rc = true;
                    while (warn != null) {
                            System.out.println ("SQLState: " +
                                    warn.getSQLState ());
                            System.out.println ("Message:  " +
                                    warn.getMessage ());
                            System.out.println ("Vendor:   " +
                                    warn.getErrorCode ());
                            System.out.println ("");
                            warn = warn.getNextWarning ();
                    }
            }
            return rc;
    }

    //-------------------------------------------------------------------
    // dispResultSet
    // Displays all columns and rows in the given result set
    //-------------------------------------------------------------------

    private static void dispResultSet (ResultSet rs)
            throws SQLException
    {
            int i;

            // Get the ResultSetMetaData.  This will be used for
            // the column headings

            ResultSetMetaData rsmd = rs.getMetaData ();

            // Get the number of columns in the result set

            int numCols = rsmd.getColumnCount ();

            // Display column headings

            for (i=1; i<=numCols; i++) {
                    if (i > 1) System.out.print(",");
                    System.out.print(rsmd.getColumnLabel(i));
                    /// Display COLUMN WIDTH ////
                    System.out.print("(" + String.valueOf(rsmd.getColumnDisplayWidth(i)) + ") ");
            }
            System.out.println("");

            // Display data, fetching until end of the result set

            boolean more = rs.next ();
            while (more) {

                    // Loop through each column, getting the
                    // column data and displaying

                    for (i=1; i<=numCols; i++) {
                            if (i > 1) System.out.print(",");
                            System.out.print(rs.getString(i));
                    }
                    System.out.println("");

                    // Fetch the next result set row

                    more = rs.next ();
            }
    }

}



========== tear here



If you know how this problem might be fixed, list the solution below:
---------------------------------------------------------------------

Apparently Java's definition of metadata is not the same as other querents.
Thus a Java-conforming type of metadata needs to be sent back from the
server when so requested.

pgsql-bugs by date:

Previous
From: Franz Korntner
Date:
Subject: 'SELECT DISTINCT' and 'ORDER BY' dont combine
Next
From:
Date:
Subject: configure