001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: SQLDocument.java 468638 2006-10-28 06:52:06Z minchau $
020     */
021    
022    package org.apache.xalan.lib.sql;
023    
024    import java.util.Vector;
025    
026    import org.apache.xalan.extensions.ExpressionContext;
027    import org.apache.xpath.XPathContext;
028    
029    import org.apache.xml.dtm.DTMManager;
030    import org.apache.xml.dtm.DTM;
031    import java.sql.Connection;
032    import java.sql.Statement;
033    import java.sql.ResultSet;
034    import java.sql.ResultSetMetaData;
035    import java.sql.SQLException;
036    import java.sql.*;
037    import org.apache.xml.dtm.ref.*;
038    
039    /**
040     * The SQL Document is the main controlling class the executesa SQL Query
041     */
042    public class SQLDocument extends DTMDocument
043    {
044    
045      /**
046       */
047      private boolean DEBUG = false;
048    
049      /**
050       */
051      private static final String S_NAMESPACE = "http://xml.apache.org/xalan/SQLExtension";
052    
053    
054      /**
055       */
056      private static final String S_SQL = "sql";
057    
058      /**
059       */
060      private static final String S_ROW_SET = "row-set";
061    
062      /**
063       */
064      private static final String S_METADATA = "metadata";
065    
066      /**
067       */
068      private static final String S_COLUMN_HEADER = "column-header";
069    
070      /**
071       */
072      private static final String S_ROW = "row";
073    
074      /**
075       */
076      private static final String S_COL = "col";
077    
078      /**
079       */
080      private static final String S_OUT_PARAMETERS = "out-parameters";
081    
082      /**
083       */
084      private static final String S_CATALOGUE_NAME = "catalogue-name";
085      /**
086       */
087      private static final String S_DISPLAY_SIZE = "column-display-size";
088      /**
089       */
090      private static final String S_COLUMN_LABEL = "column-label";
091      /**
092       */
093      private static final String S_COLUMN_NAME = "column-name";
094      /**
095       */
096      private static final String S_COLUMN_TYPE = "column-type";
097      /**
098       */
099      private static final String S_COLUMN_TYPENAME = "column-typename";
100      /**
101       */
102      private static final String S_PRECISION = "precision";
103      /**
104       */
105      private static final String S_SCALE = "scale";
106      /**
107       */
108      private static final String S_SCHEMA_NAME = "schema-name";
109      /**
110       */
111      private static final String S_TABLE_NAME = "table-name";
112      /**
113       */
114      private static final String S_CASESENSITIVE = "case-sensitive";
115      /**
116       */
117      private static final String S_DEFINITELYWRITABLE = "definitely-writable";
118      /**
119       */
120      private static final String S_ISNULLABLE = "nullable";
121      /**
122       */
123      private static final String S_ISSIGNED = "signed";
124      /**
125       */
126      private static final String S_ISWRITEABLE = "writable";
127      /**
128       */
129      private static final String S_ISSEARCHABLE = "searchable";
130    
131      /**
132       */
133      private int m_SQL_TypeID = 0;
134      /**
135       */
136      private int m_MetaData_TypeID = 0;
137      /**
138       */
139      private int m_ColumnHeader_TypeID = 0;
140      /**
141       */
142      private int m_RowSet_TypeID = 0;
143      /**
144       */
145      private int m_Row_TypeID = 0;
146      /**
147       */
148      private int m_Col_TypeID = 0;
149      /**
150       */
151      private int m_OutParameter_TypeID = 0;
152    
153      /**
154       */
155      private int m_ColAttrib_CATALOGUE_NAME_TypeID = 0;
156      /**
157       */
158      private int m_ColAttrib_DISPLAY_SIZE_TypeID = 0;
159      /**
160       */
161      private int m_ColAttrib_COLUMN_LABEL_TypeID = 0;
162      /**
163       */
164      private int m_ColAttrib_COLUMN_NAME_TypeID = 0;
165      /**
166       */
167      private int m_ColAttrib_COLUMN_TYPE_TypeID = 0;
168      /**
169       */
170      private int m_ColAttrib_COLUMN_TYPENAME_TypeID = 0;
171      /**
172       */
173      private int m_ColAttrib_PRECISION_TypeID = 0;
174      /**
175       */
176      private int m_ColAttrib_SCALE_TypeID = 0;
177      /**
178       */
179      private int m_ColAttrib_SCHEMA_NAME_TypeID = 0;
180      /**
181       */
182      private int m_ColAttrib_TABLE_NAME_TypeID = 0;
183      /**
184       */
185      private int m_ColAttrib_CASESENSITIVE_TypeID = 0;
186      /**
187       */
188      private int m_ColAttrib_DEFINITELYWRITEABLE_TypeID = 0;
189      /**
190       */
191      private int m_ColAttrib_ISNULLABLE_TypeID = 0;
192      /**
193       */
194      private int m_ColAttrib_ISSIGNED_TypeID = 0;
195      /**
196       */
197      private int m_ColAttrib_ISWRITEABLE_TypeID = 0;
198      /**
199       */
200      private int m_ColAttrib_ISSEARCHABLE_TypeID = 0;
201    
202      /**
203       * The Statement used to extract the data from the database connection.
204       */
205      private Statement m_Statement = null;
206    
207      /**
208       * Expression COntext used to creat this document
209       * may be used to grab variables from the XSL processor
210       */
211      private ExpressionContext m_ExpressionContext = null;
212    
213      /**
214       * The Connection Pool where we has derived all of our connections
215       * for this document
216       */
217      private ConnectionPool m_ConnectionPool = null;
218    
219      /**
220       * The current ResultSet.
221       */
222      private ResultSet m_ResultSet = null;
223    
224      /**
225       * The parameter definitions if this is a callable
226       * statement with output parameters.
227       */
228      private SQLQueryParser m_QueryParser = null;
229    
230      /**
231       * As the column header array is built, keep the node index
232       * for each Column.
233       * The primary use of this is to locate the first attribute for
234       * each column in each row as we add records.
235       */
236      private int[] m_ColHeadersIdx;
237    
238      /**
239       * An indicator on how many columns are in this query
240       */
241      private int m_ColCount;
242    
243      /**
244       * The Index of the MetaData Node. Currently the MetaData Node contains the
245       *
246       */
247      private int m_MetaDataIdx = DTM.NULL;
248    
249      /**
250       * The index of the Row Set node. This is the sibling directly after
251       * the last Column Header.
252       */
253      private int m_RowSetIdx = DTM.NULL;
254    
255      /**
256       */
257      private int m_SQLIdx = DTM.NULL;
258    
259      /**
260       * Demark the first row element where we started adding rows into the
261       * Document.
262       */
263      private int m_FirstRowIdx = DTM.NULL;
264    
265      /**
266       * Keep track of the Last row inserted into the DTM from the ResultSet.
267       * This will be used as the index of the parent Row Element when adding
268       * a row.
269       */
270      private int m_LastRowIdx = DTM.NULL;
271    
272      /**
273       * Streaming Mode Control, In Streaming mode we reduce the memory
274       * footprint since we only use a single row instance.
275       */
276      private boolean m_StreamingMode = true;
277    
278      /**
279       * Multiple Result sets mode (metadata inside rowset).
280       */
281      private boolean m_MultipleResults = false;
282    
283      /**
284       * Flag to detect if an error occured during an operation
285       * Defines how errors are handled and how the SQL Connection
286       * is closed.
287       */
288      private boolean m_HasErrors = false;
289    
290      /**
291       * Is statement caching enabled.
292       */
293      private boolean m_IsStatementCachingEnabled = false;
294    
295      /**
296       * XConnection this document came from.
297       */
298      private XConnection m_XConnection = null;
299    
300      /**
301       * @param mgr
302       * @param ident
303       * @throws SQLException
304       */
305      // public cSQLDocument(DTMManager mgr, int ident, Statement stmt,
306      //  ResultSet singleResult, Vector paramdefs, boolean streamingMode,
307      // boolean multipleResults, boolean statementCachingEnabled) throws SQLException
308    
309      public SQLDocument(DTMManager mgr, int ident)
310      {
311        super(mgr, ident);
312      }
313    
314      /**
315       * This static method simplifies the creation of an SQL Document and allows
316       * us to embedd the complexity of creating / handling the dtmIdent inside
317       * the document. This type of method may better placed inside the DTMDocument
318       * code
319       */
320      public static SQLDocument getNewDocument(ExpressionContext exprContext)
321      {
322        DTMManager mgr =
323          ((XPathContext.XPathExpressionContext)exprContext).getDTMManager();
324        DTMManagerDefault  mgrDefault = (DTMManagerDefault) mgr;
325    
326    
327        int dtmIdent = mgrDefault.getFirstFreeDTMID();
328    
329        SQLDocument doc =
330          new SQLDocument(mgr, dtmIdent << DTMManager.IDENT_DTM_NODE_BITS);
331    
332        // Register the document
333        mgrDefault.addDTM(doc, dtmIdent);
334        doc.setExpressionContext(exprContext);
335    
336        return doc;
337      }
338    
339      /**
340       * When building the SQL Document, we need to store the Expression
341       * Context that was used to create the document. This will be se to
342       * reference items int he XSLT process such as any variables that were
343       * present.
344       */
345      protected void setExpressionContext(ExpressionContext expr)
346      {
347        m_ExpressionContext = expr;
348      }
349    
350      /**
351       * Return the context used to build this document
352       */
353      public ExpressionContext getExpressionContext()
354      {
355        return m_ExpressionContext;
356      }
357    
358    
359      public void execute(XConnection xconn, SQLQueryParser query)
360        throws SQLException
361      {
362        try
363        {
364          m_StreamingMode = "true".equals(xconn.getFeature("streaming"));
365          m_MultipleResults = "true".equals(xconn.getFeature("multiple-results"));
366          m_IsStatementCachingEnabled = "true".equals(xconn.getFeature("cache-statements"));
367          m_XConnection = xconn;
368          m_QueryParser = query;
369    
370          executeSQLStatement();
371    
372          createExpandedNameTable();
373    
374          // Start the document here
375          m_DocumentIdx = addElement(0, m_Document_TypeID, DTM.NULL, DTM.NULL);
376          m_SQLIdx = addElement(1, m_SQL_TypeID,  m_DocumentIdx, DTM.NULL);
377    
378    
379          if ( ! m_MultipleResults )
380            extractSQLMetaData(m_ResultSet.getMetaData());
381    
382          // Only grab the first row, subsequent rows will be
383          // fetched on demand.
384          // We need to do this here so at least on row is set up
385          // to measure when we are actually reading rows.
386    
387          // We won't grab the first record in case the skip function
388          // is applied prior to looking at the first record.
389          // JCG Changed 9/15/04
390          // addRowToDTMFromResultSet();
391        }
392        catch(SQLException e)
393        {
394          m_HasErrors = true;
395          throw e;
396        }
397      }
398    
399      private void executeSQLStatement() throws SQLException
400      {
401        m_ConnectionPool = m_XConnection.getConnectionPool();
402    
403        Connection conn = m_ConnectionPool.getConnection();
404    
405        if (! m_QueryParser.hasParameters() )
406        {
407          m_Statement = conn.createStatement();
408          m_ResultSet = m_Statement.executeQuery(m_QueryParser.getSQLQuery());
409    
410    
411        }
412    
413        else if (m_QueryParser.isCallable())
414        {
415          CallableStatement cstmt =
416            conn.prepareCall(m_QueryParser.getSQLQuery());
417          m_QueryParser.registerOutputParameters(cstmt);
418          m_QueryParser.populateStatement(cstmt, m_ExpressionContext);
419          m_Statement = cstmt;
420          if (! cstmt.execute()) throw new SQLException("Error in Callable Statement");
421    
422          m_ResultSet = m_Statement.getResultSet();
423        }
424        else
425        {
426          PreparedStatement stmt =
427            conn.prepareStatement(m_QueryParser.getSQLQuery());
428          m_QueryParser.populateStatement(stmt, m_ExpressionContext);
429          m_Statement = stmt;
430          m_ResultSet = stmt.executeQuery();
431        }
432    
433      }
434    
435      /**
436       * Push the record set forward value rows. Used to help in 
437       * SQL pagination.
438       * 
439       * @param value
440       */
441      public void skip( int value )
442      {
443        try
444        {
445          if (m_ResultSet != null) m_ResultSet.relative(value);
446        }
447        catch(Exception origEx)
448        {
449          // For now let's assume that the relative method is not supported.
450          // So let's do it manually.
451          try
452          {
453            for (int x=0; x<value; x++)
454            {
455              if (! m_ResultSet.next()) break;
456            }
457          }
458          catch(Exception e)
459          {
460            // If we still fail, add in both exceptions
461            m_XConnection.setError(origEx, this, checkWarnings());
462            m_XConnection.setError(e, this, checkWarnings());
463          }
464        }
465      }
466    
467    
468      /**
469       * Extract the Meta Data and build the Column Attribute List.
470       * @param meta
471       * @return
472       */
473      private void extractSQLMetaData( ResultSetMetaData meta )
474      {
475        // Build the Node Tree, just add the Column Header
476        // branch now, the Row & col elements will be added
477        // on request.
478    
479        // Add in the row-set Element
480    
481        // Add in the MetaData Element
482        m_MetaDataIdx = addElement(1, m_MetaData_TypeID,  m_MultipleResults ? m_RowSetIdx : m_SQLIdx, DTM.NULL);
483    
484        try
485        {
486          m_ColCount = meta.getColumnCount();
487          m_ColHeadersIdx = new int[m_ColCount];
488        }
489        catch(Exception e)
490        {
491          m_XConnection.setError(e, this, checkWarnings());
492          //error("ERROR Extracting Metadata");
493        }
494    
495        // The ColHeaderIdx will be used to keep track of the
496        // Element entries for the individual Column Header.
497        int lastColHeaderIdx = DTM.NULL;
498    
499        // JDBC Columms Start at 1
500        int i = 1;
501        for (i=1; i<= m_ColCount; i++)
502        {
503          m_ColHeadersIdx[i-1] =
504            addElement(2,m_ColumnHeader_TypeID, m_MetaDataIdx, lastColHeaderIdx);
505    
506          lastColHeaderIdx = m_ColHeadersIdx[i-1];
507          // A bit brute force, but not sure how to clean it up
508    
509          try
510          {
511            addAttributeToNode(
512              meta.getColumnName(i),
513              m_ColAttrib_COLUMN_NAME_TypeID, lastColHeaderIdx);
514          }
515          catch(Exception e)
516          {
517            addAttributeToNode(
518              S_ATTRIB_NOT_SUPPORTED,
519              m_ColAttrib_COLUMN_NAME_TypeID, lastColHeaderIdx);
520          }
521    
522          try
523          {
524            addAttributeToNode(
525              meta.getColumnLabel(i),
526              m_ColAttrib_COLUMN_LABEL_TypeID, lastColHeaderIdx);
527          }
528          catch(Exception e)
529          {
530            addAttributeToNode(
531              S_ATTRIB_NOT_SUPPORTED,
532              m_ColAttrib_COLUMN_LABEL_TypeID, lastColHeaderIdx);
533          }
534    
535          try
536          {
537            addAttributeToNode(
538              meta.getCatalogName(i),
539              m_ColAttrib_CATALOGUE_NAME_TypeID, lastColHeaderIdx);
540          }
541          catch(Exception e)
542          {
543            addAttributeToNode(
544              S_ATTRIB_NOT_SUPPORTED,
545              m_ColAttrib_CATALOGUE_NAME_TypeID, lastColHeaderIdx);
546          }
547    
548          try
549          {
550            addAttributeToNode(
551              new Integer(meta.getColumnDisplaySize(i)),
552              m_ColAttrib_DISPLAY_SIZE_TypeID, lastColHeaderIdx);
553          }
554          catch(Exception e)
555          {
556            addAttributeToNode(
557              S_ATTRIB_NOT_SUPPORTED,
558              m_ColAttrib_DISPLAY_SIZE_TypeID, lastColHeaderIdx);
559          }
560    
561          try
562          {
563            addAttributeToNode(
564              new Integer(meta.getColumnType(i)),
565              m_ColAttrib_COLUMN_TYPE_TypeID, lastColHeaderIdx);
566          }
567          catch(Exception e)
568          {
569            addAttributeToNode(
570              S_ATTRIB_NOT_SUPPORTED,
571              m_ColAttrib_COLUMN_TYPE_TypeID, lastColHeaderIdx);
572          }
573    
574          try
575          {
576            addAttributeToNode(
577              meta.getColumnTypeName(i),
578              m_ColAttrib_COLUMN_TYPENAME_TypeID, lastColHeaderIdx);
579          }
580          catch(Exception e)
581          {
582            addAttributeToNode(
583              S_ATTRIB_NOT_SUPPORTED,
584              m_ColAttrib_COLUMN_TYPENAME_TypeID, lastColHeaderIdx);
585          }
586    
587          try
588          {
589            addAttributeToNode(
590              new Integer(meta.getPrecision(i)),
591              m_ColAttrib_PRECISION_TypeID, lastColHeaderIdx);
592          }
593          catch(Exception e)
594          {
595            addAttributeToNode(
596              S_ATTRIB_NOT_SUPPORTED,
597              m_ColAttrib_PRECISION_TypeID, lastColHeaderIdx);
598          }
599          try
600          {
601            addAttributeToNode(
602              new Integer(meta.getScale(i)),
603              m_ColAttrib_SCALE_TypeID, lastColHeaderIdx);
604          }
605          catch(Exception e)
606          {
607            addAttributeToNode(
608              S_ATTRIB_NOT_SUPPORTED,
609              m_ColAttrib_SCALE_TypeID, lastColHeaderIdx);
610          }
611    
612          try
613          {
614            addAttributeToNode(
615              meta.getSchemaName(i),
616              m_ColAttrib_SCHEMA_NAME_TypeID, lastColHeaderIdx);
617          }
618          catch(Exception e)
619          {
620            addAttributeToNode(
621              S_ATTRIB_NOT_SUPPORTED,
622              m_ColAttrib_SCHEMA_NAME_TypeID, lastColHeaderIdx);
623          }
624          try
625          {
626            addAttributeToNode(
627              meta.getTableName(i),
628              m_ColAttrib_TABLE_NAME_TypeID, lastColHeaderIdx);
629          }
630          catch(Exception e)
631          {
632            addAttributeToNode(
633              S_ATTRIB_NOT_SUPPORTED,
634              m_ColAttrib_TABLE_NAME_TypeID, lastColHeaderIdx);
635          }
636    
637          try
638          {
639            addAttributeToNode(
640              meta.isCaseSensitive(i) ? S_ISTRUE : S_ISFALSE,
641              m_ColAttrib_CASESENSITIVE_TypeID, lastColHeaderIdx);
642          }
643          catch(Exception e)
644          {
645            addAttributeToNode(
646              S_ATTRIB_NOT_SUPPORTED,
647              m_ColAttrib_CASESENSITIVE_TypeID, lastColHeaderIdx);
648          }
649    
650          try
651          {
652            addAttributeToNode(
653              meta.isDefinitelyWritable(i) ? S_ISTRUE : S_ISFALSE,
654              m_ColAttrib_DEFINITELYWRITEABLE_TypeID, lastColHeaderIdx);
655          }
656          catch(Exception e)
657          {
658            addAttributeToNode(
659              S_ATTRIB_NOT_SUPPORTED,
660              m_ColAttrib_DEFINITELYWRITEABLE_TypeID, lastColHeaderIdx);
661          }
662    
663          try
664          {
665            addAttributeToNode(
666              meta.isNullable(i) != 0 ? S_ISTRUE : S_ISFALSE,
667              m_ColAttrib_ISNULLABLE_TypeID, lastColHeaderIdx);
668          }
669          catch(Exception e)
670          {
671            addAttributeToNode(
672              S_ATTRIB_NOT_SUPPORTED,
673              m_ColAttrib_ISNULLABLE_TypeID, lastColHeaderIdx);
674          }
675    
676          try
677          {
678            addAttributeToNode(
679              meta.isSigned(i) ? S_ISTRUE : S_ISFALSE,
680              m_ColAttrib_ISSIGNED_TypeID, lastColHeaderIdx);
681          }
682          catch(Exception e)
683          {
684            addAttributeToNode(
685              S_ATTRIB_NOT_SUPPORTED,
686              m_ColAttrib_ISSIGNED_TypeID, lastColHeaderIdx);
687          }
688    
689          try
690          {
691            addAttributeToNode(
692              meta.isWritable(i) == true ? S_ISTRUE : S_ISFALSE,
693              m_ColAttrib_ISWRITEABLE_TypeID, lastColHeaderIdx);
694          }
695          catch(Exception e)
696          {
697            addAttributeToNode(
698              S_ATTRIB_NOT_SUPPORTED,
699              m_ColAttrib_ISWRITEABLE_TypeID, lastColHeaderIdx);
700          }
701    
702          try
703          {
704            addAttributeToNode(
705              meta.isSearchable(i) == true ? S_ISTRUE : S_ISFALSE,
706              m_ColAttrib_ISSEARCHABLE_TypeID, lastColHeaderIdx);
707          }
708          catch(Exception e)
709          {
710            addAttributeToNode(
711              S_ATTRIB_NOT_SUPPORTED,
712              m_ColAttrib_ISSEARCHABLE_TypeID, lastColHeaderIdx);
713          }
714        }
715      }
716    
717      /**
718       * Populate the Expanded Name Table with the Node that we will use.
719       * Keep a reference of each of the types for access speed.
720       * @return
721       */
722      protected void createExpandedNameTable( )
723      {
724        super.createExpandedNameTable();
725    
726        m_SQL_TypeID =
727          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_SQL, DTM.ELEMENT_NODE);
728    
729        m_MetaData_TypeID =
730          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_METADATA, DTM.ELEMENT_NODE);
731    
732        m_ColumnHeader_TypeID =
733          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_COLUMN_HEADER, DTM.ELEMENT_NODE);
734        m_RowSet_TypeID =
735          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_ROW_SET, DTM.ELEMENT_NODE);
736        m_Row_TypeID =
737          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_ROW, DTM.ELEMENT_NODE);
738        m_Col_TypeID =
739          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_COL, DTM.ELEMENT_NODE);
740        m_OutParameter_TypeID =
741          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_OUT_PARAMETERS, DTM.ELEMENT_NODE);
742    
743        m_ColAttrib_CATALOGUE_NAME_TypeID =
744          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_CATALOGUE_NAME, DTM.ATTRIBUTE_NODE);
745        m_ColAttrib_DISPLAY_SIZE_TypeID =
746          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_DISPLAY_SIZE, DTM.ATTRIBUTE_NODE);
747        m_ColAttrib_COLUMN_LABEL_TypeID =
748          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_COLUMN_LABEL, DTM.ATTRIBUTE_NODE);
749        m_ColAttrib_COLUMN_NAME_TypeID =
750          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_COLUMN_NAME, DTM.ATTRIBUTE_NODE);
751        m_ColAttrib_COLUMN_TYPE_TypeID =
752          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_COLUMN_TYPE, DTM.ATTRIBUTE_NODE);
753        m_ColAttrib_COLUMN_TYPENAME_TypeID =
754          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_COLUMN_TYPENAME, DTM.ATTRIBUTE_NODE);
755        m_ColAttrib_PRECISION_TypeID =
756          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_PRECISION, DTM.ATTRIBUTE_NODE);
757        m_ColAttrib_SCALE_TypeID =
758          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_SCALE, DTM.ATTRIBUTE_NODE);
759        m_ColAttrib_SCHEMA_NAME_TypeID =
760          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_SCHEMA_NAME, DTM.ATTRIBUTE_NODE);
761        m_ColAttrib_TABLE_NAME_TypeID =
762          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_TABLE_NAME, DTM.ATTRIBUTE_NODE);
763        m_ColAttrib_CASESENSITIVE_TypeID =
764          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_CASESENSITIVE, DTM.ATTRIBUTE_NODE);
765        m_ColAttrib_DEFINITELYWRITEABLE_TypeID =
766          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_DEFINITELYWRITABLE, DTM.ATTRIBUTE_NODE);
767        m_ColAttrib_ISNULLABLE_TypeID =
768          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_ISNULLABLE, DTM.ATTRIBUTE_NODE);
769        m_ColAttrib_ISSIGNED_TypeID =
770          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_ISSIGNED, DTM.ATTRIBUTE_NODE);
771        m_ColAttrib_ISWRITEABLE_TypeID =
772          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_ISWRITEABLE, DTM.ATTRIBUTE_NODE);
773        m_ColAttrib_ISSEARCHABLE_TypeID =
774          m_expandedNameTable.getExpandedTypeID(S_NAMESPACE, S_ISSEARCHABLE, DTM.ATTRIBUTE_NODE);
775      }
776    
777    
778      /**
779       * Pull a record from the result set and map it to a DTM based ROW
780       * If we are in Streaming mode, then only create a single row and
781       * keep copying the data into the same row. This will keep the memory
782       * footprint constint independant of the RecordSet Size. If we are not
783       * in Streaming mode then create ROWS for the whole tree.
784       * @return
785       */
786      private boolean addRowToDTMFromResultSet( )
787      {
788        try
789        {
790          // If we have not started the RowSet yet, then add it to the
791          // tree.
792          if (m_FirstRowIdx == DTM.NULL)
793          {
794            m_RowSetIdx =
795              addElement(1, m_RowSet_TypeID,  m_SQLIdx, m_MultipleResults ? m_RowSetIdx : m_MetaDataIdx);
796            if ( m_MultipleResults ) extractSQLMetaData(m_ResultSet.getMetaData());
797          }
798    
799    
800          // Check to see if all the data has been read from the Query.
801          // If we are at the end the signal that event
802          if ( ! m_ResultSet.next())
803          {
804            // In Streaming mode, the current ROW will always point back
805            // to itself until all the data was read. Once the Query is
806            // empty then point the next row to DTM.NULL so that the stream
807            // ends. Only do this if we have statted the loop to begin with.
808    
809            if (m_StreamingMode && (m_LastRowIdx != DTM.NULL))
810            {
811              // We are at the end, so let's untie the mark
812              m_nextsib.setElementAt(DTM.NULL, m_LastRowIdx);
813            }
814    
815            m_ResultSet.close();
816            if ( m_MultipleResults )
817            {
818              while ( !m_Statement.getMoreResults() && m_Statement.getUpdateCount() >= 0 ) ;
819              m_ResultSet = m_Statement.getResultSet();
820            }
821            else
822              m_ResultSet = null;
823    
824            if ( m_ResultSet != null )
825            {
826              m_FirstRowIdx = DTM.NULL;
827              addRowToDTMFromResultSet();
828            }
829            else
830            {
831              Vector parameters = m_QueryParser.getParameters();
832              // Get output parameters.
833              if ( parameters != null )
834              {
835                int outParamIdx = addElement(1, m_OutParameter_TypeID,  m_SQLIdx, m_RowSetIdx);
836                int lastColID = DTM.NULL;
837                for ( int indx = 0 ; indx < parameters.size() ; indx++ )
838                {
839                  QueryParameter parm = (QueryParameter)parameters.elementAt(indx);
840                  if ( parm.isOutput() )
841                  {
842                    Object rawobj = ((CallableStatement)m_Statement).getObject(indx + 1);
843                    lastColID = addElementWithData(rawobj, 2, m_Col_TypeID, outParamIdx, lastColID);
844                    addAttributeToNode(parm.getName(), m_ColAttrib_COLUMN_NAME_TypeID, lastColID);
845                    addAttributeToNode(parm.getName(), m_ColAttrib_COLUMN_LABEL_TypeID, lastColID);
846                    addAttributeToNode(new Integer(parm.getType()), m_ColAttrib_COLUMN_TYPE_TypeID, lastColID);
847                    addAttributeToNode(parm.getTypeName(), m_ColAttrib_COLUMN_TYPENAME_TypeID, lastColID);
848                  }
849                }
850              }
851    
852              SQLWarning warn = checkWarnings();
853              if ( warn != null )   m_XConnection.setError(null, null, warn);
854            }
855    
856            return false;
857          }
858    
859          // If this is the first time here, start the new level
860          if (m_FirstRowIdx == DTM.NULL)
861          {
862            m_FirstRowIdx =
863              addElement(2, m_Row_TypeID, m_RowSetIdx, m_MultipleResults ? m_MetaDataIdx : DTM.NULL);
864    
865            m_LastRowIdx = m_FirstRowIdx;
866    
867            if (m_StreamingMode)
868            {
869              // Let's tie the rows together until the end.
870              m_nextsib.setElementAt(m_LastRowIdx, m_LastRowIdx);
871            }
872    
873          }
874          else
875          {
876            //
877            // If we are in Streaming mode, then only use a single row instance
878            if (! m_StreamingMode)
879            {
880              m_LastRowIdx = addElement(2, m_Row_TypeID, m_RowSetIdx, m_LastRowIdx);
881            }
882          }
883    
884          // If we are not in streaming mode, this will always be DTM.NULL
885          // If we are in streaming mode, it will only be DTM.NULL the first time
886          int colID = _firstch(m_LastRowIdx);
887    
888          // Keep Track of who our parent was when adding new col objects.
889          int pcolID = DTM.NULL;
890    
891          // Columns in JDBC Start at 1 and go to the Extent
892          for (int i=1; i<= m_ColCount; i++)
893          {
894            // Just grab the Column Object Type, we will convert it to a string
895            // later.
896            Object o = m_ResultSet.getObject(i);
897    
898            // Create a new column object if one does not exist.
899            // In Streaming mode, this mechinism will reuse the column
900            // data the second and subsequent row accesses.
901            if (colID == DTM.NULL)
902            {
903              pcolID = addElementWithData(o,3,m_Col_TypeID, m_LastRowIdx, pcolID);
904              cloneAttributeFromNode(pcolID, m_ColHeadersIdx[i-1]);
905            }
906            else
907            {
908              // We must be in streaming mode, so let's just replace the data
909              // If the firstch was not set then we have a major error
910              int dataIdent = _firstch(colID);
911              if (dataIdent == DTM.NULL)
912              {
913                error("Streaming Mode, Data Error");
914              }
915              else
916              {
917                m_ObjectArray.setAt(dataIdent, o);
918              }
919            } // If
920    
921            // In streaming mode, this will be !DTM.NULL
922            // So if the elements were already established then we
923            // should be able to walk them in order.
924            if (colID != DTM.NULL)
925            {
926              colID = _nextsib(colID);
927            }
928    
929          } // For Col Loop
930        }
931        catch(Exception e)
932        {
933          if (DEBUG)
934          {
935            System.out.println(
936              "SQL Error Fetching next row [" + e.getLocalizedMessage() + "]");
937          }
938    
939          m_XConnection.setError(e, this, checkWarnings());
940          m_HasErrors = true;
941        }
942    
943        // Only do a single row...
944        return true;
945      }
946    
947    
948      /**
949       * Used by the XConnection to determine if the Document should
950       * handle the document differently.
951       */
952      public boolean hasErrors()
953      {
954        return m_HasErrors;
955      }
956    
957      /**
958       * Close down any resources used by this document. If an SQL Error occure
959       * while the document was being accessed, the SQL Connection used to create
960       * this document will be released to the Connection Pool on error. This allows
961       * the COnnection Pool to give special attention to any connection that may
962       * be in a errored state.
963       *
964       */
965      public void close(boolean flushConnPool )
966      {
967        try
968        {
969          SQLWarning warn = checkWarnings();
970          if ( warn != null ) m_XConnection.setError(null, null, warn);
971        }
972        catch(Exception e) {}
973    
974        try
975        {
976          if (null != m_ResultSet)
977          {
978            m_ResultSet.close();
979            m_ResultSet = null;
980          }
981        }
982        catch(Exception e) {}
983    
984    
985        Connection conn = null;
986    
987        try
988        {
989          if (null != m_Statement)
990          {
991            conn = m_Statement.getConnection();
992            m_Statement.close();
993            m_Statement = null;
994          }
995        }
996        catch(Exception e) {}
997    
998        try
999        {
1000          if (conn != null)
1001          {
1002            if (m_HasErrors)  m_ConnectionPool.releaseConnectionOnError(conn);
1003            else m_ConnectionPool.releaseConnection(conn);
1004    //        if (flushConnPool)  m_ConnectionPool.freeUnused();
1005          }
1006        }
1007        catch(Exception e) {}
1008    
1009        getManager().release(this, true);
1010      }
1011    
1012      /**
1013       * @return
1014       */
1015      protected boolean nextNode( )
1016      {
1017        if (DEBUG) System.out.println("nextNode()");
1018        try
1019        {
1020          return false;
1021    //      return m_ResultSet.isAfterLast();
1022        }
1023        catch(Exception e)
1024        {
1025          return false;
1026        }
1027      }
1028    
1029      /**
1030       * @param identity
1031       * @return
1032       */
1033      protected int _nextsib( int identity )
1034      {
1035        // If we are asking for the next row and we have not
1036        // been there yet then let's see if we can get another
1037        // row from the ResultSet.
1038        //
1039    
1040      if ( m_ResultSet != null )
1041      {
1042          int id = _exptype(identity);
1043          
1044          // We need to prime the pump since we don't do it in execute any more.
1045          if (m_FirstRowIdx == DTM.NULL)
1046          {
1047            addRowToDTMFromResultSet();
1048          }
1049          
1050          if (
1051            ( id == m_Row_TypeID) &&
1052            (identity >= m_LastRowIdx) )
1053          {
1054            if (DEBUG) System.out.println("reading from the ResultSet");
1055            addRowToDTMFromResultSet();
1056          }
1057          else if ( m_MultipleResults && identity == m_RowSetIdx )
1058          {
1059            if (DEBUG) System.out.println("reading for next ResultSet");
1060          int startIdx = m_RowSetIdx;
1061          while ( startIdx == m_RowSetIdx && m_ResultSet != null )
1062                addRowToDTMFromResultSet();
1063          }
1064      }
1065    
1066        return super._nextsib(identity);
1067      }
1068    
1069      public void documentRegistration()
1070      {
1071        if (DEBUG) System.out.println("Document Registration");
1072      }
1073    
1074      public void documentRelease()
1075      {
1076        if (DEBUG) System.out.println("Document Release");
1077      }
1078    
1079      public SQLWarning checkWarnings()
1080      {
1081        SQLWarning warn = null;
1082        if ( m_Statement != null )
1083        {
1084          try
1085          {
1086            warn = m_Statement.getWarnings();
1087            m_Statement.clearWarnings();
1088          }
1089          catch (SQLException se) {}
1090        }
1091        return(warn);
1092      }
1093    }