/** * 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); }
/** * Opens this database. The database should be opened after construction. or reopened by the * close(int closemode) method during a "shutdown compact". Closes the log if there is an error. */ void reopen() throws HsqlException { boolean isNew; setState(DATABASE_OPENING); try { databaseProperties = new HsqlDatabaseProperties(this); isNew = !DatabaseURL.isFileBasedDatabaseType(sType) || !databaseProperties.checkFileExists(); if (isNew && urlProperties.isPropertyTrue("ifexists")) { throw Trace.error(Trace.DATABASE_NOT_EXISTS, sName); } databaseProperties.load(); databaseProperties.setURLProperties(urlProperties); compiledStatementManager.reset(); nameManager = new HsqlNameManager(); granteeManager = new GranteeManager(this); userManager = new UserManager(this); hAlias = Library.getAliasMap(); schemaManager = new SchemaManager(this); bReferentialIntegrity = true; sessionManager = new SessionManager(this); txManager = new TransactionManager(this); collation = new Collation(); dbInfo = DatabaseInformation.newDatabaseInformation(this); databaseProperties.setDatabaseVariables(); if (DatabaseURL.isFileBasedDatabaseType(sType)) { logger.openLog(this); } if (isNew) { sessionManager .getSysSession() .sqlExecuteDirectNoPreChecks("CREATE USER SA PASSWORD \"\" ADMIN"); logger.synchLogForce(); } dbInfo.setWithContent(true); } catch (Throwable e) { logger.closeLog(Database.CLOSEMODE_IMMEDIATELY); logger.releaseLock(); setState(DATABASE_SHUTDOWN); clearStructures(); DatabaseManager.removeDatabase(this); if (!(e instanceof HsqlException)) { e = Trace.error(Trace.GENERAL_ERROR, e.toString()); } throw (HsqlException) e; } setState(DATABASE_ONLINE); }
/** * 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); }
/** * Ensures system table producer's table cache, if it exists, is set dirty. After this call * up-to-date versions are generated in response to system table requests. * * <p>Also resets all prepared statements if a change to database structure can possibly affect * any existing prepared statement's validity. * * <p>The argument is false if the change to the database structure does not affect the prepared * statement, such as when a new table is added. * * <p>The argument is typically true when a database object is dropped, altered or a permission * was revoked. * * @param resetPrepared If true, reset all prepared statements. */ public void setMetaDirty(boolean resetPrepared) { if (dbInfo != null) { dbInfo.setDirty(); } if (resetPrepared) { compiledStatementManager.resetStatements(); } }
/** * Opens this database. The database should be opened after construction. or reopened by the * close(int closemode) method during a "shutdown compact". Closes the log if there is an error. */ void reopen() throws HsqlException { setState(DATABASE_OPENING); try { User sysUser; isNew = (sType == DatabaseManager.S_MEM || !HsqlProperties.checkFileExists(sPath, isFilesInJar(), getClass())); databaseProperties = new HsqlDatabaseProperties(this); databaseProperties.load(); databaseProperties.setURLProperties(urlProperties); compiledStatementManager.reset(); tTable = new HsqlArrayList(); userManager = new UserManager(); hAlias = Library.getAliasMap(); nameManager = new HsqlNameManager(); triggerNameList = new DatabaseObjectNames(); indexNameList = new DatabaseObjectNames(); constraintNameList = new DatabaseObjectNames(); sequenceManager = new SequenceManager(); bReferentialIntegrity = true; sysUser = userManager.createSysUser(this); sessionManager = new SessionManager(this, sysUser); dInfo = DatabaseInformation.newDatabaseInformation(this); if (sType != DatabaseManager.S_MEM) { logger.openLog(this); } if (isNew) { sessionManager .getSysSession() .sqlExecuteDirectNoPreChecks("CREATE USER SA PASSWORD \"\" ADMIN"); } dInfo.setWithContent(true); } catch (Throwable e) { logger.closeLog(Database.CLOSEMODE_IMMEDIATELY); logger.releaseLock(); setState(DATABASE_SHUTDOWN); clearStructures(); if (!(e instanceof HsqlException)) { e = Trace.error(Trace.GENERAL_ERROR, e.toString()); } throw (HsqlException) e; } setState(DATABASE_ONLINE); }
/** * Retrieves the result of freeing the statement with the given id. * * @param csid the numeric identifier of the statement * @return the result of freeing the indicated statement */ private Result sqlFreeStatement(int csid) { boolean existed; Result result; existed = compiledStatementManager.freeStatement(csid, iId); result = new Result(ResultConstants.UPDATECOUNT); if (existed) { result.iUpdateCount = 1; } return result; }
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; }