/* Copyright (c) 2001-2010, 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 java.io.InputStream; import org.hsqldb.HsqlNameManager.HsqlName; import org.hsqldb.error.Error; import org.hsqldb.error.ErrorCode; import org.hsqldb.lib.CountdownInputStream; import org.hsqldb.lib.HashMap; import org.hsqldb.lib.Iterator; import org.hsqldb.lib.LongKeyHashMap; import org.hsqldb.lib.LongKeyLongValueHashMap; import org.hsqldb.lib.OrderedHashSet; import org.hsqldb.lib.ReaderInputStream; import org.hsqldb.navigator.RowSetNavigator; import org.hsqldb.navigator.RowSetNavigatorClient; import org.hsqldb.persist.DataFileCacheSession; import org.hsqldb.persist.PersistentStore; import org.hsqldb.persist.PersistentStoreCollectionSession; import org.hsqldb.result.Result; import org.hsqldb.result.ResultLob; import org.hsqldb.result.ResultProperties; import org.hsqldb.types.BlobData; import org.hsqldb.types.ClobData; import org.hsqldb.types.LobData; import org.hsqldb.types.BlobDataID; import org.hsqldb.types.ClobDataID; /* * Session semi-persistent data structures * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 1.9.0 * @since 1.9.0 */ public class SessionData { private final Database database; private final Session session; PersistentStoreCollectionSession persistentStoreCollection; // large results LongKeyHashMap resultMap; DataFileCacheSession resultCache; // VALUE Object currentValue; // SEQUENCE HashMap sequenceMap; OrderedHashSet sequenceUpdateSet; public SessionData(Database database, Session session) { this.database = database; this.session = session; persistentStoreCollection = new PersistentStoreCollectionSession(session); } // transitional feature public PersistentStore getRowStore(TableBase table) { if (table.tableType == TableBase.SYSTEM_TABLE) { if (session.isAdmin()) { return table.store; } return persistentStoreCollection.getStore(table); } if (table.store != null) { return table.store; } if (table.isSessionBased) { return persistentStoreCollection.getStore(table); } return database.persistentStoreCollection.getStore(table); } public PersistentStore getSubqueryRowStore(TableBase table) { PersistentStore store = persistentStoreCollection.getStore(table); store.removeAll(); return store; } public PersistentStore getNewResultRowStore(TableBase table, boolean isCached) { try { PersistentStore store = session.database.logger.newStore(session, persistentStoreCollection, table, isCached); return store; } catch (HsqlException e) {} throw Error.runtimeError(ErrorCode.U_S0500, "SessionData"); } // result void setResultSetProperties(Result command, Result result) { int required = command.rsProperties; int returned = result.rsProperties; if (required != returned) { if (ResultProperties.isReadOnly(required)) { returned = ResultProperties.addHoldable(returned, ResultProperties.isHoldable(required)); } else { if (ResultProperties.isUpdatable(returned)) { if (ResultProperties.isHoldable(required)) { session.addWarning(Error.error(ErrorCode.W_36503)); } } else { returned = ResultProperties.addHoldable(returned, ResultProperties.isHoldable(required)); session.addWarning(Error.error(ErrorCode.W_36502)); } } if (ResultProperties.isSensitive(required)) { session.addWarning(Error.error(ErrorCode.W_36501)); } returned = ResultProperties.addScrollable(returned, ResultProperties.isScrollable(required)); result.rsProperties = returned; } } Result getDataResultHead(Result command, Result result, boolean isNetwork) { int fetchSize = command.getFetchSize(); result.setResultId(session.actionTimestamp); int required = command.rsProperties; int returned = result.rsProperties; if (required != returned) { if (ResultProperties.isReadOnly(required)) { returned = ResultProperties.addHoldable(returned, ResultProperties.isHoldable(required)); } else { if (ResultProperties.isReadOnly(returned)) { returned = ResultProperties.addHoldable(returned, ResultProperties.isHoldable(required)); // add warning for concurrency conflict } else { if (session.isAutoCommit()) { returned = ResultProperties.addHoldable(returned, ResultProperties.isHoldable(required)); } else { returned = ResultProperties.addHoldable(returned, false); } } } returned = ResultProperties.addScrollable(returned, ResultProperties.isScrollable(required)); result.rsProperties = returned; } boolean hold = false; boolean copy = false; if (ResultProperties.isUpdatable(result.rsProperties)) { hold = true; } if (isNetwork) { if (fetchSize != 0 && result.getNavigator().getSize() > fetchSize) { copy = true; hold = true; } } else { if (!result.getNavigator().isMemory()) { hold = true; } } if (hold) { if (resultMap == null) { resultMap = new LongKeyHashMap(); } resultMap.put(result.getResultId(), result); } if (copy) { result = Result.newDataHeadResult(session, result, 0, fetchSize); } return result; } Result getDataResultSlice(long id, int offset, int count) { RowSetNavigatorClient navigator = getRowSetSlice(id, offset, count); return Result.newDataRowsResult(navigator); } Result getDataResult(long id) { Result result = (Result) resultMap.get(id); return result; } RowSetNavigatorClient getRowSetSlice(long id, int offset, int count) { Result result = (Result) resultMap.get(id); RowSetNavigator source = result.getNavigator(); if (offset + count > source.getSize()) { count = source.getSize() - offset; } return new RowSetNavigatorClient(source, offset, count); } public void closeNavigator(long id) { Result result = (Result) resultMap.remove(id); result.getNavigator().close(); } public void closeAllNavigators() { if (resultMap == null) { return; } Iterator it = resultMap.values().iterator(); while (it.hasNext()) { Result result = (Result) it.next(); result.getNavigator().close(); } resultMap.clear(); } public void closeAllTransactionNavigators() { if (resultMap == null) { return; } Iterator it = resultMap.values().iterator(); while (it.hasNext()) { Result result = (Result) it.next(); if (!ResultProperties.isHoldable(result.rsProperties)) { result.getNavigator().close(); it.remove(); } } resultMap.clear(); } public DataFileCacheSession getResultCache() { if (resultCache == null) { String path = database.logger.getTempDirectoryPath(); if (path == null) { return null; } try { resultCache = new DataFileCacheSession(database, path + "/session_" + Long.toString(session.getId())); resultCache.open(false); } catch (Throwable t) { return null; } } return resultCache; } synchronized void closeResultCache() { if (resultCache != null) { try { resultCache.close(false); } catch (HsqlException e) {} resultCache = null; } } // lobs in results LongKeyLongValueHashMap resultLobs = new LongKeyLongValueHashMap(); // lobs in transaction boolean hasLobOps; public void addToCreatedLobs(long lobID) { hasLobOps = true; } public void adjustLobUsageCount(Object value, int adjust) { if (session.isProcessingLog || session.isProcessingScript) { return; } if (value == null) { return; } database.lobManager.adjustUsageCount(((LobData) value).getId(), adjust); hasLobOps = true; } public void adjustLobUsageCount(TableBase table, Object[] data, int adjust) { if (!table.hasLobColumn) { return; } if (session.isProcessingLog || session.isProcessingScript) { return; } for (int j = 0; j < table.columnCount; j++) { if (table.colTypes[j].isLobType()) { Object value = data[j]; if (value == null) { continue; } database.lobManager.adjustUsageCount(((LobData) value).getId(), adjust); hasLobOps = true; } } } /** * allocate storage for a new LOB */ public void allocateLobForResult(ResultLob result, InputStream inputStream) { long resultLobId = result.getLobID(); CountdownInputStream countStream; switch (result.getSubType()) { case ResultLob.LobResultTypes.REQUEST_CREATE_BYTES : { long blobId; long blobLength = result.getBlockLength(); if (inputStream == null) { blobId = resultLobId; inputStream = result.getInputStream(); } else { BlobData blob = session.createBlob(blobLength); blobId = blob.getId(); resultLobs.put(resultLobId, blobId); } countStream = new CountdownInputStream(inputStream); countStream.setCount(blobLength); database.lobManager.setBytesForNewBlob( blobId, countStream, result.getBlockLength()); break; } case ResultLob.LobResultTypes.REQUEST_CREATE_CHARS : { long clobId; long clobLength = result.getBlockLength(); if (inputStream == null) { clobId = resultLobId; if (result.getReader() != null) { inputStream = new ReaderInputStream(result.getReader()); } else { inputStream = result.getInputStream(); } } else { ClobData clob = session.createClob(clobLength); clobId = clob.getId(); resultLobs.put(resultLobId, clobId); } countStream = new CountdownInputStream(inputStream); countStream.setCount(clobLength * 2); database.lobManager.setCharsForNewClob( clobId, countStream, result.getBlockLength()); break; } } } public void registerLobForResult(Result result) { RowSetNavigator navigator = result.getNavigator(); if (navigator == null) { registerLobsForRow((Object[]) result.valueData); } else { while (navigator.next()) { Object[] data = navigator.getCurrent(); registerLobsForRow(data); } navigator.reset(); } resultLobs.clear(); } private void registerLobsForRow(Object[] data) { for (int i = 0; i < data.length; i++) { if (data[i] instanceof BlobDataID) { BlobData blob = (BlobDataID) data[i]; long id = resultLobs.get(blob.getId()); data[i] = database.lobManager.getBlob(id); } else if (data[i] instanceof ClobDataID) { ClobData clob = (ClobDataID) data[i]; long id = resultLobs.get(clob.getId()); data[i] = database.lobManager.getClob(id); } } } // public void startRowProcessing() { if (sequenceMap != null) { sequenceMap.clear(); } } public Object getSequenceValue(NumberSequence sequence) { if (sequenceMap == null) { sequenceMap = new HashMap(); sequenceUpdateSet = new OrderedHashSet(); } HsqlName key = sequence.getName(); Object value = sequenceMap.get(key); if (value == null) { value = sequence.getValueObject(); sequenceMap.put(key, value); sequenceUpdateSet.add(sequence); } return value; } }