Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/mysql/jdbc/UpdatableResultSet.java


1   /*
2    Copyright (C) 2002-2004 MySQL AB
3   
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of version 2 of the GNU General Public License as
6    published by the Free Software Foundation.
7    
8   
9    There are special exceptions to the terms and conditions of the GPL 
10   as it is applied to this software. View the full text of the 
11   exception exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
12   software distribution.
13  
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18  
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  
23   */
24  package com.mysql.jdbc;
25  
26  import java.io.UnsupportedEncodingException;
27  import java.math.BigDecimal;
28  import java.sql.SQLException;
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.List;
32  
33  
34  /**
35   * A result set that is updatable.
36   *
37   * @author Mark Matthews
38   */
39  public class UpdatableResultSet extends ResultSet {
40      /** Marker for 'stream' data when doing INSERT rows */
41      private final static byte[] STREAM_DATA_MARKER = "** STREAM DATA **"
42          .getBytes();
43  
44      /** List of primary keys */
45      private List primaryKeyIndicies = null;
46  
47      /** PreparedStatement used to delete data */
48      private com.mysql.jdbc.PreparedStatement deleter = null;
49  
50      /** PreparedStatement used to insert data */
51      private com.mysql.jdbc.PreparedStatement inserter = null;
52  
53      /** PreparedStatement used to refresh data */
54      private com.mysql.jdbc.PreparedStatement refresher;
55  
56      /** PreparedStatement used to delete data */
57      private com.mysql.jdbc.PreparedStatement updater = null;
58      private SingleByteCharsetConverter charConverter;
59      private String charEncoding;
60      private String deleteSQL = null;
61      private String insertSQL = null;
62      private String quotedIdChar = null;
63      private String refreshSQL = null;
64      private String qualifiedAndQuotedTableName;
65      private String tableOnlyName;
66  
67      /** SQL for in-place modifcation */
68      private String updateSQL = null;
69  
70      /** What is the default value for the column? */
71      private byte[][] defaultColumnValue;
72  
73      /** The binary data for the 'current' row */
74      private byte[][] savedCurrentRow;
75  
76      /** Is this result set updateable? */
77      private boolean isUpdatable = false;
78  
79      /**
80       * Creates a new UpdatableResultSet object.
81       *
82       * @param updateCount DOCUMENT ME!
83       * @param updateID DOCUMENT ME!
84       */
85      public UpdatableResultSet(long updateCount, long updateID) {
86          super(updateCount, updateID);
87      }
88  
89      // ****************************************************************
90      //
91      //                       END OF PUBLIC INTERFACE
92      //
93      // ****************************************************************
94  
95      /**
96       * Create a new ResultSet - Note that we create ResultSets to represent the
97       * results of everything.
98       *
99       * @param catalog the database in use when this result set was created
100      * @param fields an array of Field objects (basically, the ResultSet
101      *        MetaData)
102      * @param rows Vector of the actual data
103      * @param conn the status string returned from the back end
104      *
105      * @throws SQLException DOCUMENT ME!
106      */
107     public UpdatableResultSet(String catalog, Field[] fields, RowData rows,
108         com.mysql.jdbc.Connection conn) throws SQLException {
109         super(catalog, fields, rows, conn);
110         isUpdatable = checkUpdatability();
111     }
112 
113     /**
114      * Creates a new UpdatableResultSet object.
115      *
116      * @param fields DOCUMENT ME!
117      * @param rows DOCUMENT ME!
118      *
119      * @throws SQLException DOCUMENT ME!
120      */
121     public UpdatableResultSet(Field[] fields, RowData rows)
122         throws SQLException {
123         super(fields, rows);
124         isUpdatable = checkUpdatability();
125     }
126 
127     /**
128      * JDBC 2.0
129      * 
130      * <p>
131      * Determine if the cursor is after the last row in the result  set.
132      * </p>
133      *
134      * @return true if after the last row, false otherwise.  Returns false when
135      *         the result set contains no rows.
136      *
137      * @exception SQLException if a database-access error occurs.
138      */
139     public synchronized boolean isAfterLast() throws SQLException {
140         return super.isAfterLast();
141     }
142 
143     /**
144      * JDBC 2.0
145      * 
146      * <p>
147      * Determine if the cursor is before the first row in the result  set.
148      * </p>
149      *
150      * @return true if before the first row, false otherwise. Returns false
151      *         when the result set contains no rows.
152      *
153      * @exception SQLException if a database-access error occurs.
154      */
155     public synchronized boolean isBeforeFirst() throws SQLException {
156         return super.isBeforeFirst();
157     }
158 
159     /**
160      * JDBC 2.0 Return the concurrency of this result set.  The concurrency
161      * used is determined by the statement that created the result set.
162      *
163      * @return the concurrency type, CONCUR_READ_ONLY, etc.
164      *
165      * @exception SQLException if a database-access error occurs
166      */
167     public int getConcurrency() throws SQLException {
168         return (isUpdatable ? CONCUR_UPDATABLE : CONCUR_READ_ONLY);
169     }
170 
171     /**
172      * JDBC 2.0
173      * 
174      * <p>
175      * Determine if the cursor is on the first row of the result set.
176      * </p>
177      *
178      * @return true if on the first row, false otherwise.
179      *
180      * @exception SQLException if a database-access error occurs.
181      */
182     public synchronized boolean isFirst() throws SQLException {
183         return super.isFirst();
184     }
185 
186     /**
187      * JDBC 2.0
188      * 
189      * <p>
190      * Determine if the cursor is on the last row of the result set.    Note:
191      * Calling isLast() may be expensive since the JDBC driver might need to
192      * fetch ahead one row in order to determine  whether the current row is
193      * the last row in the result set.
194      * </p>
195      *
196      * @return true if on the last row, false otherwise.
197      *
198      * @exception SQLException if a database-access error occurs.
199      */
200     public synchronized boolean isLast() throws SQLException {
201         return super.isLast();
202     }
203 
204     /**
205      * JDBC 2.0
206      * 
207      * <p>
208      * Move to an absolute row number in the result set.
209      * </p>
210      * 
211      * <p>
212      * If row is positive, moves to an absolute row with respect to the
213      * beginning of the result set.  The first row is row 1, the second is row
214      * 2, etc.
215      * </p>
216      * 
217      * <p>
218      * If row is negative, moves to an absolute row position with respect to
219      * the end of result set.  For example, calling absolute(-1) positions the
220      * cursor on the last row, absolute(-2) indicates the next-to-last row,
221      * etc.
222      * </p>
223      * 
224      * <p>
225      * An attempt to position the cursor beyond the first/last row in the
226      * result set, leaves the cursor before/after the first/last row,
227      * respectively.
228      * </p>
229      * 
230      * <p>
231      * Note: Calling absolute(1) is the same as calling first(). Calling
232      * absolute(-1) is the same as calling last().
233      * </p>
234      *
235      * @param row DOCUMENT ME!
236      *
237      * @return true if on the result set, false if off.
238      *
239      * @exception SQLException if a database-access error occurs, or  row is 0,
240      *            or result set type is TYPE_FORWARD_ONLY.
241      */
242     public synchronized boolean absolute(int row) throws SQLException {
243         return super.absolute(row);
244     }
245 
246     /**
247      * JDBC 2.0
248      * 
249      * <p>
250      * Moves to the end of the result set, just after the last row.  Has no
251      * effect if the result set contains no rows.
252      * </p>
253      *
254      * @exception SQLException if a database-access error occurs, or result set
255      *            type is TYPE_FORWARD_ONLY.
256      */
257     public synchronized void afterLast() throws SQLException {
258         super.afterLast();
259     }
260 
261     /**
262      * JDBC 2.0
263      * 
264      * <p>
265      * Moves to the front of the result set, just before the first row. Has no
266      * effect if the result set contains no rows.
267      * </p>
268      *
269      * @exception SQLException if a database-access error occurs, or result set
270      *            type is TYPE_FORWARD_ONLY
271      */
272     public synchronized void beforeFirst() throws SQLException {
273         super.beforeFirst();
274     }
275 
276     /**
277      * JDBC 2.0 The cancelRowUpdates() method may be called after calling an
278      * updateXXX() method(s) and before calling updateRow() to rollback  the
279      * updates made to a row.  If no updates have been made or  updateRow()
280      * has already been called, then this method has no  effect.
281      *
282      * @exception SQLException if a database-access error occurs, or if called
283      *            when on the insert row.
284      */
285     public synchronized void cancelRowUpdates() throws SQLException {
286         if (doingUpdates) {
287             doingUpdates = false;
288             updater.clearParameters();
289         }
290     }
291 
292     /**
293      * After this call, getWarnings returns null until a new warning is
294      * reported for this ResultSet
295      *
296      * @exception java.sql.SQLException if a database access error occurs
297      */
298     public synchronized void clearWarnings() throws java.sql.SQLException {
299         warningChain = null;
300     }
301 
302     /**
303      * In some cases, it is desirable to immediately release a ResultSet
304      * database and JDBC resources instead of waiting for this to happen when
305      * it is automatically closed.  The close method provides this immediate
306      * release.
307      * 
308      * <p>
309      * <B>Note:</B> A ResultSet is automatically closed by the Statement the
310      * Statement that generated it when that Statement is closed, re-executed,
311      * or is used to retrieve the next result from a sequence of multiple
312      * results.  A ResultSet is also automatically closed when it is garbage
313      * collected.
314      * </p>
315      *
316      * @exception java.sql.SQLException if a database access error occurs
317      */
318     public synchronized void close() throws java.sql.SQLException {
319         super.close();
320     }
321 
322     /**
323      * JDBC 2.0 Delete the current row from the result set and the underlying
324      * database.  Cannot be called when on the insert row.
325      *
326      * @exception SQLException if a database-access error occurs, or if called
327      *            when on the insert row.
328      * @throws SQLException if the ResultSet is not updatable or some other
329      *         error occurs
330      */
331     public synchronized void deleteRow() throws SQLException {
332         if (!isUpdatable) {
333             throw new NotUpdatable();
334         }
335 
336         if (onInsertRow) {
337             throw new SQLException(
338                 "Can not call deleteRow() when on insert row");
339         } else if (rowData.size() == 0) {
340             throw new SQLException("Can't deleteRow() on empty result set");
341         } else if (isBeforeFirst()) {
342             throw new SQLException(
343                 "Before start of result set. Can not call deleteRow().");
344         } else if (isAfterLast()) {
345             throw new SQLException(
346                 "After end of result set. Can not call deleteRow().");
347         }
348 
349         if (deleter == null) {
350             if (deleteSQL == null) {
351                 generateStatements();
352             }
353 
354             deleter = (com.mysql.jdbc.PreparedStatement) connection
355                 .prepareStatement(deleteSQL);
356             
357             if (deleter.getMaxRows() != 0) {
358               deleter.setMaxRows(0);
359             }
360         }
361 
362         deleter.clearParameters();
363 
364         String characterEncoding = null;
365 
366         if (connection.useUnicode()) {
367             characterEncoding = connection.getEncoding();
368         }
369 
370         //
371         // FIXME: Use internal routines where possible for character
372         //        conversion!
373         try {
374             int numKeys = primaryKeyIndicies.size();
375 
376             if (numKeys == 1) {
377                 int index = ((Integer) primaryKeyIndicies.get(0)).intValue();
378                 String currentVal = ((characterEncoding == null)
379                     ? new String(thisRow[index])
380                     : new String(thisRow[index], characterEncoding));
381                 deleter.setString(1, currentVal);
382             } else {
383                 for (int i = 0; i < numKeys; i++) {
384                     int index = ((Integer) primaryKeyIndicies.get(i)).intValue();
385                     String currentVal = ((characterEncoding == null)
386                         ? new String(thisRow[index])
387                         : new String(thisRow[index], characterEncoding));
388                     deleter.setString(i + 1, currentVal);
389                 }
390             }
391 
392             deleter.executeUpdate();
393             rowData.removeRow(rowData.getCurrentRowNumber());
394         } catch (java.io.UnsupportedEncodingException encodingEx) {
395             throw new SQLException("Unsupported character encoding '"
396                 + connection.getEncoding() + "'");
397         }
398     }
399 
400     /**
401      * JDBC 2.0
402      * 
403      * <p>
404      * Moves to the first row in the result set.
405      * </p>
406      *
407      * @return true if on a valid row, false if no rows in the result set.
408      *
409      * @exception SQLException if a database-access error occurs, or result set
410      *            type is TYPE_FORWARD_ONLY.
411      */
412     public synchronized boolean first() throws SQLException {
413         return super.first();
414     }
415 
416     /**
417      * JDBC 2.0 Insert the contents of the insert row into the result set and
418      * the database.  Must be on the insert row when this method is called.
419      *
420      * @exception SQLException if a database-access error occurs, if called
421      *            when not on the insert row, or if all non-nullable columns
422      *            in the insert row have not been given a value
423      */
424     public synchronized void insertRow() throws SQLException {
425         if (!onInsertRow) {
426             throw new SQLException("Not on insert row");
427         } else {
428             inserter.executeUpdate();
429 
430             int numPrimaryKeys = 0;
431 
432             if (primaryKeyIndicies != null) {
433                 numPrimaryKeys = primaryKeyIndicies.size();
434             }
435 
436             long autoIncrementId = inserter.getLastInsertID();
437             int numFields = fields.length;
438             byte[][] newRow = new byte[numFields][];
439 
440             for (int i = 0; i < numFields; i++) {
441                 if (inserter.isNull(i)) {
442                     newRow[i] = null;
443                 } else {
444                     newRow[i] = inserter.getBytes(i);
445                 }
446 
447                 if ((numPrimaryKeys == 1) && fields[i].isPrimaryKey()
448                         && (autoIncrementId > 0)) {
449                     newRow[i] = String.valueOf(autoIncrementId).getBytes();
450                 }
451             }
452 
453             rowData.addRow(newRow);
454             resetInserter();
455         }
456     }
457 
458     /**
459      * JDBC 2.0
460      * 
461      * <p>
462      * Moves to the last row in the result set.
463      * </p>
464      *
465      * @return true if on a valid row, false if no rows in the result set.
466      *
467      * @exception SQLException if a database-access error occurs, or result set
468      *            type is TYPE_FORWARD_ONLY.
469      */
470     public synchronized boolean last() throws SQLException {
471         return super.last();
472     }
473 
474     /**
475      * JDBC 2.0 Move the cursor to the remembered cursor position, usually the
476      * current row.  Has no effect unless the cursor is on the insert  row.
477      *
478      * @exception SQLException if a database-access error occurs, or the result
479      *            set is not updatable
480      * @throws SQLException if the ResultSet is not updatable or some other
481      *         error occurs
482      */
483     public synchronized void moveToCurrentRow() throws SQLException {
484         if (!isUpdatable) {
485             throw new NotUpdatable();
486         }
487 
488         if (this.onInsertRow) {
489             onInsertRow = false;
490             this.thisRow = this.savedCurrentRow;
491         }
492     }
493 
494     /**
495      * JDBC 2.0 Move to the insert row.  The current cursor position is
496      * remembered while the cursor is positioned on the insert row. The insert
497      * row is a special row associated with an updatable result set.  It is
498      * essentially a buffer where a new row may be constructed by calling the
499      * updateXXX() methods prior to  inserting the row into the result set.
500      * Only the updateXXX(), getXXX(), and insertRow() methods may be  called
501      * when the cursor is on the insert row.  All of the columns in  a result
502      * set must be given a value each time this method is called before
503      * calling insertRow().  UpdateXXX()must be called before getXXX() on a
504      * column.
505      *
506      * @exception SQLException if a database-access error occurs, or the result
507      *            set is not updatable
508      * @throws NotUpdatable DOCUMENT ME!
509      */
510     public synchronized void moveToInsertRow() throws SQLException {
511         if (!this.isUpdatable) {
512             throw new NotUpdatable();
513         }
514 
515         if (this.inserter == null) {
516             generateStatements();
517             this.inserter = (com.mysql.jdbc.PreparedStatement) connection
518                 .prepareStatement(this.insertSQL);
519             
520             if (this.inserter.getMaxRows() != 0) {
521               this.inserter.setMaxRows(0);
522             }
523             
524             extractDefaultValues();
525             resetInserter();
526         } else {
527             resetInserter();
528         }
529 
530     int numFields = this.fields.length;
531     
532     this.onInsertRow = true;
533     this.doingUpdates = false;
534     this.savedCurrentRow = this.thisRow;
535     this.thisRow = new byte[numFields][];
536          
537         for (int i = 0; i < numFields; i++) {
538             if (this.defaultColumnValue[i] != null) {
539                 this.inserter.setBytes(i + 1, this.defaultColumnValue[i], false);
540                 
541                 // This value _could_ be changed from a getBytes(), so we
542                 // need a copy....
543                 byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length];
544                 System.arraycopy(defaultColumnValue[i], 0, defaultValueCopy, 0, defaultValueCopy.length);
545                 this.thisRow[i] = defaultValueCopy;
546             } else {
547                 this.inserter.setNull(i + 1, java.sql.Types.NULL);
548                 this.thisRow[i] = null;
549             }
550         }
551     }
552 
553     /**
554      * A ResultSet is initially positioned before its first row, the first call
555      * to next makes the first row the current row; the second call makes the
556      * second row the current row, etc.
557      * 
558      * <p>
559      * If an input stream from the previous row is open, it is implicitly
560      * closed.  The ResultSet's warning chain is cleared when a new row is
561      * read
562      * </p>
563      *
564      * @return true if the new current is valid; false if there are no more
565      *         rows
566      *
567      * @exception java.sql.SQLException if a database access error occurs
568      */
569     public synchronized boolean next() throws java.sql.SQLException {
570         return super.next();
571     }
572 
573     /**
574      * The prev method is not part of JDBC, but because of the architecture of
575      * this driver it is possible to move both forward and backward within the
576      * result set.
577      * 
578      * <p>
579      * If an input stream from the previous row is open, it is implicitly
580      * closed.  The ResultSet's warning chain is cleared when a new row is
581      * read
582      * </p>
583      *
584      * @return true if the new current is valid; false if there are no more
585      *         rows
586      *
587      * @exception java.sql.SQLException if a database access error occurs
588      */
589     public synchronized boolean prev() throws java.sql.SQLException {
590         return super.prev();
591     }
592 
593     /**
594      * JDBC 2.0
595      * 
596      * <p>
597      * Moves to the previous row in the result set.
598      * </p>
599      * 
600      * <p>
601      * Note: previous() is not the same as relative(-1) since it makes sense to
602      * call previous() when there is no current row.
603      * </p>
604      *
605      * @return true if on a valid row, false if off the result set.
606      *
607      * @exception SQLException if a database-access error occurs, or result set
608      *            type is TYPE_FORWAR_DONLY.
609      */
610     public synchronized boolean previous() throws SQLException {
611         return super.previous();
612     }
613 
614     /**
615      * JDBC 2.0 Refresh the value of the current row with its current value in
616      * the database.  Cannot be called when on the insert row. The
617      * refreshRow() method provides a way for an application to  explicitly
618      * tell the JDBC driver to refetch a row(s) from the database.  An
619      * application may want to call refreshRow() when  caching or prefetching
620      * is being done by the JDBC driver to fetch the latest value of a row
621      * from the database.  The JDBC driver  may actually refresh multiple rows
622      * at once if the fetch size is  greater than one.  All values are
623      * refetched subject to the transaction isolation  level and cursor
624      * sensitivity.  If refreshRow() is called after calling updateXXX(), but
625      * before calling updateRow() then the updates made to the row are lost.
626      * Calling refreshRow() frequently will likely slow performance.
627      *
628      * @exception SQLException if a database-access error occurs, or if called
629      *            when on the insert row.
630      * @throws NotUpdatable DOCUMENT ME!
631      */
632     public synchronized void refreshRow() throws SQLException {
633         if (!isUpdatable) {
634             throw new NotUpdatable();
635         }
636 
637         if (onInsertRow) {
638             throw new SQLException(
639                 "Can not call refreshRow() when on insert row");
640         } else if (rowData.size() == 0) {
641             throw new SQLException("Can't refreshRow() on empty result set");
642         } else if (isBeforeFirst()) {
643             throw new SQLException(
644                 "Before start of result set. Can not call refreshRow().");
645         } else if (isAfterLast()) {
646             throw new SQLException(
647                 "After end of result set. Can not call refreshRow().");
648         }
649 
650         if (refresher == null) {
651             if (refreshSQL == null) {
652                 generateStatements();
653             }
654 
655             refresher = (com.mysql.jdbc.PreparedStatement) connection
656                 .prepareStatement(refreshSQL);
657             
658             if (refresher.getMaxRows() != 0) {
659               refresher.setMaxRows(0);
660             }
661         }
662 
663         refresher.clearParameters();
664 
665         int numKeys = primaryKeyIndicies.size();
666 
667         if (numKeys == 1) {
668             byte[] dataFrom = null;
669             int index = ((Integer) primaryKeyIndicies.get(0)).intValue();
670 
671             if (!doingUpdates) {
672                 dataFrom = thisRow[index];
673             } else {
674                 dataFrom = updater.getBytes(index);
675 
676                 // Primary keys not set?
677                 if (updater.isNull(index) || (dataFrom.length == 0)) {
678                     dataFrom = thisRow[index];
679                 }
680             }
681 
682             refresher.setBytesNoEscape(1, dataFrom);
683         } else {
684             for (int i = 0; i < numKeys; i++) {
685                 byte[] dataFrom = null;
686                 int index = ((Integer) primaryKeyIndicies.get(i)).intValue();
687 
688                 if (!doingUpdates) {
689                     dataFrom = thisRow[index];
690                 } else {
691                     dataFrom = updater.getBytes(index);
692 
693                     // Primary keys not set?
694                     if (updater.isNull(index) || (dataFrom.length == 0)) {
695                         dataFrom = thisRow[index];
696                     }
697                 }
698 
699                 refresher.setBytesNoEscape(i + 1, dataFrom);
700             }
701         }
702 
703         java.sql.ResultSet rs = null;
704 
705         try {
706             rs = refresher.executeQuery();
707 
708             int numCols = rs.getMetaData().getColumnCount();
709 
710             if (rs.next()) {
711                 for (int i = 0; i < numCols; i++) {
712                     byte[] val = rs.getBytes(i + 1);
713 
714                     if ((val == null) || rs.wasNull()) {
715                         thisRow[i] = null;
716                     } else {
717                         thisRow[i] = rs.getBytes(i + 1);
718                     }
719                 }
720             } else {
721                 throw new SQLException("refreshRow() called on row that has been deleted or had primary key changed",
722                     SQLError.SQL_STATE_GENERAL_ERROR);
723             }
724         } finally {
725             if (rs != null) {
726                 try {
727                     rs.close();
728                 } catch (SQLException ex) {
729                     ; // ignore
730                 }
731             }
732         }
733     }
734 
735     /**
736      * JDBC 2.0
737      * 
738      * <p>
739      * Moves a relative number of rows, either positive or negative. Attempting
740      * to move beyond the first/last row in the result set positions the
741      * cursor before/after the the first/last row. Calling relative(0) is
742      * valid, but does not change the cursor position.
743      * </p>
744      * 
745      * <p>
746      * Note: Calling relative(1) is different than calling next() since is
747      * makes sense to call next() when there is no current row, for example,
748      * when the cursor is positioned before the first row or after the last
749      * row of the result set.
750      * </p>
751      *
752      * @param rows DOCUMENT ME!
753      *
754      * @return true if on a row, false otherwise.
755      *
756      * @exception SQLException if a database-access error occurs, or there is
757      *            no current row, or result set type is TYPE_FORWARD_ONLY.
758      */
759     public synchronized boolean relative(int rows) throws SQLException {
760         return super.relative(rows);
761     }
762 
763     /**
764      * JDBC 2.0 Determine if this row has been deleted.  A deleted row may
765      * leave a visible "hole" in a result set.  This method can be used to
766      * detect holes in a result set.  The value returned depends on whether or
767      * not the result set can detect deletions.
768      *
769      * @return true if deleted and deletes are detected
770      *
771      * @exception SQLException if a database-access error occurs
772      * @throws NotImplemented DOCUMENT ME!
773      *
774      * @see DatabaseMetaData#deletesAreDetected
775      */
776     public synchronized boolean rowDeleted() throws SQLException {
777         throw new NotImplemented();
778     }
779 
780     /**
781      * JDBC 2.0 Determine if the current row has been inserted.  The value
782      * returned  depends on whether or not the result set can detect visible
783      * inserts.
784      *
785      * @return true if inserted and inserts are detected
786      *
787      * @exception SQLException if a database-access error occurs
788      * @throws NotImplemented DOCUMENT ME!
789      *
790      * @see DatabaseMetaData#insertsAreDetected
791      */
792     public synchronized boolean rowInserted() throws SQLException {
793         throw new NotImplemented();
794     }
795 
796     //---------------------------------------------------------------------
797     // Updates
798     //---------------------------------------------------------------------
799 
800     /**
801      * JDBC 2.0 Determine if the current row has been updated.  The value
802      * returned  depends on whether or not the result set can detect updates.
803      *
804      * @return true if the row has been visibly updated by the owner or
805      *         another, and updates are detected
806      *
807      * @exception SQLException if a database-access error occurs
808      * @throws NotImplemented DOCUMENT ME!
809      *
810      * @see DatabaseMetaData#updatesAreDetected
811      */
812     public synchronized boolean rowUpdated() throws SQLException {
813         throw new NotImplemented();
814     }
815 
816     /**
817      * JDBC 2.0  Update a column with an ascii stream value. The updateXXX()
818      * methods are used to update column values in the current row, or the
819      * insert row.  The updateXXX() methods do not  update the underlying
820      * database, instead the updateRow() or insertRow() methods are called to
821      * update the database.
822      *
823      * @param columnIndex the first column is 1, the second is 2, ...
824      * @param x the new column value
825      * @param length the length of the stream
826      *
827      * @exception SQLException if a database-access error occurs
828      */
829     public synchronized void updateAsciiStream(int columnIndex,
830         java.io.InputStream x, int length) throws SQLException {
831         if (!onInsertRow) {
832             if (!doingUpdates) {
833                 doingUpdates = true;
834                 syncUpdate();
835             }
836 
837             updater.setAsciiStream(columnIndex, x, length);
838         } else {
839             inserter.setAsciiStream(columnIndex, x, length);
840             this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
841         }
842     }
843 
844     /**
845      * JDBC 2.0  Update a column with an ascii stream value. The updateXXX()
846      * methods are used to update column values in the current row, or the
847      * insert row.  The updateXXX() methods do not  update the underlying
848      * database, instead the updateRow() or insertRow() methods are called to
849      * update the database.
850      *
851      * @param columnName the name of the column
852      * @param x the new column value
853      * @param length of the stream
854      *
855      * @exception SQLException if a database-access error occurs
856      */
857     public synchronized void updateAsciiStream(String columnName,
858         java.io.InputStream x, int length) throws SQLException {
859         updateAsciiStream(findColumn(columnName), x, length);
860     }
861 
862     /**
863      * JDBC 2.0  Update a column with a BigDecimal value. The updateXXX()
864      * methods are used to update column values in the current row, or the
865      * insert row.  The updateXXX() methods do not  update the underlying
866      * database, instead the updateRow() or insertRow() methods are called to
867      * update the database.
868      *
869      * @param columnIndex the first column is 1, the second is 2, ...
870      * @param x the new column value
871      *
872      * @exception SQLException if a database-access error occurs
873      */
874     public synchronized void updateBigDecimal(int columnIndex, BigDecimal x)
875         throws SQLException {
876         if (!onInsertRow) {
877             if (!doingUpdates) {
878                 doingUpdates = true;
879                 syncUpdate();
880             }
881 
882             updater.setBigDecimal(columnIndex, x);
883         } else {
884             inserter.setBigDecimal(columnIndex, x);
885 
886             if (x == null) {
887                 this.thisRow[columnIndex - 1] = null;
888             } else {
889                 this.thisRow[columnIndex - 1] = x.toString().getBytes();
890             }
891         }
892     }
893 
894     /**
895      * JDBC 2.0  Update a column with a BigDecimal value. The updateXXX()
896      * methods are used to update column values in the current row, or the
897      * insert row.  The updateXXX() methods do not  update the underlying
898      * database, instead the updateRow() or insertRow() methods are called to
899      * update the database.
900      *
901      * @param columnName the name of the column
902      * @param x the new column value
903      *
904      * @exception SQLException if a database-access error occurs
905      */
906     public synchronized void updateBigDecimal(String columnName, BigDecimal x)
907         throws SQLException {
908         updateBigDecimal(findColumn(columnName), x);
909     }
910 
911     /**
912      * JDBC 2.0  Update a column with a binary stream value. The updateXXX()
913      * methods are used to update column values in the current row, or the
914      * insert row.  The updateXXX() methods do not  update the underlying
915      * database, instead the updateRow() or insertRow() methods are called to
916      * update the database.
917      *
918      * @param columnIndex the first column is 1, the second is 2, ...
919      * @param x the new column value
920      * @param length the length of the stream
921      *
922      * @exception SQLException if a database-access error occurs
923      */
924     public synchronized void updateBinaryStream(int columnIndex,
925         java.io.InputStream x, int length) throws SQLException {
926         if (!onInsertRow) {
927             if (!doingUpdates) {
928                 doingUpdates = true;
929                 syncUpdate();
930             }
931 
932             updater.setBinaryStream(columnIndex, x, length);
933         } else {
934             inserter.setBinaryStream(columnIndex, x, length);
935 
936             if (x == null) {
937                 this.thisRow[columnIndex - 1] = null;
938             } else {
939                 this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
940             }
941         }
942     }
943 
944     /**
945      * JDBC 2.0  Update a column with a binary stream value. The updateXXX()
946      * methods are used to update column values in the current row, or the
947      * insert row.  The updateXXX() methods do not  update the underlying
948      * database, instead the updateRow() or insertRow() methods are called to
949      * update the database.
950      *
951      * @param columnName the name of the column
952      * @param x the new column value
953      * @param length of the stream
954      *
955      * @exception SQLException if a database-access error occurs
956      */
957     public synchronized void updateBinaryStream(String columnName,
958         java.io.InputStream x, int length) throws SQLException {
959         updateBinaryStream(findColumn(columnName), x, length);
960     }
961 
962     /**
963      * @see ResultSet#updateBlob(int, Blob)
964      */
965     public synchronized void updateBlob(int columnIndex, java.sql.Blob blob)
966         throws SQLException {
967         if (!onInsertRow) {
968             if (!doingUpdates) {
969                 doingUpdates = true;
970                 syncUpdate();
971             }
972 
973             updater.setBlob(columnIndex, blob);
974         } else {
975             inserter.setBlob(columnIndex, blob);
976 
977             if (blob == null) {
978                 this.thisRow[columnIndex - 1] = null;
979             } else {
980                 this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
981             }
982         }
983     }
984 
985     /**
986      * @see ResultSet#updateBlob(String, Blob)
987      */
988     public void updateBlob(String columnName, java.sql.Blob blob)
989         throws SQLException {
990         updateBlob(findColumn(columnName), blob);
991     }
992 
993     /**
994      * JDBC 2.0  Update a column with a boolean value. The updateXXX() methods
995      * are used to update column values in the current row, or the insert row.
996      * The updateXXX() methods do not  update the underlying database, instead
997      * the updateRow() or insertRow() methods are called to update the
998      * database.
999      *
1000     * @param columnIndex the first column is 1, the second is 2, ...
1001     * @param x the new column value
1002     *
1003     * @exception SQLException if a database-access error occurs
1004     */
1005    public synchronized void updateBoolean(int columnIndex, boolean x)
1006        throws SQLException {
1007        if (!onInsertRow) {
1008            if (!doingUpdates) {
1009                doingUpdates = true;
1010                syncUpdate();
1011            }
1012
1013            updater.setBoolean(columnIndex, x);
1014        } else {
1015            inserter.setBoolean(columnIndex, x);
1016
1017            this.thisRow[columnIndex - 1] = inserter.getBytes(1);
1018        }
1019    }
1020
1021    /**
1022     * JDBC 2.0  Update a column with a boolean value. The updateXXX() methods
1023     * are used to update column values in the current row, or the insert row.
1024     * The updateXXX() methods do not  update the underlying database, instead
1025     * the updateRow() or insertRow() methods are called to update the
1026     * database.
1027     *
1028     * @param columnName the name of the column
1029     * @param x the new column value
1030     *
1031     * @exception SQLException if a database-access error occurs
1032     */
1033    public synchronized void updateBoolean(String columnName, boolean x)
1034        throws SQLException {
1035        updateBoolean(findColumn(columnName), x);
1036    }
1037
1038    /**
1039     * JDBC 2.0  Update a column with a byte value. The updateXXX() methods are
1040     * used to update column values in the current row, or the insert row. The
1041     * updateXXX() methods do not  update the underlying database, instead the
1042     * updateRow() or insertRow() methods are called to update the database.
1043     *
1044     * @param columnIndex the first column is 1, the second is 2, ...
1045     * @param x the new column value
1046     *
1047     * @exception SQLException if a database-access error occurs
1048     */
1049    public synchronized void updateByte(int columnIndex, byte x)
1050        throws SQLException {
1051        if (!onInsertRow) {
1052            if (!doingUpdates) {
1053                doingUpdates = true;
1054                syncUpdate();
1055            }
1056
1057            updater.setByte(columnIndex, x);
1058        } else {
1059            inserter.setByte(columnIndex, x);
1060
1061            this.thisRow[columnIndex - 1] = inserter.getBytes(columnIndex - 1);
1062        }
1063    }
1064
1065    /**
1066     * JDBC 2.0  Update a column with a byte value. The updateXXX() methods are
1067     * used to update column values in the current row, or the insert row. The
1068     * updateXXX() methods do not  update the underlying database, instead the
1069     * updateRow() or insertRow() methods are called to update the database.
1070     *
1071     * @param columnName the name of the column
1072     * @param x the new column value
1073     *
1074     * @exception SQLException if a database-access error occurs
1075     */
1076    public synchronized void updateByte(String columnName, byte x)
1077        throws SQLException {
1078        updateByte(findColumn(columnName), x);
1079    }
1080
1081    /**
1082     * JDBC 2.0  Update a column with a byte array value. The updateXXX()
1083     * methods are used to update column values in the current row, or the
1084     * insert row.  The updateXXX() methods do not  update the underlying
1085     * database, instead the updateRow() or insertRow() methods are called to
1086     * update the database.
1087     *
1088     * @param columnIndex the first column is 1, the second is 2, ...
1089     * @param x the new column value
1090     *
1091     * @exception SQLException if a database-access error occurs
1092     */
1093    public synchronized void updateBytes(int columnIndex, byte[] x)
1094        throws SQLException {
1095        if (!onInsertRow) {
1096            if (!doingUpdates) {
1097                doingUpdates = true;
1098                syncUpdate();
1099            }
1100
1101            updater.setBytes(columnIndex, x);
1102        } else {
1103            inserter.setBytes(columnIndex, x);
1104
1105            this.thisRow[columnIndex - 1] = x;
1106        }
1107    }
1108
1109    /**
1110     * JDBC 2.0  Update a column with a byte array value. The updateXXX()
1111     * methods are used to update column values in the current row, or the
1112     * insert row.  The updateXXX() methods do not  update the underlying
1113     * database, instead the updateRow() or insertRow() methods are called to
1114     * update the database.
1115     *
1116     * @param columnName the name of the column
1117     * @param x the new column value
1118     *
1119     * @exception SQLException if a database-access error occurs
1120     */
1121    public synchronized void updateBytes(String columnName, byte[] x)
1122        throws SQLException {
1123        updateBytes(findColumn(columnName), x);
1124    }
1125
1126    /**
1127     * JDBC 2.0  Update a column with a character stream value. The updateXXX()
1128     * methods are used to update column values in the current row, or the
1129     * insert row.  The updateXXX() methods do not  update the underlying
1130     * database, instead the updateRow() or insertRow() methods are called to
1131     * update the database.
1132     *
1133     * @param columnIndex the first column is 1, the second is 2, ...
1134     * @param x the new column value
1135     * @param length the length of the stream
1136     *
1137     * @exception SQLException if a database-access error occurs
1138     */
1139    public synchronized void updateCharacterStream(int columnIndex,
1140        java.io.Reader x, int length) throws SQLException {
1141        if (!onInsertRow) {
1142            if (!doingUpdates) {
1143                doingUpdates = true;
1144                syncUpdate();
1145            }
1146
1147            updater.setCharacterStream(columnIndex, x, length);
1148        } else {
1149            inserter.setCharacterStream(columnIndex, x, length);
1150
1151            if (x == null) {
1152                this.thisRow[columnIndex - 1] = null;
1153            } else {
1154                this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
1155            }
1156        }
1157    }
1158
1159    /**
1160     * JDBC 2.0  Update a column with a character stream value. The updateXXX()
1161     * methods are used to update column values in the current row, or the
1162     * insert row.  The updateXXX() methods do not  update the underlying
1163     * database, instead the updateRow() or insertRow() methods are called to
1164     * update the database.
1165     *
1166     * @param columnName the name of the column
1167     * @param reader the new column value
1168     * @param length of the stream
1169     *
1170     * @exception SQLException if a database-access error occurs
1171     */
1172    public synchronized void updateCharacterStream(String columnName,
1173        java.io.Reader reader, int length) throws SQLException {
1174        updateCharacterStream(findColumn(columnName), reader, length);
1175    }
1176    
1177  /**
1178    * @see ResultSet#updateClob(int, Clob)
1179    */
1180   public void updateClob(int columnIndex, java.sql.Clob clob) throws SQLException {
1181      if (clob == null) {
1182        updateNull(columnIndex);
1183      } else {
1184        updateCharacterStream(columnIndex, clob.getCharacterStream(), (int) clob.length());
1185      }
1186   }
1187
1188    /**
1189     * JDBC 2.0  Update a column with a Date value. The updateXXX() methods are
1190     * used to update column values in the current row, or the insert row. The
1191     * updateXXX() methods do not  update the underlying database, instead the
1192     * updateRow() or insertRow() methods are called to update the database.
1193     *
1194     * @param columnIndex the first column is 1, the second is 2, ...
1195     * @param x the new column value
1196     *
1197     * @exception SQLException if a database-access error occurs
1198     */
1199    public synchronized void updateDate(int columnIndex, java.sql.Date x)
1200        throws SQLException {
1201        if (!onInsertRow) {
1202            if (!doingUpdates) {
1203                doingUpdates = true;
1204                syncUpdate();
1205            }
1206
1207            updater.setDate(columnIndex, x);
1208        } else {
1209            inserter.setDate(columnIndex, x);
1210
1211            this.thisRow[columnIndex - 1] = this.inserter.getBytes(columnIndex
1212                    - 1);
1213        }
1214    }
1215
1216    /**
1217     * JDBC 2.0  Update a column with a Date value. The updateXXX() methods are
1218     * used to update column values in the current row, or the insert row. The
1219     * updateXXX() methods do not  update the underlying database, instead the
1220     * updateRow() or insertRow() methods are called to update the database.
1221     *
1222     * @param columnName the name of the column
1223     * @param x the new column value
1224     *
1225     * @exception SQLException if a database-access error occurs
1226     */
1227    public synchronized void updateDate(String columnName, java.sql.Date x)
1228        throws SQLException {
1229        updateDate(findColumn(columnName), x);
1230    }
1231
1232    /**
1233     * JDBC 2.0  Update a column with a Double value. The updateXXX() methods
1234     * are used to update column values in the current row, or the insert row.
1235     * The updateXXX() methods do not  update the underlying database, instead
1236     * the updateRow() or insertRow() methods are called to update the
1237     * database.
1238     *
1239     * @param columnIndex the first column is 1, the second is 2, ...
1240     * @param x the new column value
1241     *
1242     * @exception SQLException if a database-access error occurs
1243     */
1244    public synchronized void updateDouble(int columnIndex, double x)
1245        throws SQLException {
1246        if (!onInsertRow) {
1247            if (!doingUpdates) {
1248                doingUpdates = true;
1249                syncUpdate();
1250            }
1251
1252            updater.setDouble(columnIndex, x);
1253        } else {
1254            inserter.setDouble(columnIndex, x);
1255
1256            this.thisRow[columnIndex - 1] = this.inserter.getBytes(columnIndex
1257                    - 1);
1258        }
1259    }
1260
1261    /**
1262     * JDBC 2.0  Update a column with a double value. The updateXXX() methods
1263     * are used to update column values in the current row, or the insert row.
1264     * The updateXXX() methods do not  update the underlying database, instead
1265     * the updateRow() or insertRow() methods are called to update the
1266     * database.
1267     *
1268     * @param columnName the name of the column
1269     * @param x the new column value
1270     *
1271     * @exception SQLException if a database-access error occurs
1272     */
1273    public synchronized void updateDouble(String columnName, double x)
1274        throws SQLException {
1275        updateDouble(findColumn(columnName), x);
1276    }
1277
1278    /**
1279     * JDBC 2.0  Update a column with a float value. The updateXXX() methods
1280     * are used to update column values in the current row, or the insert row.
1281     * The updateXXX() methods do not  update the underlying database, instead
1282     * the updateRow() or insertRow() methods are called to update the
1283     * database.
1284     *
1285     * @param columnIndex the first column is 1, the second is 2, ...
1286     * @param x the new column value
1287     *
1288     * @exception SQLException if a database-access error occurs
1289     */
1290    public synchronized void updateFloat(int columnIndex, float x)
1291        throws SQLException {
1292        if (!onInsertRow) {
1293            if (!doingUpdates) {
1294                doingUpdates = true;
1295                syncUpdate();
1296            }
1297
1298            updater.setFloat(columnIndex, x);
1299        } else {
1300            inserter.setFloat(columnIndex, x);
1301
1302            this.thisRow[columnIndex - 1] = this.inserter.getBytes(columnIndex
1303                    - 1);
1304        }
1305    }
1306
1307    /**
1308     * JDBC 2.0  Update a column with a float value. The updateXXX() methods
1309     * are used to update column values in the current row, or the insert row.
1310     * The updateXXX() methods do not  update the underlying database, instead
1311     * the updateRow() or insertRow() methods are called to update the
1312     * database.
1313     *
1314     * @param columnName the name of the column
1315     * @param x the new column value
1316     *
1317     * @exception SQLException if a database-access error occurs
1318     */
1319    public synchronized void updateFloat(String columnName, float x)
1320        throws SQLException {
1321        updateFloat(findColumn(columnName), x);
1322    }
1323
1324    /**
1325     * JDBC 2.0  Update a column with an integer value. The updateXXX() methods
1326     * are used to update column values in the current row, or the insert row.
1327     * The updateXXX() methods do not  update the underlying database, instead
1328     * the updateRow() or insertRow() methods are called to update the
1329     * database.
1330     *
1331     * @param columnIndex the first column is 1, the second is 2, ...
1332     * @param x the new column value
1333     *
1334     * @exception SQLException if a database-access error occurs
1335     */
1336    public synchronized void updateInt(int columnIndex, int x)
1337        throws SQLException {
1338        if (!onInsertRow) {
1339            if (!doingUpdates) {
1340                doingUpdates = true;
1341                syncUpdate();
1342            }
1343
1344            updater.setInt(columnIndex, x);
1345        } else {
1346            inserter.setInt(columnIndex, x);
1347
1348            this.thisRow[columnIndex - 1] = this.inserter.getBytes(columnIndex
1349                    - 1);
1350        }
1351    }
1352
1353    /**
1354     * JDBC 2.0  Update a column with an integer value. The updateXXX() methods
1355     * are used to update column values in the current row, or the insert row.
1356     * The updateXXX() methods do not  update the underlying database, instead
1357     * the updateRow() or insertRow() methods are called to update the
1358     * database.
1359     *
1360     * @param columnName the name of the column
1361     * @param x the new column value
1362     *
1363     * @exception SQLException if a database-access error occurs
1364     */
1365    public synchronized void updateInt(String columnName, int x)
1366        throws SQLException {
1367        updateInt(findColumn(columnName), x);
1368    }
1369
1370    /**
1371     * JDBC 2.0  Update a column with a long value. The updateXXX() methods are
1372     * used to update column values in the current row, or the insert row. The
1373     * updateXXX() methods do not  update the underlying database, instead the
1374     * updateRow() or insertRow() methods are called to update the database.
1375     *
1376     * @param columnIndex the first column is 1, the second is 2, ...
1377     * @param x the new column value
1378     *
1379     * @exception SQLException if a database-access error occurs
1380     */
1381    public synchronized void updateLong(int columnIndex, long x)
1382        throws SQLException {
1383        if (!onInsertRow) {
1384            if (!doingUpdates) {
1385                doingUpdates = true;
1386                syncUpdate();
1387            }
1388
1389            updater.setLong(columnIndex, x);
1390        } else {
1391            inserter.setLong(columnIndex, x);
1392
1393            this.thisRow[columnIndex - 1] = this.inserter.getBytes(columnIndex
1394                    - 1);
1395        }
1396    }
1397
1398    /**
1399     * JDBC 2.0  Update a column with a long value. The updateXXX() methods are
1400     * used to update column values in the current row, or the insert row. The
1401     * updateXXX() methods do not  update the underlying database, instead the
1402     * updateRow() or insertRow() methods are called to update the database.
1403     *
1404     * @param columnName the name of the column
1405     * @param x the new column value
1406     *
1407     * @exception SQLException if a database-access error occurs
1408     */
1409    public synchronized void updateLong(String columnName, long x)
1410        throws SQLException {
1411        updateLong(findColumn(columnName), x);
1412    }
1413
1414    /**
1415     * JDBC 2.0  Give a nullable column a null value.  The updateXXX() methods
1416     * are used to update column values in the current row, or the insert row.
1417     * The updateXXX() methods do not  update the underlying database, instead
1418     * the updateRow() or insertRow() methods are called to update the
1419     * database.
1420     *
1421     * @param columnIndex the first column is 1, the second is 2, ...
1422     *
1423     * @exception SQLException if a database-access error occurs
1424     */
1425    public synchronized void updateNull(int columnIndex)
1426        throws SQLException {
1427        if (!onInsertRow) {
1428            if (!doingUpdates) {
1429                doingUpdates = true;
1430                syncUpdate();
1431            }
1432
1433            updater.setNull(columnIndex, 0);
1434        } else {
1435            inserter.setNull(columnIndex, 0);
1436
1437            this.thisRow[columnIndex - 1] = null;
1438        }
1439    }
1440
1441    /**
1442     * JDBC 2.0  Update a column with a null value. The updateXXX() methods are
1443     * used to update column values in the current row, or the insert row. The
1444     * updateXXX() methods do not  update the underlying database, instead the
1445     * updateRow() or insertRow() methods are called to update the database.
1446     *
1447     * @param columnName the name of the column
1448     *
1449     * @exception SQLException if a database-access error occurs
1450     */
1451    public synchronized void updateNull(String columnName)
1452        throws SQLException {
1453        updateNull(findColumn(columnName));
1454    }
1455
1456    /**
1457     * JDBC 2.0  Update a column with an Object value. The updateXXX() methods
1458     * are used to update column values in the current row, or the insert row.
1459     * The updateXXX() methods do not  update the underlying database, instead
1460     * the updateRow() or insertRow() methods are called to update the
1461     * database.
1462     *
1463     * @param columnIndex the first column is 1, the second is 2, ...
1464     * @param x the new column value
1465     * @param scale For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types
1466     *        this is the number of digits after the decimal.  For all other
1467     *        types this value will be ignored.
1468     *
1469     * @exception SQLException if a database-access error occurs
1470     */
1471    public synchronized void updateObject(int columnIndex, Object x, int scale)
1472        throws SQLException {
1473        if (!onInsertRow) {
1474            if (!doingUpdates) {
1475                doingUpdates = true;
1476                syncUpdate();
1477            }
1478
1479            updater.setObject(columnIndex, x);
1480        } else {
1481            inserter.setObject(columnIndex, x);
1482
1483            this.thisRow[columnIndex - 1] = this.inserter.getBytes(columnIndex
1484                    - 1);
1485        }
1486    }
1487
1488    /**
1489     * JDBC 2.0  Update a column with an Object value. The updateXXX() methods
1490     * are used to update column values in the current row, or the insert row.
1491     * The updateXXX() methods do not  update the underlying database, instead
1492     * the updateRow() or insertRow() methods are called to update the
1493     * database.
1494     *
1495     * @param columnIndex the first column is 1, the second is 2, ...
1496     * @param x the new column value
1497     *
1498     * @exception SQLException if a database-access error occurs
1499     */
1500    public synchronized void updateObject(int columnIndex, Object x)
1501        throws SQLException {
1502        if (!onInsertRow) {
1503            if (!doingUpdates) {
1504                doingUpdates = true;
1505                syncUpdate();
1506            }
1507
1508            updater.setObject(columnIndex, x);