/** * @param args * @return */ @JRubyMethod(optional = 1, rest = true) public IRubyObject execute_non_query(IRubyObject[] args) { Ruby runtime = getRuntime(); Connection connection_instance = (Connection) api.getInstanceVariable(this, "@connection"); java.sql.Connection conn = connection_instance.getInternalConnection(); checkConnectionNotClosed(conn); IRubyObject insert_key = runtime.getNil(); RubyClass resultClass = Result.createResultClass(runtime, driver); // affectedCount == 1 means 1 updated row // or 1 row in result set that represents returned key (insert...returning), // other values represents number of updated rows int affectedCount = 0; PreparedStatement sqlStatement = null; // if usePreparedStatement returns false Statement sqlSimpleStatement = null; java.sql.ResultSet keys = null; // String sqlText = prepareSqlTextForPs(api.getInstanceVariable(recv, // "@text").asJavaString(), recv, args); String doSqlText = api.convertToRubyString(api.getInstanceVariable(this, "@text")).getUnicodeValue(); String sqlText = prepareSqlTextForPs(doSqlText, args); // additional callback for driver specific SQL statement changes sqlText = driver.prepareSqlTextForPs(sqlText, args); boolean usePS = usePreparedStatement(sqlText, args); boolean hasReturnParam = false; try { if (usePS) { if (driver.supportsConnectionPrepareStatementMethodWithGKFlag()) { sqlStatement = conn.prepareStatement( sqlText, driver.supportsJdbcGeneratedKeys() ? Statement.RETURN_GENERATED_KEYS : Statement.NO_GENERATED_KEYS); } else { // If java.sql.PreparedStatement#getGeneratedKeys() is not supported, // then it is important to call java.sql.Connection#prepareStatement(String) // -- with just a single parameter -- rather java.sql.Connection# // prepareStatement(String, int) (and passing in Statement.NO_GENERATED_KEYS). // Some less-than-complete JDBC drivers do not implement all of // the overloaded prepareStatement methods: the main culprit // being SQLiteJDBC which currently throws an ugly (and cryptic) // "NYI" SQLException if Connection#prepareStatement(String, int) // is called. sqlStatement = conn.prepareStatement(sqlText); } hasReturnParam = prepareStatementFromArgs(sqlText, sqlStatement, args); } else { sqlSimpleStatement = conn.createStatement(); } long startTime = System.currentTimeMillis(); if (usePS) { boolean hasResult = sqlStatement.execute(); if (hasResult) { keys = sqlStatement.getResultSet(); } else { affectedCount = sqlStatement.getUpdateCount(); } } else { sqlSimpleStatement.execute(sqlText); } long endTime = System.currentTimeMillis(); if (usePS) debug(driver.statementToString(sqlStatement), Long.valueOf(endTime - startTime)); else debug(sqlText, Long.valueOf(endTime - startTime)); if (usePS && keys == null) { if (driver.supportsJdbcGeneratedKeys()) { // Derby, H2, and MySQL all support getGeneratedKeys(), but only // to varying extents. // // However, javaConn.getMetaData().supportsGetGeneratedKeys() // currently returns FALSE for the Derby driver, as its support // is limited. As such, we use supportsJdbcGeneratedKeys() from // our own driver definition. // // See http://issues.apache.org/jira/browse/DERBY-242 // See http://issues.apache.org/jira/browse/DERBY-2631 // (Derby only supplies getGeneratedKeys() for auto-incremented // columns) // // apparently the prepared statements always provide the // generated keys keys = sqlStatement.getGeneratedKeys(); } else if (hasReturnParam) { // Used in Oracle for INSERT ... RETURNING ... INTO ... statements insert_key = runtime.newFixnum(driver.getPreparedStatementReturnParam(sqlStatement)); } else { // If there is no support, then a custom method can be defined // to return a ResultSet with keys keys = driver.getGeneratedKeys(conn); } } if (usePS && keys != null) { insert_key = unmarshal_id_result(keys); if (insert_key != runtime.getNil()) affectedCount = (affectedCount > 0) ? affectedCount : 1; } } catch (SQLException sqle) { throw Errors.newQueryError(runtime, driver, sqle, usePS ? sqlStatement : sqlSimpleStatement); } finally { if (usePS) { JDBCUtil.close(keys, sqlStatement); } else { JDBCUtil.close(keys, sqlSimpleStatement); } keys = null; sqlStatement = null; sqlSimpleStatement = null; } IRubyObject affected_rows = runtime.newFixnum(affectedCount); return api.callMethod(resultClass, "new", new IRubyObject[] {this, affected_rows, insert_key}); }
@JRubyMethod(optional = 1, rest = true) public static IRubyObject execute_non_query(IRubyObject recv, IRubyObject[] args) { Ruby runtime = recv.getRuntime(); IRubyObject connection_instance = api.getInstanceVariable(recv, "@connection"); IRubyObject wrapped_jdbc_connection = api.getInstanceVariable(connection_instance, "@connection"); if (wrapped_jdbc_connection.isNil()) { throw DataObjectsUtils.newDriverError( runtime, errorName, "This connection has already been closed."); } java.sql.Connection conn = getConnection(wrapped_jdbc_connection); IRubyObject insert_key = runtime.getNil(); RubyClass resultClass = Result.createResultClass(runtime, moduleName, errorName, driver); // affectedCount == 1 means 1 updated row // or 1 row in result set that represents returned key (insert...returning), // other values represents numer of updated rows int affectedCount = 0; PreparedStatement sqlStatement = null; java.sql.ResultSet keys = null; // String sqlText = prepareSqlTextForPs(api.getInstanceVariable(recv, "@text").asJavaString(), // recv, args); String doSqlText = api.convertToRubyString(api.getInstanceVariable(recv, "@text")).getUnicodeValue(); String sqlText = prepareSqlTextForPs(doSqlText, recv, args); try { if (driver.supportsConnectionPrepareStatementMethodWithGKFlag()) { sqlStatement = conn.prepareStatement( sqlText, driver.supportsJdbcGeneratedKeys() ? Statement.RETURN_GENERATED_KEYS : Statement.NO_GENERATED_KEYS); } else { // If java.sql.PreparedStatement#getGeneratedKeys() is not supported, // then it is important to call java.sql.Connection#prepareStatement(String) // -- with just a single parameter -- rather java.sql.Connection# // prepareStatement(String, int) (and passing in Statement.NO_GENERATED_KEYS). // Some less-than-complete JDBC drivers do not implement all of // the overloaded prepareStatement methods: the main culprit // being SQLiteJDBC which currently throws an ugly (and cryptic) // "NYI" SQLException if Connection#prepareStatement(String, int) // is called. sqlStatement = conn.prepareStatement(sqlText); } prepareStatementFromArgs(sqlStatement, recv, args); // javaConn.setAutoCommit(true); // hangs with autocommit set to false // sqlStatement.setMaxRows(); long startTime = System.currentTimeMillis(); try { if (sqlText.contains("RETURNING")) { keys = sqlStatement.executeQuery(); } else { affectedCount = sqlStatement.executeUpdate(); } } catch (SQLException sqle) { // This is to handle the edge case of SELECT sleep(1): // an executeUpdate() will throw a SQLException if a SELECT // is passed, so we try the same query again with execute() affectedCount = 0; sqlStatement.execute(); } long endTime = System.currentTimeMillis(); debug(recv.getRuntime(), driver.toString(sqlStatement), Long.valueOf(endTime - startTime)); if (keys == null) { if (driver.supportsJdbcGeneratedKeys()) { // Derby, H2, and MySQL all support getGeneratedKeys(), but only // to varying extents. // // However, javaConn.getMetaData().supportsGetGeneratedKeys() // currently returns FALSE for the Derby driver, as its support // is limited. As such, we use supportsJdbcGeneratedKeys() from // our own driver definition. // // See http://issues.apache.org/jira/browse/DERBY-242 // See http://issues.apache.org/jira/browse/DERBY-2631 // (Derby only supplies getGeneratedKeys() for auto-incremented // columns) // // apparently the prepared statements always provide the // generated keys keys = sqlStatement.getGeneratedKeys(); } else { // If there is no support, then a custom method can be defined // to return a ResultSet with keys keys = driver.getGeneratedKeys(conn); } } if (keys != null) { insert_key = unmarshal_id_result(runtime, keys); affectedCount = (affectedCount > 0) ? affectedCount : 1; } // not needed as it will be closed in the finally clause // sqlStatement.close(); // sqlStatement = null; } catch (SQLException sqle) { // TODO: log // sqle.printStackTrace(); throw newQueryError(runtime, sqle, sqlStatement); } finally { if (sqlStatement != null) { try { sqlStatement.close(); } catch (SQLException sqle2) { } } } // return nil if no updates are made if (affectedCount <= 0) { return runtime.getNil(); } IRubyObject affected_rows = runtime.newFixnum(affectedCount); IRubyObject result = api.callMethod(resultClass, "new", new IRubyObject[] {recv, affected_rows, insert_key}); return result; }