All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mysql.jdbc.UpdatableResultSet Maven / Gradle / Ivy

There is a newer version: 8.0.33
Show newest version
/*
  Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.

  The MySQL Connector/J is licensed under the terms of the GPLv2
  , like most MySQL Connectors.
  There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
  this software, see the FOSS License Exception
  .

  This program is free software; you can redistribute it and/or modify it under the terms
  of the GNU General Public License as published by the Free Software Foundation; version 2
  of the License.

  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along with this
  program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
  Floor, Boston, MA 02110-1301  USA

 */

package com.mysql.jdbc;

import java.math.BigDecimal;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import com.mysql.jdbc.profiler.ProfilerEvent;

/**
 * A result set that is updatable.
 */
public class UpdatableResultSet extends ResultSetImpl {
    /** Marker for 'stream' data when doing INSERT rows */
    final static byte[] STREAM_DATA_MARKER = StringUtils.getBytes("** STREAM DATA **");

    protected SingleByteCharsetConverter charConverter;

    private String charEncoding;

    /** What is the default value for the column? */
    private byte[][] defaultColumnValue;

    /** PreparedStatement used to delete data */
    private com.mysql.jdbc.PreparedStatement deleter = null;

    private String deleteSQL = null;

    private boolean initializedCharConverter = false;

    /** PreparedStatement used to insert data */
    protected com.mysql.jdbc.PreparedStatement inserter = null;

    private String insertSQL = null;

    /** Is this result set updateable? */
    private boolean isUpdatable = false;

    /** Reason the result set is not updatable */
    private String notUpdatableReason = null;

    /** List of primary keys */
    private List primaryKeyIndicies = null;

    private String qualifiedAndQuotedTableName;

    private String quotedIdChar = null;

    /** PreparedStatement used to refresh data */
    private com.mysql.jdbc.PreparedStatement refresher;

    private String refreshSQL = null;

    /** The binary data for the 'current' row */
    private ResultSetRow savedCurrentRow;

    /** PreparedStatement used to delete data */
    protected com.mysql.jdbc.PreparedStatement updater = null;

    /** SQL for in-place modifcation */
    private String updateSQL = null;

    private boolean populateInserterWithDefaultValues = false;

    private Map>> databasesUsedToTablesUsed = null;

    /**
     * Creates a new ResultSet object.
     * 
     * @param catalog
     *            the database in use when we were created
     * @param fields
     *            an array of Field objects (basically, the ResultSet MetaData)
     * @param tuples
     *            actual row data
     * @param conn
     *            the Connection that created us.
     * @param creatorStmt
     * 
     * @throws SQLException
     */
    protected UpdatableResultSet(String catalog, Field[] fields, RowData tuples, MySQLConnection conn, StatementImpl creatorStmt) throws SQLException {
        super(catalog, fields, tuples, conn, creatorStmt);
        checkUpdatability();
        this.populateInserterWithDefaultValues = this.connection.getPopulateInsertRowWithDefaultValues();
    }

    /**
     * JDBC 2.0
     * 
     * 

* Move to an absolute row number in the result set. *

* *

* If row is positive, moves to an absolute row with respect to the beginning of the result set. The first row is row 1, the second is row 2, etc. *

* *

* If row is negative, moves to an absolute row position with respect to the end of result set. For example, calling absolute(-1) positions the cursor on * the last row, absolute(-2) indicates the next-to-last row, etc. *

* *

* An attempt to position the cursor beyond the first/last row in the result set, leaves the cursor before/after the first/last row, respectively. *

* *

* Note: Calling absolute(1) is the same as calling first(). Calling absolute(-1) is the same as calling last(). *

* * @param row * * @return true if on the result set, false if off. * * @exception SQLException * if a database-access error occurs, or row is 0, or result * set type is TYPE_FORWARD_ONLY. */ @Override public boolean absolute(int row) throws SQLException { return super.absolute(row); } /** * JDBC 2.0 * *

* Moves to the end of the result set, just after the last row. Has no effect if the result set contains no rows. *

* * @exception SQLException * if a database-access error occurs, or result set type is * TYPE_FORWARD_ONLY. */ @Override public void afterLast() throws SQLException { super.afterLast(); } /** * JDBC 2.0 * *

* Moves to the front of the result set, just before the first row. Has no effect if the result set contains no rows. *

* * @exception SQLException * if a database-access error occurs, or result set type is * TYPE_FORWARD_ONLY */ @Override public void beforeFirst() throws SQLException { super.beforeFirst(); } /** * JDBC 2.0 The cancelRowUpdates() method may be called after calling an * updateXXX() method(s) and before calling updateRow() to rollback the * updates made to a row. If no updates have been made or updateRow() has * already been called, then this method has no effect. * * @exception SQLException * if a database-access error occurs, or if called when on * the insert row. */ @Override public void cancelRowUpdates() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (this.doingUpdates) { this.doingUpdates = false; this.updater.clearParameters(); } } } /* * (non-Javadoc) * * @see com.mysql.jdbc.ResultSet#checkRowPos() */ @Override protected void checkRowPos() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { super.checkRowPos(); } } } /** * Is this ResultSet updateable? * * @throws SQLException */ protected void checkUpdatability() throws SQLException { try { if (this.fields == null) { // we've been created to be populated with cached metadata, and we don't have the metadata yet, we'll be called again by // Connection.initializeResultsMetadataFromCache() when the metadata has been made available return; } String singleTableName = null; String catalogName = null; int primaryKeyCount = 0; // We can only do this if we know that there is a currently selected database, or if we're talking to a > 4.1 version of MySQL server (as it returns // database names in field info) if ((this.catalog == null) || (this.catalog.length() == 0)) { this.catalog = this.fields[0].getDatabaseName(); if ((this.catalog == null) || (this.catalog.length() == 0)) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.43"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); } } if (this.fields.length > 0) { singleTableName = this.fields[0].getOriginalTableName(); catalogName = this.fields[0].getDatabaseName(); if (singleTableName == null) { singleTableName = this.fields[0].getTableName(); catalogName = this.catalog; } if (singleTableName != null && singleTableName.length() == 0) { this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); return; } if (this.fields[0].isPrimaryKey()) { primaryKeyCount++; } // // References only one table? // for (int i = 1; i < this.fields.length; i++) { String otherTableName = this.fields[i].getOriginalTableName(); String otherCatalogName = this.fields[i].getDatabaseName(); if (otherTableName == null) { otherTableName = this.fields[i].getTableName(); otherCatalogName = this.catalog; } if (otherTableName != null && otherTableName.length() == 0) { this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); return; } if ((singleTableName == null) || !otherTableName.equals(singleTableName)) { this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.0"); return; } // Can't reference more than one database if ((catalogName == null) || !otherCatalogName.equals(catalogName)) { this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.1"); return; } if (this.fields[i].isPrimaryKey()) { primaryKeyCount++; } } if ((singleTableName == null) || (singleTableName.length() == 0)) { this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.2"); return; } } else { this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); return; } if (this.connection.getStrictUpdates()) { java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); java.sql.ResultSet rs = null; HashMap primaryKeyNames = new HashMap(); try { rs = dbmd.getPrimaryKeys(catalogName, null, singleTableName); while (rs.next()) { String keyName = rs.getString(4); keyName = keyName.toUpperCase(); primaryKeyNames.put(keyName, keyName); } } finally { if (rs != null) { try { rs.close(); } catch (Exception ex) { AssertionFailedException.shouldNotHappen(ex); } rs = null; } } int existingPrimaryKeysCount = primaryKeyNames.size(); if (existingPrimaryKeysCount == 0) { this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.5"); return; // we can't update tables w/o keys } // // Contains all primary keys? // for (int i = 0; i < this.fields.length; i++) { if (this.fields[i].isPrimaryKey()) { String columnNameUC = this.fields[i].getName().toUpperCase(); if (primaryKeyNames.remove(columnNameUC) == null) { // try original name String originalName = this.fields[i].getOriginalName(); if (originalName != null) { if (primaryKeyNames.remove(originalName.toUpperCase()) == null) { // we don't know about this key, so give up :( this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.6", new Object[] { originalName }); return; } } } } } this.isUpdatable = primaryKeyNames.isEmpty(); if (!this.isUpdatable) { if (existingPrimaryKeysCount > 1) { this.notUpdatableReason = Messages.getString("NotUpdatableReason.7"); } else { this.notUpdatableReason = Messages.getString("NotUpdatableReason.4"); } return; } } // // Must have at least one primary key // if (primaryKeyCount == 0) { this.isUpdatable = false; this.notUpdatableReason = Messages.getString("NotUpdatableReason.4"); return; } this.isUpdatable = true; this.notUpdatableReason = null; return; } catch (SQLException sqlEx) { this.isUpdatable = false; this.notUpdatableReason = sqlEx.getMessage(); } } /** * JDBC 2.0 Delete the current row from the result set and the underlying * database. Cannot be called when on the insert row. * * @exception SQLException * if a database-access error occurs, or if called when on * the insert row. * @throws SQLException * if the ResultSet is not updatable or some other error occurs */ @Override public void deleteRow() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.isUpdatable) { throw new NotUpdatable(this.notUpdatableReason); } if (this.onInsertRow) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.1"), getExceptionInterceptor()); } else if (this.rowData.size() == 0) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.2"), getExceptionInterceptor()); } else if (isBeforeFirst()) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.3"), getExceptionInterceptor()); } else if (isAfterLast()) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.4"), getExceptionInterceptor()); } if (this.deleter == null) { if (this.deleteSQL == null) { generateStatements(); } this.deleter = (PreparedStatement) this.connection.clientPrepareStatement(this.deleteSQL); } this.deleter.clearParameters(); int numKeys = this.primaryKeyIndicies.size(); if (numKeys == 1) { int index = this.primaryKeyIndicies.get(0).intValue(); this.setParamValue(this.deleter, 1, this.thisRow, index, this.fields[index].getSQLType()); } else { for (int i = 0; i < numKeys; i++) { int index = this.primaryKeyIndicies.get(i).intValue(); this.setParamValue(this.deleter, i + 1, this.thisRow, index, this.fields[index].getSQLType()); } } this.deleter.executeUpdate(); this.rowData.removeRow(this.rowData.getCurrentRowNumber()); // position on previous row - Bug#27431 previous(); } } private void setParamValue(PreparedStatement ps, int psIdx, ResultSetRow row, int rsIdx, int sqlType) throws SQLException { byte[] val = row.getColumnValue(rsIdx); if (val == null) { ps.setNull(psIdx, Types.NULL); return; } switch (sqlType) { case Types.NULL: ps.setNull(psIdx, Types.NULL); break; case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: ps.setInt(psIdx, row.getInt(rsIdx)); break; case Types.BIGINT: ps.setLong(psIdx, row.getLong(rsIdx)); break; case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.DECIMAL: case Types.NUMERIC: ps.setString(psIdx, row.getString(rsIdx, this.charEncoding, this.connection)); break; case Types.DATE: ps.setDate(psIdx, row.getDateFast(rsIdx, this.connection, this, this.fastDefaultCal), this.fastDefaultCal); break; case Types.TIMESTAMP: ps.setTimestamp(psIdx, row.getTimestampFast(rsIdx, this.fastDefaultCal, this.connection.getDefaultTimeZone(), false, this.connection, this)); break; case Types.TIME: ps.setTime(psIdx, row.getTimeFast(rsIdx, this.fastDefaultCal, this.connection.getDefaultTimeZone(), false, this.connection, this)); break; case Types.FLOAT: case Types.DOUBLE: case Types.REAL: case Types.BOOLEAN: ps.setBytesNoEscapeNoQuotes(psIdx, val); break; /* * default, but also explicitly for following types: * case Types.BINARY: * case Types.BLOB: */ default: ps.setBytes(psIdx, val); break; } } private void extractDefaultValues() throws SQLException { java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); this.defaultColumnValue = new byte[this.fields.length][]; java.sql.ResultSet columnsResultSet = null; for (Map.Entry>> dbEntry : this.databasesUsedToTablesUsed.entrySet()) { //String databaseName = dbEntry.getKey().toString(); for (Map.Entry> tableEntry : dbEntry.getValue().entrySet()) { String tableName = tableEntry.getKey(); Map columnNamesToIndices = tableEntry.getValue(); try { columnsResultSet = dbmd.getColumns(this.catalog, null, tableName, "%"); while (columnsResultSet.next()) { String columnName = columnsResultSet.getString("COLUMN_NAME"); byte[] defaultValue = columnsResultSet.getBytes("COLUMN_DEF"); if (columnNamesToIndices.containsKey(columnName)) { int localColumnIndex = columnNamesToIndices.get(columnName).intValue(); this.defaultColumnValue[localColumnIndex] = defaultValue; } // else assert? } } finally { if (columnsResultSet != null) { columnsResultSet.close(); columnsResultSet = null; } } } } } /** * JDBC 2.0 * *

* Moves to the first row in the result set. *

* * @return true if on a valid row, false if no rows in the result set. * * @exception SQLException * if a database-access error occurs, or result set type is * TYPE_FORWARD_ONLY. */ @Override public boolean first() throws SQLException { return super.first(); } /** * Figure out whether or not this ResultSet is updateable, and if so, * generate the PreparedStatements to support updates. * * @throws SQLException * @throws NotUpdatable */ protected void generateStatements() throws SQLException { if (!this.isUpdatable) { this.doingUpdates = false; this.onInsertRow = false; throw new NotUpdatable(this.notUpdatableReason); } String quotedId = getQuotedIdChar(); Map tableNamesSoFar = null; if (this.connection.lowerCaseTableNames()) { tableNamesSoFar = new TreeMap(String.CASE_INSENSITIVE_ORDER); this.databasesUsedToTablesUsed = new TreeMap>>(String.CASE_INSENSITIVE_ORDER); } else { tableNamesSoFar = new TreeMap(); this.databasesUsedToTablesUsed = new TreeMap>>(); } this.primaryKeyIndicies = new ArrayList(); StringBuilder fieldValues = new StringBuilder(); StringBuilder keyValues = new StringBuilder(); StringBuilder columnNames = new StringBuilder(); StringBuilder insertPlaceHolders = new StringBuilder(); StringBuilder allTablesBuf = new StringBuilder(); Map columnIndicesToTable = new HashMap(); boolean firstTime = true; boolean keysFirstTime = true; String equalsStr = this.connection.versionMeetsMinimum(3, 23, 0) ? "<=>" : "="; for (int i = 0; i < this.fields.length; i++) { StringBuilder tableNameBuffer = new StringBuilder(); Map updColumnNameToIndex = null; // FIXME: What about no table? if (this.fields[i].getOriginalTableName() != null) { String databaseName = this.fields[i].getDatabaseName(); if ((databaseName != null) && (databaseName.length() > 0)) { tableNameBuffer.append(quotedId); tableNameBuffer.append(databaseName); tableNameBuffer.append(quotedId); tableNameBuffer.append('.'); } String tableOnlyName = this.fields[i].getOriginalTableName(); tableNameBuffer.append(quotedId); tableNameBuffer.append(tableOnlyName); tableNameBuffer.append(quotedId); String fqTableName = tableNameBuffer.toString(); if (!tableNamesSoFar.containsKey(fqTableName)) { if (!tableNamesSoFar.isEmpty()) { allTablesBuf.append(','); } allTablesBuf.append(fqTableName); tableNamesSoFar.put(fqTableName, fqTableName); } columnIndicesToTable.put(Integer.valueOf(i), fqTableName); updColumnNameToIndex = getColumnsToIndexMapForTableAndDB(databaseName, tableOnlyName); } else { String tableOnlyName = this.fields[i].getTableName(); if (tableOnlyName != null) { tableNameBuffer.append(quotedId); tableNameBuffer.append(tableOnlyName); tableNameBuffer.append(quotedId); String fqTableName = tableNameBuffer.toString(); if (!tableNamesSoFar.containsKey(fqTableName)) { if (!tableNamesSoFar.isEmpty()) { allTablesBuf.append(','); } allTablesBuf.append(fqTableName); tableNamesSoFar.put(fqTableName, fqTableName); } columnIndicesToTable.put(Integer.valueOf(i), fqTableName); updColumnNameToIndex = getColumnsToIndexMapForTableAndDB(this.catalog, tableOnlyName); } } String originalColumnName = this.fields[i].getOriginalName(); String columnName = null; if (this.connection.getIO().hasLongColumnInfo() && (originalColumnName != null) && (originalColumnName.length() > 0)) { columnName = originalColumnName; } else { columnName = this.fields[i].getName(); } if (updColumnNameToIndex != null && columnName != null) { updColumnNameToIndex.put(columnName, Integer.valueOf(i)); } String originalTableName = this.fields[i].getOriginalTableName(); String tableName = null; if (this.connection.getIO().hasLongColumnInfo() && (originalTableName != null) && (originalTableName.length() > 0)) { tableName = originalTableName; } else { tableName = this.fields[i].getTableName(); } StringBuilder fqcnBuf = new StringBuilder(); String databaseName = this.fields[i].getDatabaseName(); if (databaseName != null && databaseName.length() > 0) { fqcnBuf.append(quotedId); fqcnBuf.append(databaseName); fqcnBuf.append(quotedId); fqcnBuf.append('.'); } fqcnBuf.append(quotedId); fqcnBuf.append(tableName); fqcnBuf.append(quotedId); fqcnBuf.append('.'); fqcnBuf.append(quotedId); fqcnBuf.append(columnName); fqcnBuf.append(quotedId); String qualifiedColumnName = fqcnBuf.toString(); if (this.fields[i].isPrimaryKey()) { this.primaryKeyIndicies.add(Integer.valueOf(i)); if (!keysFirstTime) { keyValues.append(" AND "); } else { keysFirstTime = false; } keyValues.append(qualifiedColumnName); keyValues.append(equalsStr); keyValues.append("?"); } if (firstTime) { firstTime = false; fieldValues.append("SET "); } else { fieldValues.append(","); columnNames.append(","); insertPlaceHolders.append(","); } insertPlaceHolders.append("?"); columnNames.append(qualifiedColumnName); fieldValues.append(qualifiedColumnName); fieldValues.append("=?"); } this.qualifiedAndQuotedTableName = allTablesBuf.toString(); this.updateSQL = "UPDATE " + this.qualifiedAndQuotedTableName + " " + fieldValues.toString() + " WHERE " + keyValues.toString(); this.insertSQL = "INSERT INTO " + this.qualifiedAndQuotedTableName + " (" + columnNames.toString() + ") VALUES (" + insertPlaceHolders.toString() + ")"; this.refreshSQL = "SELECT " + columnNames.toString() + " FROM " + this.qualifiedAndQuotedTableName + " WHERE " + keyValues.toString(); this.deleteSQL = "DELETE FROM " + this.qualifiedAndQuotedTableName + " WHERE " + keyValues.toString(); } private Map getColumnsToIndexMapForTableAndDB(String databaseName, String tableName) { Map nameToIndex; Map> tablesUsedToColumnsMap = this.databasesUsedToTablesUsed.get(databaseName); if (tablesUsedToColumnsMap == null) { if (this.connection.lowerCaseTableNames()) { tablesUsedToColumnsMap = new TreeMap>(String.CASE_INSENSITIVE_ORDER); } else { tablesUsedToColumnsMap = new TreeMap>(); } this.databasesUsedToTablesUsed.put(databaseName, tablesUsedToColumnsMap); } nameToIndex = tablesUsedToColumnsMap.get(tableName); if (nameToIndex == null) { nameToIndex = new HashMap(); tablesUsedToColumnsMap.put(tableName, nameToIndex); } return nameToIndex; } private SingleByteCharsetConverter getCharConverter() throws SQLException { if (!this.initializedCharConverter) { this.initializedCharConverter = true; if (this.connection.getUseUnicode()) { this.charEncoding = this.connection.getEncoding(); this.charConverter = this.connection.getCharsetConverter(this.charEncoding); } } return this.charConverter; } /** * JDBC 2.0 Return the concurrency of this result set. The concurrency used * is determined by the statement that created the result set. * * @return the concurrency type, CONCUR_READ_ONLY, etc. * * @exception SQLException * if a database-access error occurs */ @Override public int getConcurrency() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { return (this.isUpdatable ? CONCUR_UPDATABLE : CONCUR_READ_ONLY); } } private String getQuotedIdChar() throws SQLException { if (this.quotedIdChar == null) { boolean useQuotedIdentifiers = this.connection.supportsQuotedIdentifiers(); if (useQuotedIdentifiers) { java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); this.quotedIdChar = dbmd.getIdentifierQuoteString(); } else { this.quotedIdChar = ""; } } return this.quotedIdChar; } /** * JDBC 2.0 Insert the contents of the insert row into the result set and * the database. Must be on the insert row when this method is called. * * @exception SQLException * if a database-access error occurs, if called when not on * the insert row, or if all non-nullable columns in the * insert row have not been given a value */ @Override public void insertRow() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.7"), getExceptionInterceptor()); } this.inserter.executeUpdate(); long autoIncrementId = this.inserter.getLastInsertID(); int numFields = this.fields.length; byte[][] newRow = new byte[numFields][]; for (int i = 0; i < numFields; i++) { if (this.inserter.isNull(i)) { newRow[i] = null; } else { newRow[i] = this.inserter.getBytesRepresentation(i); } // // WARN: This non-variant only holds if MySQL never allows more than one auto-increment key (which is the way it is _today_) // if (this.fields[i].isAutoIncrement() && autoIncrementId > 0) { newRow[i] = StringUtils.getBytes(String.valueOf(autoIncrementId)); this.inserter.setBytesNoEscapeNoQuotes(i + 1, newRow[i]); } } ResultSetRow resultSetRow = new ByteArrayRow(newRow, getExceptionInterceptor()); refreshRow(this.inserter, resultSetRow); this.rowData.addRow(resultSetRow); resetInserter(); } } /** * JDBC 2.0 * *

* Determine if the cursor is after the last row in the result set. *

* * @return true if after the last row, false otherwise. Returns false when * the result set contains no rows. * * @exception SQLException * if a database-access error occurs. */ @Override public boolean isAfterLast() throws SQLException { return super.isAfterLast(); } /** * JDBC 2.0 * *

* Determine if the cursor is before the first row in the result set. *

* * @return true if before the first row, false otherwise. Returns false when * the result set contains no rows. * * @exception SQLException * if a database-access error occurs. */ @Override public boolean isBeforeFirst() throws SQLException { return super.isBeforeFirst(); } /** * JDBC 2.0 * *

* Determine if the cursor is on the first row of the result set. *

* * @return true if on the first row, false otherwise. * * @exception SQLException * if a database-access error occurs. */ @Override public boolean isFirst() throws SQLException { return super.isFirst(); } /** * JDBC 2.0 * *

* Determine if the cursor is on the last row of the result set. Note: Calling isLast() may be expensive since the JDBC driver might need to fetch ahead one * row in order to determine whether the current row is the last row in the result set. *

* * @return true if on the last row, false otherwise. * * @exception SQLException * if a database-access error occurs. */ @Override public boolean isLast() throws SQLException { return super.isLast(); } boolean isUpdatable() { return this.isUpdatable; } /** * JDBC 2.0 * *

* Moves to the last row in the result set. *

* * @return true if on a valid row, false if no rows in the result set. * * @exception SQLException * if a database-access error occurs, or result set type is * TYPE_FORWARD_ONLY. */ @Override public boolean last() throws SQLException { return super.last(); } /** * JDBC 2.0 Move the cursor to the remembered cursor position, usually the * current row. Has no effect unless the cursor is on the insert row. * * @exception SQLException * if a database-access error occurs, or the result set is * not updatable * @throws SQLException * if the ResultSet is not updatable or some other error occurs */ @Override public void moveToCurrentRow() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.isUpdatable) { throw new NotUpdatable(this.notUpdatableReason); } if (this.onInsertRow) { this.onInsertRow = false; this.thisRow = this.savedCurrentRow; } } } /** * JDBC 2.0 Move to the insert row. The current cursor position is * remembered while the cursor is positioned on the insert row. The insert * row is a special row associated with an updatable result set. It is * essentially a buffer where a new row may be constructed by calling the * updateXXX() methods prior to inserting the row into the result set. Only * the updateXXX(), getXXX(), and insertRow() methods may be called when the * cursor is on the insert row. All of the columns in a result set must be * given a value each time this method is called before calling insertRow(). * UpdateXXX()must be called before getXXX() on a column. * * @exception SQLException * if a database-access error occurs, or the result set is * not updatable * @throws NotUpdatable */ @Override public void moveToInsertRow() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.isUpdatable) { throw new NotUpdatable(this.notUpdatableReason); } if (this.inserter == null) { if (this.insertSQL == null) { generateStatements(); } this.inserter = (PreparedStatement) this.connection.clientPrepareStatement(this.insertSQL); if (this.populateInserterWithDefaultValues) { extractDefaultValues(); } resetInserter(); } else { resetInserter(); } int numFields = this.fields.length; this.onInsertRow = true; this.doingUpdates = false; this.savedCurrentRow = this.thisRow; byte[][] newRowData = new byte[numFields][]; this.thisRow = new ByteArrayRow(newRowData, getExceptionInterceptor()); this.thisRow.setMetadata(this.fields); for (int i = 0; i < numFields; i++) { if (!this.populateInserterWithDefaultValues) { this.inserter.setBytesNoEscapeNoQuotes(i + 1, StringUtils.getBytes("DEFAULT")); newRowData = null; } else { if (this.defaultColumnValue[i] != null) { Field f = this.fields[i]; switch (f.getMysqlType()) { case MysqlDefs.FIELD_TYPE_DATE: case MysqlDefs.FIELD_TYPE_DATETIME: case MysqlDefs.FIELD_TYPE_NEWDATE: case MysqlDefs.FIELD_TYPE_TIME: case MysqlDefs.FIELD_TYPE_TIMESTAMP: if (this.defaultColumnValue[i].length > 7 && this.defaultColumnValue[i][0] == (byte) 'C' && this.defaultColumnValue[i][1] == (byte) 'U' && this.defaultColumnValue[i][2] == (byte) 'R' && this.defaultColumnValue[i][3] == (byte) 'R' && this.defaultColumnValue[i][4] == (byte) 'E' && this.defaultColumnValue[i][5] == (byte) 'N' && this.defaultColumnValue[i][6] == (byte) 'T' && this.defaultColumnValue[i][7] == (byte) '_') { this.inserter.setBytesNoEscapeNoQuotes(i + 1, this.defaultColumnValue[i]); break; } this.inserter.setBytes(i + 1, this.defaultColumnValue[i], false, false); break; default: this.inserter.setBytes(i + 1, this.defaultColumnValue[i], false, false); } // This value _could_ be changed from a getBytes(), so we need a copy.... byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length]; System.arraycopy(this.defaultColumnValue[i], 0, defaultValueCopy, 0, defaultValueCopy.length); newRowData[i] = defaultValueCopy; } else { this.inserter.setNull(i + 1, java.sql.Types.NULL); newRowData[i] = null; } } } } } // --------------------------------------------------------------------- // Updates // --------------------------------------------------------------------- /** * A ResultSet is initially positioned before its first row, the first call * to next makes the first row the current row; the second call makes the * second row the current row, etc. * *

* If an input stream from the previous row is open, it is implicitly closed. The ResultSet's warning chain is cleared when a new row is read *

* * @return true if the new current is valid; false if there are no more rows * * @exception SQLException * if a database access error occurs */ @Override public boolean next() throws SQLException { return super.next(); } /** * The prev method is not part of JDBC, but because of the architecture of * this driver it is possible to move both forward and backward within the * result set. * *

* If an input stream from the previous row is open, it is implicitly closed. The ResultSet's warning chain is cleared when a new row is read *

* * @return true if the new current is valid; false if there are no more rows * * @exception SQLException * if a database access error occurs */ @Override public boolean prev() throws SQLException { return super.prev(); } /** * JDBC 2.0 * *

* Moves to the previous row in the result set. *

* *

* Note: previous() is not the same as relative(-1) since it makes sense to call previous() when there is no current row. *

* * @return true if on a valid row, false if off the result set. * * @exception SQLException * if a database-access error occurs, or result set type is * TYPE_FORWAR_DONLY. */ @Override public boolean previous() throws SQLException { return super.previous(); } /** * Closes this ResultSet and releases resources. * * @param calledExplicitly * was realClose called by the standard ResultSet.close() method, or was it closed internally by the * driver? * * @throws SQLException * if an error occurs. */ @Override public void realClose(boolean calledExplicitly) throws SQLException { MySQLConnection locallyScopedConn = this.connection; if (locallyScopedConn == null) { return; // already closed } synchronized (checkClosed().getConnectionMutex()) { SQLException sqlEx = null; if (this.useUsageAdvisor) { if ((this.deleter == null) && (this.inserter == null) && (this.refresher == null) && (this.updater == null)) { this.eventSink = ProfilerEventHandlerFactory.getInstance(this.connection); String message = Messages.getString("UpdatableResultSet.34"); this.eventSink.consumeEvent( new ProfilerEvent(ProfilerEvent.TYPE_WARN, "", (this.owningStatement == null) ? "N/A" : this.owningStatement.currentCatalog, this.connectionId, (this.owningStatement == null) ? (-1) : this.owningStatement.getId(), this.resultId, System.currentTimeMillis(), 0, Constants.MILLIS_I18N, null, this.pointOfOrigin, message)); } } try { if (this.deleter != null) { this.deleter.close(); } } catch (SQLException ex) { sqlEx = ex; } try { if (this.inserter != null) { this.inserter.close(); } } catch (SQLException ex) { sqlEx = ex; } try { if (this.refresher != null) { this.refresher.close(); } } catch (SQLException ex) { sqlEx = ex; } try { if (this.updater != null) { this.updater.close(); } } catch (SQLException ex) { sqlEx = ex; } super.realClose(calledExplicitly); if (sqlEx != null) { throw sqlEx; } } } /** * JDBC 2.0 Refresh the value of the current row with its current value in * the database. Cannot be called when on the insert row. The refreshRow() * method provides a way for an application to explicitly tell the JDBC * driver to refetch a row(s) from the database. An application may want to * call refreshRow() when caching or prefetching is being done by the JDBC * driver to fetch the latest value of a row from the database. The JDBC * driver may actually refresh multiple rows at once if the fetch size is * greater than one. All values are refetched subject to the transaction * isolation level and cursor sensitivity. If refreshRow() is called after * calling updateXXX(), but before calling updateRow() then the updates made * to the row are lost. Calling refreshRow() frequently will likely slow * performance. * * @exception SQLException * if a database-access error occurs, or if called when on * the insert row. * @throws NotUpdatable */ @Override public void refreshRow() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.isUpdatable) { throw new NotUpdatable(); } if (this.onInsertRow) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.8"), getExceptionInterceptor()); } else if (this.rowData.size() == 0) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.9"), getExceptionInterceptor()); } else if (isBeforeFirst()) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.10"), getExceptionInterceptor()); } else if (isAfterLast()) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.11"), getExceptionInterceptor()); } refreshRow(this.updater, this.thisRow); } } private void refreshRow(PreparedStatement updateInsertStmt, ResultSetRow rowToRefresh) throws SQLException { if (this.refresher == null) { if (this.refreshSQL == null) { generateStatements(); } this.refresher = (PreparedStatement) this.connection.clientPrepareStatement(this.refreshSQL); } this.refresher.clearParameters(); int numKeys = this.primaryKeyIndicies.size(); if (numKeys == 1) { byte[] dataFrom = null; int index = this.primaryKeyIndicies.get(0).intValue(); if (!this.doingUpdates && !this.onInsertRow) { dataFrom = rowToRefresh.getColumnValue(index); } else { dataFrom = updateInsertStmt.getBytesRepresentation(index); // Primary keys not set? if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) { dataFrom = rowToRefresh.getColumnValue(index); } else { dataFrom = stripBinaryPrefix(dataFrom); } } if (this.fields[index].getvalueNeedsQuoting()) { this.refresher.setBytesNoEscape(1, dataFrom); } else { this.refresher.setBytesNoEscapeNoQuotes(1, dataFrom); } } else { for (int i = 0; i < numKeys; i++) { byte[] dataFrom = null; int index = this.primaryKeyIndicies.get(i).intValue(); if (!this.doingUpdates && !this.onInsertRow) { dataFrom = rowToRefresh.getColumnValue(index); } else { dataFrom = updateInsertStmt.getBytesRepresentation(index); // Primary keys not set? if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) { dataFrom = rowToRefresh.getColumnValue(index); } else { dataFrom = stripBinaryPrefix(dataFrom); } } this.refresher.setBytesNoEscape(i + 1, dataFrom); } } java.sql.ResultSet rs = null; try { rs = this.refresher.executeQuery(); int numCols = rs.getMetaData().getColumnCount(); if (rs.next()) { for (int i = 0; i < numCols; i++) { byte[] val = rs.getBytes(i + 1); if ((val == null) || rs.wasNull()) { rowToRefresh.setColumnValue(i, null); } else { rowToRefresh.setColumnValue(i, rs.getBytes(i + 1)); } } } else { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.12"), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor()); } } finally { if (rs != null) { try { rs.close(); } catch (SQLException ex) { // ignore } } } } /** * JDBC 2.0 * *

* Moves a relative number of rows, either positive or negative. Attempting to move beyond the first/last row in the result set positions the cursor * before/after the the first/last row. Calling relative(0) is valid, but does not change the cursor position. *

* *

* Note: Calling relative(1) is different than calling next() since is makes sense to call next() when there is no current row, for example, when the cursor * is positioned before the first row or after the last row of the result set. *

* * @param rows * * @return true if on a row, false otherwise. * * @exception SQLException * if a database-access error occurs, or there is no current * row, or result set type is TYPE_FORWARD_ONLY. */ @Override public boolean relative(int rows) throws SQLException { return super.relative(rows); } private void resetInserter() throws SQLException { this.inserter.clearParameters(); for (int i = 0; i < this.fields.length; i++) { this.inserter.setNull(i + 1, 0); } } /** * JDBC 2.0 Determine if this row has been deleted. A deleted row may leave * a visible "hole" in a result set. This method can be used to detect holes * in a result set. The value returned depends on whether or not the result * set can detect deletions. * * @return true if deleted and deletes are detected * * @exception SQLException * if a database-access error occurs * @throws NotImplemented * * @see DatabaseMetaData#deletesAreDetected */ @Override public boolean rowDeleted() throws SQLException { throw SQLError.createSQLFeatureNotSupportedException(); } /** * JDBC 2.0 Determine if the current row has been inserted. The value * returned depends on whether or not the result set can detect visible * inserts. * * @return true if inserted and inserts are detected * * @exception SQLException * if a database-access error occurs * @throws NotImplemented * * @see DatabaseMetaData#insertsAreDetected */ @Override public boolean rowInserted() throws SQLException { throw SQLError.createSQLFeatureNotSupportedException(); } /** * JDBC 2.0 Determine if the current row has been updated. The value * returned depends on whether or not the result set can detect updates. * * @return true if the row has been visibly updated by the owner or another, * and updates are detected * * @exception SQLException * if a database-access error occurs * @throws NotImplemented * * @see DatabaseMetaData#updatesAreDetected */ @Override public boolean rowUpdated() throws SQLException { throw SQLError.createSQLFeatureNotSupportedException(); } /** * Sets the concurrency type of this result set * * @param concurrencyFlag * the type of concurrency that this ResultSet should support. */ @Override protected void setResultSetConcurrency(int concurrencyFlag) { super.setResultSetConcurrency(concurrencyFlag); // // FIXME: Issue warning when asked for updateable result set, but result // set is not // updatable // // if ((concurrencyFlag == CONCUR_UPDATABLE) && !isUpdatable()) { // java.sql.SQLWarning warning = new java.sql.SQLWarning( // NotUpdatable.NOT_UPDATEABLE_MESSAGE); // } } private byte[] stripBinaryPrefix(byte[] dataFrom) { return StringUtils.stripEnclosure(dataFrom, "_binary'", "'"); } /** * Reset UPDATE prepared statement to value in current row. This_Row MUST * point to current, valid row. * * @throws SQLException */ protected void syncUpdate() throws SQLException { if (this.updater == null) { if (this.updateSQL == null) { generateStatements(); } this.updater = (PreparedStatement) this.connection.clientPrepareStatement(this.updateSQL); } int numFields = this.fields.length; this.updater.clearParameters(); for (int i = 0; i < numFields; i++) { if (this.thisRow.getColumnValue(i) != null) { if (this.fields[i].getvalueNeedsQuoting()) { this.updater.setBytes(i + 1, this.thisRow.getColumnValue(i), this.fields[i].isBinary(), false); } else { this.updater.setBytesNoEscapeNoQuotes(i + 1, this.thisRow.getColumnValue(i)); } } else { this.updater.setNull(i + 1, 0); } } int numKeys = this.primaryKeyIndicies.size(); if (numKeys == 1) { int index = this.primaryKeyIndicies.get(0).intValue(); this.setParamValue(this.updater, numFields + 1, this.thisRow, index, this.fields[index].getSQLType()); } else { for (int i = 0; i < numKeys; i++) { int idx = this.primaryKeyIndicies.get(i).intValue(); this.setParamValue(this.updater, numFields + i + 1, this.thisRow, idx, this.fields[idx].getSQLType()); } } } /** * JDBC 2.0 Update a column with an ascii stream value. The updateXXX() * methods are used to update column values in the current row, or the * insert row. The updateXXX() methods do not update the underlying * database, instead the updateRow() or insertRow() methods are called to * update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * @param length * the length of the stream * * @exception SQLException * if a database-access error occurs */ @Override public void updateAsciiStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setAsciiStream(columnIndex, x, length); } else { this.inserter.setAsciiStream(columnIndex, x, length); this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); } } } /** * JDBC 2.0 Update a column with an ascii stream value. The updateXXX() * methods are used to update column values in the current row, or the * insert row. The updateXXX() methods do not update the underlying * database, instead the updateRow() or insertRow() methods are called to * update the database. * * @param columnName * the name of the column * @param x * the new column value * @param length * of the stream * * @exception SQLException * if a database-access error occurs */ @Override public void updateAsciiStream(String columnName, java.io.InputStream x, int length) throws SQLException { updateAsciiStream(findColumn(columnName), x, length); } /** * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setBigDecimal(columnIndex, x); } else { this.inserter.setBigDecimal(columnIndex, x); if (x == null) { this.thisRow.setColumnValue(columnIndex - 1, null); } else { this.thisRow.setColumnValue(columnIndex - 1, StringUtils.getBytes(x.toString())); } } } } /** * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { updateBigDecimal(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a binary stream value. The updateXXX() * methods are used to update column values in the current row, or the * insert row. The updateXXX() methods do not update the underlying * database, instead the updateRow() or insertRow() methods are called to * update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * @param length * the length of the stream * * @exception SQLException * if a database-access error occurs */ @Override public void updateBinaryStream(int columnIndex, java.io.InputStream x, int length) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setBinaryStream(columnIndex, x, length); } else { this.inserter.setBinaryStream(columnIndex, x, length); if (x == null) { this.thisRow.setColumnValue(columnIndex - 1, null); } else { this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); } } } } /** * JDBC 2.0 Update a column with a binary stream value. The updateXXX() * methods are used to update column values in the current row, or the * insert row. The updateXXX() methods do not update the underlying * database, instead the updateRow() or insertRow() methods are called to * update the database. * * @param columnName * the name of the column * @param x * the new column value * @param length * of the stream * * @exception SQLException * if a database-access error occurs */ @Override public void updateBinaryStream(String columnName, java.io.InputStream x, int length) throws SQLException { updateBinaryStream(findColumn(columnName), x, length); } /** * @see ResultSetInternalMethods#updateBlob(int, Blob) */ @Override public void updateBlob(int columnIndex, java.sql.Blob blob) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setBlob(columnIndex, blob); } else { this.inserter.setBlob(columnIndex, blob); if (blob == null) { this.thisRow.setColumnValue(columnIndex - 1, null); } else { this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); } } } } /** * @see ResultSetInternalMethods#updateBlob(String, Blob) */ @Override public void updateBlob(String columnName, java.sql.Blob blob) throws SQLException { updateBlob(findColumn(columnName), blob); } /** * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateBoolean(int columnIndex, boolean x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setBoolean(columnIndex, x); } else { this.inserter.setBoolean(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateBoolean(String columnName, boolean x) throws SQLException { updateBoolean(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateByte(int columnIndex, byte x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setByte(columnIndex, x); } else { this.inserter.setByte(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateByte(String columnName, byte x) throws SQLException { updateByte(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateBytes(int columnIndex, byte[] x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setBytes(columnIndex, x); } else { this.inserter.setBytes(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, x); } } } /** * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateBytes(String columnName, byte[] x) throws SQLException { updateBytes(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a character stream value. The updateXXX() * methods are used to update column values in the current row, or the * insert row. The updateXXX() methods do not update the underlying * database, instead the updateRow() or insertRow() methods are called to * update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * @param length * the length of the stream * * @exception SQLException * if a database-access error occurs */ @Override public void updateCharacterStream(int columnIndex, java.io.Reader x, int length) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setCharacterStream(columnIndex, x, length); } else { this.inserter.setCharacterStream(columnIndex, x, length); if (x == null) { this.thisRow.setColumnValue(columnIndex - 1, null); } else { this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER); } } } } /** * JDBC 2.0 Update a column with a character stream value. The updateXXX() * methods are used to update column values in the current row, or the * insert row. The updateXXX() methods do not update the underlying * database, instead the updateRow() or insertRow() methods are called to * update the database. * * @param columnName * the name of the column * @param reader * the new column value * @param length * of the stream * * @exception SQLException * if a database-access error occurs */ @Override public void updateCharacterStream(String columnName, java.io.Reader reader, int length) throws SQLException { updateCharacterStream(findColumn(columnName), reader, length); } /** * @see ResultSetInternalMethods#updateClob(int, Clob) */ @Override public void updateClob(int columnIndex, java.sql.Clob clob) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (clob == null) { updateNull(columnIndex); } else { updateCharacterStream(columnIndex, clob.getCharacterStream(), (int) clob.length()); } } } /** * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateDate(int columnIndex, java.sql.Date x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setDate(columnIndex, x); } else { this.inserter.setDate(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateDate(String columnName, java.sql.Date x) throws SQLException { updateDate(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a Double value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateDouble(int columnIndex, double x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setDouble(columnIndex, x); } else { this.inserter.setDouble(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a double value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateDouble(String columnName, double x) throws SQLException { updateDouble(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a float value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateFloat(int columnIndex, float x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setFloat(columnIndex, x); } else { this.inserter.setFloat(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a float value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateFloat(String columnName, float x) throws SQLException { updateFloat(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with an integer value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateInt(int columnIndex, int x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setInt(columnIndex, x); } else { this.inserter.setInt(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with an integer value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateInt(String columnName, int x) throws SQLException { updateInt(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a long value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateLong(int columnIndex, long x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setLong(columnIndex, x); } else { this.inserter.setLong(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a long value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateLong(String columnName, long x) throws SQLException { updateLong(findColumn(columnName), x); } /** * JDBC 2.0 Give a nullable column a null value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * * @exception SQLException * if a database-access error occurs */ @Override public void updateNull(int columnIndex) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setNull(columnIndex, 0); } else { this.inserter.setNull(columnIndex, 0); this.thisRow.setColumnValue(columnIndex - 1, null); } } } /** * JDBC 2.0 Update a column with a null value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * * @exception SQLException * if a database-access error occurs */ @Override public void updateNull(String columnName) throws SQLException { updateNull(findColumn(columnName)); } /** * JDBC 2.0 Update a column with an Object value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateObject(int columnIndex, Object x) throws SQLException { updateObjectInternal(columnIndex, x, null, 0); } /** * JDBC 2.0 Update a column with an Object value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * @param scale * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types * this is the number of digits after the decimal. For all other * types this value will be ignored. * * @exception SQLException * if a database-access error occurs */ @Override public void updateObject(int columnIndex, Object x, int scale) throws SQLException { updateObjectInternal(columnIndex, x, null, scale); } /** * Internal setObject implementation. Although targetType is not part of default ResultSet methods signatures, it is used for type conversions from * JDBC42UpdatableResultSet new JDBC 4.2 updateObject() methods. * * @param columnIndex * @param x * @param targetType * @param scaleOrLength * @throws SQLException */ protected void updateObjectInternal(int columnIndex, Object x, Integer targetType, int scaleOrLength) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } if (targetType == null) { this.updater.setObject(columnIndex, x); } else { this.updater.setObject(columnIndex, x, targetType); } } else { if (targetType == null) { this.inserter.setObject(columnIndex, x); } else { this.inserter.setObject(columnIndex, x, targetType); } this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with an Object value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateObject(String columnName, Object x) throws SQLException { updateObject(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with an Object value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * @param scale * For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types * this is the number of digits after the decimal. For all other * types this value will be ignored. * * @exception SQLException * if a database-access error occurs */ @Override public void updateObject(String columnName, Object x, int scale) throws SQLException { updateObject(findColumn(columnName), x); } /** * JDBC 2.0 Update the underlying database with the new contents of the * current row. Cannot be called when on the insert row. * * @exception SQLException * if a database-access error occurs, or if called when on * the insert row * @throws NotUpdatable */ @Override public void updateRow() throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.isUpdatable) { throw new NotUpdatable(this.notUpdatableReason); } if (this.doingUpdates) { this.updater.executeUpdate(); refreshRow(); this.doingUpdates = false; } else if (this.onInsertRow) { throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.44"), getExceptionInterceptor()); } // // fixes calling updateRow() and then doing more // updates on same row... syncUpdate(); } } /** * JDBC 2.0 Update a column with a short value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateShort(int columnIndex, short x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setShort(columnIndex, x); } else { this.inserter.setShort(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a short value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateShort(String columnName, short x) throws SQLException { updateShort(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a String value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateString(int columnIndex, String x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setString(columnIndex, x); } else { this.inserter.setString(columnIndex, x); if (x == null) { this.thisRow.setColumnValue(columnIndex - 1, null); } else { if (getCharConverter() != null) { this.thisRow.setColumnValue(columnIndex - 1, StringUtils.getBytes(x, this.charConverter, this.charEncoding, this.connection.getServerCharset(), this.connection.parserKnowsUnicode(), getExceptionInterceptor())); } else { this.thisRow.setColumnValue(columnIndex - 1, StringUtils.getBytes(x)); } } } } } /** * JDBC 2.0 Update a column with a String value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateString(String columnName, String x) throws SQLException { updateString(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateTime(int columnIndex, java.sql.Time x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setTime(columnIndex, x); } else { this.inserter.setTime(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are * used to update column values in the current row, or the insert row. The * updateXXX() methods do not update the underlying database, instead the * updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateTime(String columnName, java.sql.Time x) throws SQLException { updateTime(findColumn(columnName), x); } /** * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnIndex * the first column is 1, the second is 2, ... * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateTimestamp(int columnIndex, java.sql.Timestamp x) throws SQLException { synchronized (checkClosed().getConnectionMutex()) { if (!this.onInsertRow) { if (!this.doingUpdates) { this.doingUpdates = true; syncUpdate(); } this.updater.setTimestamp(columnIndex, x); } else { this.inserter.setTimestamp(columnIndex, x); this.thisRow.setColumnValue(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex - 1)); } } } /** * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods * are used to update column values in the current row, or the insert row. * The updateXXX() methods do not update the underlying database, instead * the updateRow() or insertRow() methods are called to update the database. * * @param columnName * the name of the column * @param x * the new column value * * @exception SQLException * if a database-access error occurs */ @Override public void updateTimestamp(String columnName, java.sql.Timestamp x) throws SQLException { updateTimestamp(findColumn(columnName), x); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy