package org.postgresql.jdbc2;

// IMPORTANT NOTE: This file implements the JDBC 2 version of the driver.
// If you make any modifications to this file, you must make sure that the
// changes are also made (if relevent) to the related JDBC 1 class in the
// org.postgresql.jdbc1 package.

import java.io.*;
import java.lang.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.sql.*;
import org.postgresql.Field;
import org.postgresql.fastpath.*;
import org.postgresql.largeobject.*;
import org.postgresql.util.*;

/*
 * $Id: Connection.java,v 1.19 2002/06/11 02:55:16 barry Exp $
 *
 * A Connection represents a session with a specific database.	Within the
 * context of a Connection, SQL statements are executed and results are
 * returned.
 *
 * <P>A Connection's database is able to provide information describing
 * its tables, its supported SQL grammar, its stored procedures, the
 * capabilities of this connection, etc.  This information is obtained
 * with the getMetaData method.
 *
 * <p><B>Note:</B> By default, the Connection automatically commits changes
 * after executing each statement.	If auto-commit has been disabled, an
 * explicit commit must be done or database changes will not be saved.
 *
 * @see java.sql.Connection
 */
public class Connection extends org.postgresql.Connection implements java.sql.Connection
{
        // This is a cache of the DatabaseMetaData instance for this connection
        protected DatabaseMetaData metadata;

        /*
         * The current type mappings
         */
        protected java.util.Map typemap;

        /*
         * SQL statements without parameters are normally executed using
         * Statement objects.  If the same SQL statement is executed many
         * times, it is more efficient to use a PreparedStatement
         *
         * @return a new Statement object
         * @exception SQLException passed through from the constructor
         */
        public java.sql.Statement createStatement() throws SQLException
        {
                // The spec says default of TYPE_FORWARD_ONLY but everyone is used to
                // using TYPE_SCROLL_INSENSITIVE
                return createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
        }

        /*
         * SQL statements without parameters are normally executed using
         * Statement objects.  If the same SQL statement is executed many
         * times, it is more efficient to use a PreparedStatement
         *
         * @param resultSetType to use
         * @param resultSetCuncurrency to use
         * @return a new Statement object
         * @exception SQLException passed through from the constructor
         */
        public java.sql.Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
        {
                Statement s = new Statement(this);
                s.setResultSetType(resultSetType);
                s.setResultSetConcurrency(resultSetConcurrency);
                return s;
        }


        /*
         * A SQL statement with or without IN parameters can be pre-compiled
         * and stored in a PreparedStatement object.  This object can then
         * be used to efficiently execute this statement multiple times.
         *
         * <B>Note:</B> This method is optimized for handling parametric
         * SQL statements that benefit from precompilation if the drivers
         * supports precompilation.  PostgreSQL does not support precompilation.
         * In this case, the statement is not sent to the database until the
         * PreparedStatement is executed.  This has no direct effect on users;
         * however it does affect which method throws certain SQLExceptions
         *
         * @param sql a SQL statement that may contain one or more '?' IN
	 *     	 parameter placeholders
         * @return a new PreparedStatement object containing the pre-compiled
	 *     	 statement.
         * @exception SQLException if a database access error occurs.
         */
        public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException
        {
                return prepareStatement(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
        }

        public java.sql.PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
        {
                PreparedStatement s = new PreparedStatement(this, sql);
                s.setResultSetType(resultSetType);
                s.setResultSetConcurrency(resultSetConcurrency);
                return s;
        }

        /*
         * A SQL stored procedure call statement is handled by creating a
         * CallableStatement for it.  The CallableStatement provides methods
         * for setting up its IN and OUT parameters and methods for executing
         * it.
         *
         * <B>Note:</B> This method is optimised for handling stored procedure
         * call statements.  Some drivers may send the call statement to the
         * database when the prepareCall is done; others may wait until the
         * CallableStatement is executed.  This has no direct effect on users;
         * however, it does affect which method throws certain SQLExceptions
         *
         * @param sql a SQL statement that may contain one or more '?' parameter
	 *     	 placeholders.  Typically this statement is a JDBC function call
	 *     	 escape string.
         * @return a new CallableStatement object containing the pre-compiled
	 *     	 SQL statement
         * @exception SQLException if a database access error occurs
         */
        public java.sql.CallableStatement prepareCall(String sql) throws SQLException
        {
                return prepareCall(sql, java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
        }

        public java.sql.CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
        {
                throw new PSQLException("postgresql.con.call");
                //CallableStatement s = new CallableStatement(this,sql);
                //s.setResultSetType(resultSetType);
                //s.setResultSetConcurrency(resultSetConcurrency);
                //return s;
        }

        /*
         * Tests to see if a Connection is closed.
         *
         * Peter Feb 7 2000: Now I've discovered that this doesn't actually obey the
         * specifications. Under JDBC2.1, this should only be valid _after_ close()
         * has been called. It's result is not guraranteed to be valid before, and
         * client code should not use it to see if a connection is open. The spec says
         * that the client should monitor the SQLExceptions thrown when their queries
         * fail because the connection is dead.
         *
         * I don't like this definition. As it doesn't hurt breaking it here, our
         * isClosed() implementation does test the connection, so for PostgreSQL, you
         * can rely on isClosed() returning a valid result.
         *
         * @return the status of the connection
         * @exception SQLException (why?)
         */
        public boolean isClosed() throws SQLException
        {
                // If the stream is gone, then close() was called
                if (pg_stream == null)
                        return true;
                return false;
        }

        /*
         * A connection's database is able to provide information describing
         * its tables, its supported SQL grammar, its stored procedures, the
         * capabilities of this connection, etc.  This information is made
         * available through a DatabaseMetaData object.
         *
         * @return a DatabaseMetaData object for this connection
         * @exception SQLException if a database access error occurs
         */
        public java.sql.DatabaseMetaData getMetaData() throws SQLException
        {
                if (metadata == null)
                        metadata = new DatabaseMetaData(this);
                return metadata;
        }

        /*
         * This overides the method in org.postgresql.Connection and returns a
         * ResultSet.
         */
        public java.sql.ResultSet getResultSet(org.postgresql.Connection conn, java.sql.Statement stat, Field[] fields, Vector tuples, String status, int updateCount, long insertOID, boolean binaryCursor) throws SQLException
        {
                // In 7.1 we now test concurrency to see which class to return. If we are not working with a
                // Statement then default to a normal ResultSet object.
                if (stat != null)
                {
                        if (stat.getResultSetConcurrency() == java.sql.ResultSet.CONCUR_UPDATABLE)
                                return new org.postgresql.jdbc2.UpdateableResultSet((org.postgresql.jdbc2.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
                }

                return new org.postgresql.jdbc2.ResultSet((org.postgresql.jdbc2.Connection)conn, fields, tuples, status, updateCount, insertOID, binaryCursor);
        }

        // *****************
        // JDBC 2 extensions
        // *****************

        public java.util.Map getTypeMap() throws SQLException
        {
                // new in 7.1
                return typemap;
        }


        public void setTypeMap(java.util.Map map) throws SQLException
        {
                // new in 7.1
                typemap = map;
        }

        /*
         * This overides the standard internal getObject method so that we can
         * check the jdbc2 type map first
         *
         * @return PGobject for this type, and set to value
         * @exception SQLException if value is not correct for this type
         * @see org.postgresql.util.Serialize
         */
        public Object getObject(String type, String value) throws SQLException
        {
                if (typemap != null)
                {
                        SQLData d = (SQLData) typemap.get(type);
                        if (d != null)
                        {
                                // Handle the type (requires SQLInput & SQLOutput classes to be implemented)
                                throw org.postgresql.Driver.notImplemented();
                        }
                }

                // Default to the original method
                return super.getObject(type, value);
        }

        /* An implementation of the abstract method in the parent class.
         * This implemetation uses the jdbc2Types array to support the jdbc2
         * datatypes.  Basically jdbc1 and jdbc2 are the same, except that
         * jdbc2 adds the Array types.
         */
        public int getSQLType(String pgTypeName)
        {
                int sqlType = Types.OTHER; // default value
                for (int i = 0;i < jdbc2Types.length;i++)
                {
                        if (pgTypeName.equals(jdbc2Types[i]))
                        {
                                sqlType = jdbc2Typei[i];
                                break;
                        }
                }
                return sqlType;
        }

        /*
         * This table holds the org.postgresql names for the types supported.
         * Any types that map to Types.OTHER (eg POINT) don't go into this table.
         * They default automatically to Types.OTHER
         *
         * Note: This must be in the same order as below.
         *
         * Tip: keep these grouped together by the Types. value
         */
        private static final String jdbc2Types[] = {
                                "int2",
                                "int4", "oid",
                                "int8",
                                "cash", "money",
                                "numeric",
                                "float4",
                                "float8",
                                "bpchar", "char", "char2", "char4", "char8", "char16",
                                "varchar", "text", "name", "filename",
                                "bytea",
                                "bool",
                                "date",
                                "time",
                                "abstime", "timestamp", "timestamptz",
                                "_bool", "_char", "_int2", "_int4", "_text",
                                "_oid", "_varchar", "_int8", "_float4", "_float8",
                                "_abstime", "_date", "_time", "_timestamp", "_numeric",
                                "_bytea"
                        };

        /*
         * This table holds the JDBC type for each entry above.
         *
         * Note: This must be in the same order as above
         *
         * Tip: keep these grouped together by the Types. value
         */
        private static final int jdbc2Typei[] = {
                                                                                                Types.SMALLINT,
                                                                                                Types.INTEGER, Types.INTEGER,
                                                                                                Types.BIGINT,
                                                                                                Types.DOUBLE, Types.DOUBLE,
                                                                                                Types.NUMERIC,
                                                                                                Types.REAL,
                                                                                                Types.DOUBLE,
                                                                                                Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR, Types.CHAR,
                                                                                                Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
                                                                                                Types.BINARY,
                                                                                                Types.BIT,
                                                                                                Types.DATE,
                                                                                                Types.TIME,
                                                                                                Types.TIMESTAMP, Types.TIMESTAMP, Types.TIMESTAMP,
                                                                                                Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY,
                                                                                                Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY,
                                                                                                Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY, Types.ARRAY,
                                                                                                Types.ARRAY
                                                                                        };

        //Because the get/setLogStream methods are deprecated in JDBC2
        //we use the get/setLogWriter methods here for JDBC2 by overriding
        //the base version of this method
        protected void enableDriverManagerLogging() {
            if (DriverManager.getLogWriter() == null) {
                DriverManager.setLogWriter(new PrintWriter(System.out));
	    }
	}

}

// ***********************************************************************

