/** * Retrieves a MULTI Result describing three aspects of the CompiledStatement prepared from the * SQL argument for execution in this session context: * * <p> * * <ol> * <li>An PREPARE_ACK mode Result describing id of the statement prepared by this request. This * is used by the JDBC implementation to later identify to the engine which prepared * statement to execute. * <li>A DATA mode result describing the statement's result set metadata. This is used to * generate the JDBC ResultSetMetaData object returned by PreparedStatement.getMetaData and * CallableStatement.getMetaData. * <li>A DATA mode result describing the statement's parameter metdata. This is used to by the * JDBC implementation to determine how to send parameters back to the engine when executing * the statement. It is also used to construct the JDBC ParameterMetaData object for * PreparedStatements and CallableStatements. * * @param sql a string describing the desired statement object * @throws HsqlException is a database access error occurs * @return a MULTI Result describing the compiled statement. */ private Result sqlPrepare(String sql, int type) { CompiledStatement cs = null; int csid = compiledStatementManager.getStatementID(sql); Result rsmd; Result pmd; // ...check valid... if (csid > 0 && compiledStatementManager.isValid(csid, iId)) { cs = compiledStatementManager.getStatement(csid); rsmd = cs.describeResultSet(); pmd = cs.describeParameters(); return Result.newPrepareResponse(csid, rsmd, pmd); } // ...compile or (re)validate try { cs = sqlCompileStatement(sql, type); } catch (Throwable t) { return new Result(t, sql); } // boucherb@users // TODO: It is still unclear to me as to whether, in the case of revalidation // v.s. first compilation, the newly created CompiledStatement // object should replace the old one in the CompiledStatementManager // repository. If, for instance, a table column has been dropped and // then a column with the same name is added with different data type, // constraints, etc., the existing CompiledStatement object is not // equivalent in its effect and perhaps runs the risk of corrupting // the database. For instance, a CompiledStatement contains // fixed mappings from positions in a column value expression array // to column positions in the target table. Thus, an alteration to a // target table may leave an existing CompiledStatement's SQL // character sequence valid, but not its execution plan. // OTOH, simply replacing the old execution plan with a new one // may also be undesirable, as the intended and actual effects // may become divergent. Once again, for example, if a column name // comes to mean a different column, then by blindly replacing the // old CompiledStatement with the new, inserting, updating // or predicating happens upon an unintended column. // The only DDL operations that raise such dangers are sequences // involving dropping a columns and then adding an incompatible one // of the same name at the same position or alterations that // change the positions of columns. All other alterations to // database objects should, in theory, allow the original // CompiledStatement to continue to operate as intended. if (csid <= 0) { csid = compiledStatementManager.registerStatement(cs); } compiledStatementManager.setValidated(csid, iId, dDatabase.getDDLSCN()); rsmd = cs.describeResultSet(); pmd = cs.describeParameters(); return Result.newPrepareResponse(csid, rsmd, pmd); }
/** * Retrieves the result of executing the prepared statement whose csid and parameter values/types * are encapsulated by the cmd argument. * * @return the result of executing the statement */ private Result sqlExecute(Result cmd) { int csid; Object[] pvals; CompiledStatement cs; Expression[] parameters; csid = cmd.getStatementID(); pvals = cmd.getParameterData(); cs = compiledStatementManager.getStatement(csid); if (cs == null) { String msg = "Statement not prepared for csid: " + csid; return new Result(msg, "22019", Trace.INVALID_IDENTIFIER); } if (!compiledStatementManager.isValid(csid, iId)) { Result r = sqlPrepare(cs.sql, cs.type); if (r.iMode == ResultConstants.ERROR) { // TODO: // maybe compiledStatementManager.freeStatement(csid,iId);? return r; } } parameters = cs.parameters; // Don't bother with array length or type checks...trust the client // to send pvals with length at least as long // as parameters array and with each pval already converted to the // correct internal representation corresponding to the type try { for (int i = 0; i < parameters.length; i++) { parameters[i].bind(pvals[i]); } } catch (Throwable t) { return new Result(t, cs.sql); } return compiledStatementExecutor.execute(cs); }
private Result sqlExecuteBatch(Result cmd) { int csid; Object[] pvals; Record record; Result in; Result out; Result err; CompiledStatement cs; Expression[] parameters; int[] updateCounts; int count; csid = cmd.getStatementID(); cs = compiledStatementManager.getStatement(csid); if (cs == null) { String msg = "Statement not prepared for csid: " + csid; return new Result(msg, "22019", Trace.INVALID_IDENTIFIER); } if (!compiledStatementManager.isValid(csid, iId)) { out = sqlPrepare(cs.sql, cs.type); if (out.iMode == ResultConstants.ERROR) { return out; } } parameters = cs.parameters; count = 0; updateCounts = new int[cmd.getSize()]; record = cmd.rRoot; out = new Result(ResultConstants.SQLEXECUTE, updateCounts, 0); err = new Result(ResultConstants.ERROR); while (record != null) { pvals = record.data; in = err; try { for (int i = 0; i < parameters.length; i++) { parameters[i].bind(pvals[i]); } in = compiledStatementExecutor.execute(cs); } catch (Throwable t) { // t.printStackTrace(); // System.out.println(t.toString()); // if (t instanceof OutOfMemoryError) { // System.gc(); // } // "in" alread equals "err" // maybe test for OOME and do a gc() ? // t.printStackTrace(); } // On the client side, iterate over the vals and throw // a BatchUpdateException if a batch status value of // esultConstants.EXECUTE_FAILED is encountered in the result switch (in.iMode) { case ResultConstants.UPDATECOUNT: { updateCounts[count++] = in.iUpdateCount; break; } case ResultConstants.DATA: { // FIXME: we don't have what it takes yet // to differentiate between things like // stored procedure calls to methods with // void return type and select statements with // a single row/column containg null updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; break; } case ResultConstants.ERROR: default: { updateCounts[count++] = ResultConstants.EXECUTE_FAILED; break; } } record = record.next; } return out; }