All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.StatementManager Maven / Gradle / Ivy

There is a newer version: 2.7.4
Show newest version
/* Copyright (c) 2001-2021, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.lib.HashSet;
import org.hsqldb.lib.LongKeyHashMap;
import org.hsqldb.lib.ObjectComparator;
import org.hsqldb.result.Result;
import org.hsqldb.result.ResultMetaData;

/**
 * This class manages the reuse of Statement objects for prepared
 * statements for a Session instance.

* * A compiled statement is registered by a session to be managed.

* * The sql statement text distinguishes different compiled statements and acts * as lookup key when a session initially looks for an existing instance of * the compiled sql statement.

* * The unique compiled statement id for the sql statement is used to access the * statement.

* * Changes to database structure via DDL statements, will result in all * registered Statement objects to become invalidated. This is done by * comparing the schema change and compile timestamps. When a session * subsequently attempts to use an invalidated Statement via its id, it will * reinstantiate the Statement using its sql statement still held by this class.

* * This class keeps count of the number of time each registered compiled * statement is linked to a session. It unregisters a compiled statement when * it is not in use.

* * Modified by fredt@users from the original by campbell-burnet@users to * simplify, support multiple identical prepared statements per session, and * avoid memory leaks. Changed implementation to a session object for optimised * access.

* * @author Campbell Burnet (campbell-burnet@users dot sourceforge.net) * @author Fred Toussi (fredt@users dot sourceforge.net) * * @version 2.6.1 * @since 1.7.2 */ public final class StatementManager { /** The Database for which this is managing Statement objects. */ private Database database; /** The Session for which this is managing Statement objects */ private Session session; /** Set of wrappers for Statement object. */ private HashSet statementSet; /** Map: Statement id (int) => wrapper for Statement object. */ private LongKeyHashMap csidMap; /** * Monotonically increasing counter used to assign unique ids to * Statement objects. */ private long next_cs_id; /** * Constructs a new instance of StatementManager. * * @param session the session instance for which this object is to * manage Statement objects. */ StatementManager(Session session) { this.session = session; this.database = session.database; statementSet = new HashSet(32, new StatementComparator()); csidMap = new LongKeyHashMap(); next_cs_id = 0; } /** * Clears all internal data structures, removing any references to Statements. */ void reset() { statementSet.clear(); csidMap.clear(); next_cs_id = 0; } /** * Retrieves the next Statement identifier in the sequence. * * @return the next Statement identifier in the sequence. */ private long nextID() { next_cs_id++; return next_cs_id; } /** * Returns an existing Statement object with the given * statement identifier. Returns null if the Statement object * has expired and cannot be recompiled * * @param csid the identifier of the requested Statement object * @return the requested Statement object */ public Statement getStatement(long csid) { StatementWrapper sw = (StatementWrapper) csidMap.get(csid); if (sw == null) { return null; } return getStatement(sw); } private Statement getStatement(StatementWrapper sw) { Statement statement = sw.statement; if (statement.getCompileTimestamp() < database.schemaManager.getSchemaChangeTimestamp()) { Statement newStatement = recompileStatement(statement); if (newStatement == null) { removeStatement(statement.getID()); return null; } newStatement.setCompileTimestamp( database.txManager.getGlobalChangeTimestamp()); sw.statement = newStatement; return newStatement; } return sw.statement; } /** * Recompiles an existing statement. * * Used by transaction manager for all statements, prepared or not prepred. * * @param statement the old expired statement * @return the requested Statement object */ public Statement getStatement(Statement statement) { long csid = statement.getID(); if (csid != 0) { StatementWrapper sw = (StatementWrapper) csidMap.get(csid); if (sw != null) { return getStatement(sw); } } return recompileStatement(statement); } /** * Returns an up-to-date Statement using the SQL and settings of * the original. Returns null if the SQL cannot be compiled. * * @param cs the old expired statement * @return the new Statement object */ private Statement recompileStatement(Statement cs) { HsqlName oldSchema = session.getCurrentSchemaHsqlName(); Statement newStatement; // revalidate with the original schema try { HsqlName schema = cs.getSchemaName(); int props = cs.getCursorPropertiesRequest(); if (schema != null) { // checks the old schema exists session.setSchema(schema.name); } boolean setGenerated = cs.generatedResultMetaData() != null; newStatement = session.compileStatement(cs.getSQL(), props); newStatement.setCursorPropertiesRequest(props); if (!cs.getResultMetaData().areTypesCompatible( newStatement.getResultMetaData())) { return null; } if (!cs.getParametersMetaData().areTypesCompatible( newStatement.getParametersMetaData())) { return null; } newStatement.setCompileTimestamp( database.txManager.getGlobalChangeTimestamp()); if (setGenerated) { StatementDML si = (StatementDML) cs; newStatement.setGeneratedColumnInfo(si.generatedType, si.generatedInputMetaData); } } catch (Throwable t) { return null; } finally { session.setCurrentSchemaHsqlName(oldSchema); } return newStatement; } /** * Registers a Statement to be managed. * * @param wrapper the wrapper for the Statement to add * @return The statement id assigned to the Statement object */ private long registerStatement(StatementWrapper wrapper) { Statement cs = wrapper.statement; cs.setCompileTimestamp(database.txManager.getGlobalChangeTimestamp()); long csid = nextID(); cs.setID(csid); statementSet.add(wrapper); csidMap.put(csid, wrapper); return csid; } /** * Removes a link between a PreparedStatement and a Statement. If the * statement is not linked with any other PreparedStatement, it is * removed from management. * * @param csid the Statement identifier */ void freeStatement(long csid) { StatementWrapper sw = (StatementWrapper) csidMap.get(csid); if (sw == null) { return; } sw.usageCount--; if (sw.usageCount == 0) { removeStatement(csid); } } /** * Removes an invalidated Statement. * * @param csid the Statement identifier */ private void removeStatement(long csid) { if (csid <= 0) { // statement was never added return; } StatementWrapper sw = (StatementWrapper) csidMap.remove(csid); if (sw != null) { statementSet.remove(sw); } } /** * Compiles an SQL statement and returns a Statement Object * * @param cmd the Result holding the SQL * @return Statement */ Statement compile(Result cmd) { StatementWrapper newWrapper = new StatementWrapper(); newWrapper.sql = cmd.getMainString(); newWrapper.cursorProps = cmd.getExecuteProperties(); newWrapper.generatedType = cmd.getGeneratedResultType(); newWrapper.generatedMetaData = cmd.getGeneratedResultMetaData(); newWrapper.schemaName = session.currentSchema; StatementWrapper wrapper = statementSet.get(newWrapper); if (wrapper != null) { if (wrapper.statement.getCompileTimestamp() >= database.schemaManager.getSchemaChangeTimestamp()) { wrapper.usageCount++; return wrapper.statement; } // old version is invalid removeStatement(wrapper.statement.getID()); } wrapper = newWrapper; wrapper.statement = session.compileStatement(wrapper.sql, wrapper.cursorProps); wrapper.statement.setCursorPropertiesRequest(wrapper.cursorProps); wrapper.statement.setGeneratedColumnInfo(cmd.getGeneratedResultType(), cmd.getGeneratedResultMetaData()); registerStatement(wrapper); wrapper.usageCount = 1; return wrapper.statement; } private static class StatementComparator implements ObjectComparator { public boolean equals(StatementWrapper s1, StatementWrapper s2) { return s1.sql.equals(s2.sql) && s1.schemaName.equals(s2.schemaName) && s1.cursorProps == s2.cursorProps && s1.generatedType == s2.generatedType && ResultMetaData.areGeneratedReguestsCompatible( s1.generatedMetaData, s2.generatedMetaData); } public int hashCode(StatementWrapper a) { return a.sql.hashCode(); } public long longKey(StatementWrapper a) { return 0L; } } private static class StatementWrapper { String sql; HsqlName schemaName; int cursorProps; int generatedType; ResultMetaData generatedMetaData; Statement statement; long usageCount; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy