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 
  26 package com.sun.rowset;
  27 
  28 import java.sql.*;
  29 import javax.sql.*;
  30 import javax.naming.*;
  31 import java.io.*;
  32 import java.math.*;
  33 import java.util.*;
  34 
  35 import javax.sql.rowset.*;
  36 import javax.sql.rowset.spi.SyncProvider;
  37 import 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  */
  58 public 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 <code>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 }