/** * Executes a generic CompiledStatement. Execution includes first building any subquery result * dependencies and clearing them after the main result is built. * * @return the result of executing the statement * @param cs any valid CompiledStatement */ Result execute(CompiledStatement cs) { Result result = null; DatabaseManager.gc(); try { cs.materializeSubQueries(session); result = executeImpl(cs); } catch (Throwable t) { // t.printStackTrace(); result = new Result(t, cs.sql); } // clear redundant data cs.dematerializeSubQueries(); if (result == null) { result = emptyResult; } return result; }
/** * 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); }
/** * Binds the specified CompiledStatement object into this object's active compiled statement * registry. It is trusted completely that the caller is actually registering a previously * unregistered CompiledStatement object; no checks are done in the interest of performance. * Typically, the only caller should be a Session that is attempting to perform a prepare and has * discovered that this CompiledStatementManager has no such statement registered, as indicated by * a negative return value from {@link #getStatementID(String) getStatementID()}. * * @param cs The CompiledStatement to add * @return The compiled statement id assigned to the freshly bound CompiledStatement object */ synchronized int registerStatement(CompiledStatement cs) { cs.id = nextID(); cs.use = 0; sqlMap.put(cs.sql, cs); csidMap.put(cs.id, cs); return cs.id; }
private CompiledStatement sqlCompileStatement(String sql, int type) throws HsqlException { Tokenizer tokenizer; String token; Parser parser; int cmd; CompiledStatement cs; boolean isCmdOk; tokenizer = new Tokenizer(sql); parser = new Parser(dDatabase, tokenizer, this); token = tokenizer.getString(); cmd = Token.get(token); isCmdOk = true; switch (cmd) { case Token.SELECT: { cs = parser.compileSelectStatement(null); break; } case Token.INSERT: { cs = parser.compileInsertStatement(null); break; } case Token.UPDATE: { cs = parser.compileUpdateStatement(null); break; } case Token.DELETE: { cs = parser.compileDeleteStatement(null); break; } case Token.CALL: { if (type != CompiledStatement.CALL) { throw Trace.error(Trace.ASSERT_FAILED, "not a CALL statement"); } cs = parser.compileCallStatement(null); break; } default: { isCmdOk = false; cs = null; break; } } // In addition to requiring that the compilation was successful, // we also require that the submitted sql represents a _single_ // valid DML statement. if (!isCmdOk) { throw Trace.error(Trace.UNEXPECTED_TOKEN, token); } // fredt - now accepts semicolon and whitespace at the end of statement // fredt - investigate if it should or not while (tokenizer.getPosition() < tokenizer.getLength()) { token = tokenizer.getString(); Trace.check( token.length() == 0 || token.equals(Token.T_SEMICOLON), Trace.UNEXPECTED_TOKEN, token); } // - need to be able to key cs against its sql in statement pool // - also need to be able to revalidate its sql occasionally cs.sql = sql; return cs; }