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]