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);