src-main.org.awakefw.sql.jdbc.StatementHttp Maven / Gradle / Ivy
Show all versions of awake-sql Show documentation
/*
* This file is part of Awake SQL.
* Awake SQL: Remote JDBC access over HTTP.
* Copyright (C) 2013, KawanSoft SAS
* (http://www.kawansoft.com). All rights reserved.
*
* Awake SQL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Awake SQL 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*
* If you develop commercial activities using Awake SQL, you must:
* a) disclose and distribute all source code of your own product,
* b) license your own product under the GNU General Public License.
*
* You can be released from the requirements of the license by
* purchasing a commercial license. Buying such a license will allow you
* to ship Awake SQL with your closed source products without disclosing
* the source code.
*
* For more information, please contact KawanSoft SAS at this
* address: [email protected]
*
* Any modifications to this file must keep this entire header
* intact.
*/
package org.awakefw.sql.jdbc;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.List;
import java.util.Vector;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.awakefw.commons.jdbc.abstracts.AbstractStatement;
import org.awakefw.file.util.AwakeClientLogger;
import org.awakefw.file.util.AwakeFileUtil;
import org.awakefw.file.util.Tag;
import org.awakefw.sql.jdbc.http.JdbcHttpBatchTransfer;
import org.awakefw.sql.jdbc.http.JdbcHttpExecuteRawTransfer;
import org.awakefw.sql.jdbc.http.JdbcHttpStatementTransfer;
import org.awakefw.sql.jdbc.http.JdbcHttpTransferUtil;
import org.awakefw.sql.jdbc.util.StatementHolderFileList;
import org.awakefw.sql.json.IntArrayTransport;
import org.awakefw.sql.json.StatementHolder;
import org.awakefw.sql.util.CallableParms;
/**
* Creates and handle a Statement Cache.
* It works exactly as a "normal" Statement, try to get the ResultSet in Java
* memory when they are cache, instead of executing them in the JDBC/SQL space
* area.
*
*/
public class StatementHttp
extends AbstractStatement
implements Statement {
/** Universal and clean line separator */
protected static String CR_LF = System.getProperty("line.separator");
/** Debug flag */
public boolean DEBUG = false;
/** The HttpConnection in use */
protected ConnectionHttp connectionHttp = null;
/** The result set type. Defaults to ResultSet.TYPE_FORWARD_ONLY */
protected int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
/** The result set concurrency. Defaults to CONCUR_READ_ONLY */
protected int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
/** The result set holdability. Defaults to CLOSE_CURSORS_AT_COMMIT */
protected int resultSetHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT;
/** Max rows */
protected int maxRows = 0;
/** Fetch Size */
protected int fetchSize = 0;
/** Query Timeout */
protected int queryTimeout = 0;
/** Escape processing. We use a int value to store the fact it is not set */
protected int escapeProcessingInt = -1; // true is default value
/** The ResultSet returned by the remote execute() */
protected ResultSet rsFromExecute = null;
/** The updateCount returned by the remote execute() */
protected int updateCount = -1;
/** The list of statement batch to be executed */
// protected List batchHolderList = new
// Vector();
/** The list of statement holder are stored on a file */
protected StatementHolderFileList batchHolderFileList = null;
/** The list of files to delete at close */
protected List localFiles = new Vector();
/**
* Says if the last execute is a raw execute() (true) or an executeUpdate
* (false)
*/
protected boolean lastExecuteIsRaw = false;
/** The file received from the last raw execute() */
protected File receiveFileFromExecute = null;
/**
* Constructor
*
* @param connectionHttp
* The http Connection
* @param resultSetType
* The result set type
* @param resultSetConcurrency
* The result set concurrency
* @param resultSetHoldability
* The result set holdability
*/
public StatementHttp(ConnectionHttp connectionHttp, int resultSetType,
int resultSetConcurrency, int resultSetHoldability) {
this.connectionHttp = connectionHttp;
this.resultSetType = resultSetType;
this.resultSetConcurrency = resultSetConcurrency;
this.resultSetHoldability = resultSetHoldability;
this.batchHolderFileList = new StatementHolderFileList(connectionHttp);
}
/**
* Test if a prepared statement is still open
*
* @throws SQLException
* it the Connection is closed
*/
protected void testIfClosed() throws SQLException {
if (isClosed()) {
throw new SQLException("This Awake Statement is closed!");
}
}
/**
* Begin Kawan Softwares S.A.S implementation if Result Set is cached,
* return the cached version; else create the Result Set with a normal
* executeQuery() and put the result in memory End Kawan Softwares S.A.S
* implementation
*
* Executes the given SQL statement, which returns a single
* ResultSet
object.
*
* @param sql
* an SQL statement to be sent to the database, typically a
* static SQL SELECT
statement
* @return a ResultSet
object that contains the data produced
* by the given query; never null
* @exception SQLException
* if a database access error occurs or the given SQL
* statement produces anything other than a single
* ResultSet
object
*/
@Override
public ResultSet executeQuery(String sql) throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
sql = sql.trim();
if (connectionHttp.isStatelessMode()) {
if (!connectionHttp.getAutoCommit()) {
throw new IllegalStateException(
Tag.AWAKE
+ "executeQuery() can\'t be executed when auto commit is off.");
}
}
StatementHolder statementHolder = new StatementHolder(sql,
resultSetType, resultSetConcurrency, resultSetHoldability);
statementHolder.setFetchSize(fetchSize);
statementHolder.setMaxRows(maxRows);
statementHolder.setQueryTimeout(queryTimeout);
statementHolder.setEscapeProcessing(escapeProcessingInt);
statementHolder.setPreparedStatement(false);
statementHolder.setExecuteUpdate(false);
// Send order to Server to SQL Executor
JdbcHttpStatementTransfer jdbcHttpStatementTransfer = new JdbcHttpStatementTransfer(
connectionHttp, connectionHttp.getAuthenticationToken());
File receiveFile = jdbcHttpStatementTransfer
.getFileFromExecuteQueryOnServer(statementHolder);
debug("getFileFromexecuteOnServer() : " + receiveFile);
// Transform the Result Set in String back to an Result Set (emulated)
ResultSet rs = new ResultSetHttp(connectionHttp, statementHolder, this,
receiveFile);
return rs;
}
/**
* Executes the given SQL statement, which may be an INSERT
,
* UPDATE
, or DELETE
statement or an SQL statement
* that returns nothing, such as an SQL DDL statement.
*
* @param sql
* an SQL INSERT
, UPDATE
or
* DELETE
statement or an SQL statement that returns
* nothing
* @return either the row count for INSERT
, UPDATE
* or DELETE
statements, or 0
for SQL
* statements that return nothing
* @exception SQLException
* if a database access error occurs or the given SQL
* statement produces a ResultSet
object
*/
@Override
public int executeUpdate(String sql) throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
lastExecuteIsRaw = false;
sql = sql.trim();
int rc = 0;
// Add the statement to the statement list
StatementHolder statementHolder = new StatementHolder(sql,
resultSetType, resultSetConcurrency, resultSetHoldability);
rc = wrapExecuteUpdate(statementHolder);
return rc;
}
/**
* General execute method for all executeUpdate() methods Wrap the execution
* and get back the result (if in auto commit mode)
* @param statementHolder TODO
*
* @return the
* @throws SQLException
*/
private int wrapExecuteUpdate(StatementHolder statementHolder) throws SQLException {
statementHolder.setPreparedStatement(false);
statementHolder.setExecuteUpdate(true);
connectionHttp.addStatementHolder(statementHolder);
int rc = -1;
// Execute
if ((connectionHttp.isStatelessMode() && connectionHttp.getAutoCommit()
|| !connectionHttp.isStatelessMode())) {
try {
// Send order to Server to SQL Executor
connectionHttp.receiveFromExecuteUpdate = connectionHttp
.getStringFromExecuteUpdateListOnServer();
BufferedReader bufferedReader = new BufferedReader(
new StringReader(
connectionHttp.receiveFromExecuteUpdate));
String line1 = null;
try {
line1 = bufferedReader.readLine();
} catch (IOException e1) {
throw new SQLException(e1);
}
try {
rc = Integer.parseInt(line1);
} catch (NumberFormatException e) {
throw new SQLException(Tag.AWAKE_PRODUCT_FAIL
+ e.getMessage(),
new IOException(e.getMessage(), e));
}
} finally {
connectionHttp.resetStatementHolderList(); // Safety reset of
// list
}
}
return rc;
}
/**
* Executes the given SQL statement and signals the driver with the given
* flag about whether the auto-generated keys produced by this
* Statement
object should be made available for retrieval. The
* driver will ignore the flag if the SQL statement is not an
* INSERT
statement, or an SQL statement able to return
* auto-generated keys (the list of such statements is vendor-specific).
*
* @param sql
* an SQL Data Manipulation Language (DML) statement, such as
* INSERT
, UPDATE
or
* DELETE
; or an SQL statement that returns nothing,
* such as a DDL statement.
*
* @param autoGeneratedKeys
* a flag indicating whether auto-generated keys should be made
* available for retrieval; one of the following constants:
* Statement.RETURN_GENERATED_KEYS
* Statement.NO_GENERATED_KEYS
* @return either (1) the row count for SQL Data Manipulation Language (DML)
* statements or (2) 0 for SQL statements that return nothing
*
* @exception SQLException
* if a database access error occurs, this method is called
* on a closed Statement
, the given SQL
* statement returns a ResultSet
object, or the
* given constant is not one of those allowed
* @exception SQLFeatureNotSupportedException
* if the JDBC driver does not support this method with a
* constant of Statement.RETURN_GENERATED_KEYS
* @since 1.4
*/
@Override
public int executeUpdate(String sql, int autoGeneratedKeys)
throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
lastExecuteIsRaw = false;
sql = sql.trim();
int rc = 0;
// Add the statement to the statement list
StatementHolder statementHolder = new StatementHolder(sql,
autoGeneratedKeys);
rc = wrapExecuteUpdate(statementHolder);
return rc;
}
/**
* Executes the given SQL statement and signals the driver that the
* auto-generated keys indicated in the given array should be made available
* for retrieval. This array contains the indexes of the columns in the
* target table that contain the auto-generated keys that should be made
* available. The driver will ignore the array if the SQL statement is not
* an INSERT
statement, or an SQL statement able to return
* auto-generated keys (the list of such statements is vendor-specific).
*
* @param sql
* an SQL Data Manipulation Language (DML) statement, such as
* INSERT
, UPDATE
or
* DELETE
; or an SQL statement that returns nothing,
* such as a DDL statement.
*
* @param columnIndexes
* an array of column indexes indicating the columns that should
* be returned from the inserted row
* @return either (1) the row count for SQL Data Manipulation Language (DML)
* statements or (2) 0 for SQL statements that return nothing
*
* @exception SQLException
* if a database access error occurs, this method is called
* on a closed Statement
, the SQL statement
* returns a ResultSet
object, or the second
* argument supplied to this method is not an
* int
array whose elements are valid column
* indexes
* @throws SQLFeatureNotSupportedException
* if the JDBC driver does not support this method
* @since 1.4
*/
public int executeUpdate(String sql, int columnIndexes[])
throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
sql = sql.trim();
int rc = 0;
lastExecuteIsRaw = false;
// Add the statement to the statement list
StatementHolder statementHolder = new StatementHolder(sql,
columnIndexes);
rc = wrapExecuteUpdate(statementHolder);
return rc;
}
/**
* Executes the given SQL statement and signals the driver that the
* auto-generated keys indicated in the given array should be made available
* for retrieval. This array contains the names of the columns in the target
* table that contain the auto-generated keys that should be made available.
* The driver will ignore the array if the SQL statement is not an
* INSERT
statement, or an SQL statement able to return
* auto-generated keys (the list of such statements is vendor-specific).
*
* @param sql
* an SQL Data Manipulation Language (DML) statement, such as
* INSERT
, UPDATE
or
* DELETE
; or an SQL statement that returns nothing,
* such as a DDL statement.
* @param columnNames
* an array of the names of the columns that should be returned
* from the inserted row
* @return either the row count for INSERT
, UPDATE
* , or DELETE
statements, or 0 for SQL statements that
* return nothing
* @exception SQLException
* if a database access error occurs, this method is called
* on a closed Statement
, the SQL statement
* returns a ResultSet
object, or the second
* argument supplied to this method is not a
* String
array whose elements are valid column
* names
*
* @throws SQLFeatureNotSupportedException
* if the JDBC driver does not support this method
* @since 1.4
*/
public int executeUpdate(String sql, String columnNames[])
throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
sql = sql.trim();
int rc = 0;
lastExecuteIsRaw = false;
// Add the statement to the statement list
StatementHolder statementHolder = new StatementHolder(sql, columnNames);
rc = wrapExecuteUpdate(statementHolder);
return rc;
}
/**
* Builds a ResultSet from a string representation
*
* @return a a built ResultSet
* @throws IOException
* @throws SQLException
*/
private ResultSet buildResultSet(String resultSetStr) throws IOException,
SQLException {
// Create a Result Set from the passed String
String tempDir = AwakeFileUtil.getAwakeTempDir();
if (!tempDir.endsWith(File.separator)) {
tempDir += File.separator;
}
File rsFile = new File(tempDir + AwakeFileUtil.getUniqueId()
+ "-result-set.txt");
FileUtils.write(rsFile, resultSetStr);
localFiles.add(rsFile);
ResultSet rs = new ResultSetHttp(connectionHttp, null, this, rsFile);
return rs;
}
/**
* Retrieves any auto-generated keys created as a result of executing this
* Statement
object. If this Statement
object did
* not generate any keys, an empty ResultSet
object is
* returned.
*
*
* Note:If the columns which represent the auto-generated keys were
* not specified, the JDBC driver implementation will determine the columns
* which best represent the auto-generated keys.
*
* @return a ResultSet
object containing the auto-generated
* key(s) generated by the execution of this Statement
* object
* @exception SQLException
* if a database access error occurs or this method is called
* on a closed Statement
* @throws SQLFeatureNotSupportedException
* if the JDBC driver does not support this method
* @since 1.4
*/
@Override
public ResultSet getGeneratedKeys() throws SQLException {
testIfClosed();
try {
// Build an empty result set
ResultSet rsEmpty = buildResultSet("{\"column_1\":0,\"column_2\":1}"
+ CR_LF);
if (!lastExecuteIsRaw) {
if (connectionHttp.receiveFromExecuteUpdate == null) {
return rsEmpty;
}
// Read the first line that contains update info
BufferedReader bufferedReader = new BufferedReader(
new StringReader(
connectionHttp.receiveFromExecuteUpdate));
bufferedReader.readLine();
String ResultSetStr = "";
// Build the eventually received result set
String line = "";
while ((line = bufferedReader.readLine()) != null) {
ResultSetStr += line + CR_LF;
}
debug("ResultSetStr: " + ResultSetStr);
if (ResultSetStr.isEmpty()) {
return rsEmpty;
} else {
rsEmpty.close();
return buildResultSet(ResultSetStr);
}
}
else {
// content is to be extracted from file received by execute()
// Read the first line that contains
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(receiveFileFromExecute));
bufferedReader.readLine();
String ResultSetStr = "";
// Build the eventually received result set
String line = "";
while ((line = bufferedReader.readLine()) != null) {
ResultSetStr += line + CR_LF;
}
if (ResultSetStr.isEmpty()) {
return rsEmpty;
} else {
rsEmpty.close();
return buildResultSet(ResultSetStr);
}
} finally {
IOUtils.closeQuietly(bufferedReader);
}
}
} catch (IOException e) {
throw new SQLException(e);
}
}
// ----------------------- Multiple Results --------------------------
/**
* Executes the given SQL statement, which may return multiple results. In
* some (uncommon) situations, a single SQL statement may return multiple
* result sets and/or update counts. Normally you can ignore this unless you
* are (1) executing a stored procedure that you know may return multiple
* results or (2) you are dynamically executing an unknown SQL string.
*
* The execute
method executes an SQL statement and indicates
* the form of the first result. You must then use the methods
* getResultSet
or getUpdateCount
to retrieve the
* result, and getMoreResults
to move to any subsequent
* result(s).
*
* @param sql
* any SQL statement
* @return true
if the first result is a ResultSet
* object; false
if it is an update count or there are
* no results
* @exception SQLException
* if a database access error occurs
* @see #getResultSet
* @see #getUpdateCount
* @see #getMoreResults
*/
public boolean execute(String sql) throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
sql = sql.trim();
if (connectionHttp.isStatelessMode()) {
if (!connectionHttp.getAutoCommit()) {
throw new IllegalStateException(
Tag.AWAKE
+ "execute() can\'t be executed when auto commit is off.");
}
}
lastExecuteIsRaw = true;
StatementHolder statementHolder = new StatementHolder(sql,
resultSetType, resultSetConcurrency, resultSetHoldability);
statementHolder.setPreparedStatement(false);
statementHolder.setExecuteUpdate(false);
statementHolder.setFetchSize(fetchSize);
statementHolder.setMaxRows(maxRows);
statementHolder.setQueryTimeout(queryTimeout);
statementHolder.setEscapeProcessing(escapeProcessingInt);
// Reset the fields values
rsFromExecute = null;
updateCount = -1;
// Send order to Server to SQL Executor
JdbcHttpExecuteRawTransfer jdbcHttpExecuteRawTransfer = new JdbcHttpExecuteRawTransfer(
connectionHttp, connectionHttp.getAuthenticationToken());
receiveFileFromExecute = jdbcHttpExecuteRawTransfer
.getFileFromExecuteRaw(statementHolder);
localFiles.add(receiveFileFromExecute);
debug("getFileFromexecuteOnServer() : " + receiveFileFromExecute);
boolean fileResultSet = isFileResultSet(receiveFileFromExecute);
if (fileResultSet) {
// Transform the Result Set in String back to an Result Set
// (emulated)
rsFromExecute = new ResultSetHttp(connectionHttp, statementHolder,
this, receiveFileFromExecute);
return true;
} else {
extractGetUpdateCount();
return false;
}
}
/**
* Extract the getUpdatCount value store in the receiveFileFromExecute
* @throws SQLException
*/
private void extractGetUpdateCount() throws SQLException {
BufferedReader bufferedReader = null;
String line1 = null;
try {
bufferedReader = new BufferedReader(
new FileReader(receiveFileFromExecute));
line1 = bufferedReader.readLine();
} catch (IOException e1) {
throw new SQLException(e1);
}
finally {
IOUtils.closeQuietly(bufferedReader);
}
String updateCountStr = StringUtils.substringAfter(line1, "getUpdateCount=");
try {
updateCount = Integer.parseInt(updateCountStr);
} catch (NumberFormatException e) {
throw new SQLException(Tag.AWAKE_PRODUCT_FAIL
+ e.getMessage(),
new IOException(e.getMessage(), e));
}
}
/**
* Says if the file contains a ResultSet or if the file contains an
* UpdateCount. if file contains an UpdateCount ==> first line is numeric
*
* @param file
* the received file from the server
* @return true if the file is a result set
*/
protected boolean isFileResultSet(File file) throws SQLException {
LineNumberReader lineNumberReader = null;
try {
lineNumberReader = new LineNumberReader(new FileReader(file));
String line = lineNumberReader.readLine();
line = line.trim();
if (line.startsWith("getUpdateCount=")) {
String updateCountStr = StringUtils.substringAfter(line,
"getUpdateCount=");
try {
updateCount = Integer.parseInt(updateCountStr);
} catch (NumberFormatException e) {
throw new SQLException(Tag.AWAKE_PRODUCT_FAIL
+ e.getMessage(),
new IOException(e.getMessage(), e));
}
return false;
} else if (line.startsWith(CallableParms.NO_RESULT_SET)) {
return false;
} else {
return true;
}
} catch (IOException e) {
JdbcHttpTransferUtil.wrapExceptionAsSQLException(e);
} finally {
IOUtils.closeQuietly(lineNumberReader);
}
return false;
}
/**
* Executes the given SQL statement, which may return multiple results, and
* signals the driver that any auto-generated keys should be made available
* for retrieval. The driver will ignore this signal if the SQL statement is
* not an INSERT
statement, or an SQL statement able to return
* auto-generated keys (the list of such statements is vendor-specific).
*
* In some (uncommon) situations, a single SQL statement may return multiple
* result sets and/or update counts. Normally you can ignore this unless you
* are (1) executing a stored procedure that you know may return multiple
* results or (2) you are dynamically executing an unknown SQL string.
*
* The execute
method executes an SQL statement and indicates
* the form of the first result. You must then use the methods
* getResultSet
or getUpdateCount
to retrieve the
* result, and getMoreResults
to move to any subsequent
* result(s).
*
* @param sql
* any SQL statement
* @param autoGeneratedKeys
* a constant indicating whether auto-generated keys should be
* made available for retrieval using the method
* getGeneratedKeys
; one of the following constants:
* Statement.RETURN_GENERATED_KEYS
or
* Statement.NO_GENERATED_KEYS
* @return true
if the first result is a ResultSet
* object; false
if it is an update count or there are
* no results
* @exception SQLException
* if a database access error occurs, this method is called
* on a closed Statement
or the second parameter
* supplied to this method is not
* Statement.RETURN_GENERATED_KEYS
or
* Statement.NO_GENERATED_KEYS
.
* @exception SQLFeatureNotSupportedException
* if the JDBC driver does not support this method with a
* constant of Statement.RETURN_GENERATED_KEYS
* @see #getResultSet
* @see #getUpdateCount
* @see #getMoreResults
* @see #getGeneratedKeys
*
* @since 1.4
*/
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
sql = sql.trim();
if (connectionHttp.isStatelessMode()) {
if (!connectionHttp.getAutoCommit()) {
throw new IllegalStateException(
Tag.AWAKE
+ "execute() can\'t be executed when auto commit is off.");
}
}
lastExecuteIsRaw = true;
StatementHolder statementHolder = new StatementHolder(sql, autoGeneratedKeys);
statementHolder.setPreparedStatement(false);
statementHolder.setExecuteUpdate(false);
statementHolder.setFetchSize(fetchSize);
statementHolder.setMaxRows(maxRows);
statementHolder.setQueryTimeout(queryTimeout);
statementHolder.setEscapeProcessing(escapeProcessingInt);
// Reset the fields values
rsFromExecute = null;
updateCount = -1;
// Send order to Server to SQL Executor
JdbcHttpExecuteRawTransfer jdbcHttpExecuteRawTransfer = new JdbcHttpExecuteRawTransfer(
connectionHttp, connectionHttp.getAuthenticationToken());
receiveFileFromExecute = jdbcHttpExecuteRawTransfer
.getFileFromExecuteRaw(statementHolder);
localFiles.add(receiveFileFromExecute);
debug("getFileFromexecuteOnServer() : " + receiveFileFromExecute);
boolean fileResultSet = isFileResultSet(receiveFileFromExecute);
if (fileResultSet) {
// Transform the Result Set in String back to an Result Set
// (emulated)
rsFromExecute = new ResultSetHttp(connectionHttp, statementHolder,
this, receiveFileFromExecute);
return true;
} else {
extractGetUpdateCount();
return false;
}
}
/**
* Executes the given SQL statement, which may return multiple results, and
* signals the driver that the auto-generated keys indicated in the given
* array should be made available for retrieval. This array contains the
* indexes of the columns in the target table that contain the
* auto-generated keys that should be made available. The driver will ignore
* the array if the SQL statement is not an INSERT
statement,
* or an SQL statement able to return auto-generated keys (the list of such
* statements is vendor-specific).
*
* Under some (uncommon) situations, a single SQL statement may return
* multiple result sets and/or update counts. Normally you can ignore this
* unless you are (1) executing a stored procedure that you know may return
* multiple results or (2) you are dynamically executing an unknown SQL
* string.
*
* The execute
method executes an SQL statement and indicates
* the form of the first result. You must then use the methods
* getResultSet
or getUpdateCount
to retrieve the
* result, and getMoreResults
to move to any subsequent
* result(s).
*
* @param sql
* any SQL statement
* @param columnIndexes
* an array of the indexes of the columns in the inserted row
* that should be made available for retrieval by a call to the
* method getGeneratedKeys
* @return true
if the first result is a ResultSet
* object; false
if it is an update count or there are
* no results
* @exception SQLException
* if a database access error occurs, this method is called
* on a closed Statement
or the elements in the
* int
array passed to this method are not valid
* column indexes
* @throws SQLFeatureNotSupportedException
* if the JDBC driver does not support this method
* @see #getResultSet
* @see #getUpdateCount
* @see #getMoreResults
*
* @since 1.4
*/
public boolean execute(String sql, int columnIndexes[]) throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
sql = sql.trim();
if (connectionHttp.isStatelessMode()) {
if (!connectionHttp.getAutoCommit()) {
throw new IllegalStateException(
Tag.AWAKE
+ "execute() can\'t be executed when auto commit is off.");
}
}
lastExecuteIsRaw = true;
StatementHolder statementHolder = new StatementHolder(sql, columnIndexes);
statementHolder.setPreparedStatement(false);
statementHolder.setExecuteUpdate(false);
statementHolder.setFetchSize(fetchSize);
statementHolder.setMaxRows(maxRows);
statementHolder.setQueryTimeout(queryTimeout);
statementHolder.setEscapeProcessing(escapeProcessingInt);
// Reset the fields values
rsFromExecute = null;
updateCount = -1;
// Send order to Server to SQL Executor
JdbcHttpExecuteRawTransfer jdbcHttpExecuteRawTransfer = new JdbcHttpExecuteRawTransfer(
connectionHttp, connectionHttp.getAuthenticationToken());
receiveFileFromExecute = jdbcHttpExecuteRawTransfer
.getFileFromExecuteRaw(statementHolder);
localFiles.add(receiveFileFromExecute);
debug("getFileFromexecuteOnServer() : " + receiveFileFromExecute);
boolean fileResultSet = isFileResultSet(receiveFileFromExecute);
if (fileResultSet) {
// Transform the Result Set in String back to an Result Set
// (emulated)
rsFromExecute = new ResultSetHttp(connectionHttp, statementHolder,
this, receiveFileFromExecute);
return true;
} else {
extractGetUpdateCount();
return false;
}
}
/**
* Executes the given SQL statement, which may return multiple results, and
* signals the driver that the auto-generated keys indicated in the given
* array should be made available for retrieval. This array contains the
* names of the columns in the target table that contain the auto-generated
* keys that should be made available. The driver will ignore the array if
* the SQL statement is not an INSERT
statement, or an SQL
* statement able to return auto-generated keys (the list of such statements
* is vendor-specific).
*
* In some (uncommon) situations, a single SQL statement may return multiple
* result sets and/or update counts. Normally you can ignore this unless you
* are (1) executing a stored procedure that you know may return multiple
* results or (2) you are dynamically executing an unknown SQL string.
*
* The execute
method executes an SQL statement and indicates
* the form of the first result. You must then use the methods
* getResultSet
or getUpdateCount
to retrieve the
* result, and getMoreResults
to move to any subsequent
* result(s).
*
* @param sql
* any SQL statement
* @param columnNames
* an array of the names of the columns in the inserted row that
* should be made available for retrieval by a call to the method
* getGeneratedKeys
* @return true
if the next result is a ResultSet
* object; false
if it is an update count or there are
* no more results
* @exception SQLException
* if a database access error occurs, this method is called
* on a closed Statement
or the elements of the
* String
array passed to this method are not
* valid column names
* @throws SQLFeatureNotSupportedException
* if the JDBC driver does not support this method
* @see #getResultSet
* @see #getUpdateCount
* @see #getMoreResults
* @see #getGeneratedKeys
*
* @since 1.4
*/
public boolean execute(String sql, String columnNames[]) throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
sql = sql.trim();
if (connectionHttp.isStatelessMode()) {
if (!connectionHttp.getAutoCommit()) {
throw new IllegalStateException(
Tag.AWAKE
+ "execute() can\'t be executed when auto commit is off.");
}
}
lastExecuteIsRaw = true;
StatementHolder statementHolder = new StatementHolder(sql, columnNames);
statementHolder.setPreparedStatement(false);
statementHolder.setExecuteUpdate(false);
statementHolder.setFetchSize(fetchSize);
statementHolder.setMaxRows(maxRows);
statementHolder.setQueryTimeout(queryTimeout);
statementHolder.setEscapeProcessing(escapeProcessingInt);
// Reset the fields values
rsFromExecute = null;
updateCount = -1;
// Send order to Server to SQL Executor
JdbcHttpExecuteRawTransfer jdbcHttpExecuteRawTransfer = new JdbcHttpExecuteRawTransfer(
connectionHttp, connectionHttp.getAuthenticationToken());
receiveFileFromExecute = jdbcHttpExecuteRawTransfer
.getFileFromExecuteRaw(statementHolder);
localFiles.add(receiveFileFromExecute);
debug("getFileFromexecuteOnServer() : " + receiveFileFromExecute);
boolean fileResultSet = isFileResultSet(receiveFileFromExecute);
if (fileResultSet) {
// Transform the Result Set in String back to an Result Set
// (emulated)
rsFromExecute = new ResultSetHttp(connectionHttp, statementHolder,
this, receiveFileFromExecute);
return true;
} else {
extractGetUpdateCount();
return false;
}
}
/**
* Retrieves the current result as a ResultSet
object. This
* method should be called only once per result.
*
* @return the current result as a ResultSet
object or
* null
if the result is an update count or there are
* no more results
* @exception SQLException
* if a database access error occurs
* @see #execute
*/
public ResultSet getResultSet() throws SQLException {
return rsFromExecute;
}
/**
* Retrieves the current result as an update count; if the result is a
* ResultSet
object or there are no more results, -1 is
* returned. This method should be called only once per result.
*
* @return the current result as an update count; -1 if the current result
* is a ResultSet
object or there are no more results
* @exception SQLException
* if a database access error occurs
* @see #execute
*/
public int getUpdateCount() throws SQLException {
return updateCount;
}
/**
* Moves to this Statement
object's next result, returns
* true
if it is a ResultSet
object, and
* implicitly closes any current ResultSet
object(s) obtained
* with the method getResultSet
.
*
*
* There are no more results when the following is true:
*
*
* (!getMoreResults() && (getUpdateCount() == -1)
*
*
* @return true
if the next result is a ResultSet
* object; false
if it is an update count or there are
* no more results
* @exception SQLException
* if a database access error occurs
* @see #execute
*/
public boolean getMoreResults() throws SQLException {
// always return false for now:
return false;
}
/**
* Retrieves the first warning reported by calls on this
* Statement
object. Subsequent Statement
object
* warnings will be chained to this SQLWarning
object.
*
*
* The warning chain is automatically cleared each time a statement is
* (re)executed. This method may not be called on a closed
* Statement
object; doing so will cause an
* SQLException
to be thrown.
*
*
* Note: If you are processing a ResultSet
object, any
* warnings associated with reads on that ResultSet
object will
* be chained on it rather than on the Statement
object that
* produced it.
*
* @return the first SQLWarning
object or null
if
* there are no warnings
* @exception SQLException
* if a database access error occurs or this method is called
* on a closed statement
*/
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
/**
* Clears all the warnings reported on this Statement
object.
* After a call to this method, the method getWarnings
will
* return null
until a new warning is reported for this
* Statement
object.
*
* @exception SQLException
* if a database access error occurs
*/
@Override
public void clearWarnings() throws SQLException {
// Does nothing
}
/**
* Retrieves the maximum number of rows that a ResultSet
object
* produced by this Statement
object can contain. If this limit
* is exceeded, the excess rows are silently dropped.
*
* @return the current maximum number of rows for a ResultSet
* object produced by this Statement
object; zero means
* there is no limit
* @exception SQLException
* if a database access error occurs
* @see #setMaxRows
*/
@Override
public int getMaxRows() throws SQLException {
return maxRows;
}
/**
* Sets the limit for the maximum number of rows that any
* ResultSet
object can contain to the given number. If the
* limit is exceeded, the excess rows are silently dropped.
*
* @param max
* the new max rows limit; zero means there is no limit
* @exception SQLException
* if a database access error occurs or the condition max >=
* 0 is not satisfied
* @see #getMaxRows
*/
@Override
public void setMaxRows(int max) throws SQLException {
maxRows = max;
}
/**
* Gives the JDBC driver a hint as to the number of rows that should be
* fetched from the database when more rows are needed. The number of rows
* specified affects only result sets created using this statement. If the
* value specified is zero, then the hint is ignored. The default value is
* zero.
*
* @param rows
* the number of rows to fetch
* @exception SQLException
* if a database access error occurs, or the condition 0 <=
* rows
<= this.getMaxRows()
is not
* satisfied.
* @since 1.2
* @see #getFetchSize
*/
@Override
public void setFetchSize(int rows) throws SQLException {
fetchSize = rows;
}
/**
* Retrieves the number of result set rows that is the default fetch size
* for ResultSet
objects generated from this
* Statement
object. If this Statement
object has
* not set a fetch size by calling the method setFetchSize
, the
* return value is implementation-specific.
*
* @return the default fetch size for result sets generated from this
* Statement
object
* @exception SQLException
* if a database access error occurs
* @since 1.2
* @see #setFetchSize
*/
@Override
public int getFetchSize() throws SQLException {
return fetchSize;
}
/**
* Sets escape processing on or off. If escape scanning is on (the default),
* the driver will do escape substitution before sending the SQL statement
* to the database.
*
* Note: Since prepared statements have usually been parsed prior to making
* this call, disabling escape processing for
* PreparedStatements
objects will have no effect.
*
* @param enable
* true
to enable escape processing;
* false
to disable it
* @exception SQLException
* if a database access error occurs
*/
@Override
public void setEscapeProcessing(boolean enable) throws SQLException {
this.escapeProcessingInt = enable ? 1 : 0;
}
/**
* Retrieves the number of seconds the driver will wait for a
* Statement
object to execute. If the limit is exceeded, a
* SQLException
is thrown.
*
* @return the current query timeout limit in seconds; zero means there is
* no limit
* @exception SQLException
* if a database access error occurs
* @see #setQueryTimeout
*/
public int getQueryTimeout() throws SQLException {
return this.queryTimeout;
}
/**
* Sets the number of seconds the driver will wait for a
* Statement
object to execute to the given number of seconds.
* If the limit is exceeded, an SQLException
is thrown.
*
* @param seconds
* the new query timeout limit in seconds; zero means there is no
* limit
* @exception SQLException
* if a database access error occurs or the condition seconds
* >= 0 is not satisfied
* @see #getQueryTimeout
*/
public void setQueryTimeout(int seconds) throws SQLException {
this.queryTimeout = seconds;
}
/**
* Retrieves the result set concurrency for ResultSet
objects
* generated by this Statement
object.
*
* @return either ResultSet.CONCUR_READ_ONLY
or
* ResultSet.CONCUR_UPDATABLE
* @exception SQLException
* if a database access error occurs
* @since 1.2
*/
@Override
public int getResultSetConcurrency() throws SQLException {
return this.resultSetConcurrency;
}
/**
* Retrieves the result set type for ResultSet
objects
* generated by this Statement
object.
*
* @return one of ResultSet.TYPE_FORWARD_ONLY
,
* ResultSet.TYPE_SCROLL_INSENSITIVE
, or
* ResultSet.TYPE_SCROLL_SENSITIVE
* @exception SQLException
* if a database access error occurs
* @since 1.2
*/
@Override
public int getResultSetType() throws SQLException {
return this.resultSetType;
}
/**
* Retrieves the result set holdability for ResultSet
objects
* generated by this Statement
object.
*
* @return either ResultSet.HOLD_CURSORS_OVER_COMMIT
or
* ResultSet.CLOSE_CURSORS_AT_COMMIT
* @exception SQLException
* if a database access error occurs
*
* @since 1.4
*/
@Override
public int getResultSetHoldability() throws SQLException {
return this.resultSetHoldability;
}
/**
* Adds the given SQL command to the current list of commmands for this
* Statement
object. The commands in this list can be executed
* as a batch by calling the method executeBatch
.
*
* NOTE: This method is optional.
*
* @param sql
* typically this is a static SQL INSERT
or
* UPDATE
statement
* @exception SQLException
* if a database access error occurs, or the driver does not
* support batch updates
* @see #executeBatch
* @since 1.2
*/
@Override
public void addBatch(String sql) throws SQLException {
testIfClosed();
if (sql == null) {
throw new SQLException("sql order is null!");
}
sql = sql.trim();
if (connectionHttp.isStatelessMode()) {
if (connectionHttp.getAutoCommit()) {
throw new IllegalStateException(Tag.AWAKE
+ "addBatch() can\'t be called when auto commit is on.");
}
}
connectionHttp.resetStatementHolderList();
// Add the statement to the statement list
StatementHolder statementHolder = new StatementHolder(sql,
resultSetType, resultSetConcurrency, resultSetHoldability);
statementHolder.setPreparedStatement(false);
statementHolder.setExecuteUpdate(true);
batchHolderFileList.add(statementHolder);
}
/**
* Empties this Statement
object's current list of SQL
* commands.
*
* NOTE: This method is optional.
*
* @exception SQLException
* if a database access error occurs or the driver does not
* support batch updates
* @see #addBatch
* @since 1.2
*/
@Override
public void clearBatch() throws SQLException {
// batchHolderList = new Vector();
this.batchHolderFileList = new StatementHolderFileList(connectionHttp);
}
/**
* Submits a batch of commands to the database for execution and if all
* commands execute successfully, returns an array of update counts. The
* int
elements of the array that is returned are ordered to
* correspond to the commands in the batch, which are ordered according to
* the order in which they were added to the batch. The elements in the
* array returned by the method executeBatch
may be one of the
* following:
*
* - A number greater than or equal to zero -- indicates that the command
* was processed successfully and is an update count giving the number of
* rows in the database that were affected by the command's execution
*
- A value of
SUCCESS_NO_INFO
-- indicates that the command
* was processed successfully but that the number of rows affected is
* unknown
*
* If one of the commands in a batch update fails to execute properly, this
* method throws a BatchUpdateException
, and a JDBC driver may
* or may not continue to process the remaining commands in the batch.
* However, the driver's behavior must be consistent with a particular DBMS,
* either always continuing to process commands or never continuing to
* process commands. If the driver continues processing after a failure, the
* array returned by the method
* BatchUpdateException.getUpdateCounts
will contain as many
* elements as there are commands in the batch, and at least one of the
* elements will be the following:
*
*
- A value of
EXECUTE_FAILED
-- indicates that the command
* failed to execute successfully and occurs only if a driver continues to
* process commands after a command fails
*
*
* A driver is not required to implement this method. The possible
* implementations and return values have been modified in the Java 2 SDK,
* Standard Edition, version 1.3 to accommodate the option of continuing to
* proccess commands in a batch update after a
* BatchUpdateException
obejct has been thrown.
*
* @return an array of update counts containing one element for each command
* in the batch. The elements of the array are ordered according to
* the order in which commands were added to the batch.
* @exception SQLException
* if a database access error occurs or the driver does not
* support batch statements. Throws
* {@link BatchUpdateException} (a subclass of
* SQLException
) if one of the commands sent to
* the database fails to execute properly or attempts to
* return a result set.
* @since 1.3
*/
@Override
public int[] executeBatch() throws SQLException {
int updateCounts[] = new int[batchHolderFileList.size()];
if (batchHolderFileList.size() == 0) {
return updateCounts;
}
JdbcHttpBatchTransfer jdbcHttpBatchTransfer = new JdbcHttpBatchTransfer(
connectionHttp, connectionHttp.getAuthenticationToken());
String updateCountsStr = jdbcHttpBatchTransfer
.getStringFromExecuteStatementBatchOnServer(batchHolderFileList);
updateCounts = IntArrayTransport.fromJson(updateCountsStr);
clearBatch();
return updateCounts;
}
/**
* Retrieves the Connection
object that produced this
* Statement
object.
*
* @return the connection that produced this statement
* @exception SQLException
* if a database access error occurs
* @since 1.2
*/
@Override
public Connection getConnection() throws SQLException {
return this.connectionHttp;
}
/**
* Releases this Statement
object's database and JDBC resources
* immediately instead of waiting for this to happen when it is
* automatically closed. It is generally good practice to release resources
* as soon as you are finished with them to avoid tying up database
* resources.
*
* Calling the method close
on a Statement
object
* that is already closed has no effect.
*
* Note: A Statement
object is automatically closed when
* it is garbage collected. When a Statement
object is closed,
* its current ResultSet
object, if one exists, is also closed.
*
* @exception SQLException
* if a database access error occurs
*/
public void close() throws SQLException {
super.close();
batchHolderFileList = null;
for (File localFile : localFiles) {
boolean deleted = localFile.delete();
if (!deleted) {
//System.err.println("localFile not deleted: " + localFile);
}
}
}
/**
* Displays the given message if DEBUG is set.
*
* @param sMsg
* the debug message
*/
private void debug(String s) {
if (DEBUG) {
AwakeClientLogger.log(s);
}
}
}