/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.engine.SessionInterface;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.message.TraceObject;
import org.h2.result.ResultInterface;
import org.h2.result.ResultWithGeneratedKeys;
import org.h2.result.SimpleResult;
import org.h2.util.ParserUtil;
import org.h2.util.StringUtils;
import org.h2.util.Utils;

Represents a statement.
/** * Represents a statement. */
public class JdbcStatement extends TraceObject implements Statement, JdbcStatementBackwardsCompat { protected JdbcConnection conn; protected SessionInterface session; protected JdbcResultSet resultSet; protected int maxRows; protected int fetchSize = SysProperties.SERVER_RESULT_SET_FETCH_SIZE; protected int updateCount; protected JdbcResultSet generatedKeys; protected final int resultSetType; protected final int resultSetConcurrency; protected final boolean closedByResultSet; private volatile CommandInterface executingCommand; private int lastExecutedCommandType; private ArrayList<String> batchCommands; private boolean escapeProcessing = true; private volatile boolean cancelled; JdbcStatement(JdbcConnection conn, int id, int resultSetType, int resultSetConcurrency, boolean closeWithResultSet) { this.conn = conn; this.session = conn.getSession(); setTrace(session.getTrace(), TraceObject.STATEMENT, id); this.resultSetType = resultSetType; this.resultSetConcurrency = resultSetConcurrency; this.closedByResultSet = closeWithResultSet; }
Executes a query (select statement) and returns the result set. If another result set exists for this statement, this will be closed (even if this statement fails).
Params:
  • sql – the SQL statement to execute
Returns:the result set
/** * Executes a query (select statement) and returns the result set. * If another result set exists for this statement, this will be closed * (even if this statement fails). * * @param sql the SQL statement to execute * @return the result set */
@Override public ResultSet executeQuery(String sql) throws SQLException { try { int id = getNextId(TraceObject.RESULT_SET); if (isDebugEnabled()) { debugCodeAssign("ResultSet", TraceObject.RESULT_SET, id, "executeQuery(" + quote(sql) + ")"); } synchronized (session) { checkClosed(); closeOldResultSet(); sql = JdbcConnection.translateSQL(sql, escapeProcessing); CommandInterface command = conn.prepareCommand(sql, fetchSize); ResultInterface result; boolean lazy = false; boolean scrollable = resultSetType != ResultSet.TYPE_FORWARD_ONLY; boolean updatable = resultSetConcurrency == ResultSet.CONCUR_UPDATABLE; setExecutingStatement(command); try { result = command.executeQuery(maxRows, scrollable); lazy = result.isLazy(); } finally { if (!lazy) { setExecutingStatement(null); } } if (!lazy) { command.close(); } resultSet = new JdbcResultSet(conn, this, command, result, id, closedByResultSet, scrollable, updatable); } return resultSet; } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement (insert, update, delete, create, drop) and returns the update count. If another result set exists for this statement, this will be closed (even if this statement fails). If auto commit is on, this statement will be committed. If the statement is a DDL statement (create, drop, alter) and does not throw an exception, the current transaction (if any) is committed after executing the statement.
Params:
  • sql – the SQL statement
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement (insert, update, delete, create, drop) * and returns the update count. * If another result set exists for this statement, this will be closed * (even if this statement fails). * * If auto commit is on, this statement will be committed. * If the statement is a DDL statement (create, drop, alter) and does not * throw an exception, the current transaction (if any) is committed after * executing the statement. * * @param sql the SQL statement * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public int executeUpdate(String sql) throws SQLException { try { debugCodeCall("executeUpdate", sql); return executeUpdateInternal(sql, false); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement (insert, update, delete, create, drop) and returns the update count. If another result set exists for this statement, this will be closed (even if this statement fails). If auto commit is on, this statement will be committed. If the statement is a DDL statement (create, drop, alter) and does not throw an exception, the current transaction (if any) is committed after executing the statement.
Params:
  • sql – the SQL statement
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement (insert, update, delete, create, drop) * and returns the update count. * If another result set exists for this statement, this will be closed * (even if this statement fails). * * If auto commit is on, this statement will be committed. * If the statement is a DDL statement (create, drop, alter) and does not * throw an exception, the current transaction (if any) is committed after * executing the statement. * * @param sql the SQL statement * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public long executeLargeUpdate(String sql) throws SQLException { try { debugCodeCall("executeLargeUpdate", sql); return executeUpdateInternal(sql, false); } catch (Exception e) { throw logAndConvert(e); } } private int executeUpdateInternal(String sql, Object generatedKeysRequest) throws SQLException { checkClosedForWrite(); try { closeOldResultSet(); sql = JdbcConnection.translateSQL(sql, escapeProcessing); CommandInterface command = conn.prepareCommand(sql, fetchSize); synchronized (session) { setExecutingStatement(command); try { ResultWithGeneratedKeys result = command.executeUpdate( conn.scopeGeneratedKeys() ? false : generatedKeysRequest); updateCount = result.getUpdateCount(); ResultInterface gk = result.getGeneratedKeys(); if (gk != null) { int id = getNextId(TraceObject.RESULT_SET); generatedKeys = new JdbcResultSet(conn, this, command, gk, id, false, true, false); } } finally { setExecutingStatement(null); } } command.close(); return updateCount; } finally { afterWriting(); } }
Executes an arbitrary statement. If another result set exists for this statement, this will be closed (even if this statement fails). If the statement is a create or drop and does not throw an exception, the current transaction (if any) is committed after executing the statement. If auto commit is on, and the statement is not a select, this statement will be committed.
Params:
  • sql – the SQL statement to execute
Returns:true if a result set is available, false if not
/** * Executes an arbitrary statement. If another result set exists for this * statement, this will be closed (even if this statement fails). * * If the statement is a create or drop and does not throw an exception, the * current transaction (if any) is committed after executing the statement. * If auto commit is on, and the statement is not a select, this statement * will be committed. * * @param sql the SQL statement to execute * @return true if a result set is available, false if not */
@Override public boolean execute(String sql) throws SQLException { try { debugCodeCall("execute", sql); return executeInternal(sql, false); } catch (Exception e) { throw logAndConvert(e); } } private boolean executeInternal(String sql, Object generatedKeysRequest) throws SQLException { int id = getNextId(TraceObject.RESULT_SET); checkClosedForWrite(); try { closeOldResultSet(); sql = JdbcConnection.translateSQL(sql, escapeProcessing); CommandInterface command = conn.prepareCommand(sql, fetchSize); boolean lazy = false; boolean returnsResultSet; synchronized (session) { setExecutingStatement(command); try { if (command.isQuery()) { returnsResultSet = true; boolean scrollable = resultSetType != ResultSet.TYPE_FORWARD_ONLY; boolean updatable = resultSetConcurrency == ResultSet.CONCUR_UPDATABLE; ResultInterface result = command.executeQuery(maxRows, scrollable); lazy = result.isLazy(); resultSet = new JdbcResultSet(conn, this, command, result, id, closedByResultSet, scrollable, updatable); } else { returnsResultSet = false; ResultWithGeneratedKeys result = command.executeUpdate( conn.scopeGeneratedKeys() ? false : generatedKeysRequest); updateCount = result.getUpdateCount(); ResultInterface gk = result.getGeneratedKeys(); if (gk != null) { generatedKeys = new JdbcResultSet(conn, this, command, gk, id, false, true, false); } } } finally { if (!lazy) { setExecutingStatement(null); } } } if (!lazy) { command.close(); } return returnsResultSet; } finally { afterWriting(); } }
Returns the last result set produces by this statement.
Returns:the result set
/** * Returns the last result set produces by this statement. * * @return the result set */
@Override public ResultSet getResultSet() throws SQLException { try { checkClosed(); if (resultSet != null) { int id = resultSet.getTraceId(); debugCodeAssign("ResultSet", TraceObject.RESULT_SET, id, "getResultSet()"); } else { debugCodeCall("getResultSet"); } return resultSet; } catch (Exception e) { throw logAndConvert(e); } }
Returns the last update count of this statement.
Throws:
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback; -1 if the statement was a select).
/** * Returns the last update count of this statement. * * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, * commit or rollback; -1 if the statement was a select). * @throws SQLException if this object is closed or invalid */
@Override public int getUpdateCount() throws SQLException { try { debugCodeCall("getUpdateCount"); checkClosed(); return updateCount; } catch (Exception e) { throw logAndConvert(e); } }
Returns the last update count of this statement.
Throws:
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback; -1 if the statement was a select).
/** * Returns the last update count of this statement. * * @return the update count (number of row affected by an insert, update or * delete, or 0 if no rows or the statement was a create, drop, * commit or rollback; -1 if the statement was a select). * @throws SQLException if this object is closed or invalid */
@Override public long getLargeUpdateCount() throws SQLException { try { debugCodeCall("getLargeUpdateCount"); checkClosed(); return updateCount; } catch (Exception e) { throw logAndConvert(e); } }
Closes this statement. All result sets that where created by this statement become invalid after calling this method.
/** * Closes this statement. * All result sets that where created by this statement * become invalid after calling this method. */
@Override public void close() throws SQLException { try { debugCodeCall("close"); synchronized (session) { closeOldResultSet(); if (conn != null) { conn = null; } } } catch (Exception e) { throw logAndConvert(e); } }
Returns the connection that created this object.
Returns:the connection
/** * Returns the connection that created this object. * * @return the connection */
@Override public Connection getConnection() { debugCodeCall("getConnection"); return conn; }
Gets the first warning reported by calls on this object. This driver does not support warnings, and will always return null.
Returns:null
/** * Gets the first warning reported by calls on this object. * This driver does not support warnings, and will always return null. * * @return null */
@Override public SQLWarning getWarnings() throws SQLException { try { debugCodeCall("getWarnings"); checkClosed(); return null; } catch (Exception e) { throw logAndConvert(e); } }
Clears all warnings. As this driver does not support warnings, this call is ignored.
/** * Clears all warnings. As this driver does not support warnings, * this call is ignored. */
@Override public void clearWarnings() throws SQLException { try { debugCodeCall("clearWarnings"); checkClosed(); } catch (Exception e) { throw logAndConvert(e); } }
Sets the name of the cursor. This call is ignored.
Params:
  • name – ignored
Throws:
/** * Sets the name of the cursor. This call is ignored. * * @param name ignored * @throws SQLException if this object is closed */
@Override public void setCursorName(String name) throws SQLException { try { debugCodeCall("setCursorName", name); checkClosed(); } catch (Exception e) { throw logAndConvert(e); } }
Sets the fetch direction. This call is ignored by this driver.
Params:
  • direction – ignored
Throws:
/** * Sets the fetch direction. * This call is ignored by this driver. * * @param direction ignored * @throws SQLException if this object is closed */
@Override public void setFetchDirection(int direction) throws SQLException { try { debugCodeCall("setFetchDirection", direction); checkClosed(); } catch (Exception e) { throw logAndConvert(e); } }
Gets the fetch direction.
Throws:
Returns:FETCH_FORWARD
/** * Gets the fetch direction. * * @return FETCH_FORWARD * @throws SQLException if this object is closed */
@Override public int getFetchDirection() throws SQLException { try { debugCodeCall("getFetchDirection"); checkClosed(); return ResultSet.FETCH_FORWARD; } catch (Exception e) { throw logAndConvert(e); } }
Gets the maximum number of rows for a ResultSet.
Throws:
Returns:the number of rows where 0 means no limit
/** * Gets the maximum number of rows for a ResultSet. * * @return the number of rows where 0 means no limit * @throws SQLException if this object is closed */
@Override public int getMaxRows() throws SQLException { try { debugCodeCall("getMaxRows"); checkClosed(); return maxRows; } catch (Exception e) { throw logAndConvert(e); } }
Gets the maximum number of rows for a ResultSet.
Throws:
Returns:the number of rows where 0 means no limit
/** * Gets the maximum number of rows for a ResultSet. * * @return the number of rows where 0 means no limit * @throws SQLException if this object is closed */
@Override public long getLargeMaxRows() throws SQLException { try { debugCodeCall("getLargeMaxRows"); checkClosed(); return maxRows; } catch (Exception e) { throw logAndConvert(e); } }
Gets the maximum number of rows for a ResultSet.
Params:
  • maxRows – the number of rows where 0 means no limit
Throws:
/** * Gets the maximum number of rows for a ResultSet. * * @param maxRows the number of rows where 0 means no limit * @throws SQLException if this object is closed */
@Override public void setMaxRows(int maxRows) throws SQLException { try { debugCodeCall("setMaxRows", maxRows); checkClosed(); if (maxRows < 0) { throw DbException.getInvalidValueException("maxRows", maxRows); } this.maxRows = maxRows; } catch (Exception e) { throw logAndConvert(e); } }
Gets the maximum number of rows for a ResultSet.
Params:
  • maxRows – the number of rows where 0 means no limit
Throws:
/** * Gets the maximum number of rows for a ResultSet. * * @param maxRows the number of rows where 0 means no limit * @throws SQLException if this object is closed */
@Override public void setLargeMaxRows(long maxRows) throws SQLException { try { debugCodeCall("setLargeMaxRows", maxRows); checkClosed(); if (maxRows < 0) { throw DbException.getInvalidValueException("maxRows", maxRows); } this.maxRows = maxRows <= Integer.MAX_VALUE ? (int) maxRows : 0; } catch (Exception e) { throw logAndConvert(e); } }
Sets the number of rows suggested to read in one step. This value cannot be higher than the maximum rows (setMaxRows) set by the statement or prepared statement, otherwise an exception is throws. Setting the value to 0 will set the default value. The default value can be changed using the system property h2.serverResultSetFetchSize.
Params:
  • rows – the number of rows
Throws:
/** * Sets the number of rows suggested to read in one step. * This value cannot be higher than the maximum rows (setMaxRows) * set by the statement or prepared statement, otherwise an exception * is throws. Setting the value to 0 will set the default value. * The default value can be changed using the system property * h2.serverResultSetFetchSize. * * @param rows the number of rows * @throws SQLException if this object is closed */
@Override public void setFetchSize(int rows) throws SQLException { try { debugCodeCall("setFetchSize", rows); checkClosed(); if (rows < 0 || (rows > 0 && maxRows > 0 && rows > maxRows)) { throw DbException.getInvalidValueException("rows", rows); } if (rows == 0) { rows = SysProperties.SERVER_RESULT_SET_FETCH_SIZE; } fetchSize = rows; } catch (Exception e) { throw logAndConvert(e); } }
Gets the number of rows suggested to read in one step.
Throws:
Returns:the current fetch size
/** * Gets the number of rows suggested to read in one step. * * @return the current fetch size * @throws SQLException if this object is closed */
@Override public int getFetchSize() throws SQLException { try { debugCodeCall("getFetchSize"); checkClosed(); return fetchSize; } catch (Exception e) { throw logAndConvert(e); } }
Gets the result set concurrency created by this object.
Returns:the concurrency
/** * Gets the result set concurrency created by this object. * * @return the concurrency */
@Override public int getResultSetConcurrency() throws SQLException { try { debugCodeCall("getResultSetConcurrency"); checkClosed(); return resultSetConcurrency; } catch (Exception e) { throw logAndConvert(e); } }
Gets the result set type.
Throws:
Returns:the type
/** * Gets the result set type. * * @return the type * @throws SQLException if this object is closed */
@Override public int getResultSetType() throws SQLException { try { debugCodeCall("getResultSetType"); checkClosed(); return resultSetType; } catch (Exception e) { throw logAndConvert(e); } }
Gets the maximum number of bytes for a result set column.
Throws:
Returns:always 0 for no limit
/** * Gets the maximum number of bytes for a result set column. * * @return always 0 for no limit * @throws SQLException if this object is closed */
@Override public int getMaxFieldSize() throws SQLException { try { debugCodeCall("getMaxFieldSize"); checkClosed(); return 0; } catch (Exception e) { throw logAndConvert(e); } }
Sets the maximum number of bytes for a result set column. This method does currently do nothing for this driver.
Params:
  • max – the maximum size - ignored
Throws:
/** * Sets the maximum number of bytes for a result set column. * This method does currently do nothing for this driver. * * @param max the maximum size - ignored * @throws SQLException if this object is closed */
@Override public void setMaxFieldSize(int max) throws SQLException { try { debugCodeCall("setMaxFieldSize", max); checkClosed(); } catch (Exception e) { throw logAndConvert(e); } }
Enables or disables processing or JDBC escape syntax. See also Connection.nativeSQL.
Params:
  • enable – - true (default) or false (no conversion is attempted)
Throws:
/** * Enables or disables processing or JDBC escape syntax. * See also Connection.nativeSQL. * * @param enable - true (default) or false (no conversion is attempted) * @throws SQLException if this object is closed */
@Override public void setEscapeProcessing(boolean enable) throws SQLException { try { if (isDebugEnabled()) { debugCode("setEscapeProcessing("+enable+");"); } checkClosed(); escapeProcessing = enable; } catch (Exception e) { throw logAndConvert(e); } }
Cancels a currently running statement. This method must be called from within another thread than the execute method. Operations on large objects are not interrupted, only operations that process many rows.
Throws:
  • SQLException – if this object is closed
/** * Cancels a currently running statement. * This method must be called from within another * thread than the execute method. * Operations on large objects are not interrupted, * only operations that process many rows. * * @throws SQLException if this object is closed */
@Override public void cancel() throws SQLException { try { debugCodeCall("cancel"); checkClosed(); // executingCommand can be reset by another thread CommandInterface c = executingCommand; try { if (c != null) { c.cancel(); cancelled = true; } } finally { setExecutingStatement(null); } } catch (Exception e) { throw logAndConvert(e); } }
Check whether the statement was cancelled.
Returns:true if yes
/** * Check whether the statement was cancelled. * * @return true if yes */
public boolean isCancelled() { return cancelled; }
Gets the current query timeout in seconds. This method will return 0 if no query timeout is set. The result is rounded to the next second. For performance reasons, only the first call to this method will query the database. If the query timeout was changed in another way than calling setQueryTimeout, this method will always return the last value.
Throws:
Returns:the timeout in seconds
/** * Gets the current query timeout in seconds. * This method will return 0 if no query timeout is set. * The result is rounded to the next second. * For performance reasons, only the first call to this method * will query the database. If the query timeout was changed in another * way than calling setQueryTimeout, this method will always return * the last value. * * @return the timeout in seconds * @throws SQLException if this object is closed */
@Override public int getQueryTimeout() throws SQLException { try { debugCodeCall("getQueryTimeout"); checkClosed(); return conn.getQueryTimeout(); } catch (Exception e) { throw logAndConvert(e); } }
Sets the current query timeout in seconds. Changing the value will affect all statements of this connection. This method does not commit a transaction, and rolling back a transaction does not affect this setting.
Params:
  • seconds – the timeout in seconds - 0 means no timeout, values smaller 0 will throw an exception
Throws:
/** * Sets the current query timeout in seconds. * Changing the value will affect all statements of this connection. * This method does not commit a transaction, * and rolling back a transaction does not affect this setting. * * @param seconds the timeout in seconds - 0 means no timeout, values * smaller 0 will throw an exception * @throws SQLException if this object is closed */
@Override public void setQueryTimeout(int seconds) throws SQLException { try { debugCodeCall("setQueryTimeout", seconds); checkClosed(); if (seconds < 0) { throw DbException.getInvalidValueException("seconds", seconds); } conn.setQueryTimeout(seconds); } catch (Exception e) { throw logAndConvert(e); } }
Adds a statement to the batch.
Params:
  • sql – the SQL statement
/** * Adds a statement to the batch. * * @param sql the SQL statement */
@Override public void addBatch(String sql) throws SQLException { try { debugCodeCall("addBatch", sql); checkClosed(); sql = JdbcConnection.translateSQL(sql, escapeProcessing); if (batchCommands == null) { batchCommands = Utils.newSmallArrayList(); } batchCommands.add(sql); } catch (Exception e) { throw logAndConvert(e); } }
Clears the batch.
/** * Clears the batch. */
@Override public void clearBatch() throws SQLException { try { debugCodeCall("clearBatch"); checkClosed(); batchCommands = null; } catch (Exception e) { throw logAndConvert(e); } }
Executes the batch. If one of the batched statements fails, this database will continue.
Returns:the array of update counts
/** * Executes the batch. * If one of the batched statements fails, this database will continue. * * @return the array of update counts */
@Override public int[] executeBatch() throws SQLException { try { debugCodeCall("executeBatch"); checkClosedForWrite(); try { if (batchCommands == null) { // TODO batch: check what other database do if no commands // are set batchCommands = Utils.newSmallArrayList(); } int size = batchCommands.size(); int[] result = new int[size]; boolean error = false; SQLException next = null; for (int i = 0; i < size; i++) { String sql = batchCommands.get(i); try { result[i] = executeUpdateInternal(sql, false); } catch (Exception re) { SQLException e = logAndConvert(re); if (next == null) { next = e; } else { e.setNextException(next); next = e; } result[i] = Statement.EXECUTE_FAILED; error = true; } } batchCommands = null; if (error) { throw new JdbcBatchUpdateException(next, result); } return result; } finally { afterWriting(); } } catch (Exception e) { throw logAndConvert(e); } }
Executes the batch. If one of the batched statements fails, this database will continue.
Returns:the array of update counts
/** * Executes the batch. * If one of the batched statements fails, this database will continue. * * @return the array of update counts */
@Override public long[] executeLargeBatch() throws SQLException { int[] intResult = executeBatch(); int count = intResult.length; long[] longResult = new long[count]; for (int i = 0; i < count; i++) { longResult[i] = intResult[i]; } return longResult; }
Return a result set with generated keys from the latest executed command or an empty result set if keys were not generated or were not requested with Statement.RETURN_GENERATED_KEYS, column indexes, or column names.

Generated keys are only returned from inserted rows from INSERT, MERGE INTO, and MERGE INTO ... USING commands. Generated keys are not returned if exact values of generated columns were specified explicitly in SQL command. All columns with inserted generated values are included in the result if command was executed with Statement.RETURN_GENERATED_KEYS parameter.

If SQL command inserts multiple rows with generated keys each such inserted row is returned. Batch methods are also supported. When multiple rows are returned each row contains only generated values for this row. It's possible to insert several rows with generated values in different columns with some specific commands, in this special case the returned result set contains all used columns, but each row will contain only generated values, columns that were not generated for this row will contain null values.

H2 treats inserted value as generated in the following cases:

  • Columns with sequences including IDENTITY columns and columns with AUTO_INCREMENT if value was generated automatically (not specified in command).
  • Columns with other default values that are not evaluated into constant expressions (like DEFAULT RANDOM_UUID()) also only if default value was inserted.
  • Columns that were set by triggers.
  • Columns with values specified in command with invocation of some sequence (like INSERT INTO ... VALUES (NEXT VALUE FOR ...)).

Exact required columns for the returning result set may be specified on execution of command with names or indexes of columns to limit output or reorder columns in result set. Specifying of some column has no effect on treatment of inserted values as generated or not. If some value is not determined to be generated it will not be returned even on explicit request.

Throws:
Returns:the possibly empty result set with generated keys
/** * Return a result set with generated keys from the latest executed command or * an empty result set if keys were not generated or were not requested with * {@link Statement#RETURN_GENERATED_KEYS}, column indexes, or column names. * <p> * Generated keys are only returned from inserted rows from {@code INSERT}, * {@code MERGE INTO}, and {@code MERGE INTO ... USING} commands. Generated keys * are not returned if exact values of generated columns were specified * explicitly in SQL command. All columns with inserted generated values are * included in the result if command was executed with * {@link Statement#RETURN_GENERATED_KEYS} parameter. * </p> * <p> * If SQL command inserts multiple rows with generated keys each such inserted * row is returned. Batch methods are also supported. When multiple rows are * returned each row contains only generated values for this row. It's possible * to insert several rows with generated values in different columns with some * specific commands, in this special case the returned result set contains all * used columns, but each row will contain only generated values, columns that * were not generated for this row will contain {@code null} values. * </p> * <p> * H2 treats inserted value as generated in the following cases: * </p> * <ul> * <li>Columns with sequences including {@code IDENTITY} columns and columns * with {@code AUTO_INCREMENT} if value was generated automatically (not * specified in command).</li> * <li>Columns with other default values that are not evaluated into constant * expressions (like {@code DEFAULT RANDOM_UUID()}) also only if default value * was inserted.</li> * <li>Columns that were set by triggers.</li> * <li>Columns with values specified in command with invocation of some sequence * (like {@code INSERT INTO ... VALUES (NEXT VALUE FOR ...)}).</li> * </ul> * <p> * Exact required columns for the returning result set may be specified on * execution of command with names or indexes of columns to limit output or * reorder columns in result set. Specifying of some column has no effect on * treatment of inserted values as generated or not. If some value is not * determined to be generated it will not be returned even on explicit request. * </p> * * @return the possibly empty result set with generated keys * @throws SQLException if this object is closed */
@Override public ResultSet getGeneratedKeys() throws SQLException { try { int id = generatedKeys != null ? generatedKeys.getTraceId() : getNextId(TraceObject.RESULT_SET); if (isDebugEnabled()) { debugCodeAssign("ResultSet", TraceObject.RESULT_SET, id, "getGeneratedKeys()"); } checkClosed(); if (generatedKeys == null) { if (!conn.scopeGeneratedKeys() && session.isSupportsGeneratedKeys()) { generatedKeys = new JdbcResultSet(conn, this, null, new SimpleResult(), id, false, true, false); } else { // Compatibility mode or an old server, so use SCOPE_IDENTITY() generatedKeys = conn.getGeneratedKeys(this, id); } } return generatedKeys; } catch (Exception e) { throw logAndConvert(e); } }
Moves to the next result set - however there is always only one result set. This call also closes the current result set (if there is one). Returns true if there is a next result set (that means - it always returns false).
Throws:
Returns:false
/** * Moves to the next result set - however there is always only one result * set. This call also closes the current result set (if there is one). * Returns true if there is a next result set (that means - it always * returns false). * * @return false * @throws SQLException if this object is closed. */
@Override public boolean getMoreResults() throws SQLException { try { debugCodeCall("getMoreResults"); checkClosed(); closeOldResultSet(); return false; } catch (Exception e) { throw logAndConvert(e); } }
Move to the next result set. This method always returns false.
Params:
  • current – Statement.CLOSE_CURRENT_RESULT, Statement.KEEP_CURRENT_RESULT, or Statement.CLOSE_ALL_RESULTS
Returns:false
/** * Move to the next result set. * This method always returns false. * * @param current Statement.CLOSE_CURRENT_RESULT, * Statement.KEEP_CURRENT_RESULT, * or Statement.CLOSE_ALL_RESULTS * @return false */
@Override public boolean getMoreResults(int current) throws SQLException { try { debugCodeCall("getMoreResults", current); switch (current) { case Statement.CLOSE_CURRENT_RESULT: case Statement.CLOSE_ALL_RESULTS: checkClosed(); closeOldResultSet(); break; case Statement.KEEP_CURRENT_RESULT: // nothing to do break; default: throw DbException.getInvalidValueException("current", current); } return false; } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param autoGeneratedKeys * {@link Statement#RETURN_GENERATED_KEYS} if generated keys should * be available for retrieval, {@link Statement#NO_GENERATED_KEYS} if * generated keys should not be available * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { try { if (isDebugEnabled()) { debugCode("executeUpdate("+quote(sql)+", "+autoGeneratedKeys+");"); } return executeUpdateInternal(sql, autoGeneratedKeys == RETURN_GENERATED_KEYS); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param autoGeneratedKeys * {@link Statement#RETURN_GENERATED_KEYS} if generated keys should * be available for retrieval, {@link Statement#NO_GENERATED_KEYS} if * generated keys should not be available * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { try { if (isDebugEnabled()) { debugCode("executeLargeUpdate("+quote(sql)+", "+autoGeneratedKeys+");"); } return executeUpdateInternal(sql, autoGeneratedKeys == RETURN_GENERATED_KEYS); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
  • sql – the SQL statement
  • columnIndexes – an array of column indexes indicating the columns with generated keys that should be returned from the inserted row
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param columnIndexes * an array of column indexes indicating the columns with generated * keys that should be returned from the inserted row * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { try { if (isDebugEnabled()) { debugCode("executeUpdate("+quote(sql)+", "+quoteIntArray(columnIndexes)+");"); } return executeUpdateInternal(sql, columnIndexes); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
  • sql – the SQL statement
  • columnIndexes – an array of column indexes indicating the columns with generated keys that should be returned from the inserted row
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param columnIndexes * an array of column indexes indicating the columns with generated * keys that should be returned from the inserted row * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public long executeLargeUpdate(String sql, int columnIndexes[]) throws SQLException { try { if (isDebugEnabled()) { debugCode("executeLargeUpdate("+quote(sql)+", "+quoteIntArray(columnIndexes)+");"); } return executeUpdateInternal(sql, columnIndexes); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
  • sql – the SQL statement
  • columnNames – an array of column names indicating the columns with generated keys that should be returned from the inserted row
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param columnNames * an array of column names indicating the columns with generated * keys that should be returned from the inserted row * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public int executeUpdate(String sql, String[] columnNames) throws SQLException { try { if (isDebugEnabled()) { debugCode("executeUpdate("+quote(sql)+", "+quoteArray(columnNames)+");"); } return executeUpdateInternal(sql, columnNames); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
  • sql – the SQL statement
  • columnNames – an array of column names indicating the columns with generated keys that should be returned from the inserted row
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param columnNames * an array of column names indicating the columns with generated * keys that should be returned from the inserted row * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public long executeLargeUpdate(String sql, String columnNames[]) throws SQLException { try { if (isDebugEnabled()) { debugCode("executeLargeUpdate("+quote(sql)+", "+quoteArray(columnNames)+");"); } return executeUpdateInternal(sql, columnNames); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param autoGeneratedKeys * {@link Statement#RETURN_GENERATED_KEYS} if generated keys should * be available for retrieval, {@link Statement#NO_GENERATED_KEYS} if * generated keys should not be available * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { try { if (isDebugEnabled()) { debugCode("execute("+quote(sql)+", "+autoGeneratedKeys+");"); } return executeInternal(sql, autoGeneratedKeys == RETURN_GENERATED_KEYS); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
  • sql – the SQL statement
  • columnIndexes – an array of column indexes indicating the columns with generated keys that should be returned from the inserted row
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param columnIndexes * an array of column indexes indicating the columns with generated * keys that should be returned from the inserted row * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public boolean execute(String sql, int[] columnIndexes) throws SQLException { try { if (isDebugEnabled()) { debugCode("execute("+quote(sql)+", "+quoteIntArray(columnIndexes)+");"); } return executeInternal(sql, columnIndexes); } catch (Exception e) { throw logAndConvert(e); } }
Executes a statement and returns the update count.
Params:
  • sql – the SQL statement
  • columnNames – an array of column names indicating the columns with generated keys that should be returned from the inserted row
Throws:
  • SQLException – if a database error occurred or a select statement was executed
Returns:the update count (number of row affected by an insert, update or delete, or 0 if no rows or the statement was a create, drop, commit or rollback)
/** * Executes a statement and returns the update count. * * @param sql the SQL statement * @param columnNames * an array of column names indicating the columns with generated * keys that should be returned from the inserted row * @return the update count (number of row affected by an insert, * update or delete, or 0 if no rows or the statement was a * create, drop, commit or rollback) * @throws SQLException if a database error occurred or a * select statement was executed */
@Override public boolean execute(String sql, String[] columnNames) throws SQLException { try { if (isDebugEnabled()) { debugCode("execute("+quote(sql)+", "+quoteArray(columnNames)+");"); } return executeInternal(sql, columnNames); } catch (Exception e) { throw logAndConvert(e); } }
Gets the result set holdability.
Returns:the holdability
/** * Gets the result set holdability. * * @return the holdability */
@Override public int getResultSetHoldability() throws SQLException { try { debugCodeCall("getResultSetHoldability"); checkClosed(); return ResultSet.HOLD_CURSORS_OVER_COMMIT; } catch (Exception e) { throw logAndConvert(e); } }
[Not supported]
/** * [Not supported] */
@Override public void closeOnCompletion() { // not supported }
[Not supported]
/** * [Not supported] */
@Override public boolean isCloseOnCompletion() { return true; } // =============================================================
Check if this connection is closed. The next operation is a read request.
Throws:
Returns:true if the session was re-connected
/** * Check if this connection is closed. * The next operation is a read request. * * @return true if the session was re-connected * @throws DbException if the connection or session is closed */
boolean checkClosed() { return checkClosed(false); }
Check if this connection is closed. The next operation may be a write request.
Throws:
Returns:true if the session was re-connected
/** * Check if this connection is closed. * The next operation may be a write request. * * @return true if the session was re-connected * @throws DbException if the connection or session is closed */
boolean checkClosedForWrite() { return checkClosed(true); }
INTERNAL. Check if the statement is closed.
Params:
  • write – if the next operation is possibly writing
Throws:
Returns:true if a reconnect was required
/** * INTERNAL. * Check if the statement is closed. * * @param write if the next operation is possibly writing * @return true if a reconnect was required * @throws DbException if it is closed */
protected boolean checkClosed(boolean write) { if (conn == null) { throw DbException.get(ErrorCode.OBJECT_CLOSED); } conn.checkClosed(write); SessionInterface s = conn.getSession(); if (s != session) { session = s; trace = session.getTrace(); return true; } return false; }
Called after each write operation.
/** * Called after each write operation. */
void afterWriting() { if (conn != null) { conn.afterWriting(); } }
INTERNAL. Close and old result set if there is still one open.
/** * INTERNAL. * Close and old result set if there is still one open. */
protected void closeOldResultSet() throws SQLException { try { if (!closedByResultSet) { if (resultSet != null) { resultSet.closeInternal(); } if (generatedKeys != null) { generatedKeys.closeInternal(); } } } finally { cancelled = false; resultSet = null; updateCount = -1; generatedKeys = null; } }
INTERNAL. Set the statement that is currently running.
Params:
  • c – the command
/** * INTERNAL. * Set the statement that is currently running. * * @param c the command */
protected void setExecutingStatement(CommandInterface c) { if (c == null) { conn.setExecutingStatement(null); } else { conn.setExecutingStatement(this); lastExecutedCommandType = c.getCommandType(); } executingCommand = c; }
Called when the result set is closed.
Params:
  • command – the command
  • closeCommand – whether to close the command
/** * Called when the result set is closed. * * @param command the command * @param closeCommand whether to close the command */
void onLazyResultSetClose(CommandInterface command, boolean closeCommand) { setExecutingStatement(null); command.stop(); if (closeCommand) { command.close(); } }
INTERNAL. Get the command type of the last executed command.
/** * INTERNAL. * Get the command type of the last executed command. */
public int getLastExecutedCommandType() { return lastExecutedCommandType; }
Returns whether this statement is closed.
Returns:true if the statement is closed
/** * Returns whether this statement is closed. * * @return true if the statement is closed */
@Override public boolean isClosed() throws SQLException { try { debugCodeCall("isClosed"); return conn == null; } catch (Exception e) { throw logAndConvert(e); } }
Return an object of this class if possible.
Params:
  • iface – the class
Returns:this
/** * Return an object of this class if possible. * * @param iface the class * @return this */
@Override @SuppressWarnings("unchecked") public <T> T unwrap(Class<T> iface) throws SQLException { try { if (isWrapperFor(iface)) { return (T) this; } throw DbException.getInvalidValueException("iface", iface); } catch (Exception e) { throw logAndConvert(e); } }
Checks if unwrap can return an object of this class.
Params:
  • iface – the class
Returns:whether or not the interface is assignable from this class
/** * Checks if unwrap can return an object of this class. * * @param iface the class * @return whether or not the interface is assignable from this class */
@Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return iface != null && iface.isAssignableFrom(getClass()); }
Returns whether this object is poolable.
Returns:false
/** * Returns whether this object is poolable. * @return false */
@Override public boolean isPoolable() { debugCodeCall("isPoolable"); return false; }
Requests that this object should be pooled or not. This call is ignored.
Params:
  • poolable – the requested value
/** * Requests that this object should be pooled or not. * This call is ignored. * * @param poolable the requested value */
@Override public void setPoolable(boolean poolable) { if (isDebugEnabled()) { debugCode("setPoolable("+poolable+");"); } }
Params:
  • identifier – identifier to quote if required, may be quoted or unquoted
  • alwaysQuote – if true identifier will be quoted unconditionally
Throws:
Returns:specified identifier quoted if required, explicitly requested, or if it was already quoted
/** * @param identifier * identifier to quote if required, may be quoted or unquoted * @param alwaysQuote * if {@code true} identifier will be quoted unconditionally * @return specified identifier quoted if required, explicitly requested, or * if it was already quoted * @throws SQLException * if identifier is not a valid identifier */
@Override public String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException { if (isSimpleIdentifier(identifier)) { return alwaysQuote ? '"' + identifier + '"': identifier; } int length = identifier.length(); if (length > 0 && identifier.charAt(0) == '"') { boolean quoted = true; for (int i = 1; i < length; i++) { if (identifier.charAt(i) == '"') { quoted = !quoted; } else if (!quoted) { throw new SQLException(); } } if (quoted) { throw new SQLException(); } return identifier; } return StringUtils.quoteIdentifier(identifier); }
Params:
  • identifier – identifier to check
Returns:is specified identifier may be used without quotes
/** * @param identifier * identifier to check * @return is specified identifier may be used without quotes */
@Override public boolean isSimpleIdentifier(String identifier) throws SQLException { JdbcConnection.Settings settings = conn.getSettings(); return ParserUtil.isSimpleIdentifier(identifier, settings.databaseToUpper, settings.databaseToLower); }
INTERNAL
/** * INTERNAL */
@Override public String toString() { return getTraceObjectName(); } }