/* Copyright (c) 2001-2019, 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.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.CharArrayWriter;
import org.hsqldb.lib.CountdownInputStream;
import org.hsqldb.lib.HashMap;
import org.hsqldb.lib.HsqlByteArrayOutputStream;
import org.hsqldb.lib.Iterator;
import org.hsqldb.lib.LongKeyHashMap;
import org.hsqldb.lib.LongKeyLongValueHashMap;
import org.hsqldb.lib.ReaderInputStream;
import org.hsqldb.navigator.RowSetNavigator;
import org.hsqldb.navigator.RowSetNavigatorClient;
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.BlobDataID;
import org.hsqldb.types.ClobData;
import org.hsqldb.types.ClobDataID;
import org.hsqldb.types.LobData;

Session semi-persistent data structures.
Author:Fred Toussi (fredt@users dot sourceforge.net)
Version:2.3.5
Since:1.9.0
/** * Session semi-persistent data structures. * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.3.5 * @since 1.9.0 */
public class SessionData { private final Database database; private final Session session; public PersistentStoreCollectionSession persistentStoreCollection; // large results LongKeyHashMap resultMap; // VALUE Object currentValue; // SEQUENCE HashMap sequenceMap; HashMap sequenceUpdateMap; public SessionData(Database database, Session session) { this.database = database; this.session = session; persistentStoreCollection = new PersistentStoreCollectionSession(session); } public PersistentStore getSubqueryRowStore(TableBase table) { PersistentStore store = persistentStoreCollection.getStore(table); store.removeAll(); return store; } public PersistentStore getNewResultRowStore(TableBase table, boolean isCached) { try { PersistentStore store = persistentStoreCollection.getStore(table); if (!isCached) { store.setMemory(true); } 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.getStatement().getResultProperties(); if (required != returned) { if (ResultProperties.isUpdatable(required)) { if (ResultProperties.isReadOnly(returned)) { 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)); returned = ResultProperties.addHoldable(returned, ResultProperties.isHoldable(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); result.rsProperties = ResultProperties.addIsHeld(result.rsProperties, true); } if (copy) { result = Result.newDataHeadResult(session, result, 0, fetchSize); } return result; } Result getDataResultSlice(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 Result.newDataRowsResult(result, offset, count); } 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); if (result != null) { result.getNavigator().release(); } } public void closeAllNavigators() { if (resultMap == null) { return; } Iterator it = resultMap.values().iterator(); while (it.hasNext()) { Result result = (Result) it.next(); result.getNavigator().release(); } 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().release(); it.remove(); } } } // lob creation boolean hasLobOps; long firstNewLobID; public void registerNewLob(long lobID) { if (firstNewLobID == 0) { firstNewLobID = lobID; } hasLobOps = true; } public void clearLobOps() { firstNewLobID = 0; hasLobOps = false; } public long getFirstLobID() { return firstNewLobID; } // lobs in results LongKeyLongValueHashMap resultLobs = new LongKeyLongValueHashMap(); // lobs in transaction public void adjustLobUsageCount(LobData value, int adjust) { if (session.isProcessingLog() || session.isProcessingScript()) { return; } if (value == null) { return; } database.lobManager.adjustUsageCount(session, value.getId(), adjust); hasLobOps = true; } public void adjustLobUsageCount(TableBase table, Object[] data, int adjust) { if (!table.hasLobColumn) { return; } if (table.isTemp) { 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(session, ((LobData) value).getId(), adjust); hasLobOps = true; } } }
allocate storage for a new LOB
/** * allocate storage for a new LOB */
public void allocateLobForResult(ResultLob result, InputStream inputStream) { try { CountdownInputStream countStream; switch (result.getSubType()) { case ResultLob.LobResultTypes.REQUEST_CREATE_BYTES : { long blobId; long blobLength = result.getBlockLength(); if (blobLength < 0) { // embedded session + unknown lob length allocateBlobSegments(result, result.getInputStream()); break; } if (inputStream == null) { // embedded session + known lob length blobId = result.getLobID(); inputStream = result.getInputStream(); } else { // server session + known or unknown lob length BlobData blob = session.createBlob(blobLength); blobId = blob.getId(); resultLobs.put(result.getLobID(), 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 (clobLength < 0) { // embedded session + unknown lob length allocateClobSegments(result, result.getReader()); break; } if (inputStream == null) { clobId = result.getLobID(); // embedded session + known lob length if (result.getReader() != null) { inputStream = new ReaderInputStream(result.getReader()); } else { inputStream = result.getInputStream(); } } else { // server session + known or unknown lob length ClobData clob = session.createClob(clobLength); clobId = clob.getId(); resultLobs.put(result.getLobID(), clobId); } countStream = new CountdownInputStream(inputStream); countStream.setCount(clobLength * 2); database.lobManager.setCharsForNewClob( clobId, countStream, result.getBlockLength()); break; } case ResultLob.LobResultTypes.REQUEST_SET_BYTES : { // server session + unknown lob length long blobId = resultLobs.get(result.getLobID()); long dataLength = result.getBlockLength(); byte[] byteArray = result.getByteArray(); Result actionResult = database.lobManager.setBytes(blobId, result.getOffset(), byteArray, (int) dataLength); // FIXME: actionResult not used anymore!? break; } case ResultLob.LobResultTypes.REQUEST_SET_CHARS : { // server session + unknown lob length long clobId = resultLobs.get(result.getLobID()); long dataLength = result.getBlockLength(); char[] charArray = result.getCharArray(); Result actionResult = database.lobManager.setChars(clobId, result.getOffset(), charArray, (int) dataLength); // FIXME: actionResult not used anymore!? break; } } } catch (Throwable e) { resultLobs.clear(); throw Error.error(ErrorCode.GENERAL_ERROR, e); } } private void allocateBlobSegments(ResultLob result, InputStream stream) throws IOException { // long currentOffset = result.getOffset(); int bufferLength = session.getStreamBlockSize(); HsqlByteArrayOutputStream byteArrayOS = new HsqlByteArrayOutputStream(bufferLength); while (true) { byteArrayOS.reset(); byteArrayOS.write(stream, bufferLength); if (byteArrayOS.size() == 0) { return; } byte[] byteArray = byteArrayOS.getBuffer(); Result actionResult = database.lobManager.setBytes(result.getLobID(), currentOffset, byteArray, byteArrayOS.size()); // FIXME: actionResult not used anymore!? currentOffset += byteArrayOS.size(); if (byteArrayOS.size() < bufferLength) { return; } } } private void allocateClobSegments(ResultLob result, Reader reader) throws IOException { allocateClobSegments(result.getLobID(), result.getOffset(), reader); } private void allocateClobSegments(long lobID, long offset, Reader reader) throws IOException { int bufferLength = session.getStreamBlockSize(); CharArrayWriter charWriter = new CharArrayWriter(bufferLength); long currentOffset = offset; while (true) { charWriter.reset(); charWriter.write(reader, bufferLength); char[] charArray = charWriter.getBuffer(); if (charWriter.size() == 0) { return; } Result actionResult = database.lobManager.setChars(lobID, currentOffset, charArray, charWriter.size()); // FIXME: actionResult not used anymore!? currentOffset += charWriter.size(); if (charWriter.size() < bufferLength) { return; } } } 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 = blob.getId(); if (id < 0) { id = resultLobs.get(id); } data[i] = database.lobManager.getBlob(id); // handle invalid id; } else if (data[i] instanceof ClobDataID) { ClobData clob = (ClobDataID) data[i]; long id = clob.getId(); if (id < 0) { id = resultLobs.get(id); } data[i] = database.lobManager.getClob(id); // handle invalid id; } } } ClobData createClobFromFile(String filename, String encoding) { File file = getFile(filename); long fileLength = file.length(); InputStream is = null; try { ClobData clob = session.createClob(fileLength); is = new FileInputStream(file); Reader reader = new InputStreamReader(is, encoding); allocateClobSegments(clob.getId(), 0, reader); return clob; } catch (IOException e) { throw Error.error(ErrorCode.FILE_IO_ERROR, e.toString()); } finally { try { if (is != null) { is.close(); } } catch (Exception e) {} } } BlobData createBlobFromFile(String filename) { File file = getFile(filename); long fileLength = file.length(); InputStream is = null; try { BlobData blob = session.createBlob(fileLength); is = new FileInputStream(file); database.lobManager.setBytesForNewBlob(blob.getId(), is, fileLength); return blob; } catch (IOException e) { throw Error.error(ErrorCode.FILE_IO_ERROR); } finally { try { if (is != null) { is.close(); } } catch (Exception e) {} } } private File getFile(String name) { session.checkAdmin(); String fileName = database.logger.getSecurePath(name, false, false); if (fileName == null) { throw Error.error(ErrorCode.ACCESS_IS_DENIED, name); } File file = new File(fileName); boolean exists = file.exists(); if (!exists) { throw Error.error(ErrorCode.FILE_IO_ERROR); } return file; } // sequences public void startRowProcessing() { if (sequenceMap != null) { sequenceMap.clear(); } } public Object getSequenceValue(NumberSequence sequence) { if (sequenceMap == null) { sequenceMap = new HashMap(); sequenceUpdateMap = new HashMap(); } HsqlName key = sequence.getName(); Object value = sequenceMap.get(key); if (value == null) { value = sequence.getValueObject(); sequenceMap.put(key, value); sequenceUpdateMap.put(sequence, value); } return value; } public Object getSequenceCurrent(NumberSequence sequence) { return sequenceUpdateMap == null ? null : sequenceUpdateMap.get(sequence); } }