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 | There are special exceptions to the terms and conditions of the GPL |
9 | as it is applied to this software. View the full text of the |
10 | exception in file EXCEPTIONS-CONNECTOR-J in the directory of this |
11 | software distribution. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | |
22 | |
23 | |
24 | */ |
25 | package com.mysql.jdbc; |
26 | |
27 | import com.mysql.jdbc.profiler.ProfileEventSink; |
28 | import com.mysql.jdbc.profiler.ProfilerEvent; |
29 | |
30 | import java.math.BigDecimal; |
31 | |
32 | import java.sql.SQLException; |
33 | |
34 | import java.util.ArrayList; |
35 | import java.util.HashMap; |
36 | import java.util.List; |
37 | |
38 | /** |
39 | * A result set that is updatable. |
40 | * |
41 | * @author Mark Matthews |
42 | */ |
43 | public class UpdatableResultSet extends ResultSet { |
44 | /** Marker for 'stream' data when doing INSERT rows */ |
45 | private final static byte[] STREAM_DATA_MARKER = "** STREAM DATA **" //$NON-NLS-1$ |
46 | .getBytes(); |
47 | |
48 | private SingleByteCharsetConverter charConverter; |
49 | |
50 | private String charEncoding; |
51 | |
52 | /** What is the default value for the column? */ |
53 | private byte[][] defaultColumnValue; |
54 | |
55 | /** PreparedStatement used to delete data */ |
56 | private com.mysql.jdbc.PreparedStatement deleter = null; |
57 | |
58 | private String deleteSQL = null; |
59 | |
60 | private boolean initializedCharConverter = false; |
61 | |
62 | /** PreparedStatement used to insert data */ |
63 | private com.mysql.jdbc.PreparedStatement inserter = null; |
64 | |
65 | private String insertSQL = null; |
66 | |
67 | /** Is this result set updateable? */ |
68 | private boolean isUpdatable = false; |
69 | |
70 | /** List of primary keys */ |
71 | private List primaryKeyIndicies = null; |
72 | |
73 | private String qualifiedAndQuotedTableName; |
74 | |
75 | private String quotedIdChar = null; |
76 | |
77 | /** PreparedStatement used to refresh data */ |
78 | private com.mysql.jdbc.PreparedStatement refresher; |
79 | |
80 | private String refreshSQL = null; |
81 | |
82 | /** The binary data for the 'current' row */ |
83 | private Object[] savedCurrentRow; |
84 | |
85 | private String tableOnlyName; |
86 | |
87 | /** PreparedStatement used to delete data */ |
88 | private com.mysql.jdbc.PreparedStatement updater = null; |
89 | |
90 | /** SQL for in-place modifcation */ |
91 | private String updateSQL = null; |
92 | |
93 | /** |
94 | * Create a result set for an executeUpdate statement. |
95 | * |
96 | * @param updateCount |
97 | * the number of rows affected by the update |
98 | * @param updateID |
99 | * the autoincrement value (if any) |
100 | * @param conn |
101 | * DOCUMENT ME! |
102 | * @param creatorStmt |
103 | * DOCUMENT ME! |
104 | * |
105 | * @throws SQLException |
106 | * DOCUMENT ME! |
107 | */ |
108 | public UpdatableResultSet(long updateCount, long updateID, Connection conn, |
109 | Statement creatorStmt) throws SQLException { |
110 | super(updateCount, updateID, conn, creatorStmt); |
111 | checkUpdatability(); |
112 | } |
113 | |
114 | /** |
115 | * Creates a new ResultSet object. |
116 | * |
117 | * @param catalog |
118 | * the database in use when we were created |
119 | * @param fields |
120 | * an array of Field objects (basically, the ResultSet MetaData) |
121 | * @param tuples |
122 | * actual row data |
123 | * @param conn |
124 | * the Connection that created us. |
125 | * @param creatorStmt |
126 | * DOCUMENT ME! |
127 | * |
128 | * @throws SQLException |
129 | * DOCUMENT ME! |
130 | */ |
131 | public UpdatableResultSet(String catalog, Field[] fields, RowData tuples, |
132 | Connection conn, Statement creatorStmt) throws SQLException { |
133 | super(catalog, fields, tuples, conn, creatorStmt); |
134 | checkUpdatability(); |
135 | } |
136 | |
137 | /** |
138 | * JDBC 2.0 |
139 | * |
140 | * <p> |
141 | * Move to an absolute row number in the result set. |
142 | * </p> |
143 | * |
144 | * <p> |
145 | * If row is positive, moves to an absolute row with respect to the |
146 | * beginning of the result set. The first row is row 1, the second is row 2, |
147 | * etc. |
148 | * </p> |
149 | * |
150 | * <p> |
151 | * If row is negative, moves to an absolute row position with respect to the |
152 | * end of result set. For example, calling absolute(-1) positions the cursor |
153 | * on the last row, absolute(-2) indicates the next-to-last row, etc. |
154 | * </p> |
155 | * |
156 | * <p> |
157 | * An attempt to position the cursor beyond the first/last row in the result |
158 | * set, leaves the cursor before/after the first/last row, respectively. |
159 | * </p> |
160 | * |
161 | * <p> |
162 | * Note: Calling absolute(1) is the same as calling first(). Calling |
163 | * absolute(-1) is the same as calling last(). |
164 | * </p> |
165 | * |
166 | * @param row |
167 | * DOCUMENT ME! |
168 | * |
169 | * @return true if on the result set, false if off. |
170 | * |
171 | * @exception SQLException |
172 | * if a database-access error occurs, or row is 0, or result |
173 | * set type is TYPE_FORWARD_ONLY. |
174 | */ |
175 | public synchronized boolean absolute(int row) throws SQLException { |
176 | return super.absolute(row); |
177 | } |
178 | |
179 | /** |
180 | * JDBC 2.0 |
181 | * |
182 | * <p> |
183 | * Moves to the end of the result set, just after the last row. Has no |
184 | * effect if the result set contains no rows. |
185 | * </p> |
186 | * |
187 | * @exception SQLException |
188 | * if a database-access error occurs, or result set type is |
189 | * TYPE_FORWARD_ONLY. |
190 | */ |
191 | public synchronized void afterLast() throws SQLException { |
192 | super.afterLast(); |
193 | } |
194 | |
195 | /** |
196 | * JDBC 2.0 |
197 | * |
198 | * <p> |
199 | * Moves to the front of the result set, just before the first row. Has no |
200 | * effect if the result set contains no rows. |
201 | * </p> |
202 | * |
203 | * @exception SQLException |
204 | * if a database-access error occurs, or result set type is |
205 | * TYPE_FORWARD_ONLY |
206 | */ |
207 | public synchronized void beforeFirst() throws SQLException { |
208 | super.beforeFirst(); |
209 | } |
210 | |
211 | /** |
212 | * JDBC 2.0 The cancelRowUpdates() method may be called after calling an |
213 | * updateXXX() method(s) and before calling updateRow() to rollback the |
214 | * updates made to a row. If no updates have been made or updateRow() has |
215 | * already been called, then this method has no effect. |
216 | * |
217 | * @exception SQLException |
218 | * if a database-access error occurs, or if called when on |
219 | * the insert row. |
220 | */ |
221 | public synchronized void cancelRowUpdates() throws SQLException { |
222 | checkClosed(); |
223 | |
224 | if (this.doingUpdates) { |
225 | this.doingUpdates = false; |
226 | this.updater.clearParameters(); |
227 | } |
228 | } |
229 | |
230 | /* |
231 | * (non-Javadoc) |
232 | * |
233 | * @see com.mysql.jdbc.ResultSet#checkRowPos() |
234 | */ |
235 | protected void checkRowPos() throws SQLException { |
236 | checkClosed(); |
237 | |
238 | if (!this.onInsertRow) { |
239 | super.checkRowPos(); |
240 | } |
241 | } |
242 | |
243 | /** |
244 | * Is this ResultSet updateable? |
245 | * |
246 | * @throws SQLException |
247 | * DOCUMENT ME! |
248 | */ |
249 | private void checkUpdatability() throws SQLException { |
250 | String singleTableName = null; |
251 | String catalogName = null; |
252 | |
253 | int primaryKeyCount = 0; |
254 | |
255 | if (this.fields.length > 0) { |
256 | singleTableName = this.fields[0].getOriginalTableName(); |
257 | catalogName = this.fields[0].getDatabaseName(); |
258 | |
259 | if (singleTableName == null) { |
260 | singleTableName = this.fields[0].getTableName(); |
261 | catalogName = this.catalog; |
262 | } |
263 | |
264 | if (this.fields[0].isPrimaryKey()) { |
265 | primaryKeyCount++; |
266 | } |
267 | |
268 | // |
269 | // References only one table? |
270 | // |
271 | for (int i = 1; i < this.fields.length; i++) { |
272 | String otherTableName = this.fields[i].getOriginalTableName(); |
273 | String otherCatalogName = this.fields[i].getDatabaseName(); |
274 | |
275 | if (otherTableName == null) { |
276 | otherTableName = this.fields[i].getTableName(); |
277 | otherCatalogName = this.catalog; |
278 | } |
279 | |
280 | if ((singleTableName == null) |
281 | || !otherTableName.equals(singleTableName)) { |
282 | this.isUpdatable = false; |
283 | |
284 | return; |
285 | } |
286 | |
287 | // Can't reference more than one database |
288 | if ((catalogName == null) |
289 | || !otherCatalogName.equals(catalogName)) { |
290 | this.isUpdatable = false; |
291 | |
292 | return; |
293 | } |
294 | |
295 | if (this.fields[i].isPrimaryKey()) { |
296 | primaryKeyCount++; |
297 | } |
298 | } |
299 | |
300 | if ((singleTableName == null) || (singleTableName.length() == 0)) { |
301 | this.isUpdatable = false; |
302 | |
303 | return; |
304 | } |
305 | } else { |
306 | this.isUpdatable = false; |
307 | |
308 | return; |
309 | } |
310 | |
311 | // |
312 | // Must have at least one primary key |
313 | // |
314 | if (primaryKeyCount == 0) { |
315 | this.isUpdatable = false; |
316 | |
317 | return; |
318 | } |
319 | |
320 | // We can only do this if we know that there is a currently |
321 | // selected database, or if we're talking to a > 4.1 version |
322 | // of MySQL server (as it returns database names in field |
323 | // info) |
324 | // |
325 | if ((this.catalog == null) || (this.catalog.length() == 0)) { |
326 | this.catalog = this.fields[0].getDatabaseName(); |
327 | |
328 | if ((this.catalog == null) || (this.catalog.length() == 0)) { |
329 | throw SQLError.createSQLException(Messages |
330 | .getString("UpdatableResultSet.43") //$NON-NLS-1$ |
331 | , SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$ |
332 | } |
333 | } |
334 | |
335 | if (this.connection.getStrictUpdates()) { |
336 | java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); |
337 | |
338 | java.sql.ResultSet rs = null; |
339 | HashMap primaryKeyNames = new HashMap(); |
340 | |
341 | try { |
342 | rs = dbmd.getPrimaryKeys(catalogName, null, singleTableName); |
343 | |
344 | while (rs.next()) { |
345 | String keyName = rs.getString(4); |
346 | keyName = keyName.toUpperCase(); |
347 | primaryKeyNames.put(keyName, keyName); |
348 | } |
349 | } finally { |
350 | if (rs != null) { |
351 | try { |
352 | rs.close(); |
353 | } catch (Exception ex) { |
354 | AssertionFailedException.shouldNotHappen(ex); |
355 | } |
356 | |
357 | rs = null; |
358 | } |
359 | } |
360 | |
361 | if (primaryKeyNames.size() == 0) { |
362 | this.isUpdatable = false; |
363 | |
364 | return; // we can't update tables w/o keys |
365 | } |
366 | |
367 | // |
368 | // Contains all primary keys? |
369 | // |
370 | for (int i = 0; i < this.fields.length; i++) { |
371 | if (this.fields[i].isPrimaryKey()) { |
372 | String columnNameUC = this.fields[i].getName() |
373 | .toUpperCase(); |
374 | |
375 | if (primaryKeyNames.remove(columnNameUC) == null) { |
376 | // try original name |
377 | String originalName = this.fields[i].getOriginalName(); |
378 | |
379 | if (originalName != null) { |
380 | if (primaryKeyNames.remove(originalName |
381 | .toUpperCase()) == null) { |
382 | // we don't know about this key, so give up :( |
383 | this.isUpdatable = false; |
384 | |
385 | return; |
386 | } |
387 | } |
388 | } |
389 | } |
390 | } |
391 | |
392 | this.isUpdatable = primaryKeyNames.isEmpty(); |
393 | |
394 | return; |
395 | } |
396 | |
397 | this.isUpdatable = true; |
398 | |
399 | return; |
400 | } |
401 | |
402 | /** |
403 | * JDBC 2.0 Delete the current row from the result set and the underlying |
404 | * database. Cannot be called when on the insert row. |
405 | * |
406 | * @exception SQLException |
407 | * if a database-access error occurs, or if called when on |
408 | * the insert row. |
409 | * @throws SQLException |
410 | * if the ResultSet is not updatable or some other error occurs |
411 | */ |
412 | public synchronized void deleteRow() throws SQLException { |
413 | checkClosed(); |
414 | |
415 | if (!this.isUpdatable) { |
416 | throw new NotUpdatable(); |
417 | } |
418 | |
419 | if (this.onInsertRow) { |
420 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.1")); //$NON-NLS-1$ |
421 | } else if (this.rowData.size() == 0) { |
422 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.2")); //$NON-NLS-1$ |
423 | } else if (isBeforeFirst()) { |
424 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.3")); //$NON-NLS-1$ |
425 | } else if (isAfterLast()) { |
426 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.4")); //$NON-NLS-1$ |
427 | } |
428 | |
429 | if (this.deleter == null) { |
430 | if (this.deleteSQL == null) { |
431 | generateStatements(); |
432 | } |
433 | |
434 | this.deleter = this.connection |
435 | .clientPrepareStatement(this.deleteSQL); |
436 | } |
437 | |
438 | this.deleter.clearParameters(); |
439 | |
440 | String characterEncoding = null; |
441 | |
442 | if (this.connection.getUseUnicode()) { |
443 | characterEncoding = this.connection.getEncoding(); |
444 | } |
445 | |
446 | // |
447 | // FIXME: Use internal routines where possible for character |
448 | // conversion! |
449 | try { |
450 | int numKeys = this.primaryKeyIndicies.size(); |
451 | |
452 | if (numKeys == 1) { |
453 | int index = ((Integer) this.primaryKeyIndicies.get(0)) |
454 | .intValue(); |
455 | String currentVal = ((characterEncoding == null) ? new String( |
456 | (byte[]) this.thisRow[index]) : new String( |
457 | (byte[]) this.thisRow[index], characterEncoding)); |
458 | this.deleter.setString(1, currentVal); |
459 | } else { |
460 | for (int i = 0; i < numKeys; i++) { |
461 | int index = ((Integer) this.primaryKeyIndicies.get(i)) |
462 | .intValue(); |
463 | String currentVal = ((characterEncoding == null) ? new String( |
464 | (byte[]) this.thisRow[index]) |
465 | : new String((byte[]) this.thisRow[index], |
466 | characterEncoding)); |
467 | this.deleter.setString(i + 1, currentVal); |
468 | } |
469 | } |
470 | |
471 | this.deleter.executeUpdate(); |
472 | this.rowData.removeRow(this.rowData.getCurrentRowNumber()); |
473 | } catch (java.io.UnsupportedEncodingException encodingEx) { |
474 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.39", //$NON-NLS-1$ |
475 | new Object[] { this.charEncoding }) //$NON-NLS-1$ |
476 | , SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$ |
477 | } |
478 | } |
479 | |
480 | private synchronized void extractDefaultValues() throws SQLException { |
481 | java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); |
482 | |
483 | java.sql.ResultSet columnsResultSet = null; |
484 | |
485 | try { |
486 | columnsResultSet = dbmd.getColumns(this.catalog, null, |
487 | this.tableOnlyName, "%"); //$NON-NLS-1$ |
488 | |
489 | HashMap columnNameToDefaultValueMap = new HashMap( |
490 | this.fields.length /* at least this big... */); |
491 | |
492 | while (columnsResultSet.next()) { |
493 | String columnName = columnsResultSet.getString("COLUMN_NAME"); //$NON-NLS-1$ |
494 | byte[] defaultValue = columnsResultSet.getBytes("COLUMN_DEF"); //$NON-NLS-1$ |
495 | |
496 | columnNameToDefaultValueMap.put(columnName, defaultValue); |
497 | } |
498 | |
499 | int numFields = this.fields.length; |
500 | |
501 | this.defaultColumnValue = new byte[numFields][]; |
502 | |
503 | for (int i = 0; i < numFields; i++) { |
504 | String defValTableName = this.fields[i].getOriginalName(); |
505 | |
506 | if ((defValTableName == null) |
507 | || (defValTableName.length() == 0)) { |
508 | defValTableName = this.fields[i].getName(); |
509 | } |
510 | |
511 | if (defValTableName != null) { |
512 | byte[] defaultVal = (byte[]) columnNameToDefaultValueMap |
513 | .get(defValTableName); |
514 | |
515 | this.defaultColumnValue[i] = defaultVal; |
516 | } |
517 | } |
518 | } finally { |
519 | if (columnsResultSet != null) { |
520 | columnsResultSet.close(); |
521 | |
522 | columnsResultSet = null; |
523 | } |
524 | } |
525 | } |
526 | |
527 | /** |
528 | * JDBC 2.0 |
529 | * |
530 | * <p> |
531 | * Moves to the first row in the result set. |
532 | * </p> |
533 | * |
534 | * @return true if on a valid row, false if no rows in the result set. |
535 | * |
536 | * @exception SQLException |
537 | * if a database-access error occurs, or result set type is |
538 | * TYPE_FORWARD_ONLY. |
539 | */ |
540 | public synchronized boolean first() throws SQLException { |
541 | return super.first(); |
542 | } |
543 | |
544 | /** |
545 | * Figure out whether or not this ResultSet is updateable, and if so, |
546 | * generate the PreparedStatements to support updates. |
547 | * |
548 | * @throws SQLException |
549 | * DOCUMENT ME! |
550 | * @throws NotUpdatable |
551 | * DOCUMENT ME! |
552 | */ |
553 | protected synchronized void generateStatements() throws SQLException { |
554 | if (!this.isUpdatable) { |
555 | this.doingUpdates = false; |
556 | this.onInsertRow = false; |
557 | |
558 | throw new NotUpdatable(); |
559 | } |
560 | |
561 | String quotedId = getQuotedIdChar(); |
562 | |
563 | if (this.fields[0].getOriginalTableName() != null) { |
564 | StringBuffer tableNameBuffer = new StringBuffer(); |
565 | |
566 | String databaseName = this.fields[0].getDatabaseName(); |
567 | |
568 | if ((databaseName != null) && (databaseName.length() > 0)) { |
569 | tableNameBuffer.append(quotedId); |
570 | tableNameBuffer.append(databaseName); |
571 | tableNameBuffer.append(quotedId); |
572 | tableNameBuffer.append('.'); |
573 | } |
574 | |
575 | this.tableOnlyName = this.fields[0].getOriginalTableName(); |
576 | |
577 | tableNameBuffer.append(quotedId); |
578 | tableNameBuffer.append(this.tableOnlyName); |
579 | tableNameBuffer.append(quotedId); |
580 | |
581 | this.qualifiedAndQuotedTableName = tableNameBuffer.toString(); |
582 | } else { |
583 | StringBuffer tableNameBuffer = new StringBuffer(); |
584 | |
585 | this.tableOnlyName = this.fields[0].getTableName(); |
586 | |
587 | tableNameBuffer.append(quotedId); |
588 | tableNameBuffer.append(this.tableOnlyName); |
589 | tableNameBuffer.append(quotedId); |
590 | |
591 | this.qualifiedAndQuotedTableName = tableNameBuffer.toString(); |
592 | } |
593 | |
594 | this.primaryKeyIndicies = new ArrayList(); |
595 | |
596 | StringBuffer fieldValues = new StringBuffer(); |
597 | StringBuffer keyValues = new StringBuffer(); |
598 | StringBuffer columnNames = new StringBuffer(); |
599 | StringBuffer insertPlaceHolders = new StringBuffer(); |
600 | boolean firstTime = true; |
601 | boolean keysFirstTime = true; |
602 | |
603 | String equalsStr = this.connection.versionMeetsMinimum(3, 23, 0) ? "<=>" |
604 | : "="; |
605 | |
606 | for (int i = 0; i < this.fields.length; i++) { |
607 | String originalColumnName = this.fields[i].getOriginalName(); |
608 | String columnName = null; |
609 | |
610 | if (this.connection.getIO().hasLongColumnInfo() |
611 | && (originalColumnName != null) |
612 | && (originalColumnName.length() > 0)) { |
613 | columnName = originalColumnName; |
614 | } else { |
615 | columnName = this.fields[i].getName(); |
616 | } |
617 | |
618 | if (this.fields[i].isPrimaryKey()) { |
619 | this.primaryKeyIndicies.add(new Integer(i)); |
620 | |
621 | if (!keysFirstTime) { |
622 | keyValues.append(" AND "); //$NON-NLS-1$ |
623 | } else { |
624 | keysFirstTime = false; |
625 | } |
626 | |
627 | keyValues.append(quotedId); |
628 | keyValues.append(columnName); |
629 | keyValues.append(quotedId); |
630 | keyValues.append(equalsStr); |
631 | keyValues.append("?"); //$NON-NLS-1$ |
632 | } |
633 | |
634 | if (firstTime) { |
635 | firstTime = false; |
636 | fieldValues.append("SET "); //$NON-NLS-1$ |
637 | } else { |
638 | fieldValues.append(","); //$NON-NLS-1$ |
639 | columnNames.append(","); //$NON-NLS-1$ |
640 | insertPlaceHolders.append(","); //$NON-NLS-1$ |
641 | } |
642 | |
643 | insertPlaceHolders.append("?"); //$NON-NLS-1$ |
644 | |
645 | columnNames.append(quotedId); |
646 | columnNames.append(columnName); |
647 | columnNames.append(quotedId); |
648 | |
649 | fieldValues.append(quotedId); |
650 | fieldValues.append(columnName); |
651 | fieldValues.append(quotedId); |
652 | fieldValues.append("=?"); //$NON-NLS-1$ |
653 | } |
654 | |
655 | this.updateSQL = "UPDATE " + this.qualifiedAndQuotedTableName + " " //$NON-NLS-1$ //$NON-NLS-2$ |
656 | + fieldValues.toString() //$NON-NLS-1$ //$NON-NLS-2$ |
657 | + " WHERE " + keyValues.toString(); //$NON-NLS-1$ |
658 | this.insertSQL = "INSERT INTO " + this.qualifiedAndQuotedTableName //$NON-NLS-1$ |
659 | + " (" + columnNames.toString() //$NON-NLS-1$ //$NON-NLS-2$ |
660 | + ") VALUES (" + insertPlaceHolders.toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
661 | this.refreshSQL = "SELECT " + columnNames.toString() + " FROM " //$NON-NLS-1$ //$NON-NLS-2$ |
662 | + this.qualifiedAndQuotedTableName //$NON-NLS-1$ //$NON-NLS-2$ |
663 | + " WHERE " + keyValues.toString(); //$NON-NLS-1$ |
664 | this.deleteSQL = "DELETE FROM " + this.qualifiedAndQuotedTableName //$NON-NLS-1$ |
665 | + " WHERE " //$NON-NLS-1$ //$NON-NLS-2$ |
666 | + keyValues.toString(); |
667 | } |
668 | |
669 | private synchronized SingleByteCharsetConverter getCharConverter() |
670 | throws SQLException { |
671 | if (!this.initializedCharConverter) { |
672 | this.initializedCharConverter = true; |
673 | |
674 | if (this.connection.getUseUnicode()) { |
675 | this.charEncoding = connection.getEncoding(); |
676 | this.charConverter = this.connection |
677 | .getCharsetConverter(this.charEncoding); |
678 | } |
679 | } |
680 | |
681 | return this.charConverter; |
682 | } |
683 | |
684 | /** |
685 | * JDBC 2.0 Return the concurrency of this result set. The concurrency used |
686 | * is determined by the statement that created the result set. |
687 | * |
688 | * @return the concurrency type, CONCUR_READ_ONLY, etc. |
689 | * |
690 | * @exception SQLException |
691 | * if a database-access error occurs |
692 | */ |
693 | public int getConcurrency() throws SQLException { |
694 | return (this.isUpdatable ? CONCUR_UPDATABLE : CONCUR_READ_ONLY); |
695 | } |
696 | |
697 | private synchronized String getQuotedIdChar() throws SQLException { |
698 | if (this.quotedIdChar == null) { |
699 | boolean useQuotedIdentifiers = this.connection |
700 | .supportsQuotedIdentifiers(); |
701 | |
702 | if (useQuotedIdentifiers) { |
703 | java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); |
704 | this.quotedIdChar = dbmd.getIdentifierQuoteString(); |
705 | } else { |
706 | this.quotedIdChar = ""; //$NON-NLS-1$ |
707 | } |
708 | } |
709 | |
710 | return this.quotedIdChar; |
711 | } |
712 | |
713 | /** |
714 | * JDBC 2.0 Insert the contents of the insert row into the result set and |
715 | * the database. Must be on the insert row when this method is called. |
716 | * |
717 | * @exception SQLException |
718 | * if a database-access error occurs, if called when not on |
719 | * the insert row, or if all non-nullable columns in the |
720 | * insert row have not been given a value |
721 | */ |
722 | public synchronized void insertRow() throws SQLException { |
723 | checkClosed(); |
724 | |
725 | if (!this.onInsertRow) { |
726 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.7")); //$NON-NLS-1$ |
727 | } |
728 | |
729 | this.inserter.executeUpdate(); |
730 | |
731 | long autoIncrementId = this.inserter.getLastInsertID(); |
732 | int numFields = this.fields.length; |
733 | byte[][] newRow = new byte[numFields][]; |
734 | |
735 | for (int i = 0; i < numFields; i++) { |
736 | if (this.inserter.isNull(i)) { |
737 | newRow[i] = null; |
738 | } else { |
739 | newRow[i] = this.inserter.getBytesRepresentation(i); |
740 | } |
741 | |
742 | // |
743 | // WARN: This non-variant only holds if MySQL never allows more |
744 | // than one auto-increment key (which is the way it is _today_) |
745 | // |
746 | if (this.fields[i].isAutoIncrement() && autoIncrementId > 0) { |
747 | newRow[i] = String.valueOf(autoIncrementId).getBytes(); |
748 | } |
749 | } |
750 | |
751 | this.rowData.addRow(newRow); |
752 | resetInserter(); |
753 | } |
754 | |
755 | /** |
756 | * JDBC 2.0 |
757 | * |
758 | * <p> |
759 | * Determine if the cursor is after the last row in the result set. |
760 | * </p> |
761 | * |
762 | * @return true if after the last row, false otherwise. Returns false when |
763 | * the result set contains no rows. |
764 | * |
765 | * @exception SQLException |
766 | * if a database-access error occurs. |
767 | */ |
768 | public synchronized boolean isAfterLast() throws SQLException { |
769 | return super.isAfterLast(); |
770 | } |
771 | |
772 | /** |
773 | * JDBC 2.0 |
774 | * |
775 | * <p> |
776 | * Determine if the cursor is before the first row in the result set. |
777 | * </p> |
778 | * |
779 | * @return true if before the first row, false otherwise. Returns false when |
780 | * the result set contains no rows. |
781 | * |
782 | * @exception SQLException |
783 | * if a database-access error occurs. |
784 | */ |
785 | public synchronized boolean isBeforeFirst() throws SQLException { |
786 | return super.isBeforeFirst(); |
787 | } |
788 | |
789 | /** |
790 | * JDBC 2.0 |
791 | * |
792 | * <p> |
793 | * Determine if the cursor is on the first row of the result set. |
794 | * </p> |
795 | * |
796 | * @return true if on the first row, false otherwise. |
797 | * |
798 | * @exception SQLException |
799 | * if a database-access error occurs. |
800 | */ |
801 | public synchronized boolean isFirst() throws SQLException { |
802 | return super.isFirst(); |
803 | } |
804 | |
805 | /** |
806 | * JDBC 2.0 |
807 | * |
808 | * <p> |
809 | * Determine if the cursor is on the last row of the result set. Note: |
810 | * Calling isLast() may be expensive since the JDBC driver might need to |
811 | * fetch ahead one row in order to determine whether the current row is the |
812 | * last row in the result set. |
813 | * </p> |
814 | * |
815 | * @return true if on the last row, false otherwise. |
816 | * |
817 | * @exception SQLException |
818 | * if a database-access error occurs. |
819 | */ |
820 | public synchronized boolean isLast() throws SQLException { |
821 | return super.isLast(); |
822 | } |
823 | |
824 | boolean isUpdatable() { |
825 | return this.isUpdatable; |
826 | } |
827 | |
828 | /** |
829 | * JDBC 2.0 |
830 | * |
831 | * <p> |
832 | * Moves to the last row in the result set. |
833 | * </p> |
834 | * |
835 | * @return true if on a valid row, false if no rows in the result set. |
836 | * |
837 | * @exception SQLException |
838 | * if a database-access error occurs, or result set type is |
839 | * TYPE_FORWARD_ONLY. |
840 | */ |
841 | public synchronized boolean last() throws SQLException { |
842 | return super.last(); |
843 | } |
844 | |
845 | /** |
846 | * JDBC 2.0 Move the cursor to the remembered cursor position, usually the |
847 | * current row. Has no effect unless the cursor is on the insert row. |
848 | * |
849 | * @exception SQLException |
850 | * if a database-access error occurs, or the result set is |
851 | * not updatable |
852 | * @throws SQLException |
853 | * if the ResultSet is not updatable or some other error occurs |
854 | */ |
855 | public synchronized void moveToCurrentRow() throws SQLException { |
856 | checkClosed(); |
857 | |
858 | if (!this.isUpdatable) { |
859 | throw new NotUpdatable(); |
860 | } |
861 | |
862 | if (this.onInsertRow) { |
863 | this.onInsertRow = false; |
864 | this.thisRow = this.savedCurrentRow; |
865 | } |
866 | } |
867 | |
868 | /** |
869 | * JDBC 2.0 Move to the insert row. The current cursor position is |
870 | * remembered while the cursor is positioned on the insert row. The insert |
871 | * row is a special row associated with an updatable result set. It is |
872 | * essentially a buffer where a new row may be constructed by calling the |
873 | * updateXXX() methods prior to inserting the row into the result set. Only |
874 | * the updateXXX(), getXXX(), and insertRow() methods may be called when the |
875 | * cursor is on the insert row. All of the columns in a result set must be |
876 | * given a value each time this method is called before calling insertRow(). |
877 | * UpdateXXX()must be called before getXXX() on a column. |
878 | * |
879 | * @exception SQLException |
880 | * if a database-access error occurs, or the result set is |
881 | * not updatable |
882 | * @throws NotUpdatable |
883 | * DOCUMENT ME! |
884 | */ |
885 | public synchronized void moveToInsertRow() throws SQLException { |
886 | checkClosed(); |
887 | |
888 | if (!this.isUpdatable) { |
889 | throw new NotUpdatable(); |
890 | } |
891 | |
892 | if (this.inserter == null) { |
893 | if (this.insertSQL == null) { |
894 | generateStatements(); |
895 | } |
896 | |
897 | this.inserter = this.connection |
898 | .clientPrepareStatement(this.insertSQL); |
899 | extractDefaultValues(); |
900 | resetInserter(); |
901 | } else { |
902 | resetInserter(); |
903 | } |
904 | |
905 | int numFields = this.fields.length; |
906 | |
907 | this.onInsertRow = true; |
908 | this.doingUpdates = false; |
909 | this.savedCurrentRow = this.thisRow; |
910 | this.thisRow = new byte[numFields][]; |
911 | |
912 | for (int i = 0; i < numFields; i++) { |
913 | if (this.defaultColumnValue[i] != null) { |
914 | Field f = this.fields[i]; |
915 | |
916 | switch (f.getMysqlType()) { |
917 | case MysqlDefs.FIELD_TYPE_DATE: |
918 | case MysqlDefs.FIELD_TYPE_DATETIME: |
919 | case MysqlDefs.FIELD_TYPE_NEWDATE: |
920 | case MysqlDefs.FIELD_TYPE_TIME: |
921 | case MysqlDefs.FIELD_TYPE_TIMESTAMP: |
922 | |
923 | if (this.defaultColumnValue[i].length > 7 |
924 | && this.defaultColumnValue[i][0] == (byte) 'C' |
925 | && this.defaultColumnValue[i][1] == (byte) 'U' |
926 | && this.defaultColumnValue[i][2] == (byte) 'R' |
927 | && this.defaultColumnValue[i][3] == (byte) 'R' |
928 | && this.defaultColumnValue[i][4] == (byte) 'E' |
929 | && this.defaultColumnValue[i][5] == (byte) 'N' |
930 | && this.defaultColumnValue[i][6] == (byte) 'T' |
931 | && this.defaultColumnValue[i][7] == (byte) '_') { |
932 | this.inserter.setBytesNoEscapeNoQuotes(i + 1, |
933 | this.defaultColumnValue[i]); |
934 | |
935 | break; |
936 | } |
937 | default: |
938 | this.inserter.setBytes(i + 1, this.defaultColumnValue[i], |
939 | false, false); |
940 | } |
941 | |
942 | // This value _could_ be changed from a getBytes(), so we |
943 | // need a copy.... |
944 | byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length]; |
945 | System.arraycopy(defaultColumnValue[i], 0, defaultValueCopy, 0, |
946 | defaultValueCopy.length); |
947 | this.thisRow[i] = defaultValueCopy; |
948 | } else { |
949 | this.inserter.setNull(i + 1, java.sql.Types.NULL); |
950 | this.thisRow[i] = null; |
951 | } |
952 | } |
953 | } |
954 | |
955 | // --------------------------------------------------------------------- |
956 | // Updates |
957 | // --------------------------------------------------------------------- |
958 | |
959 | /** |
960 | * A ResultSet is initially positioned before its first row, the first call |
961 | * to next makes the first row the current row; the second call makes the |
962 | * second row the current row, etc. |
963 | * |
964 | * <p> |
965 | * If an input stream from the previous row is open, it is implicitly |
966 | * closed. The ResultSet's warning chain is cleared when a new row is read |
967 | * </p> |
968 | * |
969 | * @return true if the new current is valid; false if there are no more rows |
970 | * |
971 | * @exception SQLException |
972 | * if a database access error occurs |
973 | */ |
974 | public synchronized boolean next() throws SQLException { |
975 | return super.next(); |
976 | } |
977 | |
978 | /** |
979 | * The prev method is not part of JDBC, but because of the architecture of |
980 | * this driver it is possible to move both forward and backward within the |
981 | * result set. |
982 | * |
983 | * <p> |
984 | * If an input stream from the previous row is open, it is implicitly |
985 | * closed. The ResultSet's warning chain is cleared when a new row is read |
986 | * </p> |
987 | * |
988 | * @return true if the new current is valid; false if there are no more rows |
989 | * |
990 | * @exception SQLException |
991 | * if a database access error occurs |
992 | */ |
993 | public synchronized boolean prev() throws SQLException { |
994 | return super.prev(); |
995 | } |
996 | |
997 | /** |
998 | * JDBC 2.0 |
999 | * |
1000 | * <p> |
1001 | * Moves to the previous row in the result set. |
1002 | * </p> |
1003 | * |
1004 | * <p> |
1005 | * Note: previous() is not the same as relative(-1) since it makes sense to |
1006 | * call previous() when there is no current row. |
1007 | * </p> |
1008 | * |
1009 | * @return true if on a valid row, false if off the result set. |
1010 | * |
1011 | * @exception SQLException |
1012 | * if a database-access error occurs, or result set type is |
1013 | * TYPE_FORWAR_DONLY. |
1014 | */ |
1015 | public synchronized boolean previous() throws SQLException { |
1016 | return super.previous(); |
1017 | } |
1018 | |
1019 | /** |
1020 | * Closes this ResultSet, releasing all resources. |
1021 | * |
1022 | * @param calledExplicitly |
1023 | * was this called from close()? |
1024 | * |
1025 | * @throws SQLException |
1026 | * if an error occurs. |
1027 | */ |
1028 | protected void realClose(boolean calledExplicitly) throws SQLException { |
1029 | if (this.isClosed) { |
1030 | return; |
1031 | } |
1032 | |
1033 | SQLException sqlEx = null; |
1034 | |
1035 | if (this.useUsageAdvisor) { |
1036 | if ((this.deleter == null) && (this.inserter == null) |
1037 | && (this.refresher == null) && (this.updater == null)) { |
1038 | this.eventSink = ProfileEventSink.getInstance(this.connection); |
1039 | |
1040 | String message = Messages.getString("UpdatableResultSet.34"); //$NON-NLS-1$ |
1041 | |
1042 | this.eventSink.consumeEvent(new ProfilerEvent( |
1043 | ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$ |
1044 | (this.owningStatement == null) ? "N/A" //$NON-NLS-1$ |
1045 | : this.owningStatement.currentCatalog, //$NON-NLS-1$ |
1046 | this.connectionId, |
1047 | (this.owningStatement == null) ? (-1) |
1048 | : this.owningStatement.getId(), this.resultId, |
1049 | System.currentTimeMillis(), 0, null, |
1050 | this.pointOfOrigin, message)); |
1051 | } |
1052 | } |
1053 | |
1054 | try { |
1055 | if (this.deleter != null) { |
1056 | this.deleter.close(); |
1057 | } |
1058 | } catch (SQLException ex) { |
1059 | sqlEx = ex; |
1060 | } |
1061 | |
1062 | try { |
1063 | if (this.inserter != null) { |
1064 | this.inserter.close(); |
1065 | } |
1066 | } catch (SQLException ex) { |
1067 | sqlEx = ex; |
1068 | } |
1069 | |
1070 | try { |
1071 | if (this.refresher != null) { |
1072 | this.refresher.close(); |
1073 | } |
1074 | } catch (SQLException ex) { |
1075 | sqlEx = ex; |
1076 | } |
1077 | |
1078 | try { |
1079 | if (this.updater != null) { |
1080 | this.updater.close(); |
1081 | } |
1082 | } catch (SQLException ex) { |
1083 | sqlEx = ex; |
1084 | } |
1085 | |
1086 | super.realClose(calledExplicitly); |
1087 | |
1088 | if (sqlEx != null) { |
1089 | throw sqlEx; |
1090 | } |
1091 | } |
1092 | |
1093 | /** |
1094 | * JDBC 2.0 Refresh the value of the current row with its current value in |
1095 | * the database. Cannot be called when on the insert row. The refreshRow() |
1096 | * method provides a way for an application to explicitly tell the JDBC |
1097 | * driver to refetch a row(s) from the database. An application may want to |
1098 | * call refreshRow() when caching or prefetching is being done by the JDBC |
1099 | * driver to fetch the latest value of a row from the database. The JDBC |
1100 | * driver may actually refresh multiple rows at once if the fetch size is |
1101 | * greater than one. All values are refetched subject to the transaction |
1102 | * isolation level and cursor sensitivity. If refreshRow() is called after |
1103 | * calling updateXXX(), but before calling updateRow() then the updates made |
1104 | * to the row are lost. Calling refreshRow() frequently will likely slow |
1105 | * performance. |
1106 | * |
1107 | * @exception SQLException |
1108 | * if a database-access error occurs, or if called when on |
1109 | * the insert row. |
1110 | * @throws NotUpdatable |
1111 | * DOCUMENT ME! |
1112 | */ |
1113 | public synchronized void refreshRow() throws SQLException { |
1114 | checkClosed(); |
1115 | |
1116 | if (!this.isUpdatable) { |
1117 | throw new NotUpdatable(); |
1118 | } |
1119 | |
1120 | if (this.onInsertRow) { |
1121 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.8")); //$NON-NLS-1$ |
1122 | } else if (this.rowData.size() == 0) { |
1123 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.9")); //$NON-NLS-1$ |
1124 | } else if (isBeforeFirst()) { |
1125 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.10")); //$NON-NLS-1$ |
1126 | } else if (isAfterLast()) { |
1127 | throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.11")); //$NON-NLS-1$ |
1128 | } |
1129 | |
1130 | if (this.refresher == null) { |
1131 | if (this.refreshSQL == null) { |
1132 | generateStatements(); |
1133 | } |
1134 | |
1135 | this.refresher = this.connection |
1136 | .clientPrepareStatement(this.refreshSQL); |
1137 | } |
1138 | |
1139 | this.refresher.clearParameters(); |
1140 | |
1141 | int numKeys = this.primaryKeyIndicies.size(); |
1142 | |
1143 | if (numKeys == 1) { |
1144 | byte[] dataFrom = null; |
1145 | int index = ((Integer) this.primaryKeyIndicies.get(0)).intValue(); |
1146 | |
1147 | if (!this.doingUpdates) { |
1148 | dataFrom = (byte[]) this.thisRow[index]; |
1149 | } else { |
1150 | dataFrom = this.updater.getBytesRepresentation(index); |
1151 | |
1152 | // Primary keys not set? |
1153 | if (this.updater.isNull(index) || (dataFrom.length == 0)) { |
1154 | dataFrom = (byte[]) this.thisRow[index]; |
1155 | } else { |
1156 | dataFrom = stripBinaryPrefix(dataFrom); |
1157 | } |
1158 | } |
1159 | |
1160 | this.refresher.setBytesNoEscape(1, dataFrom); |
1161 | } else { |
1162 | for (int i = 0; i < numKeys; i++) { |
1163 | byte[] dataFrom = null; |
1164 | int index = ((Integer) this.primaryKeyIndicies.get(i)) |
1165 | .intValue(); |
1166 | |
1167 | if (!this.doingUpdates) { |
1168 | dataFrom = (byte[]) this.thisRow[index]; |
1169 | } else { |
1170 | dataFrom = this.updater.getBytesRepresentation(index); |
1171 | |
1172 | // Primary keys not set? |
1173 | if (this.updater.isNull(index) || (dataFrom.length == 0)) { |
1174 | dataFrom = (byte[]) this.thisRow[index]; |
1175 | } else { |
1176 | dataFrom = stripBinaryPrefix(dataFrom); |
1177 | } |
1178 | } |
1179 | |
1180 | this.refresher.setBytesNoEscape(i + 1, dataFrom); |
1181 | } |
1182 | } |
1183 | |
1184 | java.sql.ResultSet rs = null; |
1185 | |
1186 | try { |
1187 | rs = this.refresher.executeQuery(); |
1188 | |
1189 | int numCols = rs.getMetaData().getColumnCount(); |
1190 | |
1191 | if (rs.next()) { |
1192 | for (int i = 0; i < numCols; i++) { |
1193 | byte[] val = rs.getBytes(i + 1); |
1194 | |
1195 | if ((val == null) || rs.wasNull()) { |
1196 | this.thisRow[i] = null; |
1197 | } else { |
1198 | this.thisRow[i] = rs.getBytes(i + 1); |
1199 | } |
1200 | } |
1201 | } else { |
1202 | throw SQLError.createSQLException(Messages |
1203 | .getString("UpdatableResultSet.12"), //$NON-NLS-1$ |
1204 | SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$ |
1205 | } |
1206 | } finally { |
1207 | if (rs != null) { |
1208 | try { |
1209 | rs.close(); |
1210 | } catch (SQLException ex) { |
1211 | ; // ignore |
1212 | } |
1213 | } |
1214 | } |
1215 | } |
1216 | |
1217 | /** |
1218 | * JDBC 2.0 |
1219 | * |
1220 | * <p> |
1221 | * Moves a relative number of rows, either positive or negative. Attempting |
1222 | * to move beyond the first/last row in the result set positions the cursor |
1223 | * before/after the the first/last row. Calling relative(0) is valid, but |
1224 | * does not change the cursor position. |
1225 | * </p> |
1226 | * |
1227 | * <p> |
1228 | * Note: Calling relative(1) is different than calling next() since is makes |
1229 | * sense to call next() when there is no current row, for example, when the |
1230 | * cursor is positioned before the first row or after the last row of the |
1231 | * result set. |
1232 | * </p> |
1233 | * |
1234 | * @param rows |
1235 | * DOCUMENT ME! |
1236 | * |
1237 | * @return true if on a row, false otherwise. |
1238 | * |
1239 | * @exception SQLException |
1240 | * if a database-access error occurs, or there is no current |
1241 | * row, or result set type is TYPE_FORWARD_ONLY. |
1242 | */ |
1243 | public synchronized boolean relative(int rows) throws SQLException { |
1244 | return super.relative(rows); |
1245 | } |
1246 | |
1247 | private void resetInserter() throws SQLException { |
1248 | this.inserter.clearParameters(); |
1249 | |
1250 | for (int i = 0; i < this.fields.length; i++) { |
1251 | this.inserter.setNull(i + 1, 0); |
1252 | } |
1253 | } |
1254 | |
1255 | /** |
1256 | * JDBC 2.0 Determine if this row has been deleted. A deleted row may leave |
1257 | * a visible "hole" in a result set. This method can be used to detect holes |
1258 | * in a result set. The value returned depends on whether or not the result |
1259 | * set can detect deletions. |
1260 | * |
1261 | * @return true if deleted and deletes are detected |
1262 | * |
1263 | * @exception SQLException |
1264 | * if a database-access error occurs |
1265 | * @throws NotImplemented |
1266 | * DOCUMENT ME! |
1267 | * |
1268 | * @see DatabaseMetaData#deletesAreDetected |
1269 | */ |
1270 | public synchronized boolean rowDeleted() throws SQLException { |
1271 | throw new NotImplemented(); |
1272 | } |
1273 | |
1274 | /** |
1275 | * JDBC 2.0 Determine if the current row has been inserted. The value |
1276 | * returned depends on whether or not the result set can detect visible |
1277 | * inserts. |
1278 | * |
1279 | * @return true if inserted and inserts are detected |
1280 | * |
1281 | * @exception SQLException |
1282 | * if a database-access error occurs |
1283 | * @throws NotImplemented |
1284 | * DOCUMENT ME! |
1285 | * |
1286 | * @see DatabaseMetaData#insertsAreDetected |
1287 | */ |
1288 | public synchronized boolean rowInserted() throws SQLException { |
1289 | throw new NotImplemented(); |
1290 | } |
1291 | |
1292 | /** |
1293 | * JDBC 2.0 Determine if the current row has been updated. The value |
1294 | * returned depends on whether or not the result set can detect updates. |
1295 | * |
1296 | * @return true if the row has been visibly updated by the owner or another, |
1297 | * and updates are detected |
1298 | * |
1299 | * @exception SQLException |
1300 | * if a database-access error occurs |
1301 | * @throws NotImplemented |
1302 | * DOCUMENT ME! |
1303 | * |
1304 | * @see DatabaseMetaData#updatesAreDetected |
1305 | */ |
1306 | public synchronized boolean rowUpdated() throws SQLException { |
1307 | throw new NotImplemented(); |
1308 | } |
1309 | |
1310 | /** |
1311 | * Sets the concurrency type of this result set |
1312 | * |
1313 | * @param concurrencyFlag |
1314 | * the type of concurrency that this ResultSet should support. |
1315 | */ |
1316 | protected void setResultSetConcurrency(int concurrencyFlag) { |
1317 | super.setResultSetConcurrency(concurrencyFlag); |
1318 | |
1319 | // |
1320 | // FIXME: Issue warning when asked for updateable result set, but result |
1321 | // set is not |
1322 | // updatable |
1323 | // |
1324 | // if ((concurrencyFlag == CONCUR_UPDATABLE) && !isUpdatable()) { |
1325 | // java.sql.SQLWarning warning = new java.sql.SQLWarning( |
1326 | // NotUpdatable.NOT_UPDATEABLE_MESSAGE); |
1327 | // } |
1328 | } |
1329 | |
1330 | private byte[] stripBinaryPrefix(byte[] dataFrom) { |
1331 | return StringUtils.stripEnclosure(dataFrom, "_binary'", "'"); |
1332 | } |
1333 | |
1334 | /** |
1335 | * Reset UPDATE prepared statement to value in current row. This_Row MUST |
1336 | * point to current, valid row. |
1337 | * |
1338 | * @throws SQLException |
1339 | * DOCUMENT ME! |
1340 | */ |
1341 | synchronized void syncUpdate() throws SQLException { |
1342 | if (this.updater == null) { |
1343 | if (this.updateSQL == null) { |
1344 | generateStatements(); |
1345 | } |
1346 | |
1347 | this.updater = this.connection |
1348 | .clientPrepareStatement(this.updateSQL); |
1349 | } |
1350 | |
1351 | int numFields = this.fields.length; |
1352 | this.updater.clearParameters(); |
1353 | |
1354 | for (int i = 0; i < numFields; i++) { |
1355 | if (this.thisRow[i] != null) { |
1356 | this.updater.setBytes(i + 1, (byte[]) this.thisRow[i], |
1357 | this.fields[i].isBinary(), false); |
1358 | } else { |
1359 | this.updater.setNull(i + 1, 0); |
1360 | } |
1361 | } |
1362 | |
1363 | int numKeys = this.primaryKeyIndicies.size(); |
1364 | |
1365 | if (numKeys == 1) { |
1366 | int index = ((Integer) this.primaryKeyIndicies.get(0)).intValue(); |
1367 | byte[] keyData = (byte[]) this.thisRow[index]; |
1368 | this.updater.setBytes(numFields + 1, keyData, false, false); |
1369 | } else { |
1370 | for (int i = 0; i < numKeys; i++) { |
1371 | byte[] currentVal = (byte[]) this.thisRow[((Integer) this.primaryKeyIndicies |
1372 | .get(i)).intValue()]; |
1373 | |
1374 | if (currentVal != null) { |
1375 | this.updater.setBytes(numFields + i + 1, currentVal, false, |
1376 | false); |
1377 | } else { |
1378 | this.updater.setNull(numFields + i + 1, 0); |
1379 | } |
1380 | } |
1381 | } |
1382 | } |
1383 | |
1384 | /** |
1385 | * JDBC 2.0 Update a column with an ascii stream value. The updateXXX() |
1386 | * methods are used to update column values in the current row, or the |
1387 | * insert row. The updateXXX() methods do not update the underlying |
1388 | * database, instead the updateRow() or insertRow() methods are called to |
1389 | * update the database. |
1390 | * |
1391 | * @param columnIndex |
1392 | * the first column is 1, the second is 2, ... |
1393 | * @param x |
1394 | * the new column value |
1395 | * @param length |
1396 | * the length of the stream |
1397 | * |
1398 | * @exception SQLException |
1399 | * if a database-access error occurs |
1400 | */ |
1401 | public synchronized void updateAsciiStream(int columnIndex, |
1402 | java.io.InputStream x, int length) throws SQLException { |
1403 | if (!this.onInsertRow) { |
1404 | if (!this.doingUpdates) { |
1405 | this.doingUpdates = true; |
1406 | syncUpdate(); |
1407 | } |
1408 | |
1409 | this.updater.setAsciiStream(columnIndex, x, length); |
1410 | } else { |
1411 | this.inserter.setAsciiStream(columnIndex, x, length); |
1412 | this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER; |
1413 | } |
1414 | } |
1415 | |
1416 | /** |
1417 | * JDBC 2.0 Update a column with an ascii stream value. The updateXXX() |
1418 | * methods are used to update column values in the current row, or the |
1419 | * insert row. The updateXXX() methods do not update the underlying |
1420 | * database, instead the updateRow() or insertRow() methods are called to |
1421 | * update the database. |
1422 | * |
1423 | * @param columnName |
1424 | * the name of the column |
1425 | * @param x |
1426 | * the new column value |
1427 | * @param length |
1428 | * of the stream |
1429 | * |
1430 | * @exception SQLException |
1431 | * if a database-access error occurs |
1432 | */ |
1433 | public synchronized void updateAsciiStream(String columnName, |
1434 | java.io.InputStream x, int length) throws SQLException { |
1435 | updateAsciiStream(findColumn(columnName), x, length); |
1436 | } |
1437 | |
1438 | /** |
1439 | * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods |
1440 | * are used to update column values in the current row, or the insert row. |
1441 | * The updateXXX() methods do not update the underlying database, instead |
1442 | * the updateRow() or insertRow() methods are called to update the database. |
1443 | * |
1444 | * @param columnIndex |
1445 | * the first column is 1, the second is 2, ... |
1446 | * @param x |
1447 | * the new column value |
1448 | * |
1449 | * @exception SQLException |
1450 | * if a database-access error occurs |
1451 | */ |
1452 | public synchronized void updateBigDecimal(int columnIndex, BigDecimal x) |
1453 | throws SQLException { |
1454 | if (!this.onInsertRow) { |
1455 | if (!this.doingUpdates) { |
1456 | this.doingUpdates = true; |
1457 | syncUpdate(); |
1458 | } |
1459 | |
1460 | this.updater.setBigDecimal(columnIndex, x); |
1461 | } else { |
1462 | this.inserter.setBigDecimal(columnIndex, x); |
1463 | |
1464 | if (x == null) { |
1465 | this.thisRow[columnIndex - 1] = null; |
1466 | } else { |
1467 | this.thisRow[columnIndex - 1] = x.toString().getBytes(); |
1468 | } |
1469 | } |
1470 | } |
1471 | |
1472 | /** |
1473 | * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods |
1474 | * are used to update column values in the current row, or the insert row. |
1475 | * The updateXXX() methods do not update the underlying database, instead |
1476 | * the updateRow() or insertRow() methods are called to update the database. |
1477 | * |
1478 | * @param columnName |
1479 | * the name of the column |
1480 | * @param x |
1481 | * the new column value |
1482 | * |
1483 | * @exception SQLException |
1484 | * if a database-access error occurs |
1485 | */ |
1486 | public synchronized void updateBigDecimal(String columnName, BigDecimal x) |
1487 | throws SQLException { |
1488 | updateBigDecimal(findColumn(columnName), x); |
1489 | } |
1490 | |
1491 | /** |
1492 | * JDBC 2.0 Update a column with a binary stream value. The updateXXX() |
1493 | * methods are used to update column values in the current row, or the |
1494 | * insert row. The updateXXX() methods do not update the underlying |
1495 | * database, instead the updateRow() or insertRow() methods are called to |
1496 | * update the database. |
1497 | * |
1498 | * @param columnIndex |
1499 | * the first column is 1, the second is 2, ... |
1500 | * @param x |
1501 | * the new column value |
1502 | * @param length |
1503 | * the length of the stream |
1504 | * |
1505 | * @exception SQLException |
1506 | * if a database-access error occurs |
1507 | */ |
1508 | public synchronized void updateBinaryStream(int columnIndex, |
1509 | java.io.InputStream x, int length) throws SQLException { |
1510 | if (!this.onInsertRow) { |
1511 | if (!this.doingUpdates) { |
1512 | this.doingUpdates = true; |
1513 | syncUpdate(); |
1514 | } |
1515 | |
1516 | this.updater.setBinaryStream(columnIndex, x, length); |
1517 | } else { |
1518 | this.inserter.setBinaryStream(columnIndex, x, length); |
1519 | |
1520 | if (x == null) { |
1521 | this.thisRow[columnIndex - 1] = null; |
1522 | } else { |
1523 | this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER; |
1524 | } |
1525 | } |
1526 | } |
1527 | |
1528 | /** |
1529 | * JDBC 2.0 Update a column with a binary stream value. The updateXXX() |
1530 | * methods are used to update column values in the current row, or the |
1531 | * insert row. The updateXXX() methods do not update the underlying |
1532 | * database, instead the updateRow() or insertRow() methods are called to |
1533 | * update the database. |
1534 | * |
1535 | * @param columnName |
1536 | * the name of the column |
1537 | * @param x |
1538 | * the new column value |
1539 | * @param length |
1540 | * of the stream |
1541 | * |
1542 | * @exception SQLException |
1543 | * if a database-access error occurs |
1544 | */ |
1545 | public synchronized void updateBinaryStream(String columnName, |
1546 | java.io.InputStream x, int length) throws SQLException { |
1547 | updateBinaryStream(findColumn(columnName), x, length); |
1548 | } |
1549 | |
1550 | /** |
1551 | * @see ResultSet#updateBlob(int, Blob) |
1552 | */ |
1553 | public synchronized void updateBlob(int columnIndex, java.sql.Blob blob) |
1554 | throws SQLException { |
1555 | if (!this.onInsertRow) { |
1556 | if (!this.doingUpdates) { |
1557 | this.doingUpdates = true; |
1558 | syncUpdate(); |
1559 | } |
1560 | |
1561 | this.updater.setBlob(columnIndex, blob); |
1562 | } else { |
1563 | this.inserter.setBlob(columnIndex, blob); |
1564 | |
1565 | if (blob == null) { |
1566 | this.thisRow[columnIndex - 1] = null; |
1567 | } else { |
1568 | this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER; |
1569 | } |
1570 | } |
1571 | } |
1572 | |
1573 | /** |
1574 | * @see ResultSet#updateBlob(String, Blob) |
1575 | */ |
1576 | public synchronized void updateBlob(String columnName, java.sql.Blob blob) |
1577 | throws SQLException { |
1578 | updateBlob(findColumn(columnName), blob); |
1579 | } |
1580 | |
1581 | /** |
1582 | * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods |
1583 | * are used to update column values in the current row, or the insert row. |
1584 | * The updateXXX() methods do not update the underlying database, instead |
1585 | * the updateRow() or insertRow() methods are called to update the database. |
1586 | * |
1587 | * @param columnIndex |
1588 | * the first column is 1, the second is 2, ... |
1589 | * @param x |
1590 | * the new column value |
1591 | * |
1592 | * @exception SQLException |
1593 | * if a database-access error occurs |
1594 | */ |
1595 | public synchronized void updateBoolean(int columnIndex, boolean x) |
1596 | throws SQLException { |
1597 | if (!this.onInsertRow) { |
1598 | if (!this.doingUpdates) { |
1599 | this.doingUpdates = true; |
1600 | syncUpdate(); |
1601 | } |
1602 | |
1603 | this.updater.setBoolean(columnIndex, x); |
1604 | } else { |
1605 | this.inserter.setBoolean(columnIndex, x); |
1606 | |
1607 | this.thisRow[columnIndex - 1] = this.inserter |
1608 | .getBytesRepresentation(columnIndex - 1); |
1609 | } |
1610 | } |
1611 | |
1612 | /** |
1613 | * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods |
1614 | * are used to update column values in the current row, or the insert row. |
1615 | * The updateXXX() methods do not update the underlying database, instead |
1616 | * the updateRow() or insertRow() methods are called to update the database. |
1617 | * |
1618 | * @param columnName |
1619 | * the name of the column |
1620 | * @param x |
1621 | * the new column value |
1622 | * |
1623 | * @exception SQLException |
1624 | * if a database-access error occurs |
1625 | */ |
1626 | public synchronized void updateBoolean(String columnName, boolean x) |
1627 | throws SQLException { |
1628 | updateBoolean(findColumn(columnName), x); |
1629 | } |
1630 | |
1631 | /** |
1632 | * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are |
1633 | * used to update column values in the current row, or the insert row. The |
1634 | * updateXXX() methods do not update the underlying database, instead the |
1635 | * updateRow() or insertRow() methods are called to update the database. |
1636 | * |
1637 | * @param columnIndex |
1638 | * the first column is 1, the second is 2, ... |
1639 | * @param x |
1640 | * the new column value |
1641 | * |
1642 | * @exception SQLException |
1643 | * if a database-access error occurs |
1644 | */ |
1645 | public synchronized void updateByte(int columnIndex, byte x) |
1646 | throws SQLException { |
1647 | if (!this.onInsertRow) { |
1648 | if (!this.doingUpdates) { |
1649 | this.doingUpdates = true; |
1650 | syncUpdate(); |
1651 | } |
1652 | |
1653 | this.updater.setByte(columnIndex, x); |
1654 | } else { |
1655 | this.inserter.setByte(columnIndex, x); |
1656 | |
1657 | this.thisRow[columnIndex - 1] = this.inserter |
1658 | .getBytesRepresentation(columnIndex - 1); |
1659 | } |
1660 | } |
1661 | |
1662 | /** |
1663 | * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are |
1664 | * used to update column values in the current row, or the insert row. The |
1665 | * updateXXX() methods do not update the underlying database, instead the |
1666 | * updateRow() or insertRow() methods are called to update the database. |
1667 | * |
1668 | * @param columnName |
1669 | * the name of the column |
1670 | * @param x |
1671 | * the new column value |
1672 | * |
1673 | * @exception SQLException |
1674 | * if a database-access error occurs |
1675 | */ |
1676 | public synchronized void updateByte(String columnName, byte x) |
1677 | throws SQLException { |
1678 | updateByte(findColumn(columnName), x); |
1679 | } |
1680 | |
1681 | /** |
1682 | * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods |
1683 | * are used to update column values in the current row, or the insert row. |
1684 | * The updateXXX() methods do not update the underlying database, instead |
1685 | * the updateRow() or insertRow() methods are called to update the database. |
1686 | * |
1687 | * @param columnIndex |
1688 | * the first column is 1, the second is 2, ... |
1689 | * @param x |
1690 | * the new column value |
1691 | * |
1692 | * @exception SQLException |
1693 | * if a database-access error occurs |
1694 | */ |
1695 | public synchronized void updateBytes(int columnIndex, byte[] x) |
1696 | throws SQLException { |
1697 | if (!this.onInsertRow) { |
1698 | if (!this.doingUpdates) { |
1699 | this.doingUpdates = true; |
1700 | syncUpdate(); |
1701 | } |
1702 | |
1703 | this.updater.setBytes(columnIndex, x); |
1704 | } else { |
1705 | this.inserter.setBytes(columnIndex, x); |
1706 | |
1707 | this.thisRow[columnIndex - 1] = x; |
1708 | } |
1709 | } |
1710 | |
1711 | /** |
1712 | * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods |
1713 | * are used to update column values in the current row, or the insert row. |
1714 | * The updateXXX() methods do not update the underlying database, instead |
1715 | * the updateRow() or insertRow() methods are called to update the database. |
1716 | * |
1717 | * @param columnName |
1718 | * the name of the column |
1719 | * @param x |
1720 | * the new column value |
1721 | * |
1722 | * @exception SQLException |
1723 | * if a database-access error occurs |
1724 | */ |
1725 | public synchronized void updateBytes(String columnName, byte[] x) |
1726 | throws SQLException { |
1727 | updateBytes(findColumn(columnName), x); |
1728 | } |
1729 | |
1730 | /** |
1731 | * JDBC 2.0 Update a column with a character stream value. The updateXXX() |
1732 | * methods are used to update column values in the current row, or the |
1733 | * insert row. The updateXXX() methods do not update the underlying |
1734 | * database, instead the updateRow() or insertRow() methods are called to |
1735 | * update the database. |
1736 | * |
1737 | * @param columnIndex |
1738 | * the first column is 1, the second is 2, ... |
1739 | * @param x |
1740 | * the new column value |
1741 | * @param length |
1742 | * the length of the stream |
1743 | * |
1744 | * @exception SQLException |
1745 | * if a database-access error occurs |
1746 | */ |
1747 | public synchronized void updateCharacterStream(int columnIndex, |
1748 | java.io.Reader x, int length) throws SQLException { |
1749 | if (!this.onInsertRow) { |
1750 | if (!this.doingUpdates) { |
1751 | this.doingUpdates = true; |
1752 | syncUpdate(); |
1753 | } |
1754 | |
1755 | this.updater.setCharacterStream(columnIndex, x, length); |
1756 | } else { |
1757 | this.inserter.setCharacterStream(columnIndex, x, length); |
1758 | |
1759 | if (x == null) { |
1760 | this.thisRow[columnIndex - 1] = null; |
1761 | } else { |
1762 | this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER; |
1763 | } |
1764 | } |
1765 | } |
1766 | |
1767 | /** |
1768 | * JDBC 2.0 Update a column with a character stream value. The updateXXX() |
1769 | * methods are used to update column values in the current row, or the |
1770 | * insert row. The updateXXX() methods do not update the underlying |
1771 | * database, instead the updateRow() or insertRow() methods are called to |
1772 | * update the database. |
1773 | * |
1774 | * @param columnName |
1775 | * the name of the column |
1776 | * @param reader |
1777 | * the new column value |
1778 | * @param length |
1779 | * of the stream |
1780 | * |
1781 | * @exception SQLException |
1782 | * if a database-access error occurs |
1783 | */ |
1784 | public synchronized void updateCharacterStream(String columnName, |
1785 | java.io.Reader reader, int length) throws SQLException { |
1786 | updateCharacterStream(findColumn(columnName), reader, length); |
1787 | } |
1788 | |
1789 | /** |
1790 | * @see ResultSet#updateClob(int, Clob) |
1791 | */ |
1792 | public void updateClob(int columnIndex, java.sql.Clob clob) |
1793 | throws SQLException { |
1794 | if (clob == null) { |
1795 | updateNull(columnIndex); |
1796 | } else { |
1797 | updateCharacterStream(columnIndex, clob.getCharacterStream(), |
1798 | (int) clob.length()); |
1799 | } |
1800 | } |
1801 | |
1802 | /** |
1803 | * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are |
1804 | * used to update column values in the current row, or the insert row. The |
1805 | * updateXXX() methods do not update the underlying database, instead the |
1806 | * updateRow() or insertRow() methods are called to update the database. |
1807 | * |
1808 | * @param columnIndex |
1809 | * the first column is 1, the second is 2, ... |
1810 | * @param x |
1811 | * the new column value |
1812 | * |
1813 | * @exception SQLException |
1814 | * if a database-access error occurs |
1815 | */ |
1816 | public synchronized void updateDate(int columnIndex, java.sql.Date x) |
1817 | throws SQLException { |
1818 | if (!this.onInsertRow) { |
1819 | if (!this.doingUpdates) { |
1820 | this.doingUpdates = true; |
1821 | syncUpdate(); |
1822 | } |
1823 | |
1824 | this.updater.setDate(columnIndex, x); |
1825 | } else { |
1826 | this.inserter.setDate(columnIndex, x); |
1827 | |
1828 | this.thisRow[columnIndex - 1] = this.inserter |
1829 | .getBytesRepresentation(columnIndex - 1); |
1830 | } |
1831 | } |
1832 | |
1833 | /** |
1834 | * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are |
1835 | * used to update column values in the current row, or the insert row. The |
1836 | * updateXXX() methods do not update the underlying database, instead the |
1837 | * updateRow() or insertRow() methods are called to update the database. |
1838 | * |
1839 | * @param columnName |
1840 | * the name of the column |
1841 | * @param x |
1842 | * the new column value |
1843 | * |
1844 | * @exception SQLException |
1845 | * if a database-access error occurs |
1846 | */ |
1847 | public synchronized void updateDate(String columnName, java.sql.Date x) |
1848 | throws SQLException { |
1849 | updateDate(findColumn(columnName), x); |
1850 | } |
1851 | |
1852 | /** |
1853 | * JDBC 2.0 Update a column with a Double value. The updateXXX() methods are |
1854 | * used to update column values in the current row, or the insert row. The |
1855 | * updateXXX() methods do not update the underlying database, instead the |
1856 | * updateRow() or insertRow() methods are called to update the database. |
1857 | * |
1858 | * @param columnIndex |
1859 | * the first column is 1, the second is 2, ... |
1860 | * @param x |
1861 | * the new column value |
1862 | * |
1863 | * @exception SQLException |
1864 | * if a database-access error occurs |
1865 | */ |
1866 | public synchronized void updateDouble(int columnIndex, double x) |
1867 | throws SQLException { |
1868 | if (!this.onInsertRow) { |
1869 | if (!this.doingUpdates) { |
1870 | this.doingUpdates = true; |
1871 | syncUpdate(); |
1872 | } |
1873 | |
1874 | this.updater.setDouble(columnIndex, x); |
1875 | } else { |
1876 | this.inserter.setDouble(columnIndex, x); |
1877 | |
1878 | this.thisRow[columnIndex - 1] = this.inserter |
1879 | .getBytesRepresentation(columnIndex - 1); |
1880 | } |
1881 | } |
1882 | |
1883 | /** |
1884 | * JDBC 2.0 Update a column with a double value. The updateXXX() methods are |
1885 | * used to update column values in the current row, or the insert row. The |
1886 | * updateXXX() methods do not update the underlying database, instead the |
1887 | * updateRow() or insertRow() methods are called to update the database. |
1888 | * |
1889 | * @param columnName |
1890 | * the name of the column |
1891 | * @param x |
1892 | * the new column value |
1893 | * |
1894 | * @exception SQLException |
1895 | * if a database-access error occurs |
1896 | */ |
1897 | public synchronized void updateDouble(String columnName, double x) |
1898 | throws SQLException { |
1899 | updateDouble(findColumn(columnName), x); |
1900 | } |
1901 | |
1902 | /** |
1903 | * JDBC 2.0 Update a column with a float value. The updateXXX() methods are |
1904 | * used to update column values in the current row, or the insert row. The |
1905 | * updateXXX() methods do not update the underlying database, instead the |
1906 | * updateRow() or insertRow() methods are called to update the database. |
1907 | * |
1908 | * @param columnIndex |
1909 | * the first column is 1, the second is 2, ... |
1910 | * @param x |
1911 | * the new column value |
1912 | * |
1913 | * @exception SQLException |
1914 | * if a database-access error occurs |
1915 | */ |
1916 | public synchronized void updateFloat(int columnIndex, float x) |
1917 | throws SQLException { |
1918 | if (!this.onInsertRow) { |
1919 | if (!this.doingUpdates) { |
1920 | this.doingUpdates = true; |
1921 | syncUpdate(); |
1922 | } |
1923 | |
1924 | this.updater.setFloat(columnIndex, x); |
1925 | } else { |
1926 | this.inserter.setFloat(columnIndex, x); |
1927 | |
1928 | this.thisRow[columnIndex - 1] = this.inserter |
1929 | .getBytesRepresentation(columnIndex - 1); |
1930 | } |
1931 | } |
1932 | |
1933 | /** |
1934 | * JDBC 2.0 Update a column with a float value. The updateXXX() methods are |
1935 | * used to update column values in the current row, or the insert row. The |
1936 | * updateXXX() methods do not update the underlying database, instead the |
1937 | * updateRow() or insertRow() methods are called to update the database. |
1938 | * |
1939 | * @param columnName |
1940 | * the name of the column |
1941 | * @param x |
1942 | * the new column value |
1943 | * |
1944 | * @exception SQLException |
1945 | * if a database-access error occurs |
1946 | */ |
1947 | public synchronized void updateFloat(String columnName, float x) |
1948 | throws SQLException { |
1949 | updateFloat(findColumn(columnName), x); |
1950 | } |
1951 | |
1952 | /** |
1953 | * JDBC 2.0 Update a column with an integer value. The updateXXX() methods |
1954 | * are used to update column values in the current row, or the insert row. |
1955 | * The updateXXX() methods do not update the underlying database, instead |
1956 | * the updateRow() or insertRow() methods are called to update the database. |
1957 | * |
1958 | * @param columnIndex |
1959 | * the first column is 1, the second is 2, ... |
1960 | * @param x |
1961 | * the new column value |
1962 | * |
1963 | * @exception SQLException |
1964 | * if a database-access error occurs |
1965 | */ |
1966 | public synchronized void updateInt(int columnIndex, int x) |
1967 | throws SQLException { |
1968 | if (!this.onInsertRow) { |
1969 | if (!this.doingUpdates) { |
1970 | this.doingUpdates = true; |
1971 | syncUpdate(); |
1972 | } |
1973 | |
1974 | this.updater.setInt(columnIndex, x); |
1975 | } else { |
1976 | this.inserter.setInt(columnIndex, x); |
1977 | |
1978 | this.thisRow[columnIndex - 1] = this.inserter |
1979 | .getBytesRepresentation(columnIndex - 1); |
1980 | } |
1981 | } |
1982 | |
1983 | /** |
1984 | * JDBC 2.0 Update a column with an integer value. The updateXXX() methods |
1985 | * are used to update column values in the current row, or the insert row. |
1986 | * The updateXXX() methods do not update the underlying database, instead |
1987 | * the updateRow() or insertRow() methods are called to update the database. |
1988 | * |
1989 | * @param columnName |
1990 | * the name of the column |
1991 | * @param x |
1992 | * the new column value |
1993 | * |
1994 | * @exception SQLException |
1995 | * if a database-access error occurs |
1996 | */ |
1997 | public synchronized void updateInt(String columnName, int x) |
1998 | throws SQLException { |
1999 | updateInt(findColumn(columnName), x); |
2000 | } |
2001 | |
2002 | /** |
2003 | * JDBC 2.0 Update a column with a long value. The updateXXX() methods are |
2004 | * used to update column values in the current row, or the insert row. The |
2005 | * updateXXX() methods do not update the underlying database, instead the |
2006 | * updateRow() or insertRow() methods are called to update the database. |
2007 | * |
2008 | * @param columnIndex |
2009 | * the first column is 1, the second is 2, ... |
2010 | * @param x |
2011 | * the new column value |
2012 | * |
2013 | * @exception SQLException |
2014 | * if a database-access error occurs |
2015 | */ |
2016 | public synchronized void updateLong(int columnIndex, long x) |
2017 | throws SQLException { |
2018 | if (!this.onInsertRow) { |
2019 | if (!this.doingUpdates) { |
2020 | this.doingUpdates = true; |
2021 | syncUpdate(); |
2022 | } |
2023 | |
2024 | this.updater.setLong(columnIndex, x); |
2025 | } else { |
2026 | this.inserter.setLong(columnIndex, x); |
2027 | |
2028 | this.thisRow[columnIndex - 1] = this.inserter |
2029 | .getBytesRepresentation(columnIndex - 1); |
2030 | } |
2031 | } |
2032 | |
2033 | /** |
2034 | * JDBC 2.0 Update a column with a long value. The updateXXX() methods are |
2035 | * used to update column values in the current row, or the insert row. The |
2036 | * updateXXX() methods do not update the underlying database, instead the |
2037 | * updateRow() or insertRow() methods are called to update the database. |
2038 | * |
2039 | * @param columnName |
2040 | * the name of the column |
2041 | * @param x |
2042 | * the new column value |
2043 | * |
2044 | * @exception SQLException |
2045 | * if a database-access error occurs |
2046 | */ |
2047 | public synchronized void updateLong(String columnName, long x) |
2048 | throws SQLException { |
2049 | updateLong(findColumn(columnName), x); |
2050 | } |
2051 | |
2052 | /** |
2053 | * JDBC 2.0 Give a nullable column a null value. The updateXXX() methods are |
2054 | * used to update column values in the current row, or the insert row. The |
2055 | * updateXXX() methods do not update the underlying database, instead the |
2056 | * updateRow() or insertRow() methods are called to update the database. |
2057 | * |
2058 | * @param columnIndex |
2059 | * the first column is 1, the second is 2, ... |
2060 | * |
2061 | * @exception SQLException |
2062 | * if a database-access error occurs |
2063 | */ |
2064 | public synchronized void updateNull(int columnIndex) throws SQLException { |
2065 | if (!this.onInsertRow) { |
2066 | if (!this.doingUpdates) { |
2067 | this.doingUpdates = true; |
2068 | syncUpdate(); |
2069 | } |
2070 | |
2071 | this.updater.setNull(columnIndex, 0); |
2072 | } else { |
2073 | this.inserter.setNull(columnIndex, 0); |
2074 | |
2075 | this.thisRow[columnIndex - 1] = null; |
2076 | } |
2077 | } |
2078 | |
2079 | /** |
2080 | * JDBC 2.0 Update a column with a null value. The updateXXX() methods are |
2081 | * used to update column values in the current row, or the insert row. The |
2082 | * updateXXX() methods do not update the underlying database, instead the |
2083 | * updateRow() or insertRow() methods are called to update the database. |
2084 | * |
2085 | * @param columnName |
2086 | * the name of the column |
2087 | * |
2088 | * @exception SQLException |
2089 | * if a database-access error occurs |
2090 | */ |
2091 | public synchronized void updateNull(String columnName) throws SQLException { |
2092 | updateNull(findColumn(columnName)); |
2093 | } |
2094 | |
2095 | /** |
2096 | * JDBC 2.0 Update a column with an Object value. The updateXXX() methods |
2097 | * are used to update column values in the current row, or the insert row. |
2098 | * The updateXXX() methods do not update the underlying database, instead |
2099 | * the updateRow() or insertRow() methods are called to update the database. |
2100 | * |
2101 | * @param columnIndex |
2102 | * the first column is 1, the second is 2, ... |
2103 | * @param x |
2104 | * the new column value |
2105 | * |
2106 | * @exception SQLException |
2107 | * if a database-access error occurs |
2108 | */ |
2109 | public synchronized void updateObject(int columnIndex, Object x) |
2110 | throws SQLException { |
2111 | if (!this.onInsertRow) { |
2112 | if (!this.doingUpdates) { |
2113 | this.doingUpdates = true; |
2114 | syncUpdate(); |
2115 | } |
2116 | |
2117 | this.updater.setObject(columnIndex, x); |
2118 | } else { |
2119 | this.inserter.setObject(columnIndex, x); |
2120 | |
2121 | this.thisRow[columnIndex - 1] = this.inserter |
2122 | .getBytesRepresentation(columnIndex - 1); |
2123 | } |
2124 | } |
2125 | |
2126 | /** |
2127 | * JDBC 2.0 Update a column with an Object value. The updateXXX() methods |
2128 | * are used to update column values in the current row, or the insert row. |
2129 | * The updateXXX() methods do not update the underlying database, instead |
2130 | * the updateRow() or insertRow() methods are called to update the database. |
2131 | * |
2132 | * @param columnIndex |
2133 | * the first column is 1, the second is 2, ... |
2134 | * @param x |
2135 | * the new column value |
2136 | * @param scale |
2137 | * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types |
2138 | * this is the number of digits after the decimal. For all other |
2139 | * types this value will be ignored. |
2140 | * |
2141 | * @exception SQLException |
2142 | * if a database-access error occurs |
2143 | */ |
2144 | public synchronized void updateObject(int columnIndex, Object x, int scale) |
2145 | throws SQLException { |
2146 | if (!this.onInsertRow) { |
2147 | if (!this.doingUpdates) { |
2148 | this.doingUpdates = true; |
2149 | syncUpdate(); |
2150 | } |
2151 | |
2152 | this.updater.setObject(columnIndex, x); |
2153 | } else { |
2154 | this.inserter.setObject(columnIndex, x); |
2155 | |
2156 | this.thisRow[columnIndex - 1] = this.inserter |
2157 | .getBytesRepresentation(columnIndex - 1); |
2158 | } |
2159 | } |
2160 | |
2161 | /** |
2162 | * JDBC 2.0 Update a column with an Object value. The updateXXX() methods |
2163 | * are used to update column values in the current row, or the insert row. |
2164 | * The updateXXX() methods do not update the underlying database, instead |
2165 | * the updateRow() or insertRow() methods are called to update the database. |
2166 | * |
2167 | * @param columnName |
2168 | * the name of the column |
2169 | * @param x |
2170 | * the new column value |
2171 | * |
2172 | * @exception SQLException |
2173 | * if a database-access error occurs |
2174 | */ |
2175 | public synchronized void updateObject(String columnName, Object x) |
2176 | throws SQLException { |
2177 | updateObject(findColumn(columnName), x); |
2178 | } |
2179 | |
2180 | /** |
2181 | * JDBC 2.0 Update a column with an Object value. The updateXXX() methods |
2182 | * are used to update column values in the current row, or the insert row. |
2183 | * The updateXXX() methods do not update the underlying database, instead |
2184 | * the updateRow() or insertRow() methods are called to update the database. |
2185 | * |
2186 | * @param columnName |
2187 | * the name of the column |
2188 | * @param x |
2189 | * the new column value |
2190 | * @param scale |
2191 | * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types |
2192 | * this is the number of digits after the decimal. For all other |
2193 | * types this value will be ignored. |
2194 | * |
2195 | * @exception SQLException |
2196 | * if a database-access error occurs |
2197 | */ |
2198 | public synchronized void updateObject(String columnName, Object x, int scale) |
2199 | throws SQLException { |
2200 | updateObject(findColumn(columnName), x); |
2201 | } |
2202 | |
2203 | /** |
2204 | * JDBC 2.0 Update the underlying database with the new contents of the |
2205 | * current row. Cannot be called when on the insert row. |
2206 | * |
2207 | * @exception SQLException |
2208 | * if a database-access error occurs, or if called when on |
2209 | * the insert row |
2210 | * @throws NotUpdatable |
2211 | * DOCUMENT ME! |
2212 | */ |
2213 | public synchronized void updateRow() throws SQLException { |
2214 | if (!this.isUpdatable) { |
2215 | throw new NotUpdatable(); |
2216 | } |
2217 | |
2218 | if (this.doingUpdates) { |
2219 | this.updater.executeUpdate(); |
2220 | refreshRow(); |
2221 | this.doingUpdates = false; |
2222 | } |
2223 | |
2224 | // |
2225 | // fixes calling updateRow() and then doing more |
2226 | // updates on same row... |
2227 | syncUpdate(); |
2228 | } |
2229 | |
2230 | /** |
2231 | * JDBC 2.0 Update a column with a short value. The updateXXX() methods are |
2232 | * used to update column values in the current row, or the insert row. The |
2233 | * updateXXX() methods do not update the underlying database, instead the |
2234 | * updateRow() or insertRow() methods are called to update the database. |
2235 | * |
2236 | * @param columnIndex |
2237 | * the first column is 1, the second is 2, ... |
2238 | * @param x |
2239 | * the new column value |
2240 | * |
2241 | * @exception SQLException |
2242 | * if a database-access error occurs |
2243 | */ |
2244 | public synchronized void updateShort(int columnIndex, short x) |
2245 | throws SQLException { |
2246 | if (!this.onInsertRow) { |
2247 | if (!this.doingUpdates) { |
2248 | this.doingUpdates = true; |
2249 | syncUpdate(); |
2250 | } |
2251 | |
2252 | this.updater.setShort(columnIndex, x); |
2253 | } else { |
2254 | this.inserter.setShort(columnIndex, x); |
2255 | |
2256 | this.thisRow[columnIndex - 1] = this.inserter |
2257 | .getBytesRepresentation(columnIndex - 1); |
2258 | } |
2259 | } |
2260 | |
2261 | /** |
2262 | * JDBC 2.0 Update a column with a short value. The updateXXX() methods are |
2263 | * used to update column values in the current row, or the insert row. The |
2264 | * updateXXX() methods do not update the underlying database, instead the |
2265 | * updateRow() or insertRow() methods are called to update the database. |
2266 | * |
2267 | * @param columnName |
2268 | * the name of the column |
2269 | * @param x |
2270 | * the new column value |
2271 | * |
2272 | * @exception SQLException |
2273 | * if a database-access error occurs |
2274 | */ |
2275 | public synchronized void updateShort(String columnName, short x) |
2276 | throws SQLException { |
2277 | updateShort(findColumn(columnName), x); |
2278 | } |
2279 | |
2280 | /** |
2281 | * JDBC 2.0 Update a column with a String value. The updateXXX() methods are |
2282 | * used to update column values in the current row, or the insert row. The |
2283 | * updateXXX() methods do not update the underlying database, instead the |
2284 | * updateRow() or insertRow() methods are called to update the database. |
2285 | * |
2286 | * @param columnIndex |
2287 | * the first column is 1, the second is 2, ... |
2288 | * @param x |
2289 | * the new column value |
2290 | * |
2291 | * @exception SQLException |
2292 | * if a database-access error occurs |
2293 | */ |
2294 | public synchronized void updateString(int columnIndex, String x) |
2295 | throws SQLException { |
2296 | checkClosed(); |
2297 | |
2298 | if (!this.onInsertRow) { |
2299 | if (!this.doingUpdates) { |
2300 | this.doingUpdates = true; |
2301 | syncUpdate(); |
2302 | } |
2303 | |
2304 | this.updater.setString(columnIndex, x); |
2305 | } else { |
2306 | this.inserter.setString(columnIndex, x); |
2307 | |
2308 | if (x == null) { |
2309 | this.thisRow[columnIndex - 1] = null; |
2310 | } else { |
2311 | if (getCharConverter() != null) { |
2312 | this.thisRow[columnIndex - 1] = StringUtils.getBytes(x, |
2313 | this.charConverter, this.charEncoding, |
2314 | this.connection.getServerCharacterEncoding(), |
2315 | this.connection.parserKnowsUnicode()); |
2316 | } else { |
2317 | this.thisRow[columnIndex - 1] = x.getBytes(); |
2318 | } |
2319 | } |
2320 | } |
2321 | } |
2322 | |
2323 | /** |
2324 | * JDBC 2.0 Update a column with a String value. The updateXXX() methods are |
2325 | * used to update column values in the current row, or the insert row. The |
2326 | * updateXXX() methods do not update the underlying database, instead the |
2327 | * updateRow() or insertRow() methods are called to update the database. |
2328 | * |
2329 | * @param columnName |
2330 | * the name of the column |
2331 | * @param x |
2332 | * the new column value |
2333 | * |
2334 | * @exception SQLException |
2335 | * if a database-access error occurs |
2336 | */ |
2337 | public synchronized void updateString(String columnName, String x) |
2338 | throws SQLException { |
2339 | updateString(findColumn(columnName), x); |
2340 | } |
2341 | |
2342 | /** |
2343 | * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are |
2344 | * used to update column values in the current row, or the insert row. The |
2345 | * updateXXX() methods do not update the underlying database, instead the |
2346 | * updateRow() or insertRow() methods are called to update the database. |
2347 | * |
2348 | * @param columnIndex |
2349 | * the first column is 1, the second is 2, ... |
2350 | * @param x |
2351 | * the new column value |
2352 | * |
2353 | * @exception SQLException |
2354 | * if a database-access error occurs |
2355 | */ |
2356 | public synchronized void updateTime(int columnIndex, java.sql.Time x) |
2357 | throws SQLException { |
2358 | if (!this.onInsertRow) { |
2359 | if (!this.doingUpdates) { |
2360 | this.doingUpdates = true; |
2361 | syncUpdate(); |
2362 | } |
2363 | |
2364 | this.updater.setTime(columnIndex, x); |
2365 | } else { |
2366 | this.inserter.setTime(columnIndex, x); |
2367 | |
2368 | this.thisRow[columnIndex - 1] = this.inserter |
2369 | .getBytesRepresentation(columnIndex - 1); |
2370 | } |
2371 | } |
2372 | |
2373 | /** |
2374 | * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are |
2375 | * used to update column values in the current row, or the insert row. The |
2376 | * updateXXX() methods do not update the underlying database, instead the |
2377 | * updateRow() or insertRow() methods are called to update the database. |
2378 | * |
2379 | * @param columnName |
2380 | * the name of the column |
2381 | * @param x |
2382 | * the new column value |
2383 | * |
2384 | * @exception SQLException |
2385 | * if a database-access error occurs |
2386 | */ |
2387 | public synchronized void updateTime(String columnName, java.sql.Time x) |
2388 | throws SQLException { |
2389 | updateTime(findColumn(columnName), x); |
2390 | } |
2391 | |
2392 | /** |
2393 | * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods |
2394 | * are used to update column values in the current row, or the insert row. |
2395 | * The updateXXX() methods do not update the underlying database, instead |
2396 | * the updateRow() or insertRow() methods are called to update the database. |
2397 | * |
2398 | * @param columnIndex |
2399 | * the first column is 1, the second is 2, ... |
2400 | * @param x |
2401 | * the new column value |
2402 | * |
2403 | * @exception SQLException |
2404 | * if a database-access error occurs |
2405 | */ |
2406 | public synchronized void updateTimestamp(int columnIndex, |
2407 | java.sql.Timestamp x) throws SQLException { |
2408 | if (!this.onInsertRow) { |
2409 | if (!this.doingUpdates) { |
2410 | this.doingUpdates = true; |
2411 | syncUpdate(); |
2412 | } |
2413 | |
2414 | this.updater.setTimestamp(columnIndex, x); |
2415 | } else { |
2416 | this.inserter.setTimestamp(columnIndex, x); |
2417 | |
2418 | this.thisRow[columnIndex - 1] = this.inserter |
2419 | .getBytesRepresentation(columnIndex - 1); |
2420 | } |
2421 | } |
2422 | |
2423 | /** |
2424 | * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods |
2425 | * are used to update column values in the current row, or the insert row. |
2426 | * The updateXXX() methods do not update the underlying database, instead |
2427 | * the updateRow() or insertRow() methods are called to update the database. |
2428 | * |
2429 | * @param columnName |
2430 | * the name of the column |
2431 | * @param x |
2432 | * the new column value |
2433 | * |
2434 | * @exception SQLException |
2435 | * if a database-access error occurs |
2436 | */ |
2437 | public synchronized void updateTimestamp(String columnName, |
2438 | java.sql.Timestamp x) throws SQLException { |
2439 | updateTimestamp(findColumn(columnName), x); |
2440 | } |
2441 | } |