1/*
2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.rowset;
27
28import java.sql.*;
29import javax.sql.*;
30import javax.naming.*;
31import java.io.*;
32import java.math.*;
33import java.util.*;
34
35import javax.sql.rowset.*;
36import javax.sql.rowset.spi.SyncProvider;
37import javax.sql.rowset.spi.SyncProviderException;
38
39/**
40 * The standard implementation of the <code>JoinRowSet</code>
41 * interface providing an SQL <code>JOIN</code> between <code>RowSet</code>
42 * objects.
43 * <P>
44 * The implementation provides an ANSI-style <code>JOIN</code> providing an
45 * inner join between two tables. Any unmatched rows in either table of the
46 * join are  discarded.
47 * <p>
48 * Typically, a <code>JoinRowSet</code> implementation is leveraged by
49 * <code>RowSet</code> instances that are in a disconnected environment and
50 * thus do not have the luxury of an open connection to the data source to
51 * establish logical relationships between themselves. In other words, it is
52 * largely <code>CachedRowSet</code> objects and implementations derived from
53 * the <code>CachedRowSet</code> interface that will use the <code>JoinRowSetImpl</code>
54 * implementation.
55 *
56 * @author Amit Handa, Jonathan Bruce
57 */
58public class JoinRowSetImpl extends WebRowSetImpl implements JoinRowSet {
59    /**
60     * A <code>Vector</code> object that contains the <code>RowSet</code> objects
61     * that have been added to this <code>JoinRowSet</code> object.
62     */
63    private Vector<CachedRowSetImpl> vecRowSetsInJOIN;
64
65    /**
66     * The <code>CachedRowSet</code> object that encapsulates this
67     * <code>JoinRowSet</code> object.
68     * When <code>RowSet</code> objects are added to this <code>JoinRowSet</code>
69     * object, they are also added to <i>crsInternal</i> to form the same kind of
70     * SQL <code>JOIN</code>.  As a result, methods for making updates to this
71     * <code>JoinRowSet</code> object can use <i>crsInternal</i> methods in their
72     * implementations.
73     */
74    private CachedRowSetImpl crsInternal;
75
76    /**
77     * A <code>Vector</code> object containing the types of join that have been set
78     * for this <code>JoinRowSet</code> object.
79     * The last join type set forms the basis of succeeding joins.
80     */
81    private Vector<Integer> vecJoinType;
82
83    /**
84     * A <code>Vector</code> object containing the names of all the tables entering
85     * the join.
86     */
87    private Vector<String> vecTableNames;
88
89    /**
90     * An <code>int</code> that indicates the column index of the match column.
91     */
92    private int iMatchKey;
93
94    /**
95     * A <code>String</code> object that stores the name of the match column.
96     */
97    private String strMatchKey ;
98
99    /**
100     * An array of <code>boolean</code> values indicating the types of joins supported
101     * by this <code>JoinRowSet</code> implementation.
102     */
103    boolean[] supportedJOINs;
104
105    /**
106     * The <code>WebRowSet</code> object that encapsulates this <code>JoinRowSet</code>
107     * object. This <code>WebRowSet</code> object allows this <code>JoinRowSet</code>
108     * object to leverage the properties and methods of a <code>WebRowSet</code>
109     * object.
110     */
111    private WebRowSet wrs;
112
113
114    /**
115     * Constructor for <code>JoinRowSetImpl</code> class. Configures various internal data
116     * structures to provide mechanisms required for <code>JoinRowSet</code> interface
117     * implementation.
118     *
119     * @throws SQLException if an error occurs in instantiating an instance of
120     * <code>JoinRowSetImpl</code>
121     */
122    public JoinRowSetImpl() throws SQLException {
123
124        vecRowSetsInJOIN = new Vector<CachedRowSetImpl>();
125        crsInternal = new CachedRowSetImpl();
126        vecJoinType = new Vector<Integer>();
127        vecTableNames = new Vector<String>();
128        iMatchKey = -1;
129        strMatchKey = null;
130        supportedJOINs =
131              new boolean[] {false, true, false, false, false};
132       try {
133           resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
134        } catch(IOException ioe) {
135            throw new RuntimeException(ioe);
136        }
137
138    }
139
140    /**
141     * Adds the given <code>RowSet</code> object to this
142     * <code>JoinRowSet</code> object.  If this
143     * rowset is the first to be added to the <code>JoinRowSet</code>
144     * object, it forms the basis for the <code>JOIN</code>
145     * relationships to be formed.
146     * <p>
147     * This method should be used when the given <code>RowSet</code> object
148     * already has a match column set.
149     *
150     * @param rowset the <code>RowSet</code> object that implements the
151     *         <code>Joinable</code> interface and is to be added
152     *         to this <code>JoinRowSet</code> object
153     * @throws SQLException if an empty <code>RowSet</code> is added to the to the
154     *         <code>JoinRowSet</code>; if a match column is not set; or if an
155     *         additional <code>RowSet</code> violates the active <code>JOIN</code>
156     * @see CachedRowSet#setMatchColumn
157     */
158    public void addRowSet(Joinable rowset) throws SQLException {
159        boolean boolColId, boolColName;
160
161        boolColId = false;
162        boolColName = false;
163        CachedRowSetImpl cRowset;
164
165        if(!(rowset instanceof RowSet)) {
166            throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.notinstance").toString());
167        }
168
169        if(rowset instanceof JdbcRowSetImpl ) {
170            cRowset = new CachedRowSetImpl();
171            cRowset.populate((RowSet)rowset);
172            if(cRowset.size() == 0){
173                throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.emptyrowset").toString());
174            }
175
176
177            try {
178                int matchColumnCount = 0;
179                for(int i=0; i< rowset.getMatchColumnIndexes().length; i++) {
180                    if(rowset.getMatchColumnIndexes()[i] != -1)
181                        ++ matchColumnCount;
182                    else
183                        break;
184                }
185                int[] pCol = new int[matchColumnCount];
186                for(int i=0; i<matchColumnCount; i++)
187                   pCol[i] = rowset.getMatchColumnIndexes()[i];
188                cRowset.setMatchColumn(pCol);
189            } catch(SQLException sqle) {
190
191            }
192
193        } else {
194             cRowset = (CachedRowSetImpl)rowset;
195             if(cRowset.size() == 0){
196                 throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.emptyrowset").toString());
197             }
198        }
199
200        // Either column id or column name will be set
201        // If both not set throw exception.
202
203        try {
204             iMatchKey = (cRowset.getMatchColumnIndexes())[0];
205        } catch(SQLException sqle) {
206           //if not set catch the exception but do nothing now.
207             boolColId = true;
208        }
209
210        try {
211             strMatchKey = (cRowset.getMatchColumnNames())[0];
212        } catch(SQLException sqle) {
213           //if not set catch the exception but do nothing now.
214           boolColName = true;
215        }
216
217        if(boolColId && boolColName) {
218           // neither setter methods have been used to set
219           throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.matchnotset").toString());
220        } else {
221           //if(boolColId || boolColName)
222           // either of the setter methods have been set.
223           if(boolColId){
224              //
225              ArrayList<Integer> indices = new ArrayList<>();
226              for(int i=0;i<cRowset.getMatchColumnNames().length;i++) {
227                  if( (strMatchKey = (cRowset.getMatchColumnNames())[i]) != null) {
228                      iMatchKey = cRowset.findColumn(strMatchKey);
229                      indices.add(iMatchKey);
230                  }
231                  else
232                      break;
233              }
234              int[] indexes = new int[indices.size()];
235              for(int i=0; i<indices.size();i++)
236                  indexes[i] = indices.get(i);
237              cRowset.setMatchColumn(indexes);
238              // Set the match column here because join will be
239              // based on columnId,
240              // (nested for loop in initJOIN() checks for equality
241              //  based on columnIndex)
242           } else {
243              //do nothing, iMatchKey is set.
244           }
245           // Now both iMatchKey and strMatchKey have been set pointing
246           // to the same column
247        }
248
249        // Till first rowset setJoinType may not be set because
250        // default type is JoinRowSet.INNER_JOIN which should
251        // be set and for subsequent additions of rowset, if not set
252        // keep on adding join type as JoinRowSet.INNER_JOIN
253        // to vecJoinType.
254
255        initJOIN(cRowset);
256    }
257
258    /**
259     * Adds the given <code>RowSet</code> object to the <code>JOIN</code> relation
260     * and sets the designated column as the match column.
261     * If the given <code>RowSet</code>
262     * object is the first to be added to this <code>JoinRowSet</code>
263     * object, it forms the basis of the <code>JOIN</code> relationship to be formed
264     * when other <code>RowSet</code> objects are added .
265     * <P>
266     * This method should be used when the given <code>RowSet</code> object
267     * does not already have a match column set.
268     *
269     * @param rowset a <code>RowSet</code> object to be added to
270     *         the <code>JOIN</code> relation; must implement the <code>Joinable</code>
271     *         interface
272     * @param columnIdx an <code>int</code> giving the index of the column to be set as
273     *         the match column
274     * @throws SQLException if (1) an empty <code>RowSet</code> object is added to this
275     *         <code>JoinRowSet</code> object, (2) a match column has not been set,
276     *         or (3) the <code>RowSet</code> object being added violates the active
277     *         <code>JOIN</code>
278     * @see CachedRowSet#unsetMatchColumn
279     */
280    public void addRowSet(RowSet rowset, int columnIdx) throws SQLException {
281        //passing the rowset as well as the columnIdx to form the joinrowset.
282
283        ((CachedRowSetImpl)rowset).setMatchColumn(columnIdx);
284
285        addRowSet((Joinable)rowset);
286    }
287
288    /**
289     * Adds the given <code>RowSet</code> object to the <code>JOIN</code> relationship
290     * and sets the designated column as the match column. If the given
291     * <code>RowSet</code>
292     * object is the first to be added to this <code>JoinRowSet</code>
293     * object, it forms the basis of the <code>JOIN</code> relationship to be formed
294     * when other <code>RowSet</code> objects are added .
295     * <P>
296     * This method should be used when the given <code>RowSet</code> object
297     * does not already have a match column set.
298     *
299     * @param rowset a <code>RowSet</code> object to be added to
300     *         the <code>JOIN</code> relation
301     * @param columnName a <code>String</code> object giving the name of the column
302     *        to be set as the match column; must implement the <code>Joinable</code>
303     *        interface
304     * @throws SQLException if (1) an empty <code>RowSet</code> object is added to this
305     *         <code>JoinRowSet</code> object, (2) a match column has not been set,
306     *         or (3) the <code>RowSet</code> object being added violates the active
307     *         <code>JOIN</code>
308     */
309    public void addRowSet(RowSet rowset, String columnName) throws SQLException {
310        //passing the rowset as well as the columnIdx to form the joinrowset.
311        ((CachedRowSetImpl)rowset).setMatchColumn(columnName);
312        addRowSet((Joinable)rowset);
313    }
314
315    /**
316     * Adds the given <code>RowSet</code> objects to the <code>JOIN</code> relationship
317     * and sets the designated columns as the match columns. If the first
318     * <code>RowSet</code> object in the array of <code>RowSet</code> objects
319     * is the first to be added to this <code>JoinRowSet</code>
320     * object, it forms the basis of the <code>JOIN</code> relationship to be formed
321     * when other <code>RowSet</code> objects are added.
322     * <P>
323     * The first <code>int</code>
324     * in <i>columnIdx</i> is used to set the match column for the first
325     * <code>RowSet</code> object in <i>rowset</i>, the second <code>int</code>
326     * in <i>columnIdx</i> is used to set the match column for the second
327     * <code>RowSet</code> object in <i>rowset</i>, and so on.
328     * <P>
329     * This method should be used when the given <code>RowSet</code> objects
330     * do not already have match columns set.
331     *
332     * @param rowset an array of <code>RowSet</code> objects to be added to
333     *         the <code>JOIN</code> relation; each <code>RowSet</code> object must
334     *         implement the <code>Joinable</code> interface
335     * @param columnIdx an array of <code>int</code> values designating the columns
336     *        to be set as the
337     *        match columns for the <code>RowSet</code> objects in <i>rowset</i>
338     * @throws SQLException if the number of <code>RowSet</code> objects in
339     *         <i>rowset</i> is not equal to the number of <code>int</code> values
340     *         in <i>columnIdx</i>
341     */
342    public void addRowSet(RowSet[] rowset,
343                          int[] columnIdx) throws SQLException {
344    //validate if length of rowset array is same as length of int array.
345     if(rowset.length != columnIdx.length) {
346        throw new SQLException
347             (resBundle.handleGetObject("joinrowsetimpl.numnotequal").toString());
348     } else {
349        for(int i=0; i< rowset.length; i++) {
350           ((CachedRowSetImpl)rowset[i]).setMatchColumn(columnIdx[i]);
351           addRowSet((Joinable)rowset[i]);
352        } //end for
353     } //end if
354
355   }
356
357
358    /**
359     * Adds the given <code>RowSet</code> objects to the <code>JOIN</code> relationship
360     * and sets the designated columns as the match columns. If the first
361     * <code>RowSet</code> object in the array of <code>RowSet</code> objects
362     * is the first to be added to this <code>JoinRowSet</code>
363     * object, it forms the basis of the <code>JOIN</code> relationship to be formed
364     * when other <code>RowSet</code> objects are added.
365     * <P>
366     * The first <code>String</code> object
367     * in <i>columnName</i> is used to set the match column for the first
368     * <code>RowSet</code> object in <i>rowset</i>, the second <code>String</code>
369     * object in <i>columnName</i> is used to set the match column for the second
370     * <code>RowSet</code> object in <i>rowset</i>, and so on.
371     * <P>
372     * This method should be used when the given <code>RowSet</code> objects
373     * do not already have match columns set.
374     *
375     * @param rowset an array of <code>RowSet</code> objects to be added to
376     *         the <code>JOIN</code> relation; each <code>RowSet</code> object must
377     *         implement the <code>Joinable</code> interface
378     * @param columnName an array of <code>String</code> objects designating the columns
379     *        to be set as the
380     *        match columns for the <code>RowSet</code> objects in <i>rowset</i>
381     * @throws SQLException if the number of <code>RowSet</code> objects in
382     *         <i>rowset</i> is not equal to the number of <code>String</code> objects
383     *         in <i>columnName</i>, an empty <code>JdbcRowSet</code> is added to the
384     *         <code>JoinRowSet</code>, if a match column is not set,
385     *         or one or the <code>RowSet</code> objects in <i>rowset</i> violates the
386     *         active <code>JOIN</code>
387     */
388    public void addRowSet(RowSet[] rowset,
389                          String[] columnName) throws SQLException {
390    //validate if length of rowset array is same as length of int array.
391
392     if(rowset.length != columnName.length) {
393        throw new SQLException
394                 (resBundle.handleGetObject("joinrowsetimpl.numnotequal").toString());
395     } else {
396        for(int i=0; i< rowset.length; i++) {
397           ((CachedRowSetImpl)rowset[i]).setMatchColumn(columnName[i]);
398           addRowSet((Joinable)rowset[i]);
399        } //end for
400     } //end if
401
402    }
403
404    /**
405     * Returns a Collection of the <code>RowSet</code> object instances
406     * currently residing with the instance of the <code>JoinRowSet</code>
407     * object instance. This should return the 'n' number of RowSet contained
408     * within the JOIN and maintain any updates that have occoured while in
409     * this union.
410     *
411     * @return A <code>Collection</code> of the added <code>RowSet</code>
412     * object instances
413     * @throws SQLException if an error occours generating a collection
414     * of the originating RowSets contained within the JOIN.
415     */
416    @SuppressWarnings("rawtypes")
417    public Collection getRowSets() throws SQLException {
418        return vecRowSetsInJOIN;
419    }
420
421    /**
422     * Returns a string array of the RowSet names currently residing
423     * with the <code>JoinRowSet</code> object instance.
424     *
425     * @return a string array of the RowSet names
426     * @throws SQLException if an error occours retrieving the RowSet names
427     * @see CachedRowSet#setTableName
428     */
429    public String[] getRowSetNames() throws SQLException {
430        Object [] arr = vecTableNames.toArray();
431        String []strArr = new String[arr.length];
432
433        for( int i = 0;i < arr.length; i++) {
434           strArr[i] = arr[i].toString();
435        }
436
437        return strArr;
438    }
439
440    /**
441     * Creates a separate <code>CachedRowSet</code> object that contains the data
442     * in this <code>JoinRowSet</code> object.
443     * <P>
444     * If any updates or modifications have been applied to this <code>JoinRowSet</code>
445     * object, the <code>CachedRowSet</code> object returned by this method will
446     * not be able to persist
447     * the changes back to the originating rows and tables in the
448     * data source because the data may be from different tables. The
449     * <code>CachedRowSet</code> instance returned should not
450     * contain modification data, such as whether a row has been updated or what the
451     * original values are.  Also, the <code>CachedRowSet</code> object should clear
452     * its  properties pertaining to
453     * its originating SQL statement. An application should reset the
454     * SQL statement using the <code>RowSet.setCommand</code> method.
455     * <p>
456     * To persist changes back to the data source, the <code>JoinRowSet</code> object
457     * calls the method <code>acceptChanges</code>. Implementations
458     * can leverage the internal data and update tracking in their
459     * implementations to interact with the <code>SyncProvider</code> to persist any
460     * changes.
461     *
462     * @return a <code>CachedRowSet</code> object containing the contents of this
463     *         <code>JoinRowSet</code> object
464     * @throws SQLException if an error occurs assembling the <code>CachedRowSet</code>
465     *         object
466     * @see javax.sql.RowSet
467     * @see javax.sql.rowset.CachedRowSet
468     * @see javax.sql.rowset.spi.SyncProvider
469     */
470    public CachedRowSet toCachedRowSet() throws SQLException {
471        return crsInternal;
472    }
473
474    /**
475     * Returns <code>true</code> if this <code>JoinRowSet</code> object supports
476     * an SQL <code>CROSS_JOIN</code> and <code>false</code> if it does not.
477     *
478     * @return <code>true</code> if the CROSS_JOIN is supported; <code>false</code>
479     *         otherwise
480     */
481    public boolean supportsCrossJoin() {
482        return supportedJOINs[JoinRowSet.CROSS_JOIN];
483    }
484
485    /**
486     * Returns <code>true</code> if this <code>JoinRowSet</code> object supports
487     * an SQL <code>INNER_JOIN</code> and <code>false</code> if it does not.
488     *
489     * @return true is the INNER_JOIN is supported; false otherwise
490     */
491    public boolean supportsInnerJoin() {
492        return supportedJOINs[JoinRowSet.INNER_JOIN];
493    }
494
495    /**
496     * Returns <code>true</code> if this <code>JoinRowSet</code> object supports
497     * an SQL <code>LEFT_OUTER_JOIN</code> and <code>false</code> if it does not.
498     *
499     * @return true is the LEFT_OUTER_JOIN is supported; false otherwise
500     */
501    public boolean supportsLeftOuterJoin() {
502        return supportedJOINs[JoinRowSet.LEFT_OUTER_JOIN];
503    }
504
505    /**
506     * Returns <code>true</code> if this <code>JoinRowSet</code> object supports
507     * an SQL <code>RIGHT_OUTER_JOIN</code> and <code>false</code> if it does not.
508     *
509     * @return true is the RIGHT_OUTER_JOIN is supported; false otherwise
510     */
511    public boolean supportsRightOuterJoin() {
512        return supportedJOINs[JoinRowSet.RIGHT_OUTER_JOIN];
513    }
514
515    /**
516     * Returns <code>true</code> if this <code>JoinRowSet</code> object supports
517     * an SQL <code>FULL_JOIN</code> and <code>false</code> if it does not.
518     *
519     * @return true is the FULL_JOIN is supported; false otherwise
520     */
521    public boolean supportsFullJoin() {
522        return supportedJOINs[JoinRowSet.FULL_JOIN];
523
524    }
525
526    /**
527     * Sets the type of SQL <code>JOIN</code> that this <code>JoinRowSet</code>
528     * object will use. This method
529     * allows an application to adjust the type of <code>JOIN</code> imposed
530     * on tables contained within this <code>JoinRowSet</code> object and to do it
531     * on the fly. The last <code>JOIN</code> type set determines the type of
532     * <code>JOIN</code> to be performed.
533     * <P>
534     * Implementations should throw an <code>SQLException</code> if they do
535     * not support the given <code>JOIN</code> type.
536     *
537     * @param type one of the standard <code>JoinRowSet</code> constants
538     *        indicating the type of <code>JOIN</code>.  Must be one of the
539     *        following:
540     *            <code>JoinRowSet.CROSS_JOIN</code>
541     *            <code>JoinRowSet.INNER_JOIN</code>
542     *            <code>JoinRowSet.LEFT_OUTER_JOIN</code>
543     *            <code>JoinRowSet.RIGHT_OUTER_JOIN</code>, or
544     *            <code>JoinRowSet.FULL_JOIN</code>
545     * @throws SQLException if an unsupported <code>JOIN</code> type is set
546     */
547    public void setJoinType(int type) throws SQLException {
548        // The join which governs the join of two rowsets is the last
549        // join set, using setJoinType
550
551       if (type >= JoinRowSet.CROSS_JOIN && type <= JoinRowSet.FULL_JOIN) {
552           if (type != JoinRowSet.INNER_JOIN) {
553               // This 'if' will be removed after all joins are implemented.
554               throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.notsupported").toString());
555           } else {
556              Integer Intgr = Integer.valueOf(JoinRowSet.INNER_JOIN);
557              vecJoinType.add(Intgr);
558           }
559       } else {
560          throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.notdefined").toString());
561       }  //end if
562    }
563
564
565    /**
566     * This checks for a match column for
567     * whether it exists or not.
568     *
569     * @param CachedRowSet</code> object whose match column needs to be checked.
570     * @throws SQLException if MatchColumn is not set.
571     */
572    private boolean checkforMatchColumn(Joinable rs) throws SQLException {
573        int[] i = rs.getMatchColumnIndexes();
574        if (i.length <= 0) {
575            return false;
576        }
577        return true;
578    }
579
580    /**
581     * Internal initialization of <code>JoinRowSet</code>.
582     */
583    private void initJOIN(CachedRowSet rowset) throws SQLException {
584        try {
585
586            CachedRowSetImpl cRowset = (CachedRowSetImpl)rowset;
587            // Create a new CachedRowSet object local to this function.
588            CachedRowSetImpl crsTemp = new CachedRowSetImpl();
589            RowSetMetaDataImpl rsmd = new RowSetMetaDataImpl();
590
591            /* The following 'if block' seems to be always going true.
592               commenting this out for present
593
594            if (!supportedJOINs[1]) {
595                throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.notsupported").toString());
596            }
597
598            */
599
600            if (vecRowSetsInJOIN.isEmpty() ) {
601
602                // implies first cRowset to be added to the Join
603                // simply add this as a CachedRowSet.
604                // Also add it to the class variable of type vector
605                // do not need to check "type" of Join but it should be set.
606                crsInternal = (CachedRowSetImpl)rowset.createCopy();
607                crsInternal.setMetaData((RowSetMetaDataImpl)cRowset.getMetaData());
608                // metadata will also set the MatchColumn.
609
610                vecRowSetsInJOIN.add(cRowset);
611
612            } else {
613                // At this point we are ready to add another rowset to 'this' object
614                // Check the size of vecJoinType and vecRowSetsInJoin
615
616                // If nothing is being set, internally call setJoinType()
617                // to set to JoinRowSet.INNER_JOIN.
618
619                // For two rowsets one (valid) entry should be there in vecJoinType
620                // For three rowsets two (valid) entries should be there in vecJoinType
621
622                // Maintain vecRowSetsInJoin = vecJoinType + 1
623
624
625                if( (vecRowSetsInJOIN.size() - vecJoinType.size() ) == 2 ) {
626                   // we are going to add next rowset and setJoinType has not been set
627                   // recently, so set it to setJoinType() to JoinRowSet.INNER_JOIN.
628                   // the default join type
629
630                        setJoinType(JoinRowSet.INNER_JOIN);
631                } else if( (vecRowSetsInJOIN.size() - vecJoinType.size() ) == 1  ) {
632                   // do nothing setjoinType() has been set by programmer
633                }
634
635                // Add the table names to the class variable of type vector.
636                vecTableNames.add(crsInternal.getTableName());
637                vecTableNames.add(cRowset.getTableName());
638                // Now we have two rowsets crsInternal and cRowset which need
639                // to be INNER JOIN'ED to form a new rowset
640                // Compare table1.MatchColumn1.value1 == { table2.MatchColumn2.value1
641                //                              ... upto table2.MatchColumn2.valueN }
642                //     ...
643                // Compare table1.MatchColumn1.valueM == { table2.MatchColumn2.value1
644                //                              ... upto table2.MatchColumn2.valueN }
645                //
646                // Assuming first rowset has M rows and second N rows.
647
648                int rowCount2 = cRowset.size();
649                int rowCount1 = crsInternal.size();
650
651                // total columns in the new CachedRowSet will be sum of both -1
652                // (common column)
653                int matchColumnCount = 0;
654                for(int i=0; i< crsInternal.getMatchColumnIndexes().length; i++) {
655                    if(crsInternal.getMatchColumnIndexes()[i] != -1)
656                        ++ matchColumnCount;
657                    else
658                        break;
659                }
660
661                rsmd.setColumnCount
662                    (crsInternal.getMetaData().getColumnCount() +
663                     cRowset.getMetaData().getColumnCount() - matchColumnCount);
664
665                crsTemp.setMetaData(rsmd);
666                crsInternal.beforeFirst();
667                cRowset.beforeFirst();
668                for (int i = 1 ; i <= rowCount1 ; i++) {
669                  if(crsInternal.isAfterLast() ) {
670                    break;
671                  }
672                  if(crsInternal.next()) {
673                    cRowset.beforeFirst();
674                    for(int j = 1 ; j <= rowCount2 ; j++) {
675                         if( cRowset.isAfterLast()) {
676                            break;
677                         }
678                         if(cRowset.next()) {
679                             boolean match = true;
680                             for(int k=0; k<matchColumnCount; k++) {
681                                 if (!crsInternal.getObject( crsInternal.getMatchColumnIndexes()[k]).equals
682                                         (cRowset.getObject(cRowset.getMatchColumnIndexes()[k]))) {
683                                     match = false;
684                                     break;
685                                 }
686                             }
687                             if (match) {
688
689                                int p;
690                                int colc = 0;   // reset this variable everytime you loop
691                                // re create a JoinRowSet in crsTemp object
692                                crsTemp.moveToInsertRow();
693
694                                // create a new rowset crsTemp with data from first rowset
695                            for( p=1;
696                                p<=crsInternal.getMetaData().getColumnCount();p++) {
697
698                                match = false;
699                                for(int k=0; k<matchColumnCount; k++) {
700                                 if (p == crsInternal.getMatchColumnIndexes()[k] ) {
701                                     match = true;
702                                     break;
703                                 }
704                                }
705                                    if ( !match ) {
706
707                                    crsTemp.updateObject(++colc, crsInternal.getObject(p));
708                                    // column type also needs to be passed.
709
710                                    rsmd.setColumnName
711                                        (colc, crsInternal.getMetaData().getColumnName(p));
712                                    rsmd.setTableName(colc, crsInternal.getTableName());
713
714                                    rsmd.setColumnType(p, crsInternal.getMetaData().getColumnType(p));
715                                    rsmd.setAutoIncrement(p, crsInternal.getMetaData().isAutoIncrement(p));
716                                    rsmd.setCaseSensitive(p, crsInternal.getMetaData().isCaseSensitive(p));
717                                    rsmd.setCatalogName(p, crsInternal.getMetaData().getCatalogName(p));
718                                    rsmd.setColumnDisplaySize(p, crsInternal.getMetaData().getColumnDisplaySize(p));
719                                    rsmd.setColumnLabel(p, crsInternal.getMetaData().getColumnLabel(p));
720                                    rsmd.setColumnType(p, crsInternal.getMetaData().getColumnType(p));
721                                    rsmd.setColumnTypeName(p, crsInternal.getMetaData().getColumnTypeName(p));
722                                    rsmd.setCurrency(p,crsInternal.getMetaData().isCurrency(p) );
723                                    rsmd.setNullable(p, crsInternal.getMetaData().isNullable(p));
724                                    rsmd.setPrecision(p, crsInternal.getMetaData().getPrecision(p));
725                                    rsmd.setScale(p, crsInternal.getMetaData().getScale(p));
726                                    rsmd.setSchemaName(p, crsInternal.getMetaData().getSchemaName(p));
727                                    rsmd.setSearchable(p, crsInternal.getMetaData().isSearchable(p));
728                                    rsmd.setSigned(p, crsInternal.getMetaData().isSigned(p));
729
730                                } else {
731                                    // will happen only once, for that  merged column pass
732                                    // the types as OBJECT, if types not equal
733
734                                    crsTemp.updateObject(++colc, crsInternal.getObject(p));
735
736                                    rsmd.setColumnName(colc, crsInternal.getMetaData().getColumnName(p));
737                                    rsmd.setTableName
738                                        (colc, crsInternal.getTableName()+
739                                         "#"+
740                                         cRowset.getTableName());
741
742
743                                    rsmd.setColumnType(p, crsInternal.getMetaData().getColumnType(p));
744                                    rsmd.setAutoIncrement(p, crsInternal.getMetaData().isAutoIncrement(p));
745                                    rsmd.setCaseSensitive(p, crsInternal.getMetaData().isCaseSensitive(p));
746                                    rsmd.setCatalogName(p, crsInternal.getMetaData().getCatalogName(p));
747                                    rsmd.setColumnDisplaySize(p, crsInternal.getMetaData().getColumnDisplaySize(p));
748                                    rsmd.setColumnLabel(p, crsInternal.getMetaData().getColumnLabel(p));
749                                    rsmd.setColumnType(p, crsInternal.getMetaData().getColumnType(p));
750                                    rsmd.setColumnTypeName(p, crsInternal.getMetaData().getColumnTypeName(p));
751                                    rsmd.setCurrency(p,crsInternal.getMetaData().isCurrency(p) );
752                                    rsmd.setNullable(p, crsInternal.getMetaData().isNullable(p));
753                                    rsmd.setPrecision(p, crsInternal.getMetaData().getPrecision(p));
754                                    rsmd.setScale(p, crsInternal.getMetaData().getScale(p));
755                                    rsmd.setSchemaName(p, crsInternal.getMetaData().getSchemaName(p));
756                                    rsmd.setSearchable(p, crsInternal.getMetaData().isSearchable(p));
757                                    rsmd.setSigned(p, crsInternal.getMetaData().isSigned(p));
758
759                                    //don't do ++colc in the above statement
760                                } //end if
761                            } //end for
762
763
764                            // append the rowset crsTemp, with data from second rowset
765                            for(int q=1;
766                                q<= cRowset.getMetaData().getColumnCount();q++) {
767
768                                match = false;
769                                for(int k=0; k<matchColumnCount; k++) {
770                                 if (q == cRowset.getMatchColumnIndexes()[k] ) {
771                                     match = true;
772                                     break;
773                                 }
774                                }
775                                    if ( !match ) {
776
777                                    crsTemp.updateObject(++colc, cRowset.getObject(q));
778
779                                    rsmd.setColumnName
780                                        (colc, cRowset.getMetaData().getColumnName(q));
781                                    rsmd.setTableName(colc, cRowset.getTableName());
782
783                                    /**
784                                      * This will happen for a special case scenario. The value of 'p'
785                                      * will always be one more than the number of columns in the first
786                                      * rowset in the join. So, for a value of 'q' which is the number of
787                                      * columns in the second rowset that participates in the join.
788                                      * So decrement value of 'p' by 1 else `p+q-1` will be out of range.
789                                      **/
790
791                                    //if((p+q-1) > ((crsInternal.getMetaData().getColumnCount()) +
792                                      //            (cRowset.getMetaData().getColumnCount())     - 1)) {
793                                      // --p;
794                                    //}
795                                    rsmd.setColumnType(p+q-1, cRowset.getMetaData().getColumnType(q));
796                                    rsmd.setAutoIncrement(p+q-1, cRowset.getMetaData().isAutoIncrement(q));
797                                    rsmd.setCaseSensitive(p+q-1, cRowset.getMetaData().isCaseSensitive(q));
798                                    rsmd.setCatalogName(p+q-1, cRowset.getMetaData().getCatalogName(q));
799                                    rsmd.setColumnDisplaySize(p+q-1, cRowset.getMetaData().getColumnDisplaySize(q));
800                                    rsmd.setColumnLabel(p+q-1, cRowset.getMetaData().getColumnLabel(q));
801                                    rsmd.setColumnType(p+q-1, cRowset.getMetaData().getColumnType(q));
802                                    rsmd.setColumnTypeName(p+q-1, cRowset.getMetaData().getColumnTypeName(q));
803                                    rsmd.setCurrency(p+q-1,cRowset.getMetaData().isCurrency(q) );
804                                    rsmd.setNullable(p+q-1, cRowset.getMetaData().isNullable(q));
805                                    rsmd.setPrecision(p+q-1, cRowset.getMetaData().getPrecision(q));
806                                    rsmd.setScale(p+q-1, cRowset.getMetaData().getScale(q));
807                                    rsmd.setSchemaName(p+q-1, cRowset.getMetaData().getSchemaName(q));
808                                    rsmd.setSearchable(p+q-1, cRowset.getMetaData().isSearchable(q));
809                                    rsmd.setSigned(p+q-1, cRowset.getMetaData().isSigned(q));
810                                }
811                                else {
812                                    --p;
813                                }
814                            }
815                            crsTemp.insertRow();
816                            crsTemp.moveToCurrentRow();
817
818                        } else {
819                            // since not equa12
820                            // so do nothing
821                        } //end if
822                         // bool1 = cRowset.next();
823                         }
824
825                    } // end inner for
826                     //bool2 = crsInternal.next();
827                   }
828
829                } //end outer for
830                crsTemp.setMetaData(rsmd);
831                crsTemp.setOriginal();
832
833                // Now the join is done.
834               // Make crsInternal = crsTemp, to be ready for next merge, if at all.
835
836                int[] pCol = new int[matchColumnCount];
837                for(int i=0; i<matchColumnCount; i++)
838                   pCol[i] = crsInternal.getMatchColumnIndexes()[i];
839
840                crsInternal = (CachedRowSetImpl)crsTemp.createCopy();
841
842                // Because we add the first rowset as crsInternal to the
843                // merged rowset, so pCol will point to the Match column.
844                // until reset, am not sure we should set this or not(?)
845                // if this is not set next inner join won't happen
846                // if we explicitly do not set a set MatchColumn of
847                // the new crsInternal.
848
849                crsInternal.setMatchColumn(pCol);
850                // Add the merged rowset to the class variable of type vector.
851                crsInternal.setMetaData(rsmd);
852                vecRowSetsInJOIN.add(cRowset);
853            } //end if
854        } catch(SQLException sqle) {
855            // %%% Exception should not dump here:
856            sqle.printStackTrace();
857            throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.initerror").toString() + sqle);
858        } catch (Exception e) {
859            e.printStackTrace();
860            throw new SQLException(resBundle.handleGetObject("joinrowsetimpl.genericerr").toString() + e);
861        }
862    }
863
864    /**
865     * Return a SQL-like description of the <code>WHERE</code> clause being used
866     * in a <code>JoinRowSet</code> object instance. An implementation can describe
867     * the <code>WHERE</code> clause of the SQL <code>JOIN</code> by supplying a <code>SQL</code>
868     * strings description of <code>JOIN</code> or provide a textual description to assist
869     * applications using a <code>JoinRowSet</code>.
870     *
871     * @return whereClause a textual or SQL descripition of the logical
872     * <code>WHERE</code> cluase used in the <code>JoinRowSet</code> instance
873     * @throws SQLException if an error occurs in generating a representation
874     * of the <code>WHERE</code> clause.
875     */
876    public String getWhereClause() throws SQLException {
877
878       String strWhereClause = "Select ";
879       String whereClause;
880       String tabName= "";
881       String strTabName = "";
882       int sz,cols;
883       int j;
884       CachedRowSetImpl crs;
885
886       // get all the column(s) names from each rowset.
887       // append them with their tablenames i.e. tableName.columnName
888       // Select tableName1.columnName1,..., tableNameX.columnNameY
889       // from tableName1,...tableNameX where
890       // tableName1.(rowset1.getMatchColumnName()) ==
891       // tableName2.(rowset2.getMatchColumnName()) + "and" +
892       // tableNameX.(rowsetX.getMatchColumnName()) ==
893       // tableNameZ.(rowsetZ.getMatchColumnName()));
894
895       sz = vecRowSetsInJOIN.size();
896       for(int i=0;i<sz; i++) {
897          crs = vecRowSetsInJOIN.get(i);
898          cols = crs.getMetaData().getColumnCount();
899          tabName = tabName.concat(crs.getTableName());
900          strTabName = strTabName.concat(tabName+", ");
901          j = 1;
902          while(j<cols) {
903
904            strWhereClause = strWhereClause.concat
905                (tabName+"."+crs.getMetaData().getColumnName(j++));
906            strWhereClause = strWhereClause.concat(", ");
907          } //end while
908        } //end for
909
910
911        // now remove the last ","
912        strWhereClause = strWhereClause.substring
913             (0, strWhereClause.lastIndexOf(','));
914
915        // Add from clause
916        strWhereClause = strWhereClause.concat(" from ");
917
918        // Add the table names.
919        strWhereClause = strWhereClause.concat(strTabName);
920
921        //Remove the last ","
922        strWhereClause = strWhereClause.substring
923             (0, strWhereClause.lastIndexOf(','));
924
925        // Add the where clause
926        strWhereClause = strWhereClause.concat(" where ");
927
928        // Get the match columns
929        // rowset1.getMatchColumnName() == rowset2.getMatchColumnName()
930         for(int i=0;i<sz; i++) {
931             strWhereClause = strWhereClause.concat(
932               vecRowSetsInJOIN.get(i).getMatchColumnNames()[0]);
933             if(i%2!=0) {
934               strWhereClause = strWhereClause.concat("=");
935             }  else {
936               strWhereClause = strWhereClause.concat(" and");
937             }
938          strWhereClause = strWhereClause.concat(" ");
939         }
940
941        return strWhereClause;
942    }
943
944
945    /**
946     * Moves the cursor down one row from its current position and
947     * returns <code>true</code> if the new cursor position is a
948     * valid row.
949     * The cursor for a new <code>ResultSet</code> object is initially
950     * positioned before the first row. The first call to the method
951     * <code>next</code> moves the cursor to the first row, making it
952     * the current row; the second call makes the second row the
953     * current row, and so on.
954     *
955     * <P>If an input stream from the previous row is open, it is
956     * implicitly closed. The <code>ResultSet</code> object's warning
957     * chain is cleared when a new row is read.
958     *
959     * @return <code>true</code> if the new current row is valid;
960     *         <code>false</code> if there are no more rows
961     * @throws SQLException if an error occurs or
962     *            the cursor is not positioned in the rowset, before
963     *            the first row, or after the last row
964     */
965    public boolean next() throws SQLException {
966        return crsInternal.next();
967    }
968
969
970    /**
971     * Releases the current contents of this rowset, discarding  outstanding
972     * updates.  The rowset contains no rows after the method
973     * <code>release</code> is called. This method sends a
974     * <code>RowSetChangedEvent</code> object to all registered listeners prior
975     * to returning.
976     *
977     * @throws SQLException if an error occurs
978     */
979    public void close() throws SQLException {
980        crsInternal.close();
981    }
982
983
984    /**
985     * Reports whether the last column read was SQL <code>NULL</code>.
986     * Note that you must first call the method <code>getXXX</code>
987     * on a column to try to read its value and then call the method
988     * <code>wasNull</code> to determine whether the value was
989     * SQL <code>NULL</code>.
990     *
991     * @return <code>true</code> if the value in the last column read
992     *         was SQL <code>NULL</code>; <code>false</code> otherwise
993     * @throws SQLException if an error occurs
994     */
995    public boolean wasNull() throws SQLException {
996        return crsInternal.wasNull();
997    }
998
999    /**
1000     * Retrieves the value of the designated column in the current row
1001     * of this <code>JoinRowSetImpl</code> object as a
1002     * <code>String</code> object.
1003     *
1004     * @param columnIndex the first column is <code>1</code>, the second
1005     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1006     *        and equal to or less than the number of columns in the rowset
1007     * @return the column value; if the value is SQL <code>NULL</code>, the
1008     *         result is <code>null</code>
1009     * @throws SQLException if the given column index is out of bounds or
1010     *            the cursor is not on a valid row
1011     */
1012    public String getString(int columnIndex) throws SQLException {
1013        return crsInternal.getString(columnIndex);
1014    }
1015
1016    /**
1017     * Retrieves the value of the designated column in the current row
1018     * of this <code>JoinRowSetImpl</code> object as a
1019     * <code>boolean</code> value.
1020     *
1021     * @param columnIndex the first column is <code>1</code>, the second
1022     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1023     *        and equal to or less than the number of columns in the rowset
1024     * @return the column value; if the value is SQL <code>NULL</code>, the
1025     *         result is <code>false</code>
1026     * @throws SQLException if the given column index is out of bounds,
1027     *            the cursor is not on a valid row, or this method fails
1028     */
1029    public boolean getBoolean(int columnIndex) throws SQLException {
1030        return crsInternal.getBoolean(columnIndex);
1031    }
1032
1033    /**
1034     * Retrieves the value of the designated column in the current row
1035     * of this <code>JoinRowSetImpl</code> object as a
1036     * <code>byte</code> value.
1037     *
1038     * @param columnIndex the first column is <code>1</code>, the second
1039     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1040     *        and equal to or less than the number of columns in the rowset
1041     * @return the column value; if the value is SQL <code>NULL</code>, the
1042     *         result is <code>0</code>
1043     * @throws SQLException if the given column index is out of bounds,
1044     *            the cursor is not on a valid row, or this method fails
1045     */
1046    public byte getByte(int columnIndex) throws SQLException {
1047        return crsInternal.getByte(columnIndex);
1048    }
1049
1050    /**
1051     * Retrieves the value of the designated column in the current row
1052     * of this <code>JoinRowSetImpl</code> object as a
1053             * <code>short</code> value.
1054             *
1055     * @param columnIndex the first column is <code>1</code>, the second
1056     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1057     *        and equal to or less than the number of columns in the rowset
1058     * @return the column value; if the value is SQL <code>NULL</code>, the
1059     *         result is <code>0</code>
1060     * @throws SQLException if the given column index is out of bounds,
1061     *            the cursor is not on a valid row, or this method fails
1062     */
1063    public short getShort(int columnIndex) throws SQLException {
1064        return crsInternal.getShort(columnIndex);
1065    }
1066
1067    /**
1068     * Retrieves the value of the designated column in the current row
1069     * of this <code>JoinRowSetImpl</code> object as a
1070     * <code>short</code> value.
1071     *
1072     * @param columnIndex the first column is <code>1</code>, the second
1073     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1074     *        and equal to or less than the number of columns in the rowset
1075     * @return the column value; if the value is SQL <code>NULL</code>, the
1076     *         result is <code>0</code>
1077     * @throws SQLException if the given column index is out of bounds,
1078     *            the cursor is not on a valid row, or this method fails
1079     */
1080    public int getInt(int columnIndex) throws SQLException {
1081        return crsInternal.getInt(columnIndex);
1082    }
1083
1084    /**
1085     * Retrieves the value of the designated column in the current row
1086     * of this <code>JoinRowSetImpl</code> object as a
1087     * <code>long</code> value.
1088     *
1089     * @param columnIndex the first column is <code>1</code>, the second
1090     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1091     *        and equal to or less than the number of columns in the rowset
1092     * @return the column value; if the value is SQL <code>NULL</code>, the
1093     *         result is <code>0</code>
1094     * @throws SQLException if the given column index is out of bounds,
1095     *            the cursor is not on a valid row, or this method fails
1096     */
1097    public long getLong(int columnIndex) throws SQLException {
1098        return crsInternal.getLong(columnIndex);
1099    }
1100
1101    /**
1102     * Retrieves the value of the designated column in the current row
1103     * of this <code>JoinRowSetImpl</code> object as a
1104     * <code>float</code> value.
1105     *
1106     * @param columnIndex the first column is <code>1</code>, the second
1107     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1108     *        and equal to or less than the number of columns in the rowset
1109     * @return the column value; if the value is SQL <code>NULL</code>, the
1110     *         result is <code>0</code>
1111     * @throws SQLException if the given column index is out of bounds,
1112     *            the cursor is not on a valid row, or this method fails
1113     */
1114    public float getFloat(int columnIndex) throws SQLException {
1115        return crsInternal.getFloat(columnIndex);
1116    }
1117
1118    /**
1119     * Retrieves the value of the designated column in the current row
1120     * of this <code>JoinRowSetImpl</code> object as a
1121     * <code>double</code> value.
1122     *
1123     * @param columnIndex the first column is <code>1</code>, the second
1124     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1125     *        and equal to or less than the number of columns in the rowset
1126     * @return the column value; if the value is SQL <code>NULL</code>, the
1127     *         result is <code>0</code>
1128     * @throws SQLException if the given column index is out of bounds,
1129     *            the cursor is not on a valid row, or this method fails
1130     */
1131    public double getDouble(int columnIndex) throws SQLException {
1132        return crsInternal.getDouble(columnIndex);
1133    }
1134
1135    /**
1136     * Retrieves the value of the designated column in the current row
1137     * of this <code>JoinRowSetImpl</code> object as a
1138     * <code>java.math.BigDecimal</code> object.
1139     * <P>
1140     * This method is deprecated; use the version of <code>getBigDecimal</code>
1141     * that does not take a scale parameter and returns a value with full
1142     * precision.
1143     *
1144     * @param columnIndex the first column is <code>1</code>, the second
1145     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1146     *        and equal to or less than the number of columns in the rowset
1147     * @param scale the number of digits to the right of the decimal point in the
1148     *        value returned
1149     * @return the column value with the specified number of digits to the right
1150     *         of the decimal point; if the value is SQL <code>NULL</code>, the
1151     *         result is <code>null</code>
1152     * @throws SQLException if the given column index is out of bounds,
1153     *            the cursor is not on a valid row, or this method fails
1154     * @deprecated
1155     */
1156    @Deprecated
1157    public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
1158        return crsInternal.getBigDecimal(columnIndex);
1159    }
1160
1161    /**
1162     * Retrieves the value of the designated column in the current row
1163     * of this <code>JoinRowSetImpl</code> object as a
1164     * <code>byte array</code> value.
1165     *
1166     * @param columnIndex the first column is <code>1</code>, the second
1167     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1168     *        and equal to or less than the number of columns in the rowset
1169     * @return the column value; if the value is SQL <code>NULL</code>, the
1170     *         result is <code>null</code>
1171     * @throws SQLException if the given column index is out of bounds,
1172     *            the cursor is not on a valid row, or the value to be
1173     *            retrieved is not binary
1174     */
1175    public byte[] getBytes(int columnIndex) throws SQLException {
1176        return crsInternal.getBytes(columnIndex);
1177    }
1178
1179    /**
1180     * Retrieves the value of the designated column in the current row
1181     * of this <code>JoinRowSetImpl</code> object as a
1182     * <code>java.sql.Date</code> object.
1183     *
1184     * @param columnIndex the first column is <code>1</code>, the second
1185     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1186     *        and equal to or less than the number of columns in the rowset
1187     * @return the column value; if the value is SQL <code>NULL</code>, the
1188     *         result is <code>null</code>
1189     * @throws SQLException if the given column index is out of bounds,
1190     *            the cursor is not on a valid row, or this method fails
1191     */
1192    public java.sql.Date getDate(int columnIndex) throws SQLException {
1193        return crsInternal.getDate(columnIndex);
1194    }
1195
1196    /**
1197     * Retrieves the value of the designated column in the current row
1198     * of this <code>JoinRowSetImpl</code> object as a
1199     * <code>java.sql.Time</code> object.
1200     *
1201     * @param columnIndex the first column is <code>1</code>, the second
1202     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1203     *        and equal to or less than the number of columns in the rowset
1204     * @return the column value; if the value is SQL <code>NULL</code>, the
1205     *         result is <code>null</code>
1206     * @throws SQLException if the given column index is out of bounds,
1207     *            the cursor is not on a valid row, or this method fails
1208     */
1209    public java.sql.Time getTime(int columnIndex) throws SQLException {
1210        return crsInternal.getTime(columnIndex);
1211    }
1212
1213    /**
1214     * Retrieves the value of the designated column in the current row
1215     * of this <code>JoinRowSetImpl</code> object as a
1216     * <code>java.sql.Timestamp</code> object.
1217     *
1218     * @param columnIndex the first column is <code>1</code>, the second
1219     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1220     *        and equal to or less than the number of columns in the rowset
1221     * @return the column value; if the value is SQL <code>NULL</code>, the
1222     *         result is <code>null</code>
1223     * @throws SQLException if the given column index is out of bounds,
1224     *            the cursor is not on a valid row, or this method fails
1225     */
1226    public java.sql.Timestamp getTimestamp(int columnIndex) throws SQLException {
1227        return crsInternal.getTimestamp(columnIndex);
1228    }
1229
1230    /**
1231     * Retrieves the value of the designated column in the current row
1232     * of this <code>JoinRowSetImpl</code> object as a
1233     * <code>java.sql.Timestamp</code> object.
1234     *
1235     * @param columnIndex the first column is <code>1</code>, the second
1236     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1237     *        and equal to or less than the number of columns in the rowset
1238     * @return the column value; if the value is SQL <code>NULL</code>, the
1239     *         result is <code>null</code>
1240     * @throws SQLException if the given column index is out of bounds,
1241     *            the cursor is not on a valid row, or this method fails
1242     */
1243    public java.io.InputStream getAsciiStream(int columnIndex) throws SQLException {
1244        return crsInternal.getAsciiStream(columnIndex);
1245    }
1246
1247    /**
1248     * A column value can be retrieved as a stream of Unicode characters
1249     * and then read in chunks from the stream.  This method is particularly
1250     * suitable for retrieving large LONGVARCHAR values.  The JDBC driver will
1251     * do any necessary conversion from the database format into Unicode.
1252     *
1253     * <P><B>Note:</B> All the data in the returned stream must be
1254     * read prior to getting the value of any other column. The next
1255     * call to a get method implicitly closes the stream. . Also, a
1256     * stream may return 0 for available() whether there is data
1257     * available or not.
1258     *
1259     * @param columnIndex the first column is <code>1</code>, the second
1260     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1261     *        and equal to or less than the number of columns in this rowset
1262     * @return a Java input stream that delivers the database column value
1263     * as a stream of two byte Unicode characters.  If the value is SQL NULL
1264     * then the result is null.
1265     * @throws SQLException if an error occurs
1266     * @deprecated
1267     */
1268    @Deprecated
1269    public java.io.InputStream getUnicodeStream(int columnIndex) throws SQLException {
1270        return crsInternal.getUnicodeStream(columnIndex);
1271    }
1272
1273    /**
1274     * A column value can be retrieved as a stream of uninterpreted bytes
1275     * and then read in chunks from the stream.  This method is particularly
1276     * suitable for retrieving large LONGVARBINARY values.
1277     *
1278     * <P><B>Note:</B> All the data in the returned stream must be
1279     * read prior to getting the value of any other column. The next
1280     * call to a get method implicitly closes the stream. Also, a
1281     * stream may return 0 for available() whether there is data
1282     * available or not.
1283     *
1284     * @param columnIndex the first column is <code>1</code>, the second
1285     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1286     *        and equal to or less than the number of columns in the rowset
1287     * @return a Java input stream that delivers the database column value
1288     * as a stream of uninterpreted bytes.  If the value is SQL NULL
1289     * then the result is null.
1290     * @throws SQLException if an error occurs
1291     */
1292    public java.io.InputStream getBinaryStream(int columnIndex) throws SQLException {
1293        return crsInternal.getBinaryStream(columnIndex);
1294    }
1295
1296    // ColumnName methods
1297
1298    /**
1299     * Retrieves the value stored in the designated column
1300     * of the current row as a <code>String</code> object.
1301     *
1302     * @param columnName a <code>String</code> object giving the SQL name of
1303     *        a column in this <code>JoinRowSetImpl</code> object
1304     * @return the column value; if the value is SQL <code>NULL</code>,
1305     *         the result is <code>null</code>
1306     * @throws SQLException if the given column name does not match one of
1307     *            this rowset's column names or the cursor is not on one of
1308     *            this rowset's rows or its insert row
1309     */
1310    public String getString(String columnName) throws SQLException {
1311        return crsInternal.getString(columnName);
1312    }
1313
1314    /**
1315     * Retrieves the value stored in the designated column
1316     * of the current row as a <code>boolean</code> value.
1317     *
1318     * @param columnName a <code>String</code> object giving the SQL name of
1319     *        a column in this <code>JoinRowSetImpl</code> object
1320     * @return the column value; if the value is SQL <code>NULL</code>,
1321     *         the result is <code>false</code>
1322     * @throws SQLException if the given column name does not match one of
1323     *            this rowset's column names or the cursor is not on one of
1324     *            this rowset's rows or its insert row
1325     */
1326    public boolean getBoolean(String columnName) throws SQLException {
1327        return crsInternal.getBoolean(columnName);
1328    }
1329
1330    /**
1331     * Retrieves the value stored in the designated column
1332     * of the current row as a <code>byte</code> value.
1333     *
1334     * @param columnName a <code>String</code> object giving the SQL name of
1335     *        a column in this <code>JoinRowSetImpl</code> object
1336     * @return the column value; if the value is SQL <code>NULL</code>,
1337     *         the result is <code>0</code>
1338     * @throws SQLException if the given column name does not match one of
1339     *            this rowset's column names or the cursor is not on one of
1340     *            this rowset's rows or its insert row
1341     */
1342    public byte getByte(String columnName) throws SQLException {
1343        return crsInternal.getByte(columnName);
1344    }
1345
1346    /**
1347     * Retrieves the value stored in the designated column
1348     * of the current row as a <code>short</code> value.
1349     *
1350     * @param columnName a <code>String</code> object giving the SQL name of
1351     *        a column in this <code>JoinRowSetImpl</code> object
1352     * @return the column value; if the value is SQL <code>NULL</code>,
1353     *         the result is <code>0</code>
1354     * @throws SQLException if the given column name does not match one of
1355     *            this rowset's column names or the cursor is not on one of
1356     *            this rowset's rows or its insert row
1357     */
1358    public short getShort(String columnName) throws SQLException {
1359        return crsInternal.getShort(columnName);
1360    }
1361
1362    /**
1363     * Retrieves the value stored in the designated column
1364     * of the current row as an <code>int</code> value.
1365     *
1366     * @param columnName a <code>String</code> object giving the SQL name of
1367     *        a column in this <code>JoinRowSetImpl</code> object
1368     * @return the column value; if the value is SQL <code>NULL</code>,
1369     *         the result is <code>0</code>
1370     * @throws SQLException if the given column name does not match one of
1371     *            this rowset's column names or the cursor is not on one of
1372     *            this rowset's rows or its insert row
1373     */
1374    public int getInt(String columnName) throws SQLException {
1375        return crsInternal.getInt(columnName);
1376    }
1377
1378    /**
1379     * Retrieves the value stored in the designated column
1380     * of the current row as a <code>long</code> value.
1381     *
1382     * @param columnName a <code>String</code> object giving the SQL name of
1383     *        a column in this <code>JoinRowSetImpl</code> object
1384     * @return the column value; if the value is SQL <code>NULL</code>,
1385     *         the result is <code>0</code>
1386     * @throws SQLException if the given column name does not match one of
1387     *            this rowset's column names or the cursor is not on one of
1388     *            this rowset's rows or its insert row
1389     */
1390    public long getLong(String columnName) throws SQLException {
1391        return crsInternal.getLong(columnName);
1392    }
1393
1394    /**
1395     * Retrieves the value stored in the designated column
1396     * of the current row as a <code>float</code> value.
1397     *
1398     * @param columnName a <code>String</code> object giving the SQL name of
1399     *        a column in this <code>JoinRowSetImpl</code> object
1400     * @return the column value; if the value is SQL <code>NULL</code>,
1401     *         the result is <code>0</code>
1402     * @throws SQLException if the given column name does not match one of
1403     *            this rowset's column names or the cursor is not on one of
1404     *            this rowset's rows or its insert row
1405     */
1406    public float getFloat(String columnName) throws SQLException {
1407        return crsInternal.getFloat(columnName);
1408    }
1409
1410    /**
1411     * Retrieves the value stored in the designated column
1412     * of the current row as a <code>double</code> value.
1413     *
1414     * @param columnName a <code>String</code> object giving the SQL name of
1415     *        a column in this <code>JoinRowSetImpl</code> object
1416     * @return the column value; if the value is SQL <code>NULL</code>,
1417     *         the result is <code>0</code>
1418     * @throws SQLException if the given column name does not match one of
1419     *            this rowset's column names or the cursor is not on one of
1420     *            this rowset's rows or its insert row
1421     */
1422    public double getDouble(String columnName) throws SQLException {
1423        return crsInternal.getDouble(columnName);
1424    }
1425
1426    /**
1427     * Retrieves the value stored in the designated column
1428     * of the current row as a <code>java.math.BigDecimal</code> object.
1429     *
1430     * @param columnName a <code>String</code> object giving the SQL name of
1431     *        a column in this <code>JoinRowSetImpl</code> object
1432     * @param scale the number of digits to the right of the decimal point
1433     * @return the column value; if the value is SQL <code>NULL</code>,
1434     *         the result is <code>null</code>
1435     * @throws SQLException if the given column name does not match one of
1436     *            this rowset's column names or the cursor is not on one of
1437     *            this rowset's rows or its insert row
1438     * @deprecated use the method <code>getBigDecimal(String columnName)</code>
1439     *             instead
1440     */
1441    @Deprecated
1442    public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException {
1443        return crsInternal.getBigDecimal(columnName);
1444    }
1445
1446    /**
1447     * Retrieves the value stored in the designated column
1448     * of the current row as a byte array.
1449     * The bytes represent the raw values returned by the driver.
1450     *
1451     * @param columnName a <code>String</code> object giving the SQL name of
1452     *        a column in this <code>JoinRowSetImpl</code> object
1453     * @return the column value; if the value is SQL <code>NULL</code>,
1454     *         the result is <code>null</code>
1455     * @throws SQLException if the given column name does not match one of
1456     *            this rowset's column names or the cursor is not on one of
1457     *            this rowset's rows or its insert row
1458     */
1459    public byte[] getBytes(String columnName) throws SQLException {
1460        return crsInternal.getBytes(columnName);
1461    }
1462
1463    /**
1464     * Retrieves the value stored in the designated column
1465     * of the current row as a <code>java.sql.Date</code> object.
1466     *
1467     * @param columnName a <code>String</code> object giving the SQL name of
1468     *        a column in this <code>JoinRowSetImpl</code> object
1469     * @return the column value; if the value is SQL <code>NULL</code>,
1470     *         the result is <code>null</code>
1471     * @throws SQLException if the given column name does not match one of
1472     *            this rowset's column names or the cursor is not on one of
1473     *            this rowset's rows or its insert row
1474     */
1475    public java.sql.Date getDate(String columnName) throws SQLException {
1476        return crsInternal.getDate(columnName);
1477    }
1478
1479    /**
1480     * Retrieves the value stored in the designated column
1481     * of the current row as a <code>java.sql.Time</code> object.
1482     *
1483     * @param columnName a <code>String</code> object giving the SQL name of
1484     *        a column in this <code>JoinRowSetImpl</code> object
1485     * @return the column value; if the value is SQL <code>NULL</code>,
1486     *         the result is <code>null</code>
1487     * @throws SQLException if the given column name does not match one of
1488     *            this rowset's column names or the cursor is not on one of
1489     *            this rowset's rows or its insert row
1490     */
1491    public java.sql.Time getTime(String columnName) throws SQLException {
1492        return crsInternal.getTime(columnName);
1493    }
1494
1495    /**
1496     * Retrieves the value stored in the designated column
1497     * of the current row as a <code>java.sql.Timestamp</code> object.
1498     *
1499     * @param columnName a <code>String</code> object giving the SQL name of
1500     *        a column in this <code>JoinRowSetImpl</code> object
1501     * @return the column value; if the value is SQL <code>NULL</code>,
1502     *         the result is <code>null</code>
1503     * @throws SQLException if the given column name does not match one of
1504     *            this rowset's column names or the cursor is not on one of
1505     *            this rowset's rows or its insert row
1506     */
1507    public java.sql.Timestamp getTimestamp(String columnName) throws SQLException {
1508        return crsInternal.getTimestamp(columnName);
1509    }
1510
1511    /**
1512     * This method is not supported, and it will throw an
1513     * <code>UnsupportedOperationException</code> if it is called.
1514     * <P>
1515     * A column value can be retrieved as a stream of ASCII characters
1516     * and then read in chunks from the stream.  This method is particularly
1517     * suitable for retrieving large LONGVARCHAR values.  The JDBC driver will
1518     * do any necessary conversion from the database format into ASCII format.
1519     *
1520     * <P><B>Note:</B> All the data in the returned stream must
1521     * be read prior to getting the value of any other column. The
1522     * next call to a <code>getXXX</code> method implicitly closes the stream.
1523     *
1524     * @param columnName a <code>String</code> object giving the SQL name of
1525     *        a column in this <code>JoinRowSetImpl</code> object
1526     * @return a Java input stream that delivers the database column value
1527     *         as a stream of one-byte ASCII characters.  If the value is SQL
1528     *         <code>NULL</code>, the result is <code>null</code>.
1529     * @throws UnsupportedOperationException if this method is called
1530     */
1531    public java.io.InputStream getAsciiStream(String columnName) throws SQLException {
1532        return crsInternal.getAsciiStream(columnName);
1533    }
1534
1535    /**
1536     * Retrieves the value stored in the designated column
1537     * of the current row as a <code>java.io.InputStream</code> object.
1538     * A column value can be retrieved as a stream of Unicode characters
1539     * and then read in chunks from the stream.  This method is particularly
1540     * suitable for retrieving large <code>LONGVARCHAR</code> values.
1541     * The JDBC driver will do any necessary conversion from the database
1542     * format into Unicode.
1543     *
1544     * <P><B>Note:</B> All the data in the returned stream must
1545     * be read prior to getting the value of any other column. The
1546     * next call to a <code>getXXX</code> method implicitly closes the stream.
1547     *
1548     * @param columnName a <code>String</code> object giving the SQL name of
1549     *        a column in this <code>JoinRowSetImpl</code> object
1550     * @return a Java input stream that delivers the database column value
1551     *         as a stream of two-byte Unicode characters.  If the value is
1552     *         SQL <code>NULL</code>, the result is <code>null</code>.
1553     * @throws SQLException if the given column name does not match one of
1554     *            this rowset's column names or the cursor is not on one of
1555     *            this rowset's rows or its insert row
1556     * @deprecated use the method <code>getCharacterStream</code> instead
1557     */
1558    @Deprecated
1559    public java.io.InputStream getUnicodeStream(String columnName) throws SQLException {
1560        return crsInternal.getUnicodeStream(columnName);
1561    }
1562
1563    /**
1564     * Retrieves the value stored in the designated column
1565     * of the current row as a <code>java.io.InputStream</code> object.
1566     * A column value can be retrieved as a stream of uninterpreted bytes
1567     * and then read in chunks from the stream.  This method is particularly
1568     * suitable for retrieving large <code>LONGVARBINARY</code> values.
1569     *
1570     * <P><B>Note:</B> All the data in the returned stream must
1571     * be read prior to getting the value of any other column. The
1572     * next call to a get method implicitly closes the stream.
1573     *
1574     * @param columnName a <code>String</code> object giving the SQL name of
1575     *        a column in this <code>JoinRowSetImpl</code> object
1576     * @return a Java input stream that delivers the database column value
1577     *         as a stream of uninterpreted bytes.  If the value is SQL
1578     *         <code>NULL</code>, the result is <code>null</code>.
1579     * @throws SQLException if the given column name does not match one of
1580     *            this rowset's column names or the cursor is not on one of
1581     *            this rowset's rows or its insert row
1582     */
1583    public java.io.InputStream getBinaryStream(String columnName) throws SQLException {
1584        return crsInternal.getBinaryStream(columnName);
1585    }
1586
1587    /* The first warning reported by calls on this <code>JoinRowSetImpl</code>
1588     * object is returned. Subsequent <code>JoinRowSetImpl</code> warnings will
1589     * be chained to this <code>SQLWarning</code>.
1590     *
1591     * <P>The warning chain is automatically cleared each time a new
1592     * row is read.
1593     *
1594     * <P><B>Note:</B> This warning chain only covers warnings caused
1595     * by <code>ResultSet</code> methods.  Any warning caused by statement
1596     * methods (such as reading OUT parameters) will be chained on the
1597     * <code>Statement</code> object.
1598     *
1599     * @return the first SQLWarning or null
1600     * @throws UnsupportedOperationException if this method is called
1601     */
1602    public SQLWarning getWarnings() {
1603        return crsInternal.getWarnings();
1604    }
1605
1606    /**
1607     * Throws an <code>UnsupportedOperationException</code> if called.
1608     * <P>
1609     * After a call to this method, the <code>getWarnings</code> method
1610     * returns <code>null</code> until a new warning is reported for this
1611     * <code>JoinRowSetImpl</code> object.
1612     *
1613     * @throws UnsupportedOperationException if this method is called
1614     */
1615     public void clearWarnings() {
1616        crsInternal.clearWarnings();
1617    }
1618
1619    /**
1620     * Retrieves the name of the SQL cursor used by this
1621     * <code>JoinRowSetImpl</code> object.
1622     *
1623     * <P>In SQL, a result table is retrieved through a cursor that is
1624     * named. The current row of a result can be updated or deleted
1625     * using a positioned update/delete statement that references the
1626     * cursor name. To insure that the cursor has the proper isolation
1627     * level to support an update operation, the cursor's <code>SELECT</code>
1628     * statement should be of the form 'select for update'. If the 'for update'
1629     * clause is omitted, positioned updates may fail.
1630     *
1631     * <P>JDBC supports this SQL feature by providing the name of the
1632     * SQL cursor used by a <code>ResultSet</code> object. The current row
1633     * of a result set is also the current row of this SQL cursor.
1634     *
1635     * <P><B>Note:</B> If positioned updates are not supported, an
1636     * <code>SQLException</code> is thrown.
1637     *
1638     * @return the SQL cursor name for this <code>JoinRowSetImpl</code> object's
1639     *         cursor
1640     * @throws SQLException if an error occurs
1641     */
1642    public String getCursorName() throws SQLException {
1643        return crsInternal.getCursorName();
1644    }
1645
1646    /**
1647     * Retrieves the <code>ResultSetMetaData</code> object that contains
1648     * information about this <code>CachedRowsSet</code> object. The
1649     * information includes the number of columns, the data type for each
1650     * column, and other properties for each column.
1651     *
1652     * @return the <code>ResultSetMetaData</code> object that describes this
1653     *         <code>JoinRowSetImpl</code> object's columns
1654     * @throws SQLException if an error occurs
1655     */
1656    public ResultSetMetaData getMetaData() throws SQLException {
1657        return crsInternal.getMetaData();
1658    }
1659
1660    /**
1661     * Retrieves the value of the designated column in the current row
1662     * of this <code>JoinRowSetImpl</code> object as an
1663     * <code>Object</code> value.
1664     * <P>
1665     * The type of the <code>Object</code> will be the default
1666     * Java object type corresponding to the column's SQL type,
1667     * following the mapping for built-in types specified in the JDBC
1668     * specification.
1669     * <P>
1670     * This method may also be used to read datatabase-specific
1671     * abstract data types.
1672     * <P>
1673     * This implementation of the method <code>getObject</code> extends its
1674     * behavior so that it gets the attributes of an SQL structured type as
1675     * as an array of <code>Object</code> values.  This method also custom
1676     * maps SQL user-defined types to classes in the Java programming language.
1677     * When the specified column contains
1678     * a structured or distinct value, the behavior of this method is as
1679     * if it were a call to the method <code>getObject(columnIndex,
1680     * this.getStatement().getConnection().getTypeMap())</code>.
1681     *
1682     * @param columnIndex the first column is <code>1</code>, the second
1683     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1684     *        and equal to or less than the number of columns in the rowset
1685     * @return a <code>java.lang.Object</code> holding the column value;
1686     *         if the value is SQL <code>NULL</code>, the result is <code>null</code>
1687     * @throws SQLException if the given column index is out of bounds,
1688     *            the cursor is not on a valid row, or there is a problem getting
1689     *            the <code>Class</code> object for a custom mapping
1690     * @since 1.2
1691     */
1692    public Object getObject(int columnIndex) throws SQLException {
1693        return crsInternal.getObject(columnIndex);
1694    }
1695
1696    /**
1697     * Retrieves the value of the designated column in the current row
1698     * of this <code>JoinRowSetImpl</code> object as an
1699     * <code>Object</code> value.
1700     * <P>
1701     * The type of the <code>Object</code> will be the default
1702     * Java object type corresponding to the column's SQL type,
1703     * following the mapping for built-in types specified in the JDBC
1704     * specification.
1705     * <P>
1706     * This method may also be used to read datatabase-specific
1707     * abstract data types.
1708     * <P>
1709     * This implementation of the method <code>getObject</code> extends its
1710     * behavior so that it gets the attributes of an SQL structured type as
1711     * as an array of <code>Object</code> values.  This method also custom
1712     * maps SQL user-defined types to classes
1713     * in the Java programming language. When the specified column contains
1714     * a structured or distinct value, the behavior of this method is as
1715     * if it were a call to the method <code>getObject(columnIndex,
1716     * this.getStatement().getConnection().getTypeMap())</code>.
1717     *
1718     * @param columnIndex the first column is <code>1</code>, the second
1719     *         is <code>2</code>, and so on; must be <code>1</code> or larger
1720     *         and equal to or less than the number of columns in the rowset
1721     * @param map a <code>java.util.Map</code> object showing the mapping
1722     *         from SQL type names to classes in the Java programming
1723     *         language
1724     * @return a <code>java.lang.Object</code> holding the column value;
1725     *         if the value is SQL <code>NULL</code>, the result is
1726     *         <code>null</code>
1727     * @throws SQLException if (1) the given column name does not match
1728     *         one of this rowset's column names, (2) the cursor is not
1729     *         on a valid row, or (3) there is a problem getting
1730     *         the <code>Class</code> object for a custom mapping
1731     */
1732    public Object getObject(int columnIndex,
1733                            java.util.Map<String,Class<?>> map)
1734    throws SQLException {
1735        return crsInternal.getObject(columnIndex, map);
1736    }
1737
1738    /**
1739     * Retrieves the value of the designated column in the current row
1740     * of this <code>JoinRowSetImpl</code> object as an
1741     * <code>Object</code> value.
1742     * <P>
1743     * The type of the <code>Object</code> will be the default
1744     * Java object type corresponding to the column's SQL type,
1745     * following the mapping for built-in types specified in the JDBC
1746     * specification.
1747     * <P>
1748     * This method may also be used to read datatabase-specific
1749     * abstract data types.
1750     * <P>
1751     * This implementation of the method <code>getObject</code> extends its
1752     * behavior so that it gets the attributes of an SQL structured type as
1753     * as an array of <code>Object</code> values.  This method also custom
1754     * maps SQL user-defined types to classes
1755     * in the Java programming language. When the specified column contains
1756     * a structured or distinct value, the behavior of this method is as
1757     * if it were a call to the method <code>getObject(columnIndex,
1758     * this.getStatement().getConnection().getTypeMap())</code>.
1759     *
1760     * @param columnName a <code>String</code> object that must match the
1761     *        SQL name of a column in this rowset, ignoring case
1762     * @return a <code>java.lang.Object</code> holding the column value;
1763     *        if the value is SQL <code>NULL</code>, the result is
1764     *        <code>null</code>
1765     * @throws SQLException if (1) the given column name does not match
1766     *        one of this rowset's column names, (2) the cursor is not
1767     *        on a valid row, or (3) there is a problem getting
1768     *        the <code>Class</code> object for a custom mapping
1769     */
1770    public Object getObject(String columnName) throws SQLException {
1771        return crsInternal.getObject(columnName);
1772    }
1773
1774    /**
1775     * Retrieves the value of the designated column in this
1776     * <code>JoinRowSetImpl</code> object as an <code>Object</code> in
1777     * the Java programming lanugage, using the given
1778     * <code>java.util.Map</code> object to custom map the value if
1779     * appropriate.
1780     *
1781     * @param columnName a <code>String</code> object that must match the
1782     *        SQL name of a column in this rowset, ignoring case
1783     * @param map a <code>java.util.Map</code> object showing the mapping
1784     *            from SQL type names to classes in the Java programming
1785     *            language
1786     * @return an <code>Object</code> representing the SQL value
1787     * @throws SQLException if the given column index is out of bounds or
1788     *            the cursor is not on one of this rowset's rows or its
1789     *            insert row
1790     */
1791    public Object getObject(String columnName,
1792                            java.util.Map<String,Class<?>> map)
1793        throws SQLException {
1794        return crsInternal.getObject(columnName, map);
1795    }
1796
1797    /**
1798     * Retrieves the value stored in the designated column
1799     * of the current row as a <code>java.io.Reader</code> object.
1800     *
1801     * <P><B>Note:</B> All the data in the returned stream must
1802     * be read prior to getting the value of any other column. The
1803     * next call to a <code>getXXX</code> method implicitly closes the stream.
1804     *
1805     * @param columnIndex the first column is <code>1</code>, the second
1806     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1807     *        and equal to or less than the number of columns in the rowset
1808     * @return a Java character stream that delivers the database column value
1809     *         as a <code>java.io.Reader</code> object.  If the value is
1810     *         SQL <code>NULL</code>, the result is <code>null</code>.
1811     * @throws SQLException if the given column index is out of bounds,
1812     *            the cursor is not on a valid row, or there is a type mismatch
1813     */
1814    public java.io.Reader getCharacterStream(int columnIndex) throws SQLException {
1815        return crsInternal.getCharacterStream(columnIndex);
1816    }
1817
1818    /**
1819     * Retrieves the value stored in the designated column
1820     * of the current row as a <code>java.io.Reader</code> object.
1821     *
1822     * <P><B>Note:</B> All the data in the returned stream must
1823     * be read prior to getting the value of any other column. The
1824     * next call to a <code>getXXX</code> method implicitly closes the stream.
1825     *
1826     * @param columnName a <code>String</code> object giving the SQL name of
1827     *        a column in this <code>JoinRowSetImpl</code> object
1828     * @return a Java input stream that delivers the database column value
1829     *         as a stream of two-byte Unicode characters.  If the value is
1830     *         SQL <code>NULL</code>, the result is <code>null</code>.
1831     * @throws SQLException if the given column index is out of bounds,
1832     *            the cursor is not on a valid row, or there is a type mismatch
1833     */
1834    public java.io.Reader getCharacterStream(String columnName) throws SQLException {
1835        return crsInternal.getCharacterStream(columnName);
1836    }
1837
1838    /**
1839     * Retrieves the value of the designated column in the current row
1840     * of this <code>JoinRowSetImpl</code> object as a
1841     * <code>java.math.BigDecimal</code> object.
1842     *
1843     * @param columnIndex the first column is <code>1</code>, the second
1844     *        is <code>2</code>, and so on; must be <code>1</code> or larger
1845     *        and equal to or less than the number of columns in the rowset
1846     * @return a <code>java.math.BigDecimal</code> value with full precision;
1847     *         if the value is SQL <code>NULL</code>, the result is <code>null</code>
1848     * @throws SQLException if the given column index is out of bounds,
1849     *            the cursor is not on a valid row, or this method fails
1850     */
1851    public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
1852       return crsInternal.getBigDecimal(columnIndex);
1853    }
1854
1855    /**
1856     * Retrieves the value of the designated column in the current row
1857     * of this <code>JoinRowSetImpl</code> object as a
1858     * <code>java.math.BigDecimal</code> object.
1859     *
1860     * @param columnName a <code>String</code> object that must match the
1861     *        SQL name of a column in this rowset, ignoring case
1862     * @return a <code>java.math.BigDecimal</code> value with full precision;
1863     *         if the value is SQL <code>NULL</code>, the result is <code>null</code>
1864     * @throws SQLException if the given column index is out of bounds,
1865     *            the cursor is not on a valid row, or this method fails
1866     */
1867    public BigDecimal getBigDecimal(String columnName) throws SQLException {
1868       return crsInternal.getBigDecimal(columnName);
1869    }
1870
1871    /**
1872     * Returns the number of rows in this <code>JoinRowSetImpl</code> object.
1873     *
1874     * @return number of rows in the rowset
1875     */
1876    public int size() {
1877        return crsInternal.size();
1878    }
1879
1880    /**
1881     * Indicates whether the cursor is before the first row in this
1882     * <code>JoinRowSetImpl</code> object.
1883     *
1884     * @return <code>true</code> if the cursor is before the first row;
1885     *         <code>false</code> otherwise or if the rowset contains no rows
1886     * @throws SQLException if an error occurs
1887     */
1888    public boolean isBeforeFirst() throws SQLException {
1889        return crsInternal.isBeforeFirst();
1890    }
1891
1892    /**
1893     * Indicates whether the cursor is after the last row in this
1894     * <code>JoinRowSetImpl</code> object.
1895     *
1896     * @return <code>true</code> if the cursor is after the last row;
1897     *         <code>false</code> otherwise or if the rowset contains no rows
1898     * @throws SQLException if an error occurs
1899     */
1900    public boolean isAfterLast() throws SQLException {
1901        return crsInternal.isAfterLast();
1902    }
1903
1904    /**
1905     * Indicates whether the cursor is on the first row in this
1906     * <code>JoinRowSetImpl</code> object.
1907     *
1908     * @return <code>true</code> if the cursor is on the first row;
1909     *         <code>false</code> otherwise or if the rowset contains no rows
1910     * @throws SQLException if an error occurs
1911     */
1912    public boolean isFirst() throws SQLException {
1913        return crsInternal.isFirst();
1914    }
1915
1916    /**
1917     * Indicates whether the cursor is on the last row in this
1918     * <code>JoinRowSetImpl</code> object.
1919     * <P>
1920     * Note: Calling the method <code>isLast</code> may be expensive
1921     * because the JDBC driver might need to fetch ahead one row in order
1922     * to determine whether the current row is the last row in this rowset.
1923     *
1924     * @return <code>true</code> if the cursor is on the last row;
1925     *         <code>false</code> otherwise or if this rowset contains no rows
1926     * @throws SQLException if an error occurs
1927     */
1928    public boolean isLast() throws SQLException {
1929        return crsInternal.isLast();
1930    }
1931
1932    /**
1933     * Moves this <code>JoinRowSetImpl</code> object's cursor to the front of
1934     * the rowset, just before the first row. This method has no effect if
1935     * this rowset contains no rows.
1936     *
1937     * @throws SQLException if an error occurs or the type of this rowset
1938     *            is <code>ResultSet.TYPE_FORWARD_ONLY</code>
1939     */
1940    public void beforeFirst() throws SQLException {
1941        crsInternal.beforeFirst();
1942    }
1943
1944    /**
1945     * Moves this <code>JoinRowSetImpl</code> object's cursor to the end of
1946     * the rowset, just after the last row. This method has no effect if
1947     * this rowset contains no rows.
1948     *
1949     * @throws SQLException if an error occurs
1950     */
1951    public void afterLast() throws SQLException {
1952        crsInternal.afterLast();
1953    }
1954
1955    /**
1956     * Moves this <code>JoinRowSetImpl</code> object's cursor to the first row
1957     * and returns <code>true</code> if the operation was successful.  This
1958     * method also notifies registered listeners that the cursor has moved.
1959     *
1960     * @return <code>true</code> if the cursor is on a valid row;
1961     *         <code>false</code> otherwise or if there are no rows in this
1962     *         <code>JoinRowSetImpl</code> object
1963     * @throws SQLException if the type of this rowset
1964     *            is <code>ResultSet.TYPE_FORWARD_ONLY</code>
1965     */
1966    public boolean first() throws SQLException {
1967        return crsInternal.first();
1968    }
1969
1970
1971    /**
1972     * Moves this <code>JoinRowSetImpl</code> object's cursor to the last row
1973     * and returns <code>true</code> if the operation was successful.  This
1974     * method also notifies registered listeners that the cursor has moved.
1975     *
1976     * @return <code>true</code> if the cursor is on a valid row;
1977     *         <code>false</code> otherwise or if there are no rows in this
1978     *         <code>JoinRowSetImpl</code> object
1979     * @throws SQLException if the type of this rowset
1980     *            is <code>ResultSet.TYPE_FORWARD_ONLY</code>
1981     */
1982    public boolean last() throws SQLException {
1983        return crsInternal.last();
1984    }
1985
1986    /**
1987     * Returns the number of the current row in this <code>JoinRowSetImpl</code>
1988     * object. The first row is number 1, the second number 2, and so on.
1989     *
1990     * @return the number of the current row;  <code>0</code> if there is no
1991     *         current row
1992     * @throws SQLException if an error occurs
1993     */
1994    public int getRow() throws SQLException {
1995        return crsInternal.getRow();
1996    }
1997
1998    /**
1999     * Moves this <code>JoinRowSetImpl</code> object's cursor to the row number
2000     * specified.
2001     *
2002     * <p>If the number is positive, the cursor moves to an absolute row with
2003     * respect to the beginning of the rowset.  The first row is row 1, the second
2004     * is row 2, and so on.  For example, the following command, in which
2005     * <code>crs</code> is a <code>JoinRowSetImpl</code> object, moves the cursor
2006     * to the fourth row, starting from the beginning of the rowset.
2007     * <PRE><code>
2008     *
2009     *    crs.absolute(4);
2010     *
2011     * </code> </PRE>
2012     * <P>
2013     * If the number is negative, the cursor moves to an absolute row position
2014     * with respect to the end of the rowset.  For example, calling
2015     * <code>absolute(-1)</code> positions the cursor on the last row,
2016     * <code>absolute(-2)</code> moves it on the next-to-last row, and so on.
2017     * If the <code>JoinRowSetImpl</code> object <code>crs</code> has five rows,
2018     * the following command moves the cursor to the fourth-to-last row, which
2019     * in the case of a  rowset with five rows, is also the second row, counting
2020     * from the beginning.
2021     * <PRE><code>
2022     *
2023     *    crs.absolute(-4);
2024     *
2025     * </code> </PRE>
2026     *
2027     * If the number specified is larger than the number of rows, the cursor
2028     * will move to the position after the last row. If the number specified
2029     * would move the cursor one or more rows before the first row, the cursor
2030     * moves to the position before the first row.
2031     * <P>
2032     * Note: Calling <code>absolute(1)</code> is the same as calling the
2033     * method <code>first()</code>.  Calling <code>absolute(-1)</code> is the
2034     * same as calling <code>last()</code>.
2035     *
2036     * @param row a positive number to indicate the row, starting row numbering from
2037     *        the first row, which is <code>1</code>; a negative number to indicate
2038     *        the row, starting row numbering from the last row, which is
2039     *        <code>-1</code>; must not be <code>0</code>
2040     * @return <code>true</code> if the cursor is on the rowset; <code>false</code>
2041     *         otherwise
2042     * @throws SQLException if the given cursor position is <code>0</code> or the
2043     *            type of this rowset is <code>ResultSet.TYPE_FORWARD_ONLY</code>
2044     */
2045    public boolean absolute(int row) throws SQLException {
2046        return crsInternal.absolute(row);
2047    }
2048
2049    /**
2050     * Moves the cursor the specified number of rows from the current
2051     * position, with a positive number moving it forward and a
2052     * negative number moving it backward.
2053     * <P>
2054     * If the number is positive, the cursor moves the specified number of
2055     * rows toward the end of the rowset, starting at the current row.
2056     * For example, the following command, in which
2057     * <code>crs</code> is a <code>JoinRowSetImpl</code> object with 100 rows,
2058     * moves the cursor forward four rows from the current row.  If the
2059     * current row is 50, the cursor would move to row 54.
2060     * <PRE><code>
2061     *
2062     *    crs.relative(4);
2063     *
2064     * </code> </PRE>
2065     * <P>
2066     * If the number is negative, the cursor moves back toward the beginning
2067     * the specified number of rows, starting at the current row.
2068     * For example, calling the method
2069     * <code>absolute(-1)</code> positions the cursor on the last row,
2070     * <code>absolute(-2)</code> moves it on the next-to-last row, and so on.
2071     * If the <code>JoinRowSetImpl</code> object <code>crs</code> has five rows,
2072     * the following command moves the cursor to the fourth-to-last row, which
2073     * in the case of a  rowset with five rows, is also the second row
2074     * from the beginning.
2075     * <PRE><code>
2076     *
2077     *    crs.absolute(-4);
2078     *
2079     * </code> </PRE>
2080     *
2081     * If the number specified is larger than the number of rows, the cursor
2082     * will move to the position after the last row. If the number specified
2083     * would move the cursor one or more rows before the first row, the cursor
2084     * moves to the position before the first row. In both cases, this method
2085     * throws an <code>SQLException</code>.
2086     * <P>
2087     * Note: Calling <code>absolute(1)</code> is the same as calling the
2088     * method <code>first()</code>.  Calling <code>absolute(-1)</code> is the
2089     * same as calling <code>last()</code>.  Calling <code>relative(0)</code>
2090     * is valid, but it does not change the cursor position.
2091     *
2092     * @param rows an <code>int</code> indicating the number of rows to move
2093     *             the cursor, starting at the current row; a positive number
2094     *             moves the cursor forward; a negative number moves the cursor
2095     *             backward; must not move the cursor past the valid
2096     *             rows
2097     * @return <code>true</code> if the cursor is on a row in this
2098     *         <code>JoinRowSetImpl</code> object; <code>false</code>
2099     *         otherwise
2100     * @throws SQLException if there are no rows in this rowset, the cursor is
2101     *         positioned either before the first row or after the last row, or
2102     *         the rowset is type <code>ResultSet.TYPE_FORWARD_ONLY</code>
2103     */
2104    public boolean relative(int rows) throws SQLException {
2105        return crsInternal.relative(rows);
2106    }
2107
2108    /**
2109     * Moves this <code>JoinRowSetImpl</code> object's cursor to the
2110     * previous row and returns <code>true</code> if the cursor is on
2111     * a valid row or <code>false</code> if it is not.
2112     * This method also notifies all listeners registered with this
2113     * <code>JoinRowSetImpl</code> object that its cursor has moved.
2114     * <P>
2115     * Note: calling the method <code>previous()</code> is not the same
2116     * as calling the method <code>relative(-1)</code>.  This is true
2117     * because it is possible to call <code>previous()</code> from the insert
2118     * row, from after the last row, or from the current row, whereas
2119     * <code>relative</code> may only be called from the current row.
2120     * <P>
2121     * The method <code>previous</code> may used in a <code>while</code>
2122     * loop to iterate through a rowset starting after the last row
2123     * and moving toward the beginning. The loop ends when <code>previous</code>
2124     * returns <code>false</code>, meaning that there are no more rows.
2125     * For example, the following code fragment retrieves all the data in
2126     * the <code>JoinRowSetImpl</code> object <code>crs</code>, which has
2127     * three columns.  Note that the cursor must initially be positioned
2128     * after the last row so that the first call to the method
2129     * <code>previous</code> places the cursor on the last line.
2130     * <PRE> <code>
2131     *
2132     *     crs.afterLast();
2133     *     while (previous()) {
2134     *         String name = crs.getString(1);
2135     *         int age = crs.getInt(2);
2136     *         short ssn = crs.getShort(3);
2137     *         System.out.println(name + "   " + age + "   " + ssn);
2138     *     }
2139     *
2140     * </code> </PRE>
2141     * This method throws an <code>SQLException</code> if the cursor is not
2142     * on a row in the rowset, before the first row, or after the last row.
2143     *
2144     * @return <code>true</code> if the cursor is on a valid row;
2145     *         <code>false</code> if it is before the first row or after the
2146     *         last row
2147     * @throws SQLException if the cursor is not on a valid position or the
2148     *           type of this rowset is <code>ResultSet.TYPE_FORWARD_ONLY</code>
2149     */
2150    public boolean previous() throws SQLException {
2151        return crsInternal.previous();
2152    }
2153
2154    /**
2155     * Returns the index of the column whose name is <i>columnName</i>.
2156     *
2157     * @param columnName a <code>String</code> object giving the name of the
2158     *        column for which the index will be returned; the name must
2159     *        match the SQL name of a column in this <code>JoinRowSet</code>
2160     *        object, ignoring case
2161     * @throws SQLException if the given column name does not match one of the
2162     *         column names for this <code>JoinRowSet</code> object
2163     */
2164    public int findColumn(String columnName) throws SQLException {
2165        return crsInternal.findColumn(columnName);
2166    }
2167
2168    /**
2169     * Indicates whether the current row of this <code>JoinRowSetImpl</code>
2170     * object has been updated.  The value returned
2171     * depends on whether this rowset can detect updates: <code>false</code>
2172     * will always be returned if it does not detect updates.
2173     *
2174     * @return <code>true</code> if the row has been visibly updated
2175     *         by the owner or another and updates are detected;
2176     *         <code>false</code> otherwise
2177     * @throws SQLException if the cursor is on the insert row or not
2178     *            on a valid row
2179     *
2180     * @see DatabaseMetaData#updatesAreDetected
2181     */
2182    public boolean rowUpdated() throws SQLException {
2183        return crsInternal.rowUpdated();
2184    }
2185
2186    /**
2187     * Indicates whether the designated column of the current row of
2188     * this <code>JoinRowSetImpl</code> object has been updated. The
2189     * value returned depends on whether this rowset can detcted updates:
2190     * <code>false</code> will always be returned if it does not detect updates.
2191     *
2192     * @return <code>true</code> if the column updated
2193     *          <code>false</code> otherwse
2194     * @throws SQLException if the cursor is on the insert row or not
2195     *          on a valid row
2196     * @see DatabaseMetaData#updatesAreDetected
2197     */
2198    public boolean columnUpdated(int indexColumn) throws SQLException {
2199        return crsInternal.columnUpdated(indexColumn);
2200    }
2201
2202    /**
2203     * Indicates whether the current row has been inserted.  The value returned
2204     * depends on whether or not the rowset can detect visible inserts.
2205     *
2206     * @return <code>true</code> if a row has been inserted and inserts are detected;
2207     *         <code>false</code> otherwise
2208     * @throws SQLException if the cursor is on the insert row or not
2209     *            not on a valid row
2210     *
2211     * @see DatabaseMetaData#insertsAreDetected
2212     */
2213    public boolean rowInserted() throws SQLException {
2214        return crsInternal.rowInserted();
2215    }
2216
2217    /**
2218     * Indicates whether the current row has been deleted.  A deleted row
2219     * may leave a visible "hole" in a rowset.  This method can be used to
2220     * detect such holes if the rowset can detect deletions. This method
2221     * will always return <code>false</code> if this rowset cannot detect
2222     * deletions.
2223     *
2224     * @return <code>true</code> if (1)the current row is blank, indicating that
2225     *         the row has been deleted, and (2)deletions are detected;
2226     *         <code>false</code> otherwise
2227     * @throws SQLException if the cursor is on a valid row in this rowset
2228     * @see DatabaseMetaData#deletesAreDetected
2229     */
2230    public boolean rowDeleted() throws SQLException {
2231        return crsInternal.rowDeleted();
2232    }
2233
2234    /**
2235     * Sets the designated nullable column in the current row or the
2236     * insert row of this <code>JoinRowSetImpl</code> object with
2237     * <code>null</code> value.
2238     * <P>
2239     * This method updates a column value in the current row or the insert
2240     * row of this rowset; however, another method must be called to complete
2241     * the update process. If the cursor is on a row in the rowset, the
2242     * method {@link #updateRow} must be called to mark the row as updated
2243     * and to notify listeners that the row has changed.
2244     * If the cursor is on the insert row, the method {@link #insertRow}
2245     * must be called to insert the new row into this rowset and to notify
2246     * listeners that a row has changed.
2247     * <P>
2248     * In order to propagate updates in this rowset to the underlying
2249     * data source, an application must call the method acceptChanges
2250     * after it calls either <code>updateRow</code> or <code>insertRow</code>.
2251     *
2252     * @param columnIndex the first column is <code>1</code>, the second
2253     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2254     *        and equal to or less than the number of columns in this rowset
2255     * @throws SQLException if (1) the given column index is out of bounds,
2256     *            (2) the cursor is not on one of this rowset's rows or its
2257     *            insert row, or (3) this rowset is
2258     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2259     */
2260    public void updateNull(int columnIndex) throws SQLException {
2261        crsInternal.updateNull(columnIndex);
2262    }
2263
2264    /**
2265     * Sets the designated column in either the current row or the insert
2266     * row of this <code>JoinRowSetImpl</code> object with the given
2267     * <code>boolean</code> value.
2268     * <P>
2269     * This method updates a column value in the current row or the insert
2270     * row of this rowset, but it does not update the database.
2271     * If the cursor is on a row in the rowset, the
2272     * method {@link #updateRow} must be called to update the database.
2273     * If the cursor is on the insert row, the method {@link #insertRow}
2274     * must be called, which will insert the new row into both this rowset
2275     * and the database. Both of these methods must be called before the
2276     * cursor moves to another row.
2277     *
2278     * @param columnIndex the first column is <code>1</code>, the second
2279     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2280     *        and equal to or less than the number of columns in this rowset
2281     * @param x the new column value
2282     * @throws SQLException if (1) the given column index is out of bounds,
2283     *            (2) the cursor is not on one of this rowset's rows or its
2284     *            insert row, or (3) this rowset is
2285     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2286     */
2287    public void updateBoolean(int columnIndex, boolean x) throws SQLException {
2288        crsInternal.updateBoolean(columnIndex, x);
2289    }
2290
2291    /**
2292     * Sets the designated column in either the current row or the insert
2293     * row of this <code>JoinRowSetImpl</code> object with the given
2294     * <code>byte</code> value.
2295     * <P>
2296     * This method updates a column value in the current row or the insert
2297     * row of this rowset, but it does not update the database.
2298     * If the cursor is on a row in the rowset, the
2299     * method {@link #updateRow} must be called to update the database.
2300     * If the cursor is on the insert row, the method {@link #insertRow}
2301     * must be called, which will insert the new row into both this rowset
2302     * and the database. Both of these methods must be called before the
2303     * cursor moves to another row.
2304     *
2305     * @param columnIndex the first column is <code>1</code>, the second
2306     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2307     *        and equal to or less than the number of columns in this rowset
2308     * @param x the new column value
2309     * @throws SQLException if (1) the given column index is out of bounds,
2310     *            (2) the cursor is not on one of this rowset's rows or its
2311     *            insert row, or (3) this rowset is
2312     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2313     */
2314    public void updateByte(int columnIndex, byte x) throws SQLException {
2315        crsInternal.updateByte(columnIndex, x);
2316    }
2317
2318    /**
2319     * Sets the designated column in either the current row or the insert
2320     * row of this <code>JoinRowSetImpl</code> object with the given
2321     * <code>short</code> value.
2322     * <P>
2323     * This method updates a column value in the current row or the insert
2324     * row of this rowset, but it does not update the database.
2325     * If the cursor is on a row in the rowset, the
2326     * method {@link #updateRow} must be called to update the database.
2327     * If the cursor is on the insert row, the method {@link #insertRow}
2328     * must be called, which will insert the new row into both this rowset
2329     * and the database. Both of these methods must be called before the
2330     * cursor moves to another row.
2331     *
2332     * @param columnIndex the first column is <code>1</code>, the second
2333     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2334     *        and equal to or less than the number of columns in this rowset
2335     * @param x the new column value
2336     * @throws SQLException if (1) the given column index is out of bounds,
2337     *            (2) the cursor is not on one of this rowset's rows or its
2338     *            insert row, or (3) this rowset is
2339     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2340     */
2341    public void updateShort(int columnIndex, short x) throws SQLException {
2342        crsInternal.updateShort(columnIndex, x);
2343    }
2344
2345    /**
2346     * Sets the designated column in either the current row or the insert
2347     * row of this <code>JoinRowSetImpl</code> object with the given
2348     * <code>int</code> value.
2349     * <P>
2350     * This method updates a column value in the current row or the insert
2351     * row of this rowset, but it does not update the database.
2352     * If the cursor is on a row in the rowset, the
2353     * method {@link #updateRow} must be called to update the database.
2354     * If the cursor is on the insert row, the method {@link #insertRow}
2355     * must be called, which will insert the new row into both this rowset
2356     * and the database. Both of these methods must be called before the
2357     * cursor moves to another row.
2358     *
2359     * @param columnIndex the first column is <code>1</code>, the second
2360     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2361     *        and equal to or less than the number of columns in this rowset
2362     * @param x the new column value
2363     * @throws SQLException if (1) the given column index is out of bounds,
2364     *            (2) the cursor is not on one of this rowset's rows or its
2365     *            insert row, or (3) this rowset is
2366     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2367     */
2368    public void updateInt(int columnIndex, int x) throws SQLException {
2369        crsInternal.updateInt(columnIndex, x);
2370    }
2371
2372    /**
2373     * Sets the designated column in either the current row or the insert
2374     * row of this <code>JoinRowSetImpl</code> object with the given
2375     * <code>long</code> value.
2376     * <P>
2377     * This method updates a column value in the current row or the insert
2378     * row of this rowset, but it does not update the database.
2379     * If the cursor is on a row in the rowset, the
2380     * method {@link #updateRow} must be called to update the database.
2381     * If the cursor is on the insert row, the method {@link #insertRow}
2382     * must be called, which will insert the new row into both this rowset
2383     * and the database. Both of these methods must be called before the
2384     * cursor moves to another row.
2385     *
2386     * @param columnIndex the first column is <code>1</code>, the second
2387     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2388     *        and equal to or less than the number of columns in this rowset
2389     * @param x the new column value
2390     * @throws SQLException if (1) the given column index is out of bounds,
2391     *            (2) the cursor is not on one of this rowset's rows or its
2392     *            insert row, or (3) this rowset is
2393     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2394     */
2395    public void updateLong(int columnIndex, long x) throws SQLException {
2396        crsInternal.updateLong(columnIndex, x);
2397    }
2398
2399    /**
2400     * Sets the designated column in either the current row or the insert
2401     * row of this <code>JoinRowSetImpl</code> object with the given
2402     * <code>float</code> value.
2403     * <P>
2404     * This method updates a column value in the current row or the insert
2405     * row of this rowset, but it does not update the database.
2406     * If the cursor is on a row in the rowset, the
2407     * method {@link #updateRow} must be called to update the database.
2408     * If the cursor is on the insert row, the method {@link #insertRow}
2409     * must be called, which will insert the new row into both this rowset
2410     * and the database. Both of these methods must be called before the
2411     * cursor moves to another row.
2412     *
2413     * @param columnIndex the first column is <code>1</code>, the second
2414     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2415     *        and equal to or less than the number of columns in this rowset
2416     * @param x the new column value
2417     * @throws SQLException if (1) the given column index is out of bounds,
2418     *            (2) the cursor is not on one of this rowset's rows or its
2419     *            insert row, or (3) this rowset is
2420     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2421     */
2422    public void updateFloat(int columnIndex, float x) throws SQLException {
2423        crsInternal.updateFloat(columnIndex, x);
2424    }
2425
2426    /**
2427     * Sets the designated column in either the current row or the insert
2428     * row of this <code>JoinRowSetImpl</code> object with the given
2429     * <code>double</code> value.
2430     *
2431     * This method updates a column value in either the current row or
2432     * the insert row of this rowset, but it does not update the
2433     * database.  If the cursor is on a row in the rowset, the
2434     * method {@link #updateRow} must be called to update the database.
2435     * If the cursor is on the insert row, the method {@link #insertRow}
2436     * must be called, which will insert the new row into both this rowset
2437     * and the database. Both of these methods must be called before the
2438     * cursor moves to another row.
2439     *
2440     * @param columnIndex the first column is <code>1</code>, the second
2441     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2442     *        and equal to or less than the number of columns in this rowset
2443     * @param x the new column value
2444     * @throws SQLException if (1) the given column index is out of bounds,
2445     *            (2) the cursor is not on one of this rowset's rows or its
2446     *            insert row, or (3) this rowset is
2447     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2448     */
2449    public void updateDouble(int columnIndex, double x) throws SQLException {
2450        crsInternal.updateDouble(columnIndex, x);
2451    }
2452
2453    /**
2454     * Sets the designated column in either the current row or the insert
2455     * row of this <code>JoinRowSetImpl</code> object with the given
2456     * <code>java.math.BigDecimal</code> object.
2457     * <P>
2458     * This method updates a column value in the current row or the insert
2459     * row of this rowset, but it does not update the database.
2460     * If the cursor is on a row in the rowset, the
2461     * method {@link #updateRow} must be called to update the database.
2462     * If the cursor is on the insert row, the method {@link #insertRow}
2463     * must be called, which will insert the new row into both this rowset
2464     * and the database. Both of these methods must be called before the
2465     * cursor moves to another row.
2466     *
2467     * @param columnIndex the first column is <code>1</code>, the second
2468     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2469     *        and equal to or less than the number of columns in this rowset
2470     * @param x the new column value
2471     * @throws SQLException if (1) the given column index is out of bounds,
2472     *            (2) the cursor is not on one of this rowset's rows or its
2473     *            insert row, or (3) this rowset is
2474     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2475     */
2476    public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException {
2477        crsInternal.updateBigDecimal(columnIndex, x);
2478    }
2479
2480    /**
2481     * Sets the designated column in either the current row or the insert
2482     * row of this <code>JoinRowSetImpl</code> object with the given
2483     * <code>String</code> object.
2484     * <P>
2485     * This method updates a column value in either the current row or
2486     * the insert row of this rowset, but it does not update the
2487     * database.  If the cursor is on a row in the rowset, the
2488     * method {@link #updateRow} must be called to mark the row as updated.
2489     * If the cursor is on the insert row, the method {@link #insertRow}
2490     * must be called to insert the new row into this rowset and mark it
2491     * as inserted. Both of these methods must be called before the
2492     * cursor moves to another row.
2493     * <P>
2494     * The method <code>acceptChanges</code> must be called if the
2495     * updated values are to be written back to the underlying database.
2496     *
2497     * @param columnIndex the first column is <code>1</code>, the second
2498     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2499     *        and equal to or less than the number of columns in this rowset
2500     * @param x the new column value
2501     * @throws SQLException if (1) the given column index is out of bounds,
2502     *            (2) the cursor is not on one of this rowset's rows or its
2503     *            insert row, or (3) this rowset is
2504     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2505     */
2506    public void updateString(int columnIndex, String x) throws SQLException {
2507        crsInternal.updateString(columnIndex, x);
2508    }
2509
2510    /**
2511     * Sets the designated column in either the current row or the insert
2512     * row of this <code>JoinRowSetImpl</code> object with the given
2513     * <code>byte</code> array.
2514     *
2515     * This method updates a column value in either the current row or
2516     * the insert row of this rowset, but it does not update the
2517     * database.  If the cursor is on a row in the rowset, the
2518     * method {@link #updateRow} must be called to update the database.
2519     * If the cursor is on the insert row, the method {@link #insertRow}
2520     * must be called, which will insert the new row into both this rowset
2521     * and the database. Both of these methods must be called before the
2522     * cursor moves to another row.
2523     *
2524     * @param columnIndex the first column is <code>1</code>, the second
2525     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2526     *        and equal to or less than the number of columns in this rowset
2527     * @param x the new column value
2528     * @throws SQLException if (1) the given column index is out of bounds,
2529     *            (2) the cursor is not on one of this rowset's rows or its
2530     *            insert row, or (3) this rowset is
2531     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2532     */
2533    public void updateBytes(int columnIndex, byte x[]) throws SQLException {
2534        crsInternal.updateBytes(columnIndex, x);
2535    }
2536
2537    /**
2538     * Sets the designated column in either the current row or the insert
2539     * row of this <code>JoinRowSetImpl</code> object with the given
2540     * <code>Date</code> object.
2541     *
2542     * This method updates a column value in either the current row or
2543     * the insert row of this rowset, but it does not update the
2544     * database.  If the cursor is on a row in the rowset, the
2545     * method {@link #updateRow} must be called to update the database.
2546     * If the cursor is on the insert row, the method {@link #insertRow}
2547     * must be called, which will insert the new row into both this rowset
2548     * and the database. Both of these methods must be called before the
2549     * cursor moves to another row.
2550     *
2551     * @param columnIndex the first column is <code>1</code>, the second
2552     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2553     *        and equal to or less than the number of columns in this rowset
2554     * @param x the new column value
2555     * @throws SQLException if (1) the given column index is out of bounds,
2556     *            (2) the cursor is not on one of this rowset's rows or its
2557     *            insert row, (3) the type of the designated column is not
2558     *            an SQL <code>DATE</code> or <code>TIMESTAMP</code>, or
2559     *            (4) this rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2560     */
2561    public void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
2562        crsInternal.updateDate(columnIndex, x);
2563    }
2564
2565    /**
2566     * Sets the designated column in either the current row or the insert
2567     * row of this <code>JoinRowSetImpl</code> object with the given
2568     * <code>Time</code> object.
2569     *
2570     * This method updates a column value in either the current row or
2571     * the insert row of this rowset, but it does not update the
2572     * database.  If the cursor is on a row in the rowset, the
2573     * method {@link #updateRow} must be called to update the database.
2574     * If the cursor is on the insert row, the method {@link #insertRow}
2575     * must be called, which will insert the new row into both this rowset
2576     * and the database. Both of these methods must be called before the
2577     * cursor moves to another row.
2578     *
2579     * @param columnIndex the first column is <code>1</code>, the second
2580     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2581     *        and equal to or less than the number of columns in this rowset
2582     * @param x the new column value
2583     * @throws SQLException if (1) the given column index is out of bounds,
2584     *            (2) the cursor is not on one of this rowset's rows or its
2585     *            insert row, (3) the type of the designated column is not
2586     *            an SQL <code>TIME</code> or <code>TIMESTAMP</code>, or
2587     *            (4) this rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2588     */
2589    public void updateTime(int columnIndex, java.sql.Time x) throws SQLException {
2590        crsInternal.updateTime(columnIndex, x);
2591    }
2592
2593    /**
2594     * Sets the designated column in either the current row or the insert
2595     * row of this <code>JoinRowSetImpl</code> object with the given
2596     * <code>Timestamp</code> object.
2597     *
2598     * This method updates a column value in either the current row or
2599     * the insert row of this rowset, but it does not update the
2600     * database.  If the cursor is on a row in the rowset, the
2601     * method {@link #updateRow} must be called to update the database.
2602     * If the cursor is on the insert row, the method {@link #insertRow}
2603     * must be called, which will insert the new row into both this rowset
2604     * and the database. Both of these methods must be called before the
2605     * cursor moves to another row.
2606     *
2607     * @param columnIndex the first column is <code>1</code>, the second
2608     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2609     *        and equal to or less than the number of columns in this rowset
2610     * @param x the new column value
2611     * @throws SQLException if (1) the given column index is out of bounds,
2612     *            (2) the cursor is not on one of this rowset's rows or its
2613     *            insert row, (3) the type of the designated column is not
2614     *            an SQL <code>DATE</code>, <code>TIME</code>, or
2615     *            <code>TIMESTAMP</code>, or (4) this rowset is
2616     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2617     */
2618    public void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException {
2619        crsInternal.updateTimestamp(columnIndex, x);
2620    }
2621
2622    /*
2623     * Sets the designated column in either the current row or the insert
2624     * row of this <code>JoinRowSetImpl</code> object with the given
2625     * ASCII stream value.
2626     * <P>
2627     * This method updates a column value in either the current row or
2628     * the insert row of this rowset, but it does not update the
2629     * database.  If the cursor is on a row in the rowset, the
2630     * method {@link #updateRow} must be called to update the database.
2631     * If the cursor is on the insert row, the method {@link #insertRow}
2632     * must be called, which will insert the new row into both this rowset
2633     * and the database. Both of these methods must be called before the
2634     * cursor moves to another row.
2635     *
2636     * @param columnIndex the first column is <code>1</code>, the second
2637     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2638     *        and equal to or less than the number of columns in this rowset
2639     * @param x the new column value
2640     * @param length the number of one-byte ASCII characters in the stream
2641     * @throws UnsupportedOperationException if this method is invoked
2642     */
2643    public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException {
2644        crsInternal.updateAsciiStream(columnIndex, x, length);
2645    }
2646
2647    /**
2648     * Sets the designated column in either the current row or the insert
2649     * row of this <code>JoinRowSetImpl</code> object with the given
2650     * <code>java.io.InputStream</code> object.
2651     * <P>
2652     * This method updates a column value in either the current row or
2653     * the insert row of this rowset, but it does not update the
2654     * database.  If the cursor is on a row in the rowset, the
2655     * method {@link #updateRow} must be called to update the database.
2656     * If the cursor is on the insert row, the method {@link #insertRow}
2657     * must be called, which will insert the new row into both this rowset
2658     * and the database. Both of these methods must be called before the
2659     * cursor moves to another row.
2660     *
2661     * @param columnIndex the first column is <code>1</code>, the second
2662     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2663     *        and equal to or less than the number of columns in this rowset
2664     * @param x the new column value; must be a <code>java.io.InputStream</code>
2665     *          containing <code>BINARY</code>, <code>VARBINARY</code>, or
2666     *          <code>LONGVARBINARY</code> data
2667     * @param length the length of the stream in bytes
2668     * @throws SQLException if (1) the given column index is out of bounds,
2669     *            (2) the cursor is not on one of this rowset's rows or its
2670     *            insert row, (3) the data in the stream is not binary, or
2671     *            (4) this rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2672     */
2673    public void updateBinaryStream(int columnIndex, java.io.InputStream x, int length) throws SQLException {
2674        crsInternal.updateBinaryStream(columnIndex, x, length);
2675    }
2676
2677    /**
2678     * Sets the designated column in either the current row or the insert
2679     * row of this <code>JoinRowSetImpl</code> object with the given
2680     * <code>java.io.Reader</code> object.
2681     * <P>
2682     * This method updates a column value in either the current row or
2683     * the insert row of this rowset, but it does not update the
2684     * database.  If the cursor is on a row in the rowset, the
2685     * method {@link #updateRow} must be called to update the database.
2686     * If the cursor is on the insert row, the method {@link #insertRow}
2687     * must be called, which will insert the new row into both this rowset
2688     * and the database. Both of these methods must be called before the
2689     * cursor moves to another row.
2690     *
2691     * @param columnIndex the first column is <code>1</code>, the second
2692     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2693     *        and equal to or less than the number of columns in this rowset
2694     * @param x the new column value; must be a <code>java.io.Reader</code>
2695     *          containing <code>BINARY</code>, <code>VARBINARY</code>,
2696     *          <code>LONGVARBINARY</code>, <code>CHAR</code>, <code>VARCHAR</code>,
2697     *          or <code>LONGVARCHAR</code> data
2698     * @param length the length of the stream in characters
2699     * @throws SQLException if (1) the given column index is out of bounds,
2700     *            (2) the cursor is not on one of this rowset's rows or its
2701     *            insert row, (3) the data in the stream is not a binary or
2702     *            character type, or (4) this rowset is
2703     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2704     */
2705    public void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException {
2706        crsInternal.updateCharacterStream(columnIndex, x, length);
2707    }
2708
2709    /**
2710     * Sets the designated column in either the current row or the insert
2711     * row of this <code>JoinRowSetImpl</code> object with the given
2712     * <code>Object</code> value.  The <code>scale</code> parameter indicates
2713     * the number of digits to the right of the decimal point and is ignored
2714     * if the new column value is not a type that will be mapped to an SQL
2715     * <code>DECIMAL</code> or <code>NUMERIC</code> value.
2716     * <P>
2717     * This method updates a column value in either the current row or
2718     * the insert row of this rowset, but it does not update the
2719     * database.  If the cursor is on a row in the rowset, the
2720     * method {@link #updateRow} must be called to update the database.
2721     * If the cursor is on the insert row, the method {@link #insertRow}
2722     * must be called, which will insert the new row into both this rowset
2723     * and the database. Both of these methods must be called before the
2724     * cursor moves to another row.
2725     *
2726     * @param columnIndex the first column is <code>1</code>, the second
2727     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2728     *        and equal to or less than the number of columns in this rowset
2729     * @param x the new column value
2730     * @param scale the number of digits to the right of the decimal point (for
2731     *              <code>DECIMAL</code> and <code>NUMERIC</code> types only)
2732     * @throws SQLException if (1) the given column index is out of bounds,
2733     *            (2) the cursor is not on one of this rowset's rows or its
2734     *            insert row, or (3) this rowset is
2735     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2736     */
2737    public void updateObject(int columnIndex, Object x, int scale) throws SQLException {
2738        crsInternal.updateObject(columnIndex, x, scale);
2739    }
2740
2741    /**
2742     * Sets the designated column in either the current row or the insert
2743     * row of this <code>JoinRowSetImpl</code> object with the given
2744     * <code>Object</code> value.
2745     * <P>
2746     * This method updates a column value in either the current row or
2747     * the insert row of this rowset, but it does not update the
2748     * database.  If the cursor is on a row in the rowset, the
2749     * method {@link #updateRow} must be called to update the database.
2750     * If the cursor is on the insert row, the method {@link #insertRow}
2751     * must be called, which will insert the new row into both this rowset
2752     * and the database. Both of these methods must be called before the
2753     * cursor moves to another row.
2754     *
2755     * @param columnIndex the first column is <code>1</code>, the second
2756     *        is <code>2</code>, and so on; must be <code>1</code> or larger
2757     *        and equal to or less than the number of columns in this rowset
2758     * @param x the new column value
2759     * @throws SQLException if (1) the given column index is out of bounds,
2760     *            (2) the cursor is not on one of this rowset's rows or its
2761     *            insert row, or (3) this rowset is
2762     *            <code>ResultSet.CONCUR_READ_ONLY</code>
2763     */
2764    public void updateObject(int columnIndex, Object x) throws SQLException {
2765        crsInternal.updateObject(columnIndex, x);
2766    }
2767
2768    // columnName updates
2769
2770    /**
2771     * Sets the designated nullable column in the current row or the
2772     * insert row of this <code>JoinRowSetImpl</code> object with
2773     * <code>null</code> value.
2774     * <P>
2775     * This method updates a column value in the current row or the insert
2776     * row of this rowset, but it does not update the database.
2777     * If the cursor is on a row in the rowset, the
2778     * method {@link #updateRow} must be called to update the database.
2779     * If the cursor is on the insert row, the method {@link #insertRow}
2780     * must be called, which will insert the new row into both this rowset
2781     * and the database.
2782     *
2783     * @param columnName a <code>String</code> object that must match the
2784     *        SQL name of a column in this rowset, ignoring case
2785     * @throws SQLException if (1) the given column name does not match the
2786     *            name of a column in this rowset, (2) the cursor is not on
2787     *            one of this rowset's rows or its insert row, or (3) this
2788     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2789     */
2790    public void updateNull(String columnName) throws SQLException {
2791        crsInternal.updateNull(columnName);
2792    }
2793
2794    /**
2795     * Sets the designated column in either the current row or the insert
2796     * row of this <code>JoinRowSetImpl</code> object with the given
2797     * <code>boolean</code> value.
2798     * <P>
2799     * This method updates a column value in the current row or the insert
2800     * row of this rowset, but it does not update the database.
2801     * If the cursor is on a row in the rowset, the
2802     * method {@link #updateRow} must be called to update the database.
2803     * If the cursor is on the insert row, the method {@link #insertRow}
2804     * must be called, which will insert the new row into both this rowset
2805     * and the database. Both of these methods must be called before the
2806     * cursor moves to another row.
2807     *
2808     * @param columnName a <code>String</code> object that must match the
2809     *        SQL name of a column in this rowset, ignoring case
2810     * @param x the new column value
2811     * @throws SQLException if (1) the given column name does not match the
2812     *            name of a column in this rowset, (2) the cursor is not on
2813     *            one of this rowset's rows or its insert row, or (3) this
2814     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2815     */
2816    public void updateBoolean(String columnName, boolean x) throws SQLException {
2817        crsInternal.updateBoolean(columnName, x);
2818    }
2819
2820    /**
2821     * Sets the designated column in either the current row or the insert
2822     * row of this <code>JoinRowSetImpl</code> object with the given
2823     * <code>byte</code> value.
2824     * <P>
2825     * This method updates a column value in the current row or the insert
2826     * row of this rowset, but it does not update the database.
2827     * If the cursor is on a row in the rowset, the
2828     * method {@link #updateRow} must be called to update the database.
2829     * If the cursor is on the insert row, the method {@link #insertRow}
2830     * must be called, which will insert the new row into both this rowset
2831     * and the database. Both of these methods must be called before the
2832     * cursor moves to another row.
2833     *
2834     * @param columnName a <code>String</code> object that must match the
2835     *        SQL name of a column in this rowset, ignoring case
2836     * @param x the new column value
2837     * @throws SQLException if (1) the given column name does not match the
2838     *            name of a column in this rowset, (2) the cursor is not on
2839     *            one of this rowset's rows or its insert row, or (3) this
2840     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2841     */
2842    public void updateByte(String columnName, byte x) throws SQLException {
2843        crsInternal.updateByte(columnName, x);
2844    }
2845
2846    /**
2847     * Sets the designated column in either the current row or the insert
2848     * row of this <code>JoinRowSetImpl</code> object with the given
2849     * <code>short</code> value.
2850     * <P>
2851     * This method updates a column value in the current row or the insert
2852     * row of this rowset, but it does not update the database.
2853     * If the cursor is on a row in the rowset, the
2854     * method {@link #updateRow} must be called to update the database.
2855     * If the cursor is on the insert row, the method {@link #insertRow}
2856     * must be called, which will insert the new row into both this rowset
2857     * and the database. Both of these methods must be called before the
2858     * cursor moves to another row.
2859     *
2860     * @param columnName a <code>String</code> object that must match the
2861     *        SQL name of a column in this rowset, ignoring case
2862     * @param x the new column value
2863     * @throws SQLException if (1) the given column name does not match the
2864     *            name of a column in this rowset, (2) the cursor is not on
2865     *            one of this rowset's rows or its insert row, or (3) this
2866     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2867     */
2868    public void updateShort(String columnName, short x) throws SQLException {
2869        crsInternal.updateShort(columnName, x);
2870    }
2871
2872    /**
2873     * Sets the designated column in either the current row or the insert
2874     * row of this <code>JoinRowSetImpl</code> object with the given
2875     * <code>int</code> value.
2876     * <P>
2877     * This method updates a column value in the current row or the insert
2878     * row of this rowset, but it does not update the database.
2879     * If the cursor is on a row in the rowset, the
2880     * method {@link #updateRow} must be called to update the database.
2881     * If the cursor is on the insert row, the method {@link #insertRow}
2882     * must be called, which will insert the new row into both this rowset
2883     * and the database. Both of these methods must be called before the
2884     * cursor moves to another row.
2885     *
2886     * @param columnName a <code>String</code> object that must match the
2887     *        SQL name of a column in this rowset, ignoring case
2888     * @param x the new column value
2889     * @throws SQLException if (1) the given column name does not match the
2890     *            name of a column in this rowset, (2) the cursor is not on
2891     *            one of this rowset's rows or its insert row, or (3) this
2892     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2893     */
2894    public void updateInt(String columnName, int x) throws SQLException {
2895        crsInternal.updateInt(columnName, x);
2896    }
2897
2898    /**
2899     * Sets the designated column in either the current row or the insert
2900     * row of this <code>JoinRowSetImpl</code> object with the given
2901     * <code>long</code> value.
2902     * <P>
2903     * This method updates a column value in the current row or the insert
2904     * row of this rowset, but it does not update the database.
2905     * If the cursor is on a row in the rowset, the
2906     * method {@link #updateRow} must be called to update the database.
2907     * If the cursor is on the insert row, the method {@link #insertRow}
2908     * must be called, which will insert the new row into both this rowset
2909     * and the database. Both of these methods must be called before the
2910     * cursor moves to another row.
2911     *
2912     * @param columnName a <code>String</code> object that must match the
2913     *        SQL name of a column in this rowset, ignoring case
2914     * @param x the new column value
2915     * @throws SQLException if (1) the given column name does not match the
2916     *            name of a column in this rowset, (2) the cursor is not on
2917     *            one of this rowset's rows or its insert row, or (3) this
2918     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2919     */
2920    public void updateLong(String columnName, long x) throws SQLException {
2921        crsInternal.updateLong(columnName, x);
2922    }
2923
2924    /**
2925     * Sets the designated column in either the current row or the insert
2926     * row of this <code>JoinRowSetImpl</code> object with the given
2927     * <code>float</code> value.
2928     * <P>
2929     * This method updates a column value in the current row or the insert
2930     * row of this rowset, but it does not update the database.
2931     * If the cursor is on a row in the rowset, the
2932     * method {@link #updateRow} must be called to update the database.
2933     * If the cursor is on the insert row, the method {@link #insertRow}
2934     * must be called, which will insert the new row into both this rowset
2935     * and the database. Both of these methods must be called before the
2936     * cursor moves to another row.
2937     *
2938     * @param columnName a <code>String</code> object that must match the
2939     *        SQL name of a column in this rowset, ignoring case
2940     * @param x the new column value
2941     * @throws SQLException if (1) the given column name does not match the
2942     *            name of a column in this rowset, (2) the cursor is not on
2943     *            one of this rowset's rows or its insert row, or (3) this
2944     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2945     */
2946    public void updateFloat(String columnName, float x) throws SQLException {
2947        crsInternal.updateFloat(columnName, x);
2948    }
2949
2950    /**
2951     * Sets the designated column in either the current row or the insert
2952     * row of this <code>JoinRowSetImpl</code> object with the given
2953     * <code>double</code> value.
2954     *
2955     * This method updates a column value in either the current row or
2956     * the insert row of this rowset, but it does not update the
2957     * database.  If the cursor is on a row in the rowset, the
2958     * method {@link #updateRow} must be called to update the database.
2959     * If the cursor is on the insert row, the method {@link #insertRow}
2960     * must be called, which will insert the new row into both this rowset
2961     * and the database. Both of these methods must be called before the
2962     * cursor moves to another row.
2963     *
2964     * @param columnName a <code>String</code> object that must match the
2965     *        SQL name of a column in this rowset, ignoring case
2966     * @param x the new column value
2967     * @throws SQLException if (1) the given column name does not match the
2968     *            name of a column in this rowset, (2) the cursor is not on
2969     *            one of this rowset's rows or its insert row, or (3) this
2970     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2971     */
2972    public void updateDouble(String columnName, double x) throws SQLException {
2973        crsInternal.updateDouble(columnName, x);
2974    }
2975
2976    /**
2977     * Sets the designated column in either the current row or the insert
2978     * row of this <code>JoinRowSetImpl</code> object with the given
2979     * <code>java.math.BigDecimal</code> object.
2980     * <P>
2981     * This method updates a column value in the current row or the insert
2982     * row of this rowset, but it does not update the database.
2983     * If the cursor is on a row in the rowset, the
2984     * method {@link #updateRow} must be called to update the database.
2985     * If the cursor is on the insert row, the method {@link #insertRow}
2986     * must be called, which will insert the new row into both this rowset
2987     * and the database. Both of these methods must be called before the
2988     * cursor moves to another row.
2989     *
2990     * @param columnName a <code>String</code> object that must match the
2991     *        SQL name of a column in this rowset, ignoring case
2992     * @param x the new column value
2993     * @throws SQLException if (1) the given column name does not match the
2994     *            name of a column in this rowset, (2) the cursor is not on
2995     *            one of this rowset's rows or its insert row, or (3) this
2996     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
2997     */
2998    public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException {
2999        crsInternal.updateBigDecimal(columnName, x);
3000    }
3001
3002    /**
3003     * Sets the designated column in either the current row or the insert
3004     * row of this <code>JoinRowSetImpl</code> object with the given
3005     * <code>String</code> object.
3006     *
3007     * This method updates a column value in either the current row or
3008     * the insert row of this rowset, but it does not update the
3009     * database.  If the cursor is on a row in the rowset, the
3010     * method {@link #updateRow} must be called to update the database.
3011     * If the cursor is on the insert row, the method {@link #insertRow}
3012     * must be called, which will insert the new row into both this rowset
3013     * and the database. Both of these methods must be called before the
3014     * cursor moves to another row.
3015     *
3016     * @param columnName a <code>String</code> object that must match the
3017     *        SQL name of a column in this rowset, ignoring case
3018     * @param x the new column value
3019     * @throws SQLException if (1) the given column name does not match the
3020     *            name of a column in this rowset, (2) the cursor is not on
3021     *            one of this rowset's rows or its insert row, or (3) this
3022     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
3023     */
3024    public void updateString(String columnName, String x) throws SQLException {
3025        crsInternal.updateString(columnName, x);
3026    }
3027
3028    /**
3029     * Sets the designated column in either the current row or the insert
3030     * row of this <code>JoinRowSetImpl</code> object with the given
3031     * <code>byte</code> array.
3032     *
3033     * This method updates a column value in either the current row or
3034     * the insert row of this rowset, but it does not update the
3035     * database.  If the cursor is on a row in the rowset, the
3036     * method {@link #updateRow} must be called to update the database.
3037     * If the cursor is on the insert row, the method {@link #insertRow}
3038     * must be called, which will insert the new row into both this rowset
3039     * and the database. Both of these methods must be called before the
3040     * cursor moves to another row.
3041     *
3042     * @param columnName a <code>String</code> object that must match the
3043     *        SQL name of a column in this rowset, ignoring case
3044     * @param x the new column value
3045     * @throws SQLException if (1) the given column name does not match the
3046     *            name of a column in this rowset, (2) the cursor is not on
3047     *            one of this rowset's rows or its insert row, or (3) this
3048     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
3049     */
3050    public void updateBytes(String columnName, byte x[]) throws SQLException {
3051        crsInternal.updateBytes(columnName, x);
3052    }
3053
3054    /**
3055     * Sets the designated column in either the current row or the insert
3056     * row of this <code>JoinRowSetImpl</code> object with the given
3057     * <code>Date</code> object.
3058     *
3059     * This method updates a column value in either the current row or
3060     * the insert row of this rowset, but it does not update the
3061     * database.  If the cursor is on a row in the rowset, the
3062     * method {@link #updateRow} must be called to update the database.
3063     * If the cursor is on the insert row, the method {@link #insertRow}
3064     * must be called, which will insert the new row into both this rowset
3065     * and the database. Both of these methods must be called before the
3066     * cursor moves to another row.
3067     *
3068     * @param columnName a <code>String</code> object that must match the
3069     *        SQL name of a column in this rowset, ignoring case
3070     * @param x the new column value
3071     * @throws SQLException if (1) the given column name does not match the
3072     *            name of a column in this rowset, (2) the cursor is not on
3073     *            one of this rowset's rows or its insert row, (3) the type
3074     *            of the designated column is not an SQL <code>DATE</code> or
3075     *            <code>TIMESTAMP</code>, or (4) this rowset is
3076     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3077     */
3078    public void updateDate(String columnName, java.sql.Date x) throws SQLException {
3079        crsInternal.updateDate(columnName, x);
3080    }
3081
3082    /**
3083     * Sets the designated column in either the current row or the insert
3084     * row of this <code>JoinRowSetImpl</code> object with the given
3085     * <code>Time</code> object.
3086     *
3087     * This method updates a column value in either the current row or
3088     * the insert row of this rowset, but it does not update the
3089     * database.  If the cursor is on a row in the rowset, the
3090     * method {@link #updateRow} must be called to update the database.
3091     * If the cursor is on the insert row, the method {@link #insertRow}
3092     * must be called, which will insert the new row into both this rowset
3093     * and the database. Both of these methods must be called before the
3094     * cursor moves to another row.
3095     *
3096     * @param columnName a <code>String</code> object that must match the
3097     *        SQL name of a column in this rowset, ignoring case
3098     * @param x the new column value
3099     * @throws SQLException if (1) the given column name does not match the
3100     *            name of a column in this rowset, (2) the cursor is not on
3101     *            one of this rowset's rows or its insert row, (3) the type
3102     *            of the designated column is not an SQL <code>TIME</code> or
3103     *            <code>TIMESTAMP</code>, or (4) this rowset is
3104     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3105     */
3106    public void updateTime(String columnName, java.sql.Time x) throws SQLException {
3107        crsInternal.updateTime(columnName, x);
3108    }
3109
3110    /**
3111     * Sets the designated column in either the current row or the insert
3112     * row of this <code>JoinRowSetImpl</code> object with the given
3113     * <code>Timestamp</code> object.
3114     *
3115     * This method updates a column value in either the current row or
3116     * the insert row of this rowset, but it does not update the
3117     * database.  If the cursor is on a row in the rowset, the
3118     * method {@link #updateRow} must be called to update the database.
3119     * If the cursor is on the insert row, the method {@link #insertRow}
3120     * must be called, which will insert the new row into both this rowset
3121     * and the database. Both of these methods must be called before the
3122     * cursor moves to another row.
3123     *
3124     * @param columnName a <code>String</code> object that must match the
3125     *        SQL name of a column in this rowset, ignoring case
3126     * @param x the new column value
3127     * @throws SQLException if the given column index is out of bounds or
3128     *            the cursor is not on one of this rowset's rows or its
3129     *            insert row
3130     * @throws SQLException if (1) the given column name does not match the
3131     *            name of a column in this rowset, (2) the cursor is not on
3132     *            one of this rowset's rows or its insert row, (3) the type
3133     *            of the designated column is not an SQL <code>DATE</code>,
3134     *            <code>TIME</code>, or <code>TIMESTAMP</code>, or (4) this
3135     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
3136     */
3137    public void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException {
3138        crsInternal.updateTimestamp(columnName, x);
3139    }
3140
3141    /**
3142     * Unsupported; throws an <code>UnsupportedOperationException</code>
3143     * if called.
3144     * <P>
3145     * Sets the designated column in either the current row or the insert
3146     * row of this <code>JoinRowSetImpl</code> object with the given
3147     * ASCII stream value.
3148     * <P>
3149     * This method updates a column value in either the current row or
3150     * the insert row of this rowset, but it does not update the
3151     * database.  If the cursor is on a row in the rowset, the
3152     * method {@link #updateRow} must be called to update the database.
3153     * If the cursor is on the insert row, the method {@link #insertRow}
3154     * must be called, which will insert the new row into both this rowset
3155     * and the database. Both of these methods must be called before the
3156     * cursor moves to another row.
3157     *
3158     * @param columnName a <code>String</code> object that must match the
3159     *        SQL name of a column in this rowset, ignoring case
3160     * @param x the new column value
3161     * @param length the number of one-byte ASCII characters in the stream
3162     * @throws UnsupportedOperationException if this method is invoked
3163     */
3164    public void updateAsciiStream(String columnName, java.io.InputStream x, int length) throws SQLException {
3165        crsInternal.updateAsciiStream(columnName, x, length);
3166    }
3167
3168    /**
3169     * Sets the designated column in either the current row or the insert
3170     * row of this <code>JoinRowSetImpl</code> object with the given
3171     * <code>java.io.InputStream</code> object.
3172     * <P>
3173     * This method updates a column value in either the current row or
3174     * the insert row of this rowset, but it does not update the
3175     * database.  If the cursor is on a row in the rowset, the
3176     * method {@link #updateRow} must be called to update the database.
3177     * If the cursor is on the insert row, the method {@link #insertRow}
3178     * must be called, which will insert the new row into both this rowset
3179     * and the database. Both of these methods must be called before the
3180     * cursor moves to another row.
3181     *
3182     * @param columnName a <code>String</code> object that must match the
3183     *        SQL name of a column in this rowset, ignoring case
3184     * @param x the new column value; must be a <code>java.io.InputStream</code>
3185     *          containing <code>BINARY</code>, <code>VARBINARY</code>, or
3186     *          <code>LONGVARBINARY</code> data
3187     * @param length the length of the stream in bytes
3188     * @throws SQLException if (1) the given column name does not match the
3189     *            name of a column in this rowset, (2) the cursor is not on
3190     *            one of this rowset's rows or its insert row, (3) the data
3191     *            in the stream is not binary, or (4) this rowset is
3192     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3193     */
3194    public void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException {
3195        crsInternal.updateBinaryStream(columnName, x, length);
3196    }
3197
3198    /**
3199     * Sets the designated column in either the current row or the insert
3200     * row of this <code>JoinRowSetImpl</code> object with the given
3201     * <code>java.io.Reader</code> object.
3202     * <P>
3203     * This method updates a column value in either the current row or
3204     * the insert row of this rowset, but it does not update the
3205     * database.  If the cursor is on a row in the rowset, the
3206     * method {@link #updateRow} must be called to update the database.
3207     * If the cursor is on the insert row, the method {@link #insertRow}
3208     * must be called, which will insert the new row into both this rowset
3209     * and the database. Both of these methods must be called before the
3210     * cursor moves to another row.
3211     *
3212     * @param columnName a <code>String</code> object that must match the
3213     *        SQL name of a column in this rowset, ignoring case
3214     * @param x the new column value; must be a <code>java.io.Reader</code>
3215     *          containing <code>BINARY</code>, <code>VARBINARY</code>,
3216     *          <code>LONGVARBINARY</code>, <code>CHAR</code>, <code>VARCHAR</code>,
3217     *          or <code>LONGVARCHAR</code> data
3218     * @param length the length of the stream in characters
3219     * @throws SQLException if (1) the given column name does not match the
3220     *            name of a column in this rowset, (2) the cursor is not on
3221     *            one of this rowset's rows or its insert row, (3) the data
3222     *            in the stream is not a binary or character type, or (4) this
3223     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
3224     */
3225    public void updateCharacterStream(String columnName, java.io.Reader x, int length) throws SQLException {
3226        crsInternal.updateCharacterStream(columnName, x, length);
3227    }
3228
3229    /**
3230     * Sets the designated column in either the current row or the insert
3231     * row of this <code>JoinRowSetImpl</code> object with the given
3232     * <code>Object</code> value.  The <code>scale</code> parameter
3233     * indicates the number of digits to the right of the decimal point
3234     * and is ignored if the new column value is not a type that will be
3235     *  mapped to an SQL <code>DECIMAL</code> or <code>NUMERIC</code> value.
3236     * <P>
3237     * This method updates a column value in either the current row or
3238     * the insert row of this rowset, but it does not update the
3239     * database.  If the cursor is on a row in the rowset, the
3240     * method {@link #updateRow} must be called to update the database.
3241     * If the cursor is on the insert row, the method {@link #insertRow}
3242     * must be called, which will insert the new row into both this rowset
3243     * and the database. Both of these methods must be called before the
3244     * cursor moves to another row.
3245     *
3246     * @param columnName a <code>String</code> object that must match the
3247     *        SQL name of a column in this rowset, ignoring case
3248     * @param x the new column value
3249     * @param scale the number of digits to the right of the decimal point (for
3250     *              <code>DECIMAL</code> and <code>NUMERIC</code> types only)
3251     * @throws SQLException if the given column index is out of bounds or
3252     *            the cursor is not on one of this rowset's rows or its
3253     *            insert row
3254     * @throws SQLException if (1) the given column name does not match the
3255     *            name of a column in this rowset, (2) the cursor is not on
3256     *            one of this rowset's rows or its insert row, or (3) this
3257     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
3258     */
3259    public void updateObject(String columnName, Object x, int scale) throws SQLException {
3260        crsInternal.updateObject(columnName, x, scale);
3261    }
3262
3263    /**
3264     * Sets the designated column in either the current row or the insert
3265     * row of this <code>JoinRowSetImpl</code> object with the given
3266     * <code>Object</code> value.
3267     * <P>
3268     * This method updates a column value in either the current row or
3269     * the insert row of this rowset, but it does not update the
3270     * database.  If the cursor is on a row in the rowset, the
3271     * method {@link #updateRow} must be called to update the database.
3272     * If the cursor is on the insert row, the method {@link #insertRow}
3273     * must be called, which will insert the new row into both this rowset
3274     * and the database. Both of these methods must be called before the
3275     * cursor moves to another row.
3276     *
3277     * @param columnName a <code>String</code> object that must match the
3278     *        SQL name of a column in this rowset, ignoring case
3279     * @param x the new column value
3280     * @throws SQLException if (1) the given column name does not match the
3281     *            name of a column in this rowset, (2) the cursor is not on
3282     *            one of this rowset's rows or its insert row, or (3) this
3283     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
3284     */
3285    public void updateObject(String columnName, Object x) throws SQLException {
3286        crsInternal.updateObject(columnName, x);
3287    }
3288
3289    /**
3290     * Inserts the contents of this <code>JoinRowSetImpl</code> object's insert
3291     * row into this rowset immediately following the current row.
3292     * If the current row is the
3293     * position after the last row or before the first row, the new row will
3294     * be inserted at the end of the rowset.  This method also notifies
3295     * listeners registered with this rowset that the row has changed.
3296     * <P>
3297     * The cursor must be on the insert row when this method is called.
3298     *
3299     * @throws SQLException if (1) the cursor is not on the insert row,
3300     *            (2) one or more of the non-nullable columns in the insert
3301     *            row has not been given a value, or (3) this rowset is
3302     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3303     */
3304    public void insertRow() throws SQLException {
3305        crsInternal.insertRow();
3306    }
3307
3308    /**
3309     * Marks the current row of this <code>JoinRowSetImpl</code> object as
3310     * updated and notifies listeners registered with this rowset that the
3311     * row has changed.
3312     * <P>
3313     * This method  cannot be called when the cursor is on the insert row, and
3314     * it should be called before the cursor moves to another row.  If it is
3315     * called after the cursor moves to another row, this method has no effect,
3316     * and the updates made before the cursor moved will be lost.
3317     *
3318     * @throws SQLException if the cursor is on the insert row or this
3319     *            rowset is <code>ResultSet.CONCUR_READ_ONLY</code>
3320     */
3321    public void updateRow() throws SQLException {
3322        crsInternal.updateRow();
3323    }
3324
3325    /**
3326     * Deletes the current row from this <code>JoinRowSetImpl</code> object and
3327     * notifies listeners registered with this rowset that a row has changed.
3328     * This method cannot be called when the cursor is on the insert row.
3329     * <P>
3330     * This method marks the current row as deleted, but it does not delete
3331     * the row from the underlying data source.  The method
3332     * <code>acceptChanges</code> must be called to delete the row in
3333     * the data source.
3334     *
3335     * @throws SQLException if (1) this method is called when the cursor
3336     *            is on the insert row, before the first row, or after the
3337     *            last row or (2) this rowset is
3338     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3339     */
3340    public void deleteRow() throws SQLException {
3341        crsInternal.deleteRow();
3342    }
3343
3344    /**
3345     * Sets the current row with its original value and marks the row as
3346     * not updated, thus undoing any changes made to the row since the
3347     * last call to the methods <code>updateRow</code> or <code>deleteRow</code>.
3348     * This method should be called only when the cursor is on a row in
3349     * this rowset.
3350     *
3351     * @throws SQLException if the cursor is on the insert row, before the
3352     *            first row, or after the last row
3353     */
3354    public void refreshRow() throws SQLException {
3355        crsInternal.refreshRow();
3356    }
3357
3358    /**
3359     * Rolls back any updates made to the current row of this
3360     * <code>JoinRowSetImpl</code> object and notifies listeners that
3361     * a row has changed.  To have an effect, this method
3362     * must be called after an <code>updateXXX</code> method has been
3363     * called and before the method <code>updateRow</code> has been called.
3364     * If no updates have been made or the method <code>updateRow</code>
3365     * has already been called, this method has no effect.
3366     * <P>
3367     * After <code>updateRow</code> is called it is the
3368     * <code>cancelRowUpdates</code> has no affect on the newly
3369     * inserted values. The method <code>cancelRowInsert</code> can
3370     * be used to remove any rows inserted into the RowSet.
3371     *
3372     * @throws SQLException if the cursor is on the insert row, before the
3373     *            first row, or after the last row
3374     */
3375    public void cancelRowUpdates() throws SQLException {
3376        crsInternal.cancelRowUpdates();
3377    }
3378
3379    /**
3380     * Moves the cursor for this <code>JoinRowSetImpl</code> object
3381     * to the insert row.  The current row in the rowset is remembered
3382     * while the cursor is on the insert row.
3383     * <P>
3384     * The insert row is a special row associated with an updatable
3385     * rowset.  It is essentially a buffer where a new row may
3386     * be constructed by calling the appropriate <code>updateXXX</code>
3387     * methods to assign a value to each column in the row.  A complete
3388     * row must be constructed; that is, every column that is not nullable
3389     * must be assigned a value.  In order for the new row to become part
3390     * of this rowset, the method <code>insertRow</code> must be called
3391     * before the cursor is moved back to the rowset.
3392     * <P>
3393     * Only certain methods may be invoked while the cursor is on the insert
3394     * row; many methods throw an exception if they are called while the
3395     * cursor is there.  In addition to the <code>updateXXX</code>
3396     * and <code>insertRow</code> methods, only the <code>getXXX</code> methods
3397     * may be called when the cursor is on the insert row.  A <code>getXXX</code>
3398     * method should be called on a column only after an <code>updateXXX</code>
3399     * method has been called on that column; otherwise, the value returned is
3400     * undetermined.
3401     *
3402     * @throws SQLException if this <code>JoinRowSetImpl</code> object is
3403     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3404     */
3405    public void moveToInsertRow() throws SQLException {
3406        crsInternal.moveToInsertRow();
3407    }
3408
3409    /**
3410     * Moves the cursor for this <code>JoinRowSetImpl</code> object to
3411     * the current row.  The current row is the row the cursor was on
3412     * when the method <code>moveToInsertRow</code> was called.
3413     * <P>
3414     * Calling this method has no effect unless it is called while the
3415     * cursor is on the insert row.
3416     *
3417     * @throws SQLException if an error occurs
3418     */
3419    public void moveToCurrentRow() throws SQLException {
3420        crsInternal.moveToCurrentRow();
3421    }
3422
3423    /**
3424     * Returns <code>null</code>.
3425     *
3426     * @return <code>null</code>
3427     * @throws SQLException if an error occurs
3428     */
3429    public Statement getStatement() throws SQLException {
3430        return crsInternal.getStatement();
3431    }
3432
3433    /**
3434     * Retrieves the value of the designated column in this
3435     * <code>JoinRowSetImpl</code> object as a <code>Ref</code> object
3436     * in the Java programming lanugage.
3437     *
3438     * @param columnIndex the first column is <code>1</code>, the second
3439     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3440     *        and equal to or less than the number of columns in this rowset
3441     * @return a <code>Ref</code> object representing an SQL<code> REF</code> value
3442     * @throws SQLException if (1) the given column index is out of bounds,
3443     *            (2) the cursor is not on one of this rowset's rows or its
3444     *            insert row, or (3) the designated column does not store an
3445     *            SQL <code>REF</code> value
3446     */
3447    public Ref getRef(int columnIndex) throws SQLException {
3448        return crsInternal.getRef(columnIndex);
3449    }
3450
3451    /**
3452     * Retrieves the value of the designated column in this
3453     * <code>JoinRowSetImpl</code> object as a <code>Blob</code> object
3454     * in the Java programming lanugage.
3455     *
3456     * @param columnIndex the first column is <code>1</code>, the second
3457     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3458     *        and equal to or less than the number of columns in this rowset
3459     * @return a <code>Blob</code> object representing an SQL <code>BLOB</code> value
3460     * @throws SQLException if (1) the given column index is out of bounds,
3461     *            (2) the cursor is not on one of this rowset's rows or its
3462     *            insert row, or (3) the designated column does not store an
3463     *            SQL <code>BLOB</code> value
3464     */
3465    public Blob getBlob(int columnIndex) throws SQLException {
3466        return crsInternal.getBlob(columnIndex);
3467    }
3468
3469    /**
3470     * Retrieves the value of the designated column in this
3471     * <code>JoinRowSetImpl</code> object as a <code>Clob</code> object
3472     * in the Java programming lanugage.
3473     *
3474     * @param columnIndex the first column is <code>1</code>, the second
3475     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3476     *        and equal to or less than the number of columns in this rowset
3477     * @return a <code>Clob</code> object representing an SQL <code>CLOB</code> value
3478     * @throws SQLException if (1) the given column index is out of bounds,
3479     *            (2) the cursor is not on one of this rowset's rows or its
3480     *            insert row, or (3) the designated column does not store an
3481     *            SQL <code>CLOB</code> value
3482     */
3483    public Clob getClob(int columnIndex) throws SQLException {
3484        return crsInternal.getClob(columnIndex);
3485    }
3486
3487    /**
3488     * Retrieves the value of the designated column in this
3489     * <code>JoinRowSetImpl</code> object as an <code>Array</code> object
3490     * in the Java programming lanugage.
3491     *
3492     * @param columnIndex the first column is <code>1</code>, the second
3493     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3494     *        and equal to or less than the number of columns in this rowset
3495     * @return an <code>Array</code> object representing an SQL
3496     *         <code>ARRAY</code> value
3497     * @throws SQLException if (1) the given column index is out of bounds,
3498     *            (2) the cursor is not on one of this rowset's rows or its
3499     *            insert row, or (3) the designated column does not store an
3500     *            SQL <code>ARRAY</code> value
3501     */
3502     public Array getArray(int columnIndex) throws SQLException {
3503        return crsInternal.getArray(columnIndex);
3504    }
3505
3506    // ColumnName
3507
3508    /**
3509     * Retrieves the value of the designated column in this
3510     * <code>JoinRowSetImpl</code> object as a <code>Ref</code> object
3511     * in the Java programming lanugage.
3512     *
3513     * @param columnName a <code>String</code> object that must match the
3514     *        SQL name of a column in this rowset, ignoring case
3515     * @return a <code>Ref</code> object representing an SQL<code> REF</code> value
3516     * @throws SQLException  if (1) the given column name is not the name
3517     *         of a column in this rowset, (2) the cursor is not on one of
3518     *         this rowset's rows or its insert row, or (3) the column value
3519     *         is not an SQL <code>REF</code> value
3520     */
3521    public Ref getRef(String columnName) throws SQLException {
3522        return crsInternal.getRef(columnName);
3523    }
3524
3525    /**
3526     * Retrieves the value of the designated column in this
3527     * <code>JoinRowSetImpl</code> object as a <code>Blob</code> object
3528     * in the Java programming lanugage.
3529     *
3530     * @param columnName a <code>String</code> object that must match the
3531     *        SQL name of a column in this rowset, ignoring case
3532     * @return a <code>Blob</code> object representing an SQL
3533     *        <code>BLOB</code> value
3534     * @throws SQLException if (1) the given column name is not the name of
3535     *        a column in this rowset, (2) the cursor is not on one of
3536     *        this rowset's rows or its insert row, or (3) the designated
3537     *        column does not store an SQL <code>BLOB</code> value
3538     */
3539    public Blob getBlob(String columnName) throws SQLException {
3540        return crsInternal.getBlob(columnName);
3541    }
3542
3543    /**
3544     * Retrieves the value of the designated column in this
3545     * <code>JoinRowSetImpl</code> object as a <code>Clob</code> object
3546     * in the Java programming lanugage.
3547     *
3548     * @param columnName a <code>String</code> object that must match the
3549     *        SQL name of a column in this rowset, ignoring case
3550     * @return a <code>Clob</code> object representing an SQL
3551     *         <code>CLOB</code> value
3552     * @throws SQLException if (1) the given column name is not the name of
3553     *            a column in this rowset, (2) the cursor is not on one of
3554     *            this rowset's rows or its insert row, or (3) the designated
3555     *            column does not store an SQL <code>CLOB</code> value
3556     */
3557    public Clob getClob(String columnName) throws SQLException {
3558        return crsInternal.getClob(columnName);
3559    }
3560
3561    /**
3562     * Retrieves the value of the designated column in this
3563     * <code>JoinRowSetImpl</code> object as an <code>Array</code> object
3564     * in the Java programming lanugage.
3565     *
3566     * @param columnName a <code>String</code> object that must match the
3567     *        SQL name of a column in this rowset, ignoring case
3568     * @return an <code>Array</code> object representing an SQL
3569     *        <code>ARRAY</code> value
3570     * @throws SQLException if (1) the given column name is not the name of
3571     *        a column in this rowset, (2) the cursor is not on one of
3572     *        this rowset's rows or its insert row, or (3) the designated
3573     *        column does not store an SQL <code>ARRAY</code> value
3574     */
3575    public Array getArray(String columnName) throws SQLException {
3576        return crsInternal.getArray(columnName);
3577    }
3578
3579    /**
3580     * Retrieves the value of the designated column in the current row
3581     * of this <code>JoinRowSetImpl</code> object as a <code>java.sql.Date</code>
3582     * object, using the given <code>Calendar</code> object to construct an
3583     * appropriate millisecond value for the date.
3584     *
3585     * @param columnIndex the first column is <code>1</code>, the second
3586     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3587     *        and equal to or less than the number of columns in the rowset
3588     * @param cal the <code>java.util.Calendar</code> object to use in
3589     *            constructing the date
3590     * @return the column value; if the value is SQL <code>NULL</code>,
3591     *         the result is <code>null</code>
3592     * @throws SQLException if (1) the given column name is not the name of
3593     *            a column in this rowset, (2) the cursor is not on one of
3594     *            this rowset's rows or its insert row, or (3) the designated
3595     *            column does not store an SQL <code>DATE</code> or
3596     *            <code>TIMESTAMP</code> value
3597     */
3598    public java.sql.Date getDate(int columnIndex, Calendar cal) throws SQLException {
3599        return crsInternal.getDate(columnIndex, cal);
3600    }
3601
3602    /**
3603     * Retrieves the value of the designated column in the current row
3604     * of this <code>JoinRowSetImpl</code> object as a <code>java.sql.Date</code>
3605     * object, using the given <code>Calendar</code> object to construct an
3606     * appropriate millisecond value for the date.
3607     *
3608     * @param columnName a <code>String</code> object that must match the
3609     *        SQL name of a column in this rowset, ignoring case
3610     * @param cal the <code>java.util.Calendar</code> object to use in
3611     *            constructing the date
3612     * @return the column value; if the value is SQL <code>NULL</code>,
3613     *         the result is <code>null</code>
3614     * @throws SQLException if (1) the given column name is not the name of
3615     *            a column in this rowset, (2) the cursor is not on one of
3616     *            this rowset's rows or its insert row, or (3) the designated
3617     *            column does not store an SQL <code>DATE</code> or
3618     *            <code>TIMESTAMP</code> value
3619     */
3620    public java.sql.Date getDate(String columnName, Calendar cal) throws SQLException {
3621        return crsInternal.getDate(columnName, cal);
3622    }
3623
3624    /**
3625     * Retrieves the value of the designated column in the current row
3626     * of this <code>JoinRowSetImpl</code> object as a <code>java.sql.Time</code>
3627     * object, using the given <code>Calendar</code> object to construct an
3628     * appropriate millisecond value for the date.
3629     *
3630     * @param columnIndex the first column is <code>1</code>, the second
3631     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3632     *        and equal to or less than the number of columns in the rowset
3633     * @param cal the <code>java.util.Calendar</code> object to use in
3634     *            constructing the date
3635     * @return the column value; if the value is SQL <code>NULL</code>,
3636     *         the result is <code>null</code>
3637     * @throws SQLException if (1) the given column name is not the name of
3638     *            a column in this rowset, (2) the cursor is not on one of
3639     *            this rowset's rows or its insert row, or (3) the designated
3640     *            column does not store an SQL <code>TIME</code> or
3641     *            <code>TIMESTAMP</code> value
3642     */
3643    public java.sql.Time getTime(int columnIndex, Calendar cal) throws SQLException {
3644        return crsInternal.getTime(columnIndex, cal);
3645    }
3646
3647    /**
3648     * Retrieves the value of the designated column in the current row
3649     * of this <code>JoinRowSetImpl</code> object as a <code>java.sql.Time</code>
3650     * object, using the given <code>Calendar</code> object to construct an
3651     * appropriate millisecond value for the date.
3652     *
3653     * @param columnName a <code>String</code> object that must match the
3654     *        SQL name of a column in this rowset, ignoring case
3655     * @param cal the <code>java.util.Calendar</code> object to use in
3656     *            constructing the date
3657     * @return the column value; if the value is SQL <code>NULL</code>,
3658     *         the result is <code>null</code>
3659     * @throws SQLException if (1) the given column name is not the name of
3660     *            a column in this rowset, (2) the cursor is not on one of
3661     *            this rowset's rows or its insert row, or (3) the designated
3662     *            column does not store an SQL <code>TIME</code> or
3663     *            <code>TIMESTAMP</code> value
3664     */
3665    public java.sql.Time getTime(String columnName, Calendar cal) throws SQLException {
3666        return crsInternal.getTime(columnName, cal);
3667    }
3668
3669    /**
3670     * Retrieves the value of the designated column in the current row
3671     * of this <code>JoinRowSetImpl</code> object as a <code>java.sql.Timestamp</code>
3672     * object, using the given <code>Calendar</code> object to construct an
3673     * appropriate millisecond value for the date.
3674     *
3675     * @param columnIndex the first column is <code>1</code>, the second
3676     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3677     *        and equal to or less than the number of columns in the rowset
3678     * @param cal the <code>java.util.Calendar</code> object to use in
3679     *            constructing the date
3680     * @return the column value; if the value is SQL <code>NULL</code>,
3681     *         the result is <code>null</code>
3682     * @throws SQLException if (1) the given column name is not the name of
3683     *            a column in this rowset, (2) the cursor is not on one of
3684     *            this rowset's rows or its insert row, or (3) the designated
3685     *            column does not store an SQL <code>TIME</code> or
3686     *            <code>TIMESTAMP</code> value
3687     */
3688    public java.sql.Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
3689        return crsInternal.getTimestamp(columnIndex, cal);
3690    }
3691
3692    /**
3693     * Retrieves the value of the designated column in the current row
3694     * of this <code>JoinRowSetImpl</code> object as a
3695     * <code>java.sql.Timestamp</code> object, using the given
3696     * <code>Calendar</code> object to construct an appropriate
3697     * millisecond value for the date.
3698     *
3699     * @param columnName a <code>String</code> object that must match the
3700     *        SQL name of a column in this rowset, ignoring case
3701     * @param cal the <code>java.util.Calendar</code> object to use in
3702     *            constructing the date
3703     * @return the column value; if the value is SQL <code>NULL</code>,
3704     *         the result is <code>null</code>
3705     * @throws SQLException if (1) the given column name is not the name of
3706     *            a column in this rowset, (2) the cursor is not on one of
3707     *            this rowset's rows or its insert row, or (3) the designated
3708     *            column does not store an SQL <code>DATE</code>,
3709     *            <code>TIME</code>, or <code>TIMESTAMP</code> value
3710     */
3711    public java.sql.Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException {
3712        return crsInternal.getTimestamp(columnName, cal);
3713    }
3714
3715   /**
3716    * Sets the metadata for this <code>JoinRowSetImpl</code> object
3717    * with the given <code>RowSetMetaData</code> object.
3718    *
3719    * @param md a <code>RowSetMetaData</code> object instance containing
3720    *            metadata about the columsn in the rowset
3721    * @throws SQLException if invalid meta data is supplied to the
3722    *            rowset
3723    */
3724    public void setMetaData(RowSetMetaData md) throws SQLException {
3725        crsInternal.setMetaData(md);
3726    }
3727
3728    public ResultSet getOriginal() throws SQLException {
3729        return crsInternal.getOriginal();
3730    }
3731
3732   /**
3733    * Returns a result set containing the original value of the rowset.
3734    * The cursor is positioned before the first row in the result set.
3735    * Only rows contained in the result set returned by getOriginal()
3736    * are said to have an original value.
3737    *
3738    * @return the original result set of the rowset
3739    * @throws SQLException if an error occurs produce the
3740    *           <code>ResultSet</code> object
3741    */
3742    public ResultSet getOriginalRow() throws SQLException {
3743        return crsInternal.getOriginalRow();
3744    }
3745
3746   /**
3747    * Returns a result set containing the original value of the current
3748    * row only.
3749    *
3750    * @throws SQLException if there is no current row
3751    * @see #setOriginalRow
3752    */
3753    public void setOriginalRow() throws SQLException {
3754        crsInternal.setOriginalRow();
3755    }
3756
3757   /**
3758    * Returns the columns that make a key to uniquely identify a
3759    * row in this <code>JoinRowSetImpl</code> object.
3760    *
3761    * @return an array of column number that constites a primary
3762    *           key for this rowset. This array should be empty
3763    *           if no columns is representitive of a primary key
3764    * @throws SQLException if the rowset is empty or no columns
3765    *           are designated as primary keys
3766    * @see #setKeyColumns
3767    */
3768    public int[] getKeyColumns() throws SQLException {
3769        return crsInternal.getKeyColumns();
3770    }
3771
3772    /**
3773     * Sets this <code>JoinRowSetImpl</code> object's
3774     * <code>keyCols</code> field with the given array of column
3775     * numbers, which forms a key for uniquely identifying a row
3776     * in this rowset.
3777     *
3778     * @param cols an array of <code>int</code> indicating the
3779     *        columns that form a primary key for this
3780     *        <code>JoinRowSetImpl</code> object; every
3781     *        element in the array must be greater than
3782     *        <code>0</code> and less than or equal to the number
3783     *        of columns in this rowset
3784     * @throws SQLException if any of the numbers in the
3785     *            given array is not valid for this rowset
3786     * @see #getKeyColumns
3787     */
3788    public void setKeyColumns(int[] cols) throws SQLException {
3789        crsInternal.setKeyColumns(cols);
3790    }
3791
3792    /**
3793     * Sets the designated column in either the current row or the insert
3794     * row of this <code>JoinRowSetImpl</code> object with the given
3795     * <code>Ref</code> value.
3796     * <P>
3797     * This method updates a column value in the current row or the insert
3798     * row of this rowset, but it does not update the database.
3799     * If the cursor is on a row in the rowset, the
3800     * method {@link #updateRow} must be called to update the database.
3801     * If the cursor is on the insert row, the method {@link #insertRow}
3802     * must be called, which will insert the new row into both this rowset
3803     * and the database. Either of these methods must be called before the
3804     * cursor moves to another row.
3805     *
3806     * @param columnIndex the first column is <code>1</code>, the second
3807     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3808     *        and equal to or less than the number of columns in this rowset
3809     * @param ref the <code>java.sql.Ref</code> object that will be set as
3810     *         the new column value
3811     * @throws SQLException if (1) the given column index is out of bounds,
3812     *            (2) the cursor is not on one of this rowset's rows or its
3813     *            insert row, or (3) this rowset is
3814     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3815     */
3816    public void updateRef(int columnIndex, java.sql.Ref ref) throws SQLException {
3817        crsInternal.updateRef(columnIndex, ref);
3818    }
3819
3820    /**
3821     * Sets the designated column in either the current row or the insert
3822     * row of this <code>JoinRowSetImpl</code> object with the given
3823     * <code>Ref</code> value.
3824     * <P>
3825     * This method updates a column value in the current row or the insert
3826     * row of this rowset, but it does not update the database.
3827     * If the cursor is on a row in the rowset, the
3828     * method {@link #updateRow} must be called to update the database.
3829     * If the cursor is on the insert row, the method {@link #insertRow}
3830     * must be called, which will insert the new row into both this rowset
3831     * and the database. Either of these methods must be called before the
3832     * cursor moves to another row.
3833     *
3834     * @param columnName a <code>String</code> object giving the name of the column
3835     *        to be updated; must match one of the column names in this
3836     *        <code>JoinRowSetImpl</code> object
3837     * @param ref the <code>java.sql.Ref</code> object that will be set as
3838     *         the new column value
3839     * @throws SQLException if (1) the given column name is not valid,
3840     *            (2) the cursor is not on one of this rowset's rows or its
3841     *            insert row, or (3) this rowset is
3842     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3843     */
3844    public void updateRef(String columnName, java.sql.Ref ref) throws SQLException {
3845        crsInternal.updateRef(columnName, ref);
3846    }
3847
3848    /**
3849     * Sets the designated column in either the current row or the insert
3850     * row of this <code>JoinRowSetImpl</code> object with the given
3851     * <code>Clob</code> object.
3852     * <P>
3853     * This method updates a column value in the current row or the insert
3854     * row of this rowset, but it does not update the database.
3855     * If the cursor is on a row in the rowset, the
3856     * method {@link #updateRow} must be called to update the database.
3857     * If the cursor is on the insert row, the method {@link #insertRow}
3858     * must be called, which will insert the new row into both this rowset
3859     * and the database. Either of these methods must be called before the
3860     * cursor moves to another row.
3861     *
3862     * @param columnIndex the first column is <code>1</code>, the second
3863     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3864     *        and equal to or less than the number of columns in this rowset
3865     * @param c the <code>java.sql.Clob</code> object that will be set as
3866     *         the new column value
3867     * @throws SQLException if (1) the given column index is out of bounds,
3868     *            (2) the cursor is not on one of this rowset's rows or its
3869     *            insert row, or (3) this rowset is
3870     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3871     */
3872    public void updateClob(int columnIndex, Clob c) throws SQLException {
3873        crsInternal.updateClob(columnIndex, c);
3874    }
3875
3876    /**
3877     * Sets the designated column in either the current row or the insert
3878     * row of this <code>JoinRowSetImpl</code> object with the given
3879     * <code>Clob</code> object.
3880     * <P>
3881     * This method updates a column value in the current row or the insert
3882     * row of this rowset, but it does not update the database.
3883     * If the cursor is on a row in the rowset, the
3884     * method {@link #updateRow} must be called to update the database.
3885     * If the cursor is on the insert row, the method {@link #insertRow}
3886     * must be called, which will insert the new row into both this rowset
3887     * and the database. Either of these methods must be called before the
3888     * cursor moves to another row.
3889     *
3890     * @param columnName a <code>String</code> object giving the name of the column
3891     *        to be updated; must match one of the column names in this
3892     *        <code>JoinRowSetImpl</code> object
3893     * @param c the <code>java.sql.Clob</code> object that will be set as
3894     *         the new column value
3895     * @throws SQLException if (1) the given column name is not valid,
3896     *            (2) the cursor is not on one of this rowset's rows or its
3897     *            insert row, or (3) this rowset is
3898     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3899     */
3900    public void updateClob(String columnName, Clob c) throws SQLException {
3901        crsInternal.updateClob(columnName, c);
3902    }
3903
3904    /**
3905     * Sets the designated column in either the current row or the insert
3906     * row of this <code>JoinRowSetImpl</code> object with the given
3907     * <code>Blob</code> value.
3908     * <P>
3909     * This method updates a column value in the current row or the insert
3910     * row of this rowset, but it does not update the database.
3911     * If the cursor is on a row in the rowset, the
3912     * method {@link #updateRow} must be called to update the database.
3913     * If the cursor is on the insert row, the method {@link #insertRow}
3914     * must be called, which will insert the new row into both this rowset
3915     * and the database. Either of these methods must be called before the
3916     * cursor moves to another row.
3917     *
3918     * @param columnIndex the first column is <code>1</code>, the second
3919     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3920     *        and equal to or less than the number of columns in this rowset
3921     * @param b the <code>java.sql.Blob</code> object that will be set as
3922     *         the new column value
3923     * @throws SQLException if (1) the given column index is out of bounds,
3924     *            (2) the cursor is not on one of this rowset's rows or its
3925     *            insert row, or (3) this rowset is
3926     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3927     */
3928    public void updateBlob(int columnIndex, Blob b) throws SQLException {
3929         crsInternal.updateBlob(columnIndex, b);
3930    }
3931
3932    /**
3933     * Sets the designated column in either the current row or the insert
3934     * row of this <code>JoinRowSetImpl</code> object with the given
3935     * <code>Blob</code> object.
3936     * <P>
3937     * This method updates a column value in the current row or the insert
3938     * row of this rowset, but it does not update the database.
3939     * If the cursor is on a row in the rowset, the
3940     * method {@link #updateRow} must be called to update the database.
3941     * If the cursor is on the insert row, the method {@link #insertRow}
3942     * must be called, which will insert the new row into both this rowset
3943     * and the database. Either of these methods must be called before the
3944     * cursor moves to another row.
3945     *
3946     * @param columnName a <code>String</code> object giving the name of the column
3947     *        to be updated; must match one of the column names in this
3948     *        <code>JoinRowSetImpl</code> object
3949     * @param b the <code>java.sql.Blob</code> object that will be set as
3950     *         the new column value
3951     * @throws SQLException if (1) the given column name is not valid,
3952     *            (2) the cursor is not on one of this rowset's rows or its
3953     *            insert row, or (3) this rowset is
3954     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3955     */
3956    public void updateBlob(String columnName, Blob b) throws SQLException {
3957         crsInternal.updateBlob(columnName, b);
3958    }
3959
3960    /**
3961     * Sets the designated column in either the current row or the insert
3962     * row of this <code>JoinRowSetImpl</code> object with the given
3963     * <code>Array</code> object.
3964     * <P>
3965     * This method updates a column value in the current row or the insert
3966     * row of this rowset, but it does not update the database.
3967     * If the cursor is on a row in the rowset, the
3968     * method {@link #updateRow} must be called to update the database.
3969     * If the cursor is on the insert row, the method {@link #insertRow}
3970     * must be called, which will insert the new row into both this rowset
3971     * and the database. Either of these methods must be called before the
3972     * cursor moves to another row.
3973     *
3974     * @param columnIndex the first column is <code>1</code>, the second
3975     *        is <code>2</code>, and so on; must be <code>1</code> or larger
3976     *        and equal to or less than the number of columns in this rowset
3977     * @param a the <code>java.sql.Array</code> object that will be set as
3978     *         the new column value
3979     * @throws SQLException if (1) the given column index is out of bounds,
3980     *            (2) the cursor is not on one of this rowset's rows or its
3981     *            insert row, or (3) this rowset is
3982     *            <code>ResultSet.CONCUR_READ_ONLY</code>
3983     */
3984    public void updateArray(int columnIndex, Array a) throws SQLException {
3985         crsInternal.updateArray(columnIndex, a);
3986    }
3987
3988    /**
3989     * Sets the designated column in either the current row or the insert
3990     * row of this <code>JoinRowSetImpl</code> object with the given
3991     * <code>Array</code> object.
3992     * <P>
3993     * This method updates a column value in the current row or the insert
3994     * row of this rowset, but it does not update the database.
3995     * If the cursor is on a row in the rowset, the
3996     * method {@link #updateRow} must be called to update the database.
3997     * If the cursor is on the insert row, the method {@link #insertRow}
3998     * must be called, which will insert the new row into both this rowset
3999     * and the database. Either of these methods must be called before the
4000     * cursor moves to another row.
4001     *
4002     * @param columnName a <code>String</code> object giving the name of the column
4003     *        to be updated; must match one of the column names in this
4004     *        <code>JoinRowSetImpl</code> object
4005     * @param a the <code>java.sql.Array</code> object that will be set as
4006     *         the new column value
4007     * @throws SQLException if (1) the given column name is not valid,
4008     *            (2) the cursor is not on one of this rowset's rows or its
4009     *            insert row, or (3) this rowset is
4010     *            <code>ResultSet.CONCUR_READ_ONLY</code>
4011     */
4012    public void updateArray(String columnName, Array a) throws SQLException {
4013         crsInternal.updateArray(columnName, a);
4014    }
4015
4016    /**
4017     * Populates this <code>JoinRowSetImpl</code> object with data.
4018     * This form of the method uses the rowset's user, password, and url or
4019     * data source name properties to create a database
4020     * connection.  If properties that are needed
4021     * have not been set, this method will throw an exception.
4022     * <P>
4023     * Another form of this method uses an existing JDBC <code>Connection</code>
4024     * object instead of creating a new one; therefore, it ignores the
4025     * properties used for establishing a new connection.
4026     * <P>
4027     * The query specified by the command property is executed to create a
4028     * <code>ResultSet</code> object from which to retrieve data.
4029     * The current contents of the rowset are discarded, and the
4030     * rowset's metadata is also (re)set.  If there are outstanding updates,
4031     * they are also ignored.
4032     * <P>
4033     * The method <code>execute</code> closes any database connections that it
4034     * creates.
4035     *
4036     * @throws SQLException if an error occurs or the
4037     *                         necessary properties have not been set
4038     */
4039    public void execute() throws SQLException {
4040        crsInternal.execute();
4041    }
4042
4043    /**
4044     * Populates this <code>JoinRowSetImpl</code> object with data,
4045     * using the given connection to produce the result set from
4046     * which data will be read.  A second form of this method,
4047     * which takes no arguments, uses the values from this rowset's
4048     * user, password, and either url or data source properties to
4049     * create a new database connection. The form of <code>execute</code>
4050     * that is given a connection ignores these properties.
4051     *
4052     *  @param conn A standard JDBC <code>Connection</code> object with valid
4053     *           properties that the <code>JoinRowSet</code> implementation
4054     *           can pass to a synchronization provider to establish a
4055     *           connection to the datasource
4056     * @throws SQLException if an invalid <code>Connection</code> is supplied
4057     *           or an error occurs in establishing the connection to the
4058     *           data soure
4059     * @see java.sql.Connection
4060     */
4061    public void execute(Connection conn) throws SQLException {
4062        crsInternal.execute(conn);
4063    }
4064
4065    /**
4066     * Provide interface coverage for getURL(int) in
4067     * ResultSet{@literal ->}RowSet
4068     */
4069    public java.net.URL getURL(int columnIndex) throws SQLException {
4070        return crsInternal.getURL(columnIndex);
4071    }
4072
4073    /**
4074     * Provide interface coverage for getURL(String) in
4075     * ResultSet{@literal ->}RowSet
4076     */
4077    public java.net.URL getURL(String columnName) throws SQLException {
4078        return crsInternal.getURL(columnName);
4079    }
4080
4081   /**
4082    * Creates a new <code>WebRowSet</code> object, populates it with the
4083    * data in the given <code>ResultSet</code> object, and writes it
4084    * to the given <code>java.io.Writer</code> object in XML format.
4085    *
4086    * @throws SQLException if an error occurs writing out the rowset
4087    *          contents to XML
4088    */
4089    public void writeXml(ResultSet rs, java.io.Writer writer)
4090        throws SQLException {
4091             wrs = new WebRowSetImpl();
4092             wrs.populate(rs);
4093             wrs.writeXml(writer);
4094    }
4095
4096    /**
4097     * Writes this <code>JoinRowSet</code> object to the given
4098     * <code>java.io.Writer</code> object in XML format. In
4099     * addition to the rowset's data, its properties and metadata
4100     * are also included.
4101     *
4102     * @throws SQLException if an error occurs writing out the rowset
4103     *          contents to XML
4104     */
4105    public void writeXml(java.io.Writer writer) throws SQLException {
4106        createWebRowSet().writeXml(writer);
4107}
4108
4109    /**
4110     * Reads this <code>JoinRowSet</code> object in its XML format.
4111     *
4112     * @throws SQLException if a database access error occurs
4113     */
4114    public void readXml(java.io.Reader reader) throws SQLException {
4115        wrs = new WebRowSetImpl();
4116        wrs.readXml(reader);
4117        crsInternal = (CachedRowSetImpl)wrs;
4118    }
4119
4120    // Stream based methods
4121    /**
4122     * Reads a stream based XML input to populate an <code>WebRowSet</code>
4123     *
4124     * @throws SQLException if a data source access occurs
4125     * @throws IOException if a IO exception occurs
4126     */
4127    public void readXml(java.io.InputStream iStream) throws SQLException, IOException {
4128         wrs = new WebRowSetImpl();
4129         wrs.readXml(iStream);
4130         crsInternal = (CachedRowSetImpl)wrs;
4131    }
4132
4133    /**
4134     * Creates an output stream of the internal state and contents of a
4135     * <code>WebRowSet</code> for XML proceessing
4136     *
4137     * @throws SQLException if a datasource access occurs
4138     * @throws IOException if an IO exception occurs
4139     */
4140    public void writeXml(java.io.OutputStream oStream) throws SQLException, IOException {
4141         createWebRowSet().writeXml(oStream);
4142    }
4143
4144    /**
4145     * Creates a new <code>WebRowSet</code> object, populates it with
4146     * the contents of the <code>ResultSet</code> and creates an output
4147     * streams the internal state and contents of the rowset for XML processing.
4148     *
4149     * @throws SQLException if a datasource access occurs
4150     * @throws IOException if an IO exception occurs
4151     */
4152    public void writeXml(ResultSet rs, java.io.OutputStream oStream) throws SQLException, IOException {
4153             wrs = new WebRowSetImpl();
4154             wrs.populate(rs);
4155             wrs.writeXml(oStream);
4156    }
4157
4158    /**
4159     * %%% Javadoc comments to be added here
4160     */
4161    private WebRowSet createWebRowSet() throws SQLException {
4162       if(wrs != null) {
4163           // check if it has already been initialized.
4164           return wrs;
4165       } else {
4166         wrs = new WebRowSetImpl();
4167          crsInternal.beforeFirst();
4168          wrs.populate(crsInternal);
4169          return wrs;
4170       }
4171    }
4172
4173    /**
4174     * Returns the last set SQL <code>JOIN</code> type in this JoinRowSetImpl
4175     * object
4176     *
4177     * @return joinType One of the standard JoinRowSet static field JOIN types
4178     * @throws SQLException if an error occurs determining the current join type
4179     */
4180    public int getJoinType() throws SQLException {
4181        if (vecJoinType == null) {
4182            // Default JoinRowSet type
4183            this.setJoinType(JoinRowSet.INNER_JOIN);
4184        }
4185        Integer i = vecJoinType.get(vecJoinType.size()-1);
4186        return i.intValue();
4187    }
4188
4189    /**
4190    * The listener will be notified whenever an event occurs on this <code>JoinRowSet</code>
4191    * object.
4192    * <P>
4193    * A listener might, for example, be a table or graph that needs to
4194    * be updated in order to accurately reflect the current state of
4195    * the <code>RowSet</code> object.
4196    * <p>
4197    * <b>Note</b>: if the <code>RowSetListener</code> object is
4198    * <code>null</code>, this method silently discards the <code>null</code>
4199    * value and does not add a null reference to the set of listeners.
4200    * <p>
4201    * <b>Note</b>: if the listener is already set, and the new <code>RowSetListerner</code>
4202    * instance is added to the set of listeners already registered to receive
4203    * event notifications from this <code>RowSet</code>.
4204    *
4205    * @param listener an object that has implemented the
4206    *     <code>javax.sql.RowSetListener</code> interface and wants to be notified
4207    *     of any events that occur on this <code>JoinRowSet</code> object; May be
4208    *     null.
4209    * @see #removeRowSetListener
4210    */
4211    public void addRowSetListener(RowSetListener listener) {
4212        crsInternal.addRowSetListener(listener);
4213    }
4214
4215    /**
4216    * Removes the designated object from this <code>JoinRowSet</code> object's list of listeners.
4217    * If the given argument is not a registered listener, this method
4218    * does nothing.
4219    *
4220    *  <b>Note</b>: if the <code>RowSetListener</code> object is
4221    * <code>null</code>, this method silently discards the <code>null</code>
4222    * value.
4223    *
4224    * @param listener a <code>RowSetListener</code> object that is on the list
4225    *        of listeners for this <code>JoinRowSet</code> object
4226    * @see #addRowSetListener
4227    */
4228     public void removeRowSetListener(RowSetListener listener) {
4229        crsInternal.removeRowSetListener(listener);
4230    }
4231
4232    /**
4233     * Converts this <code>JoinRowSetImpl</code> object to a collection
4234     * of tables. The sample implementation utilitizes the <code>TreeMap</code>
4235     * collection type.
4236     * This class guarantees that the map will be in ascending key order,
4237     * sorted according to the natural order for the key's class.
4238     *
4239     * @return a <code>Collection</code> object consisting of tables,
4240     *         each of which is a copy of a row in this
4241     *         <code>JoinRowSetImpl</code> object
4242     * @throws SQLException if an error occurs in generating the collection
4243     * @see #toCollection(int)
4244     * @see #toCollection(String)
4245     * @see java.util.TreeMap
4246     */
4247     public Collection<?> toCollection() throws SQLException {
4248        return crsInternal.toCollection();
4249    }
4250
4251    /**
4252     * Returns the specified column of this <code>JoinRowSetImpl</code> object
4253     * as a <code>Collection</code> object.  This method makes a copy of the
4254     * column's data and utilitizes the <code>Vector</code> to establish the
4255     * collection. The <code>Vector</code> class implements a growable array
4256     * objects allowing the individual components to be accessed using an
4257     * an integer index similar to that of an array.
4258     *
4259     * @return a <code>Collection</code> object that contains the value(s)
4260     *         stored in the specified column of this
4261     *         <code>JoinRowSetImpl</code>
4262     *         object
4263     * @throws SQLException if an error occurs generated the collection; or
4264     *          an invalid column is provided.
4265     * @see #toCollection()
4266     * @see #toCollection(String)
4267     * @see java.util.Vector
4268     */
4269    public Collection<?> toCollection(int column) throws SQLException {
4270        return crsInternal.toCollection(column);
4271    }
4272
4273    /**
4274     * Returns the specified column of this <code>JoinRowSetImpl</code> object
4275     * as a <code>Collection</code> object.  This method makes a copy of the
4276     * column's data and utilitizes the <code>Vector</code> to establish the
4277     * collection. The <code>Vector</code> class implements a growable array
4278     * objects allowing the individual components to be accessed using an
4279     * an integer index similar to that of an array.
4280     *
4281     * @return a <code>Collection</code> object that contains the value(s)
4282     *         stored in the specified column of this
4283     *         <code>JoinRowSetImpl</code>
4284     *         object
4285     * @throws SQLException if an error occurs generated the collection; or
4286     *          an invalid column is provided.
4287     * @see #toCollection()
4288     * @see #toCollection(int)
4289     * @see java.util.Vector
4290     */
4291    public Collection<?> toCollection(String column) throws SQLException {
4292        return crsInternal.toCollection(column);
4293    }
4294
4295    /**
4296     * Creates a <code>RowSet</code> object that is a copy of
4297     * this <code>JoinRowSetImpl</code> object's table structure
4298     * and the constraints only.
4299     * There will be no data in the object being returned.
4300     * Updates made on a copy are not visible to the original rowset.
4301     * <P>
4302     * This helps in getting the underlying XML schema which can
4303     * be used as the basis for populating a <code>WebRowSet</code>.
4304     *
4305     * @return a new <code>CachedRowSet</code> object that is a copy
4306     * of this <code>JoinRowSetImpl</code> object's schema and
4307     * retains all the constraints on the original rowset but contains
4308     * no data
4309     * @throws SQLException if an error occurs in generating the copy
4310     * of the <code>CachedRowSet</code> object
4311     * @see #createShared
4312     * @see #createCopy
4313     * @see #createCopyNoConstraints
4314     * @see javax.sql.RowSetEvent
4315     * @see javax.sql.RowSetListener
4316     */
4317     public CachedRowSet createCopySchema() throws SQLException {
4318         return crsInternal.createCopySchema();
4319     }
4320
4321     /**
4322      * {@inheritDoc}
4323      */
4324     public void setSyncProvider(String providerStr) throws SQLException {
4325         crsInternal.setSyncProvider(providerStr);
4326     }
4327
4328     /**
4329      * {@inheritDoc}
4330      */
4331     public void acceptChanges() throws SyncProviderException {
4332         crsInternal.acceptChanges();
4333     }
4334
4335     /**
4336      * {@inheritDoc}
4337      */
4338     public SyncProvider getSyncProvider() throws SQLException {
4339        return crsInternal.getSyncProvider();
4340     }
4341
4342    /**
4343     * This method re populates the resBundle
4344     * during the deserialization process
4345     *
4346     */
4347     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
4348        // Default state initialization happens here
4349        ois.defaultReadObject();
4350        // Initialization of transient Res Bundle happens here .
4351        try {
4352           resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle();
4353        } catch(IOException ioe) {
4354            throw new RuntimeException(ioe);
4355        }
4356
4357     }
4358
4359     static final long serialVersionUID = -5590501621560008453L;
4360}
4361