Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.mysql.cj.jdbc.result.UpdatableResultSet Maven / Gradle / Ivy
/*
* Copyright (c) 2002, 2020, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
* Free Software Foundation.
*
* This program is also distributed with certain software (including but not
* limited to OpenSSL) that is licensed under separate terms, as designated in a
* particular file or component or in included license documentation. The
* authors of MySQL hereby grant you an additional permission to link the
* program and your derivative works with the separately licensed software that
* they have included with MySQL.
*
* Without limiting anything contained in the foregoing, this file, which is
* part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
* version 1.0, a copy of which can be found at
* http://oss.oracle.com/licenses/universal-foss-exception.
*
* 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, version 2.0,
* 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.cj.jdbc.result;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.sql.Clob;
import java.sql.JDBCType;
import java.sql.NClob;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLType;
import java.sql.SQLXML;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.mysql.cj.Messages;
import com.mysql.cj.MysqlType;
import com.mysql.cj.conf.PropertyDefinitions.DatabaseTerm;
import com.mysql.cj.conf.PropertyKey;
import com.mysql.cj.exceptions.AssertionFailedException;
import com.mysql.cj.exceptions.ExceptionFactory;
import com.mysql.cj.exceptions.FeatureNotAvailableException;
import com.mysql.cj.exceptions.MysqlErrorNumbers;
import com.mysql.cj.jdbc.ClientPreparedStatement;
import com.mysql.cj.jdbc.JdbcConnection;
import com.mysql.cj.jdbc.MysqlSQLXML;
import com.mysql.cj.jdbc.ServerPreparedStatement;
import com.mysql.cj.jdbc.StatementImpl;
import com.mysql.cj.jdbc.exceptions.NotUpdatable;
import com.mysql.cj.jdbc.exceptions.SQLError;
import com.mysql.cj.log.ProfilerEvent;
import com.mysql.cj.protocol.ResultsetRow;
import com.mysql.cj.protocol.ResultsetRows;
import com.mysql.cj.protocol.a.result.ByteArrayRow;
import com.mysql.cj.result.Field;
import com.mysql.cj.result.Row;
import com.mysql.cj.util.StringUtils;
/**
* 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 **");
private String charEncoding;
/** What is the default value for the column? */
private byte[][] defaultColumnValue;
/** PreparedStatement used to delete data */
private ClientPreparedStatement deleter = null;
private String deleteSQL = null;
/** PreparedStatement used to insert data */
protected ClientPreparedStatement inserter = null;
private String insertSQL = null;
/** Is this result set updatable? */
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 ClientPreparedStatement refresher;
private String refreshSQL = null;
/** The binary data for the 'current' row */
private Row savedCurrentRow;
/** PreparedStatement used to delete data */
protected ClientPreparedStatement updater = null;
/** SQL for in-place modifcation */
private String updateSQL = null;
private boolean populateInserterWithDefaultValues = false;
private boolean pedantic;
private boolean hasLongColumnInfo = false;
private Map>> databasesUsedToTablesUsed = null;
/** Are we on the insert row? */
private boolean onInsertRow = false;
/** Are we in the middle of doing updates to the current row? */
protected boolean doingUpdates = false;
/**
* Creates a new ResultSet object.
*
* @param tuples
* actual row data
* @param conn
* the Connection that created us.
* @param creatorStmt
* statement owning this result set
*
* @throws SQLException
* if an error occurs
*/
public UpdatableResultSet(ResultsetRows tuples, JdbcConnection conn, StatementImpl creatorStmt) throws SQLException {
super(tuples, conn, creatorStmt);
checkUpdatability();
this.charEncoding = this.session.getPropertySet().getStringProperty(PropertyKey.characterEncoding).getValue();
this.populateInserterWithDefaultValues = this.getSession().getPropertySet().getBooleanProperty(PropertyKey.populateInsertRowWithDefaultValues)
.getValue();
this.pedantic = this.getSession().getPropertySet().getBooleanProperty(PropertyKey.pedantic).getValue();
this.hasLongColumnInfo = this.getSession().getServerSession().hasLongColumnInfo();
}
@Override
public boolean absolute(int row) throws SQLException {
boolean ret = super.absolute(row);
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
return ret;
}
@Override
public void afterLast() throws SQLException {
super.afterLast();
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
}
@Override
public void beforeFirst() throws SQLException {
super.beforeFirst();
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
}
@Override
public void cancelRowUpdates() throws SQLException {
if (this.doingUpdates) {
this.doingUpdates = false;
this.updater.clearParameters();
}
}
@Override
protected void checkRowPos() throws SQLException {
if (!this.onInsertRow) {
super.checkRowPos();
}
}
/**
* Is this ResultSet updatable?
*
* @throws SQLException
* if an error occurs
*/
public void checkUpdatability() throws SQLException {
try {
if (this.getMetadata() == 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 dbName = null;
int primaryKeyCount = 0;
Field[] fields = this.getMetadata().getFields();
// 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.db == null) || (this.db.length() == 0)) {
this.db = fields[0].getDatabaseName();
if ((this.db == null) || (this.db.length() == 0)) {
throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.43"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
}
if (fields.length > 0) {
singleTableName = fields[0].getOriginalTableName();
dbName = fields[0].getDatabaseName();
if (singleTableName == null) {
singleTableName = fields[0].getTableName();
dbName = this.db;
}
if (singleTableName == null) {
this.isUpdatable = false;
this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
return;
}
if (fields[0].isPrimaryKey()) {
primaryKeyCount++;
}
//
// References only one table?
//
for (int i = 1; i < fields.length; i++) {
String otherTableName = fields[i].getOriginalTableName();
String otherDbName = fields[i].getDatabaseName();
if (otherTableName == null) {
otherTableName = fields[i].getTableName();
otherDbName = this.db;
}
if (otherTableName == null) {
this.isUpdatable = false;
this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
return;
}
if (!otherTableName.equals(singleTableName)) {
this.isUpdatable = false;
this.notUpdatableReason = Messages.getString("NotUpdatableReason.0");
return;
}
// Can't reference more than one database
if ((dbName == null) || !otherDbName.equals(dbName)) {
this.isUpdatable = false;
this.notUpdatableReason = Messages.getString("NotUpdatableReason.1");
return;
}
if (fields[i].isPrimaryKey()) {
primaryKeyCount++;
}
}
} else {
this.isUpdatable = false;
this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
return;
}
if (this.getSession().getPropertySet().getBooleanProperty(PropertyKey.strictUpdates).getValue()) {
java.sql.DatabaseMetaData dbmd = this.getConnection().getMetaData();
java.sql.ResultSet rs = null;
HashMap primaryKeyNames = new HashMap<>();
try {
rs = this.session.getPropertySet().getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA
? dbmd.getPrimaryKeys(null, dbName, singleTableName)
: dbmd.getPrimaryKeys(dbName, 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 < fields.length; i++) {
if (fields[i].isPrimaryKey()) {
String columnNameUC = fields[i].getName().toUpperCase();
if (primaryKeyNames.remove(columnNameUC) == null) {
// try original name
String originalName = 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) {
this.notUpdatableReason = existingPrimaryKeysCount > 1 ? Messages.getString("NotUpdatableReason.7")
: 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();
}
}
@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 = (ClientPreparedStatement) this.connection.clientPrepareStatement(this.deleteSQL);
}
this.deleter.clearParameters();
int numKeys = this.primaryKeyIndicies.size();
for (int i = 0; i < numKeys; i++) {
int index = this.primaryKeyIndicies.get(i).intValue();
this.setParamValue(this.deleter, i + 1, this.thisRow, index, this.getMetadata().getFields()[index]);
}
this.deleter.executeUpdate();
this.rowData.remove();
prev(); // position on previous row - Bug#27431
}
}
private void setParamValue(ClientPreparedStatement ps, int psIdx, Row row, int rsIdx, Field field) throws SQLException {
byte[] val = row.getBytes(rsIdx);
if (val == null) {
ps.setNull(psIdx, MysqlType.NULL);
return;
}
switch (field.getMysqlType()) {
case NULL:
ps.setNull(psIdx, MysqlType.NULL);
break;
case TINYINT:
case TINYINT_UNSIGNED:
case SMALLINT:
case SMALLINT_UNSIGNED:
case MEDIUMINT:
case MEDIUMINT_UNSIGNED:
case INT:
case INT_UNSIGNED:
case YEAR:
ps.setInt(psIdx, getInt(rsIdx + 1));
break;
case BIGINT:
ps.setLong(psIdx, getLong(rsIdx + 1));
break;
case BIGINT_UNSIGNED:
ps.setBigInteger(psIdx, getBigInteger(rsIdx + 1));
break;
case CHAR:
case ENUM:
case SET:
case VARCHAR:
case JSON:
case TINYTEXT:
case TEXT:
case MEDIUMTEXT:
case LONGTEXT:
case DECIMAL:
case DECIMAL_UNSIGNED:
ps.setString(psIdx, getString(rsIdx + 1));
break;
case DATE:
ps.setDate(psIdx, getDate(rsIdx + 1));
break;
case TIMESTAMP:
case DATETIME:
ps.setTimestamp(psIdx, getTimestamp(rsIdx + 1), null, field.getDecimals());
break;
case TIME:
// TODO adjust nanos to decimal numbers
ps.setTime(psIdx, getTime(rsIdx + 1));
break;
case DOUBLE:
case DOUBLE_UNSIGNED:
case FLOAT:
case FLOAT_UNSIGNED:
case BOOLEAN:
case BIT:
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.getConnection().getMetaData();
this.defaultColumnValue = new byte[this.getMetadata().getFields().length][];
java.sql.ResultSet columnsResultSet = null;
for (Map.Entry>> dbEntry : this.databasesUsedToTablesUsed.entrySet()) {
for (Map.Entry> tableEntry : dbEntry.getValue().entrySet()) {
String tableName = tableEntry.getKey();
Map columnNamesToIndices = tableEntry.getValue();
try {
columnsResultSet = this.session.getPropertySet().getEnumProperty(PropertyKey.databaseTerm).getValue() == DatabaseTerm.SCHEMA
? dbmd.getColumns(null, this.db, tableName, "%")
: dbmd.getColumns(this.db, 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;
}
}
}
}
}
@Override
public boolean first() throws SQLException {
boolean ret = super.first();
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
return ret;
}
/**
* Figure out whether or not this ResultSet is updatable, and if so,
* generate the PreparedStatements to support updates.
*
* @throws SQLException
* if an error occurs
* @throws NotUpdatable
* if result set was marked as not updatable
*/
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.session.getServerSession().isLowerCaseTableNames()) {
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<>();
Field[] fields = this.getMetadata().getFields();
for (int i = 0; i < fields.length; i++) {
Map updColumnNameToIndex = null;
// FIXME: What about no table?
if (fields[i].getOriginalTableName() != null) {
String databaseName = fields[i].getDatabaseName();
String tableOnlyName = fields[i].getOriginalTableName();
String fqTableName = StringUtils.getFullyQualifiedName(databaseName, tableOnlyName, quotedId, this.pedantic);
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 = fields[i].getTableName();
if (tableOnlyName != null) {
String fqTableName = StringUtils.quoteIdentifier(tableOnlyName, quotedId, this.pedantic);
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.db, tableOnlyName);
}
}
String originalColumnName = fields[i].getOriginalName();
String columnName = this.hasLongColumnInfo && originalColumnName != null && originalColumnName.length() > 0 ? originalColumnName
: fields[i].getName();
if (updColumnNameToIndex != null && columnName != null) {
updColumnNameToIndex.put(columnName, Integer.valueOf(i));
}
String originalTableName = fields[i].getOriginalTableName();
String tableName = this.hasLongColumnInfo && originalTableName != null && originalTableName.length() > 0 ? originalTableName
: fields[i].getTableName();
String databaseName = fields[i].getDatabaseName();
String qualifiedColumnName = new StringBuilder() //
.append(StringUtils.getFullyQualifiedName(databaseName, tableName, quotedId, this.pedantic)) //
.append('.') //
.append(StringUtils.quoteIdentifier(columnName, quotedId, this.pedantic)).toString();
if (fields[i].isPrimaryKey()) {
this.primaryKeyIndicies.add(Integer.valueOf(i));
if (keyValues.length() > 0) {
keyValues.append(" AND ");
}
keyValues.append(qualifiedColumnName);
keyValues.append("<=>");
keyValues.append("?");
}
if (fieldValues.length() == 0) {
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) {
tablesUsedToColumnsMap = this.session.getServerSession().isLowerCaseTableNames() ? new TreeMap<>(String.CASE_INSENSITIVE_ORDER) : new TreeMap<>();
this.databasesUsedToTablesUsed.put(databaseName, tablesUsedToColumnsMap);
}
nameToIndex = tablesUsedToColumnsMap.get(tableName);
if (nameToIndex == null) {
nameToIndex = new HashMap<>();
tablesUsedToColumnsMap.put(tableName, nameToIndex);
}
return nameToIndex;
}
@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) {
this.quotedIdChar = this.session.getIdentifierQuoteString();
}
return this.quotedIdChar;
}
@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();
Field[] fields = this.getMetadata().getFields();
byte[][] newRow = new byte[fields.length][];
for (int i = 0; i < fields.length; i++) {
newRow[i] = this.inserter.isNull(i + 1) ? null : this.inserter.getBytesRepresentation(i + 1);
// WARN: This non-variant only holds if MySQL never allows more than one auto-increment key (which is the way it is _today_)
if (fields[i].isAutoIncrement() && autoIncrementId > 0) {
newRow[i] = StringUtils.getBytes(String.valueOf(autoIncrementId));
this.inserter.setBytesNoEscapeNoQuotes(i + 1, newRow[i]);
}
}
Row resultSetRow = new ByteArrayRow(newRow, getExceptionInterceptor());
// inserter is always a client-side prepared statement, so it's safe to use it with ByteArrayRow for server-side prepared statement too
refreshRow(this.inserter, resultSetRow);
this.rowData.addRow(resultSetRow);
resetInserter();
}
}
@Override
public boolean isAfterLast() throws SQLException {
return super.isAfterLast();
}
@Override
public boolean isBeforeFirst() throws SQLException {
return super.isBeforeFirst();
}
@Override
public boolean isFirst() throws SQLException {
return super.isFirst();
}
@Override
public boolean isLast() throws SQLException {
return super.isLast();
}
boolean isUpdatable() {
return this.isUpdatable;
}
@Override
public boolean last() throws SQLException {
boolean ret = super.last();
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
return ret;
}
@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;
}
}
}
@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 = (ClientPreparedStatement) this.getConnection().clientPrepareStatement(this.insertSQL);
this.inserter.getQueryBindings().setColumnDefinition(this.getMetadata());
if (this.populateInserterWithDefaultValues) {
extractDefaultValues();
}
}
resetInserter();
Field[] fields = this.getMetadata().getFields();
int numFields = 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.getMetadata());
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 = fields[i];
switch (f.getMysqlTypeId()) {
case MysqlType.FIELD_TYPE_DATE:
case MysqlType.FIELD_TYPE_DATETIME:
case MysqlType.FIELD_TYPE_TIME:
case MysqlType.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]);
} else {
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, MysqlType.NULL);
newRowData[i] = null;
}
}
}
}
}
@Override
public boolean next() throws SQLException {
boolean ret = super.next();
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
return ret;
}
@Override
public boolean prev() throws SQLException {
boolean ret = super.prev();
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
return ret;
}
@Override
public boolean previous() throws SQLException {
boolean ret = super.previous();
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
return ret;
}
@Override
public void realClose(boolean calledExplicitly) throws SQLException {
if (this.isClosed) {
return;
}
synchronized (checkClosed().getConnectionMutex()) {
SQLException sqlEx = null;
if (this.useUsageAdvisor) {
if ((this.deleter == null) && (this.inserter == null) && (this.refresher == null) && (this.updater == null)) {
this.eventSink.processEvent(ProfilerEvent.TYPE_USAGE, this.session, this.getOwningStatement(), this, 0, new Throwable(),
Messages.getString("UpdatableResultSet.34"));
}
}
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;
}
}
}
@Override
public void refreshRow() throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (getType() == ResultSet.TYPE_FORWARD_ONLY) {
throw ExceptionFactory.createException(Messages.getString("ResultSet.ForwardOnly"));
}
if (!this.isUpdatable) {
throw new NotUpdatable(Messages.getString("NotUpdatable.0"));
}
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(ClientPreparedStatement updateInsertStmt, Row rowToRefresh) throws SQLException {
if (this.refresher == null) {
if (this.refreshSQL == null) {
generateStatements();
}
// We're going to copy bytes from refresher results to rowToRefresh, thus we need them to have the same protocol encoding
this.refresher = ((ResultsetRow) this.thisRow).isBinaryEncoded()
? (ClientPreparedStatement) this.getConnection().serverPrepareStatement(this.refreshSQL)
: (ClientPreparedStatement) this.getConnection().clientPrepareStatement(this.refreshSQL);
this.refresher.getQueryBindings().setColumnDefinition(this.getMetadata());
}
this.refresher.clearParameters();
int numKeys = this.primaryKeyIndicies.size();
for (int i = 0; i < numKeys; i++) {
byte[] dataFrom = null;
int index = this.primaryKeyIndicies.get(i).intValue();
if (!this.doingUpdates && !this.onInsertRow) {
this.setParamValue(this.refresher, i + 1, this.thisRow, index, this.getMetadata().getFields()[index]);
continue;
}
dataFrom = updateInsertStmt.getBytesRepresentation(index + 1);
// Primary keys not set?
if (updateInsertStmt.isNull(index + 1) || (dataFrom.length == 0)) {
this.setParamValue(this.refresher, i + 1, this.thisRow, index, this.getMetadata().getFields()[index]);
continue;
}
dataFrom = StringUtils.stripEnclosure(dataFrom, "_binary'", "'");
byte[] origBytes = updateInsertStmt.getOrigBytes(i + 1);
if (origBytes != null) {
// It happens when updateInsertStmt parameter is set as a HEX literal.
// We need to avoid quotation in this case.
if (this.refresher instanceof ServerPreparedStatement) {
// Server-side prepared statement does not recognize HEX literal sent as a parameter to execute command,
// it's treated as a usual string and quoted on server side. Thus we send original bytes instead.
this.refresher.setBytesNoEscapeNoQuotes(i + 1, origBytes);
} else {
this.refresher.setBytesNoEscapeNoQuotes(i + 1, dataFrom);
}
} else {
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);
rowToRefresh.setBytes(i, (val == null) || rs.wasNull() ? null : val);
}
} else {
throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.12"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException ex) {
// ignore
}
}
}
}
@Override
public boolean relative(int rows) throws SQLException {
boolean ret = super.relative(rows);
if (this.onInsertRow) {
this.onInsertRow = false;
}
if (this.doingUpdates) {
this.doingUpdates = false;
}
return ret;
}
private void resetInserter() throws SQLException {
this.inserter.clearParameters();
for (int i = 0; i < this.getMetadata().getFields().length; i++) {
this.inserter.setNull(i + 1, 0);
}
}
@Override
public boolean rowDeleted() throws SQLException {
throw SQLError.createSQLFeatureNotSupportedException();
}
@Override
public boolean rowInserted() throws SQLException {
throw SQLError.createSQLFeatureNotSupportedException();
}
@Override
public boolean rowUpdated() throws SQLException {
throw SQLError.createSQLFeatureNotSupportedException();
}
@Override
public void setResultSetConcurrency(int concurrencyFlag) {
super.setResultSetConcurrency(concurrencyFlag);
// TODO: FIXME: Issue warning when asked for updatable result set, but result set is not updatable
//
// if ((concurrencyFlag == CONCUR_UPDATABLE) && !isUpdatable()) {
// java.sql.SQLWarning warning = new java.sql.SQLWarning(
// NotUpdatable.NOT_UPDATABLE_MESSAGE);
// }
}
/**
* Reset UPDATE prepared statement to value in current row. This_Row MUST
* point to current, valid row.
*
* @throws SQLException
* if an error occurs
*/
protected void syncUpdate() throws SQLException {
if (this.updater == null) {
if (this.updateSQL == null) {
generateStatements();
}
this.updater = (ClientPreparedStatement) this.getConnection().clientPrepareStatement(this.updateSQL);
this.updater.getQueryBindings().setColumnDefinition(this.getMetadata());
}
Field[] fields = this.getMetadata().getFields();
int numFields = fields.length;
this.updater.clearParameters();
for (int i = 0; i < numFields; i++) {
if (this.thisRow.getBytes(i) != null) {
switch (fields[i].getMysqlType()) {
case DATE:
case DATETIME:
case TIME:
case TIMESTAMP:
// TODO this is a temporary workaround until Bug#71143 "Calling ResultSet.updateRow should not set all field values in UPDATE" is fixed.
// We handle these types separately to avoid fractional seconds truncation (when sendFractionalSeconds=true)
// that happens for fields we don't touch with ResultSet.updateNN(). For those fields we should pass the value as is or,
// better don't put them into final updater statement as requested by Bug#71143.
this.updater.setString(i + 1, getString(i + 1));
break;
default:
this.updater.setObject(i + 1, getObject(i + 1), fields[i].getMysqlType());
break;
}
} else {
this.updater.setNull(i + 1, 0);
}
}
int numKeys = this.primaryKeyIndicies.size();
for (int i = 0; i < numKeys; i++) {
int idx = this.primaryKeyIndicies.get(i).intValue();
this.setParamValue(this.updater, numFields + i + 1, this.thisRow, idx, fields[idx]);
}
}
@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.updater, this.thisRow);
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();
}
}
@Override
public int getHoldability() throws SQLException {
throw SQLError.createSQLFeatureNotSupportedException();
}
@Override
public void updateAsciiStream(String columnLabel, java.io.InputStream x, int length) throws SQLException {
updateAsciiStream(findColumn(columnLabel), x, length);
}
@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.setBytes(columnIndex - 1, STREAM_DATA_MARKER);
}
}
}
@Override
public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException {
updateBigDecimal(findColumn(columnLabel), x);
}
@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);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : StringUtils.getBytes(x.toString()));
}
}
}
@Override
public void updateBinaryStream(String columnLabel, java.io.InputStream x, int length) throws SQLException {
updateBinaryStream(findColumn(columnLabel), x, length);
}
@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);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER);
}
}
}
@Override
public void updateBlob(String columnLabel, java.sql.Blob blob) throws SQLException {
updateBlob(findColumn(columnLabel), 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);
this.thisRow.setBytes(columnIndex - 1, blob == null ? null : STREAM_DATA_MARKER);
}
}
}
@Override
public void updateBoolean(String columnLabel, boolean x) throws SQLException {
updateBoolean(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateByte(String columnLabel, byte x) throws SQLException {
updateByte(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateBytes(String columnLabel, byte[] x) throws SQLException {
updateBytes(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, x);
}
}
}
@Override
public void updateCharacterStream(String columnLabel, java.io.Reader reader, int length) throws SQLException {
updateCharacterStream(findColumn(columnLabel), reader, length);
}
@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);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER);
}
}
}
@Override
public void updateClob(String columnLabel, Clob clob) throws SQLException {
updateClob(findColumn(columnLabel), 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());
}
}
}
@Override
public void updateDate(String columnLabel, java.sql.Date x) throws SQLException {
updateDate(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateDouble(String columnLabel, double x) throws SQLException {
updateDouble(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateFloat(String columnLabel, float x) throws SQLException {
updateFloat(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateInt(String columnLabel, int x) throws SQLException {
updateInt(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateLong(String columnLabel, long x) throws SQLException {
updateLong(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateNull(String columnLabel) throws SQLException {
updateNull(findColumn(columnLabel));
}
@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.setBytes(columnIndex - 1, null);
}
}
}
@Override
public void updateObject(String columnLabel, Object x) throws SQLException {
updateObject(findColumn(columnLabel), x);
}
@Override
public void updateObject(int columnIndex, Object x) throws SQLException {
updateObjectInternal(columnIndex, x, (Integer) null, 0);
}
@Override
public void updateObject(String columnLabel, Object x, int scale) throws SQLException {
updateObject(findColumn(columnLabel), x, scale);
}
@Override
public void updateObject(int columnIndex, Object x, int scale) throws SQLException {
updateObjectInternal(columnIndex, x, (Integer) 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
* column index
* @param x
* value
* @param targetType
* target type
* @param scaleOrLength
* scale or length, depending on target type
* @throws SQLException
* if an error occurs
*/
protected void updateObjectInternal(int columnIndex, Object x, Integer targetType, int scaleOrLength) throws SQLException {
try {
MysqlType targetMysqlType = targetType == null ? null : MysqlType.getByJdbcType(targetType);
updateObjectInternal(columnIndex, x, targetMysqlType, scaleOrLength);
} catch (FeatureNotAvailableException nae) {
throw SQLError.createSQLFeatureNotSupportedException(Messages.getString("Statement.UnsupportedSQLType") + JDBCType.valueOf(targetType),
MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
}
}
/**
* Internal setObject implementation.
*
* @param columnIndex
* column index
* @param x
* value
* @param targetType
* target type
* @param scaleOrLength
* scale or length, depending on target type
* @throws SQLException
* if an error occurs
*/
protected void updateObjectInternal(int columnIndex, Object x, SQLType 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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateObject(String columnLabel, Object x, SQLType targetSqlType) throws SQLException {
updateObject(findColumn(columnLabel), x, targetSqlType);
}
@Override
public void updateObject(int columnIndex, Object x, SQLType targetSqlType) throws SQLException {
updateObjectInternal(columnIndex, x, targetSqlType, 0);
}
@Override
public void updateObject(String columnLabel, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
updateObject(findColumn(columnLabel), x, targetSqlType, scaleOrLength);
}
@Override
public void updateObject(int columnIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException {
updateObjectInternal(columnIndex, x, targetSqlType, scaleOrLength);
}
@Override
public void updateShort(String columnLabel, short x) throws SQLException {
updateShort(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateString(String columnLabel, String x) throws SQLException {
updateString(findColumn(columnLabel), x);
}
@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);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : StringUtils.getBytes(x, this.charEncoding));
}
}
}
@Override
public void updateTime(String columnLabel, java.sql.Time x) throws SQLException {
updateTime(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateTimestamp(String columnLabel, java.sql.Timestamp x) throws SQLException {
updateTimestamp(findColumn(columnLabel), x);
}
@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.setBytes(columnIndex - 1, this.inserter.getBytesRepresentation(columnIndex));
}
}
}
@Override
public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException {
updateAsciiStream(findColumn(columnLabel), x);
}
@Override
public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException {
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setAsciiStream(columnIndex, x);
} else {
this.inserter.setAsciiStream(columnIndex, x);
this.thisRow.setBytes(columnIndex - 1, STREAM_DATA_MARKER);
}
}
@Override
public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException {
updateAsciiStream(findColumn(columnLabel), x, length);
}
@Override
public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException {
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.setBytes(columnIndex - 1, STREAM_DATA_MARKER);
}
}
@Override
public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException {
updateBinaryStream(findColumn(columnLabel), x);
}
@Override
public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException {
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setBinaryStream(columnIndex, x);
} else {
this.inserter.setBinaryStream(columnIndex, x);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER);
}
}
@Override
public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException {
updateBinaryStream(findColumn(columnLabel), x, length);
}
@Override
public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException {
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setBinaryStream(columnIndex, x, length);
} else {
this.inserter.setBinaryStream(columnIndex, x, length);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER);
}
}
@Override
public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException {
updateBlob(findColumn(columnLabel), inputStream);
}
@Override
public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException {
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setBlob(columnIndex, inputStream);
} else {
this.inserter.setBlob(columnIndex, inputStream);
this.thisRow.setBytes(columnIndex - 1, inputStream == null ? null : STREAM_DATA_MARKER);
}
}
@Override
public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException {
updateBlob(findColumn(columnLabel), inputStream, length);
}
@Override
public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException {
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setBlob(columnIndex, inputStream, length);
} else {
this.inserter.setBlob(columnIndex, inputStream, length);
this.thisRow.setBytes(columnIndex - 1, inputStream == null ? null : STREAM_DATA_MARKER);
}
}
@Override
public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException {
updateCharacterStream(findColumn(columnLabel), reader);
}
@Override
public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setCharacterStream(columnIndex, x);
} else {
this.inserter.setCharacterStream(columnIndex, x);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER);
}
}
@Override
public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
updateCharacterStream(findColumn(columnLabel), reader, length);
}
@Override
public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setCharacterStream(columnIndex, x, length);
} else {
this.inserter.setCharacterStream(columnIndex, x, length);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER);
}
}
@Override
public void updateClob(String columnLabel, Reader reader) throws SQLException {
updateClob(findColumn(columnLabel), reader);
}
@Override
public void updateClob(int columnIndex, Reader reader) throws SQLException {
updateCharacterStream(columnIndex, reader);
}
@Override
public void updateClob(String columnLabel, Reader reader, long length) throws SQLException {
updateClob(findColumn(columnLabel), reader, length);
}
@Override
public void updateClob(int columnIndex, Reader reader, long length) throws SQLException {
updateCharacterStream(columnIndex, reader, length);
}
@Override
public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException {
updateNCharacterStream(findColumn(columnLabel), reader);
}
@Override
public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException(Messages.getString("ResultSet.16"));
}
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setNCharacterStream(columnIndex, x);
} else {
this.inserter.setNCharacterStream(columnIndex, x);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER);
}
}
@Override
public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
updateNCharacterStream(findColumn(columnLabel), reader, length);
}
@Override
public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException(Messages.getString("ResultSet.16"));
}
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setNCharacterStream(columnIndex, x, length);
} else {
this.inserter.setNCharacterStream(columnIndex, x, length);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : STREAM_DATA_MARKER);
}
}
}
@Override
public void updateNClob(String columnLabel, Reader reader) throws SQLException {
updateNClob(findColumn(columnLabel), reader);
}
@Override
public void updateNClob(int columnIndex, Reader reader) throws SQLException {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException(Messages.getString("ResultSet.17"));
}
updateCharacterStream(columnIndex, reader);
}
@Override
public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException {
updateNClob(findColumn(columnLabel), reader, length);
}
@Override
public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException(Messages.getString("ResultSet.17"));
}
updateCharacterStream(columnIndex, reader, length);
}
@Override
public void updateNClob(String columnLabel, java.sql.NClob nClob) throws SQLException {
updateNClob(findColumn(columnLabel), nClob);
}
@Override
public void updateNClob(int columnIndex, java.sql.NClob nClob) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException(Messages.getString("ResultSet.17"));
}
if (nClob == null) {
updateNull(columnIndex);
} else {
updateNCharacterStream(columnIndex, nClob.getCharacterStream(), (int) nClob.length());
}
}
}
@Override
public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException {
updateSQLXML(findColumn(columnLabel), xmlObject);
}
@Override
public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException {
updateString(columnIndex, ((MysqlSQLXML) xmlObject).getString());
}
@Override
public void updateNString(String columnLabel, String x) throws SQLException {
updateNString(findColumn(columnLabel), x);
}
@Override
public void updateNString(int columnIndex, String x) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException(Messages.getString("ResultSet.18"));
}
if (!this.onInsertRow) {
if (!this.doingUpdates) {
this.doingUpdates = true;
syncUpdate();
}
this.updater.setNString(columnIndex, x);
} else {
this.inserter.setNString(columnIndex, x);
this.thisRow.setBytes(columnIndex - 1, x == null ? null : StringUtils.getBytes(x, fieldEncoding));
}
}
}
@Override
public Reader getNCharacterStream(String columnLabel) throws SQLException {
return getNCharacterStream(findColumn(columnLabel));
}
@Override
public Reader getNCharacterStream(int columnIndex) throws SQLException {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException(Messages.getString("ResultSet.11"));
}
return getCharacterStream(columnIndex);
}
@Override
public NClob getNClob(String columnLabel) throws SQLException {
return getNClob(findColumn(columnLabel));
}
@Override
public NClob getNClob(int columnIndex) throws SQLException {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException("Can not call getNClob() when field's charset isn't UTF-8");
}
String asString = getStringForNClob(columnIndex);
if (asString == null) {
return null;
}
return new com.mysql.cj.jdbc.NClob(asString, getExceptionInterceptor());
}
@Override
public String getNString(String columnLabel) throws SQLException {
return getNString(findColumn(columnLabel));
}
@Override
public String getNString(int columnIndex) throws SQLException {
String fieldEncoding = this.getMetadata().getFields()[columnIndex - 1].getEncoding();
if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
throw new SQLException("Can not call getNString() when field's charset isn't UTF-8");
}
return getString(columnIndex);
}
@Override
public SQLXML getSQLXML(String columnLabel) throws SQLException {
return getSQLXML(findColumn(columnLabel));
}
@Override
public SQLXML getSQLXML(int columnIndex) throws SQLException {
return new MysqlSQLXML(this, columnIndex, getExceptionInterceptor());
}
private String getStringForNClob(int columnIndex) throws SQLException {
String asString = null;
String forcedEncoding = "UTF-8";
try {
byte[] asBytes = null;
asBytes = getBytes(columnIndex);
if (asBytes != null) {
asString = new String(asBytes, forcedEncoding);
}
} catch (UnsupportedEncodingException uee) {
throw SQLError.createSQLException("Unsupported character encoding " + forcedEncoding, MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
return asString;
}
@Override
public boolean isClosed() throws SQLException {
return this.isClosed;
}
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
checkClosed();
// This works for classes that aren't actually wrapping anything
return iface.isInstance(this);
}
@Override
public T unwrap(java.lang.Class iface) throws java.sql.SQLException {
try {
// This works for classes that aren't actually wrapping anything
return iface.cast(this);
} catch (ClassCastException cce) {
throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
}
}