johng       2003/06/19 11:12:10

  Modified:    java/src/org/apache/xalan/lib/sql SQLDocument.java
  Log:
  Added code by Art Welsh to hande multiple result sets and Callable statements.
  
  Revision  Changes    Path
  1.24      +317 -95   
xml-xalan/java/src/org/apache/xalan/lib/sql/SQLDocument.java
  
  Index: SQLDocument.java
  ===================================================================
  RCS file: 
/home/cvs/xml-xalan/java/src/org/apache/xalan/lib/sql/SQLDocument.java,v
  retrieving revision 1.23
  retrieving revision 1.24
  diff -u -r1.23 -r1.24
  --- SQLDocument.java  30 Jan 2003 18:45:44 -0000      1.23
  +++ SQLDocument.java  19 Jun 2003 18:12:10 -0000      1.24
  @@ -58,14 +58,20 @@
   
   package org.apache.xalan.lib.sql;
   
  +import java.util.Vector;
  +
  +import org.apache.xalan.extensions.ExpressionContext;
  +import org.apache.xpath.XPathContext;
  +
  +import org.apache.xml.dtm.DTMManager;
  +import org.apache.xml.dtm.DTM;
   import java.sql.Connection;
  +import java.sql.Statement;
   import java.sql.ResultSet;
   import java.sql.ResultSetMetaData;
   import java.sql.SQLException;
  -import java.sql.Statement;
  -
  -import org.apache.xml.dtm.DTM;
  -import org.apache.xml.dtm.DTMManager;
  +import java.sql.*;
  +import org.apache.xml.dtm.ref.*;
   
   /**
    * The SQL Document is the main controlling class the executesa SQL Query
  @@ -108,6 +114,10 @@
   
     /**
      */
  +  private static final String S_OUT_PARAMETERS = "out-parameters";
  +
  +  /**
  +   */
     private static final String S_CATALOGUE_NAME = "catalogue-name";
     /**
      */
  @@ -173,6 +183,9 @@
     /**
      */
     private int m_Col_TypeID = 0;
  +  /**
  +   */
  +  private int m_OutParameter_TypeID = 0;
   
     /**
      */
  @@ -209,7 +222,7 @@
     private int m_ColAttrib_CASESENSITIVE_TypeID = 0;
     /**
      */
  -  private int m_ColAttrib_DEFINITELYWRITABLE_TypeID = 0;
  +  private int m_ColAttrib_DEFINITELYWRITEABLE_TypeID = 0;
     /**
      */
     private int m_ColAttrib_ISNULLABLE_TypeID = 0;
  @@ -224,31 +237,32 @@
     private int m_ColAttrib_ISSEARCHABLE_TypeID = 0;
   
     /**
  -   * The DBMS Connection used to produce this SQL Document.
  -   * Will be used to clear free up the database resources on
  -   * close.
  -   */
  -  private Connection m_Connection = null;
  -
  -  /**
  -   * The Statement used to extract the data from the Database connection.
  -   * We really don't need the connection, but it is NOT defined from
  -   * JDBC Driver to driver what happens to the ResultSet if the statment
  -   * is closed prior to reading all the data needed. So as long as we are
  -   * using the ResultSet, we will track the Statement used to produce it.
  +   * The Statement used to extract the data from the database connection.
      */
     private Statement m_Statement = null;
   
     /**
  -   * The conduit to our data that will be used to fill the document.
  +   * Expression COntext used to creat this document
  +   * may be used to grab variables from the XSL processor
      */
  -  private ResultSet m_ResultSet = null;
  +  private ExpressionContext m_ExpressionContext = null;
   
     /**
  -   * The Connection Pool that originally produced the connection.
  +   * The Connection Pool where we has derived all of our connections
  +   * for this document
      */
     private ConnectionPool m_ConnectionPool = null;
   
  +  /**
  +   * The current ResultSet.
  +   */
  +  private ResultSet m_ResultSet = null;
  +
  +  /**
  +   * The parameter definitions if this is a callable
  +   * statement with output parameters.
  +   */
  +  private SQLQueryParser m_QueryParser = null;
   
     /**
      * As the column header array is built, keep the node index
  @@ -299,6 +313,28 @@
     private boolean m_StreamingMode = true;
   
     /**
  +   * Multiple Result sets mode (metadata inside rowset).
  +   */
  +  private boolean m_MultipleResults = false;
  +
  +  /**
  +   * Flag to detect if an error occured during an operation
  +   * Defines how errors are handled and how the SQL Connection
  +   * is closed.
  +   */
  +  private boolean m_HasErrors = false;
  +
  +  /**
  +   * Is statement caching enabled.
  +   */
  +  private boolean m_IsStatementCachingEnabled = false;
  +
  +  /**
  +   * XConnection this document came from.
  +   */
  +  private XConnection m_XConnection = null;
  +
  +  /**
      * @param mgr
      * @param ident
      * @param pool
  @@ -308,30 +344,130 @@
      * @param streamingMode
      * @throws SQLException
      */
  -  public SQLDocument( DTMManager mgr, int ident, ConnectionPool pool, 
Connection con, Statement stmt, ResultSet data, boolean streamingMode )throws 
SQLException
  +  // public cSQLDocument(DTMManager mgr, int ident, Statement stmt,
  +  //  ResultSet singleResult, Vector paramdefs, boolean streamingMode,
  +  // boolean multipleResults, boolean statementCachingEnabled) throws 
SQLException
  +
  +  public SQLDocument(DTMManager mgr, int ident)
     {
       super(mgr, ident);
  +  }
   
  -    m_Connection = con;
  -    m_Statement  = stmt;
  -    m_ResultSet  = data;
  -    m_ConnectionPool = pool;
  -    m_StreamingMode = streamingMode;
  -
  -    createExpandedNameTable();
  -    extractSQLMetaData(m_ResultSet.getMetaData());
  -
  -    // Only grab the first row, subsequent rows will be
  -    // fetched on demand.
  -    // We need to do this here so at least on row is set up
  -    // to measure when we are actually reading rows.
  -    addRowToDTMFromResultSet();
  -
  -// We can't do this until the Document is regiostered with the Manager
  -// Which has not happened yet
  -//    if (DEBUG) this.dumpDTM();
  +  /**
  +   * This static method simplifies the creation of an SQL Document and allows
  +   * us to embedd the complexity of creating / handling the dtmIdent inside
  +   * the document. This type of method may better placed inside the 
DTMDocument
  +   * code
  +   */
  +  public static SQLDocument getNewDocument(ExpressionContext exprContext)
  +  {
  +    DTMManager mgr =
  +      ((XPathContext.XPathExpressionContext)exprContext).getDTMManager();
  +    DTMManagerDefault  mgrDefault = (DTMManagerDefault) mgr;
  +
  +
  +    int dtmIdent = mgrDefault.getFirstFreeDTMID();
  +
  +    SQLDocument doc =
  +      new SQLDocument(mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS);
  +
  +    // Register the document
  +    mgrDefault.addDTM(doc, dtmIdent);
  +    doc.setExpressionContext(exprContext);
  +
  +    return doc;
     }
   
  +  /**
  +   * When building the SQL Document, we need to store the Expression
  +   * Context that was used to create the document. This will be se to
  +   * reference items int he XSLT process such as any variables that were
  +   * present.
  +   */
  +  protected void setExpressionContext(ExpressionContext expr)
  +  {
  +    m_ExpressionContext = expr;
  +  }
  +
  +  /**
  +   * Return the context used to build this document
  +   */
  +  public ExpressionContext getExpressionContext()
  +  {
  +    return m_ExpressionContext;
  +  }
  +
  +
  +  public void execute(XConnection xconn, SQLQueryParser query)
  +    throws SQLException
  +  {
  +    try
  +    {
  +      m_StreamingMode = "true".equals(xconn.getFeature("streaming"));
  +      m_MultipleResults = 
"true".equals(xconn.getFeature("multiple-results"));
  +      m_IsStatementCachingEnabled = 
"true".equals(xconn.getFeature("cache-statements"));
  +      m_XConnection = xconn;
  +      m_QueryParser = query;
  +
  +      executeSQLStatement();
  +
  +      createExpandedNameTable();
  +
  +      // Start the document here
  +      m_DocumentIdx = addElement(0, m_Document_TypeID, DTM.NULL, DTM.NULL);
  +      m_SQLIdx = addElement(1, m_SQL_TypeID,  m_DocumentIdx, DTM.NULL);
  +
  +
  +      if ( ! m_MultipleResults )
  +        extractSQLMetaData(m_ResultSet.getMetaData());
  +
  +      // Only grab the first row, subsequent rows will be
  +      // fetched on demand.
  +      // We need to do this here so at least on row is set up
  +      // to measure when we are actually reading rows.
  +      addRowToDTMFromResultSet();
  +    }
  +    catch(SQLException e)
  +    {
  +      m_HasErrors = true;
  +      throw e;
  +    }
  +  }
  +
  +  private void executeSQLStatement() throws SQLException
  +  {
  +    m_ConnectionPool = m_XConnection.getConnectionPool();
  +
  +    Connection conn = m_ConnectionPool.getConnection();
  +
  +    if (! m_QueryParser.hasParameters() )
  +    {
  +      m_Statement = conn.createStatement();
  +      if (! m_Statement.execute(m_QueryParser.getSQLQuery()))
  +      {
  +        throw new SQLException("Error in Query");
  +      }
  +    }
  +    else if (m_QueryParser.isCallable())
  +    {
  +      CallableStatement cstmt =
  +        conn.prepareCall(m_QueryParser.getSQLQuery());
  +      m_QueryParser.registerOutputParameters(cstmt);
  +      m_QueryParser.populateStatement(cstmt, m_ExpressionContext);
  +      m_Statement = cstmt;
  +      if (! cstmt.execute()) throw new SQLException("Error in Callable 
Statement");
  +    }
  +    else
  +    {
  +      PreparedStatement stmt =
  +        conn.prepareStatement(m_QueryParser.getSQLQuery());
  +      m_QueryParser.populateStatement(stmt, m_ExpressionContext);
  +      m_Statement = stmt;
  +      if (! stmt.execute()) throw new SQLException("Error in Prepared 
Statement");
  +    }
  +
  +    m_ResultSet = m_Statement.getResultSet();
  +  }
   
     /**
      * Extract the Meta Data and build the Column Attribute List.
  @@ -344,14 +480,10 @@
       // branch now, the Row & col elements will be added
       // on request.
   
  -    // Start the document here
  -    m_DocumentIdx = addElement(0, m_Document_TypeID, DTM.NULL, DTM.NULL);
  -
       // Add in the row-set Element
  -    m_SQLIdx = addElement(1, m_SQL_TypeID,  m_DocumentIdx, DTM.NULL);
   
       // Add in the MetaData Element
  -    m_MetaDataIdx = addElement(1, m_MetaData_TypeID,  m_SQLIdx, DTM.NULL);
  +    m_MetaDataIdx = addElement(1, m_MetaData_TypeID,  m_MultipleResults ? 
m_RowSetIdx : m_SQLIdx, DTM.NULL);
   
       try
       {
  @@ -360,7 +492,8 @@
       }
       catch(Exception e)
       {
  -      error("ERROR Extracting Metadata");
  +      m_XConnection.setError(e, this, checkWarnings());
  +      //error("ERROR Extracting Metadata");
       }
   
       // The ColHeaderIdx will be used to keep track of the
  @@ -522,13 +655,13 @@
         {
           addAttributeToNode(
             meta.isDefinitelyWritable(i) ? S_ISTRUE : S_ISFALSE,
  -          m_ColAttrib_DEFINITELYWRITABLE_TypeID, lastColHeaderIdx);
  +          m_ColAttrib_DEFINITELYWRITEABLE_TypeID, lastColHeaderIdx);
         }
         catch(Exception e)
         {
           addAttributeToNode(
             S_ATTRIB_NOT_SUPPORTED,
  -          m_ColAttrib_DEFINITELYWRITABLE_TypeID, lastColHeaderIdx);
  +          m_ColAttrib_DEFINITELYWRITEABLE_TypeID, lastColHeaderIdx);
         }
   
         try
  @@ -582,9 +715,7 @@
             S_ATTRIB_NOT_SUPPORTED,
             m_ColAttrib_ISSEARCHABLE_TypeID, lastColHeaderIdx);
         }
  -
       }
  -
     }
   
     /**
  @@ -610,7 +741,8 @@
         m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_ROW, 
DTM.ELEMENT_NODE);
       m_Col_TypeID =
         m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_COL, 
DTM.ELEMENT_NODE);
  -
  +    m_OutParameter_TypeID =
  +      m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_OUT_PARAMETERS, 
DTM.ELEMENT_NODE);
   
       m_ColAttrib_CATALOGUE_NAME_TypeID =
         m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_CATALOGUE_NAME, 
DTM.ATTRIBUTE_NODE);
  @@ -634,7 +766,7 @@
         m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_TABLE_NAME, 
DTM.ATTRIBUTE_NODE);
       m_ColAttrib_CASESENSITIVE_TypeID =
         m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_CASESENSITIVE, 
DTM.ATTRIBUTE_NODE);
  -    m_ColAttrib_DEFINITELYWRITABLE_TypeID =
  +    m_ColAttrib_DEFINITELYWRITEABLE_TypeID =
         m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, 
S_DEFINITELYWRITABLE, DTM.ATTRIBUTE_NODE);
       m_ColAttrib_ISNULLABLE_TypeID =
         m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_ISNULLABLE, 
DTM.ATTRIBUTE_NODE);
  @@ -659,15 +791,16 @@
     {
       try
       {
  -
  -
         // If we have not started the RowSet yet, then add it to the
         // tree.
  -      if (m_RowSetIdx == DTM.NULL)
  +      if (m_FirstRowIdx == DTM.NULL)
         {
  -        m_RowSetIdx = addElement(1, m_RowSet_TypeID,  m_SQLIdx, 
m_MetaDataIdx);
  +        m_RowSetIdx =
  +          addElement(1, m_RowSet_TypeID,  m_SQLIdx, m_MultipleResults ? 
m_RowSetIdx : m_MetaDataIdx);
  +        if ( m_MultipleResults ) 
extractSQLMetaData(m_ResultSet.getMetaData());
         }
   
  +
         // Check to see if all the data has been read from the Query.
         // If we are at the end the signal that event
         if ( ! m_ResultSet.next())
  @@ -683,6 +816,47 @@
             m_nextsib.setElementAt(DTM.NULL, m_LastRowIdx);
           }
   
  +        m_ResultSet.close();
  +        if ( m_MultipleResults )
  +        {
  +          while ( !m_Statement.getMoreResults() && 
m_Statement.getUpdateCount() >= 0 ) ;
  +          m_ResultSet = m_Statement.getResultSet();
  +        }
  +        else
  +          m_ResultSet = null;
  +
  +        if ( m_ResultSet != null )
  +        {
  +          m_FirstRowIdx = DTM.NULL;
  +          addRowToDTMFromResultSet();
  +        }
  +        else
  +        {
  +          Vector parameters = m_QueryParser.getParameters();
  +          // Get output parameters.
  +          if ( parameters != null )
  +          {
  +            int outParamIdx = addElement(1, m_OutParameter_TypeID,  
m_SQLIdx, m_RowSetIdx);
  +            int lastColID = DTM.NULL;
  +            for ( int indx = 0 ; indx < parameters.size() ; indx++ )
  +            {
  +              QueryParameter parm = 
(QueryParameter)parameters.elementAt(indx);
  +              if ( parm.isOutput() )
  +              {
  +                Object rawobj = 
((CallableStatement)m_Statement).getObject(indx + 1);
  +                lastColID = addElementWithData(rawobj, 2, m_Col_TypeID, 
outParamIdx, lastColID);
  +                addAttributeToNode(parm.getName(), 
m_ColAttrib_COLUMN_NAME_TypeID, lastColID);
  +                addAttributeToNode(parm.getName(), 
m_ColAttrib_COLUMN_LABEL_TypeID, lastColID);
  +                addAttributeToNode(new Integer(parm.getType()), 
m_ColAttrib_COLUMN_TYPE_TypeID, lastColID);
  +                addAttributeToNode(parm.getTypeName(), 
m_ColAttrib_COLUMN_TYPENAME_TypeID, lastColID);
  +              }
  +            }
  +          }
  +
  +          SQLWarning warn = checkWarnings();
  +          if ( warn != null )        m_XConnection.setError(null, null, 
warn);
  +        }
  +
           return false;
         }
   
  @@ -690,7 +864,8 @@
         if (m_FirstRowIdx == DTM.NULL)
         {
           m_FirstRowIdx =
  -          addElement(2, m_Row_TypeID, m_RowSetIdx, DTM.NULL);
  +          addElement(2, m_Row_TypeID, m_RowSetIdx, m_MultipleResults ? 
m_MetaDataIdx : DTM.NULL);
  +
           m_LastRowIdx = m_FirstRowIdx;
   
           if (m_StreamingMode)
  @@ -706,7 +881,7 @@
           // If we are in Streaming mode, then only use a single row instance
           if (! m_StreamingMode)
           {
  -          m_LastRowIdx = addElement(3, m_Row_TypeID, m_RowSetIdx, 
m_LastRowIdx);
  +          m_LastRowIdx = addElement(2, m_Row_TypeID, m_RowSetIdx, 
m_LastRowIdx);
           }
         }
   
  @@ -765,7 +940,8 @@
             "SQL Error Fetching next row [" + e.getLocalizedMessage() + "]");
         }
   
  -      error("SQL Error Fetching next row [" + e.getLocalizedMessage() + "]");
  +      m_XConnection.setError(e, this, checkWarnings());
  +      m_HasErrors = true;
       }
   
       // Only do a single row...
  @@ -774,44 +950,67 @@
   
   
     /**
  -   * Clean up our ties to the database but this does not necessarly
  -   * clean up the document.
  -   * @return
  +   * Used by the XConnection to determine if the Document should
  +   * handle the document differently.
      */
  -  public void close( )
  +  public boolean hasErrors()
     {
  -    if (DEBUG) System.out.println("close()");
  -
  -    try { if (null != m_ResultSet) m_ResultSet.close(); }
  -    catch(Exception e) { }
  -    try { if (null != m_Statement) m_Statement.close(); }
  -    catch(Exception e) { }
  -    try {
  -      if (null != m_Connection)
  -        m_ConnectionPool.releaseConnection(m_Connection);
  -    } catch(Exception e) { }
  +    return m_HasErrors;
     }
   
     /**
  -   * When an error occurs, the XConnection will call this method
  -   * do that we can deal with the Connection properly
  -   * @return
  +   * Close down any resources used by this document. If an SQL Error occure
  +   * while the document was being accessed, the SQL Connection used to create
  +   * this document will be released to the Connection Pool on error. This 
allows
  +   * the COnnection Pool to give special attention to any connection that may
  +   * be in a errored state.
  +   *
      */
  -  public void closeOnError( )
  +  public void close( )
     {
  -    if (DEBUG) System.out.println("close()");
  +    try
  +    {
  +      SQLWarning warn = checkWarnings();
  +      if ( warn != null ) m_XConnection.setError(null, null, warn);
  +    }
  +    catch(Exception e) {}
   
  -    try  { if (null != m_ResultSet) m_ResultSet.close();   }
  -    catch(Exception e) { }
  -    try  { if (null != m_Statement) m_Statement.close();
  -    } catch(Exception e) { }
  -    try {
  -      if (null != m_Connection)
  -        m_ConnectionPool.releaseConnectionOnError(m_Connection);
  -    } catch(Exception e) { }
  -  }
  +    try
  +    {
  +      if (null != m_ResultSet)
  +      {
  +        m_ResultSet.close();
  +        m_ResultSet = null;
  +      }
  +    }
  +    catch(Exception e) {}
   
   
  +    Connection conn = null;
  +
  +    try
  +    {
  +      if (null != m_Statement)
  +      {
  +        conn = m_Statement.getConnection();
  +        m_Statement.close();
  +        m_Statement = null;
  +      }
  +    }
  +    catch(Exception e) {}
  +
  +    try
  +    {
  +      if (conn != null)
  +      {
  +        if (m_HasErrors)  m_ConnectionPool.releaseConnectionOnError(conn);
  +        else m_ConnectionPool.releaseConnection(conn);
  +      }
  +    }
  +    catch(Exception e) {}
  +
  +    getManager().release(this, true);
  +  }
   
     /**
      * @return
  @@ -841,14 +1040,24 @@
       // row from the ResultSet.
       //
   
  -    int id = _exptype(identity);
  -    if (
  -      ( id == m_Row_TypeID) &&
  -      (identity >= m_LastRowIdx))
  -    {
  -      if (DEBUG) System.out.println("reading from the ResultSet");
  -      addRowToDTMFromResultSet();
  -    }
  +  if ( m_ResultSet != null )
  +  {
  +      int id = _exptype(identity);
  +      if (
  +        ( id == m_Row_TypeID) &&
  +        (identity >= m_LastRowIdx) )
  +      {
  +        if (DEBUG) System.out.println("reading from the ResultSet");
  +        addRowToDTMFromResultSet();
  +      }
  +      else if ( m_MultipleResults && identity == m_RowSetIdx )
  +      {
  +        if (DEBUG) System.out.println("reading for next ResultSet");
  +      int startIdx = m_RowSetIdx;
  +      while ( startIdx == m_RowSetIdx && m_ResultSet != null )
  +            addRowToDTMFromResultSet();
  +      }
  +  }
   
       return super._nextsib(identity);
     }
  @@ -863,5 +1072,18 @@
       if (DEBUG) System.out.println("Document Release");
     }
   
  -
  +  public SQLWarning checkWarnings()
  +  {
  +    SQLWarning warn = null;
  +    if ( m_Statement != null )
  +    {
  +      try
  +      {
  +        warn = m_Statement.getWarnings();
  +        m_Statement.clearWarnings();
  +      }
  +      catch (SQLException se) {}
  +    }
  +    return(warn);
  +  }
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to