blob: 637c668c3340e21374d1cc995415a0805ce19189 [file] [log] [blame]
/* 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.persist;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import org.hsqldb.Database;
import org.hsqldb.DatabaseURL;
import org.hsqldb.HsqlException;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.Session;
import org.hsqldb.Statement;
import org.hsqldb.Table;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HashMappedList;
import org.hsqldb.lib.LineGroupReader;
import org.hsqldb.navigator.RowSetNavigator;
import org.hsqldb.result.Result;
import org.hsqldb.result.ResultLob;
import org.hsqldb.result.ResultMetaData;
import org.hsqldb.result.ResultProperties;
import org.hsqldb.store.ValuePool;
import org.hsqldb.types.BlobData;
import org.hsqldb.types.BlobDataID;
import org.hsqldb.types.ClobData;
import org.hsqldb.types.ClobDataID;
import org.hsqldb.types.Types;
/**
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class LobManager {
static final String resourceFileName =
"/org/hsqldb/resources/lob-schema.sql";
static final String[] starters = new String[]{ "/*" };
//
Database database;
LobStore lobStore;
Session sysLobSession;
//
//
int lobBlockSize;
int totalBlockLimitCount = Integer.MAX_VALUE;
//
int deletedLobCount;
//
Statement getLob;
Statement getLobPart;
Statement deleteLobCall;
Statement deleteLobPartCall;
Statement divideLobPartCall;
Statement createLob;
Statement createLobPartCall;
Statement updateLobLength;
Statement updateLobUsage;
Statement getNextLobId;
Statement deleteUnusedLobs;
Statement getLobCount;
// LOBS columns
private interface LOBS {
int BLOCK_ADDR = 0;
int BLOCK_COUNT = 1;
int BLOCK_OFFSET = 2;
int LOB_ID = 3;
}
private interface LOB_IDS {
int LOB_ID = 0;
int LOB_LENGTH = 1;
int LOB_USAGE_COUNT = 2;
int LOB_TYPE = 3;
}
private interface GET_LOB_PART {
int LOB_ID = 0;
int BLOCK_OFFSET = 1;
int BLOCK_LIMIT = 2;
}
private interface DIVIDE_BLOCK {
int BLOCK_OFFSET = 0;
int LOB_ID = 1;
}
private interface DELETE_BLOCKS {
int LOB_ID = 0;
int BLOCK_OFFSET = 1;
int BLOCK_LIMIT = 2;
int TX_ID = 3;
}
private interface ALLOC_BLOCKS {
int BLOCK_COUNT = 0;
int BLOCK_OFFSET = 1;
int LOB_ID = 2;
}
private interface UPDATE_USAGE {
int BLOCK_COUNT = 0;
int LOB_ID = 1;
}
private interface UPDATE_LENGTH {
int LOB_LENGTH = 0;
int LOB_ID = 1;
}
//BLOCK_ADDR INT, BLOCK_COUNT INT, TX_ID BIGINT
private static String initialiseBlocksSQL =
"INSERT INTO SYSTEM_LOBS.BLOCKS VALUES(?,?,?)";
private static String getLobSQL =
"SELECT * FROM SYSTEM_LOBS.LOB_IDS WHERE LOB_ID = ?";
private static String getLobPartSQL =
"SELECT * FROM SYSTEM_LOBS.LOBS WHERE LOB_ID = ? AND BLOCK_OFFSET + BLOCK_COUNT > ? AND BLOCK_OFFSET < ? ORDER BY BLOCK_OFFSET";
private static String deleteLobPartCallSQL =
"CALL SYSTEM_LOBS.DELETE_BLOCKS(?,?,?,?)";
private static String createLobSQL =
"INSERT INTO SYSTEM_LOBS.LOB_IDS VALUES(?, ?, ?, ?)";
private static String updateLobLengthSQL =
"UPDATE SYSTEM_LOBS.LOB_IDS SET LOB_LENGTH = ? WHERE LOB_ID = ?";
private static String createLobPartCallSQL =
"CALL SYSTEM_LOBS.ALLOC_BLOCKS(?, ?, ?)";
private static String divideLobPartCallSQL =
"CALL SYSTEM_LOBS.DIVIDE_BLOCK(?, ?)";
private static String getSpanningBlockSQL =
"SELECT * FROM SYSTEM_LOBS.LOBS WHERE LOB_ID = ? AND ? > BLOCK_OFFSET AND ? < BLOCK_OFFSET + BLOCK_COUNT";
private static String updateLobUsageSQL =
"UPDATE SYSTEM_LOBS.LOB_IDS SET LOB_USAGE_COUNT = ? WHERE LOB_ID = ?";
private static String getNextLobIdSQL =
"VALUES NEXT VALUE FOR SYSTEM_LOBS.LOB_ID";
private static String deleteLobCallSQL =
"CALL SYSTEM_LOBS.DELETE_LOB(?, ?)";
private static String deleteUnusedCallSQL =
"CALL SYSTEM_LOBS.DELETE_UNUSED()";
private static String getLobCountSQL =
"SELECT COUNT(*) FROM SYSTEM_LOBS.LOB_IDS";
public LobManager(Database database) {
this.database = database;
}
synchronized public void createSchema() {
sysLobSession = database.sessionManager.getSysLobSession();
InputStream fis = getClass().getResourceAsStream(resourceFileName);
InputStreamReader reader = null;
try {
reader = new InputStreamReader(fis, "ISO-8859-1");
} catch (Exception e) {}
LineNumberReader lineReader = new LineNumberReader(reader);
LineGroupReader lg = new LineGroupReader(lineReader, starters);
HashMappedList map = lg.getAsMap();
lg.close();
String sql = (String) map.get("/*lob_schema_definition*/");
Statement statement = sysLobSession.compileStatement(sql,
ResultProperties.defaultPropsValue);
Result result = statement.execute(sysLobSession);
if (result.isError()) {
throw result.getException();
}
HsqlName name =
database.schemaManager.getSchemaHsqlName("SYSTEM_LOBS");
Table table = database.schemaManager.getTable(sysLobSession, "BLOCKS",
"SYSTEM_LOBS");
getLob = sysLobSession.compileStatement(getLobSQL,
ResultProperties.defaultPropsValue);
getLobPart = sysLobSession.compileStatement(getLobPartSQL,
ResultProperties.defaultPropsValue);
createLob = sysLobSession.compileStatement(createLobSQL,
ResultProperties.defaultPropsValue);
createLobPartCall =
sysLobSession.compileStatement(createLobPartCallSQL,
ResultProperties.defaultPropsValue);
divideLobPartCall =
sysLobSession.compileStatement(divideLobPartCallSQL,
ResultProperties.defaultPropsValue);
deleteLobCall = sysLobSession.compileStatement(deleteLobCallSQL,
ResultProperties.defaultPropsValue);
deleteLobPartCall =
sysLobSession.compileStatement(deleteLobPartCallSQL,
ResultProperties.defaultPropsValue);
updateLobLength = sysLobSession.compileStatement(updateLobLengthSQL,
ResultProperties.defaultPropsValue);
updateLobUsage = sysLobSession.compileStatement(updateLobUsageSQL,
ResultProperties.defaultPropsValue);
getNextLobId = sysLobSession.compileStatement(getNextLobIdSQL,
ResultProperties.defaultPropsValue);
deleteUnusedLobs = sysLobSession.compileStatement(deleteUnusedCallSQL,
ResultProperties.defaultPropsValue);
getLobCount = sysLobSession.compileStatement(getLobCountSQL,
ResultProperties.defaultPropsValue);
}
synchronized public void initialiseLobSpace() {
Statement statement =
sysLobSession.compileStatement(initialiseBlocksSQL,
ResultProperties.defaultPropsValue);
Object[] params = new Object[3];
params[0] = ValuePool.INTEGER_0;
params[1] = ValuePool.getInt(totalBlockLimitCount);
params[2] = ValuePool.getLong(0);
sysLobSession.executeCompiledStatement(statement, params);
}
synchronized public void open() {
if (lobStore != null) {
return;
}
lobBlockSize = database.logger.getLobBlockSize();
if (database.getType() == DatabaseURL.S_RES) {
lobStore = new LobStoreInJar(database, lobBlockSize);
} else if (database.getType() == DatabaseURL.S_FILE) {
lobStore = new LobStoreRAFile(database, lobBlockSize);
} else {
lobStore = new LobStoreMem(lobBlockSize);
}
}
synchronized public void close() {
lobStore.close();
}
LobStore getLobStore() {
if (lobStore == null) {
open();
}
return lobStore;
}
//
private long getNewLobID() {
Result result = getNextLobId.execute(sysLobSession);
if (result.isError()) {
return 0;
}
RowSetNavigator navigator = result.getNavigator();
boolean next = navigator.next();
if (!next) {
navigator.close();
return 0;
}
Object[] data = navigator.getCurrent();
return ((Long) data[0]).longValue();
}
private Object[] getLobHeader(long lobID) {
ResultMetaData meta = getLob.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[0] = ValuePool.getLong(lobID);
sysLobSession.sessionContext.pushDynamicArguments(params);
Result result = getLob.execute(sysLobSession);
sysLobSession.sessionContext.pop();
if (result.isError()) {
return null;
}
RowSetNavigator navigator = result.getNavigator();
boolean next = navigator.next();
if (!next) {
navigator.close();
return null;
}
Object[] data = navigator.getCurrent();
return data;
}
synchronized public BlobData getBlob(long lobID) {
Object[] data = getLobHeader(lobID);
if (data == null) {
return null;
}
BlobData blob = new BlobDataID(lobID);
return blob;
}
synchronized public ClobData getClob(long lobID) {
Object[] data = getLobHeader(lobID);
if (data == null) {
return null;
}
ClobData clob = new ClobDataID(lobID);
return clob;
}
synchronized public long createBlob(long length) {
long lobID = getNewLobID();
ResultMetaData meta = createLob.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[LOB_IDS.LOB_ID] = ValuePool.getLong(lobID);
params[LOB_IDS.LOB_LENGTH] = ValuePool.getLong(length);
params[LOB_IDS.LOB_USAGE_COUNT] = ValuePool.INTEGER_0;
params[LOB_IDS.LOB_TYPE] = ValuePool.getInt(Types.SQL_BLOB);
Result result = sysLobSession.executeCompiledStatement(createLob,
params);
return lobID;
}
synchronized public long createClob(long length) {
long lobID = getNewLobID();
ResultMetaData meta = createLob.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[LOB_IDS.LOB_ID] = ValuePool.getLong(lobID);
params[LOB_IDS.LOB_LENGTH] = ValuePool.getLong(length);
params[LOB_IDS.LOB_USAGE_COUNT] = ValuePool.INTEGER_0;
params[LOB_IDS.LOB_TYPE] = ValuePool.getInt(Types.SQL_CLOB);
Result result = sysLobSession.executeCompiledStatement(createLob,
params);
return lobID;
}
synchronized public Result deleteLob(long lobID) {
ResultMetaData meta = deleteLobCall.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[0] = ValuePool.getLong(lobID);
params[1] = ValuePool.getLong(0);
Result result = sysLobSession.executeCompiledStatement(deleteLobCall,
params);
return result;
}
synchronized public Result deleteUnusedLobs() {
Result result =
sysLobSession.executeCompiledStatement(deleteUnusedLobs,
ValuePool.emptyObjectArray);
deletedLobCount = 0;
return result;
}
synchronized public Result getLength(long lobID) {
try {
Object[] data = getLobHeader(lobID);
if (data == null) {
throw Error.error(ErrorCode.X_0F502);
}
long length = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
int type = ((Integer) data[LOB_IDS.LOB_TYPE]).intValue();
return ResultLob.newLobSetResponse(lobID, length);
} catch (HsqlException e) {
return Result.newErrorResult(e);
}
}
synchronized public int compare(BlobData a, byte[] b) {
Object[] data = getLobHeader(a.getId());
long aLength = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
int[][] aAddresses = getBlockAddresses(a.getId(), 0,
Integer.MAX_VALUE);
int aIndex = 0;
int bOffset = 0;
int aOffset = 0;
while (true) {
int aBlockOffset = aAddresses[aIndex][LOBS.BLOCK_ADDR] + aOffset;
byte[] aBytes = getLobStore().getBlockBytes(aBlockOffset, 1);
for (int i = 0; i < aBytes.length; i++) {
if (bOffset + i >= b.length) {
if (aLength == b.length) {
return 0;
}
return 1;
}
if (aBytes[i] == b[bOffset + i]) {
continue;
}
return (((int) aBytes[i]) & 0xff)
> (((int) b[bOffset + i]) & 0xff) ? 1
: -1;
}
aOffset++;
bOffset += lobBlockSize;
if (aOffset == aAddresses[aIndex][LOBS.BLOCK_COUNT]) {
aOffset = 0;
aIndex++;
}
if (aIndex == aAddresses.length) {
break;
}
}
return -1;
}
synchronized public int compare(BlobData a, BlobData b) {
if (a.getId() == b.getId()) {
return 0;
}
Object[] data = getLobHeader(a.getId());
// abnormal case
if (data == null) {
return 1;
}
long lengthA = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
data = getLobHeader(b.getId());
// abnormal case
if (data == null) {
return -1;
}
long lengthB = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
if (lengthA > lengthB) {
return 1;
}
if (lengthA < lengthB) {
return -1;
}
return compareBytes(a.getId(), b.getId());
}
// todo - implement as compareText()
synchronized public int compare(ClobData a, String b) {
Object[] data = getLobHeader(a.getId());
long aLength = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
int[][] aAddresses = getBlockAddresses(a.getId(), 0,
Integer.MAX_VALUE);
int aIndex = 0;
int bOffset = 0;
int aOffset = 0;
while (true) {
int aBlockOffset = aAddresses[aIndex][LOBS.BLOCK_ADDR] + aOffset;
byte[] aBytes = getLobStore().getBlockBytes(aBlockOffset, 1);
long aLimit = aLength
- (aAddresses[aIndex][LOBS.BLOCK_OFFSET] + aOffset)
* lobBlockSize / 2;
if (aLimit > lobBlockSize / 2) {
aLimit = lobBlockSize / 2;
}
String aString = new String(ArrayUtil.byteArrayToChars(aBytes), 0,
(int) aLimit);
int bLimit = b.length() - bOffset;
if (bLimit > lobBlockSize / 2) {
bLimit = lobBlockSize / 2;
}
String bString = b.substring(bOffset, bOffset + bLimit);
int diff = database.collation.compare(aString, bString);
if (diff != 0) {
return diff;
}
aOffset++;
bOffset += lobBlockSize / 2;
if (aOffset == aAddresses[aIndex][LOBS.BLOCK_COUNT]) {
aOffset = 0;
aIndex++;
}
if (aIndex == aAddresses.length) {
break;
}
}
return 0;
}
synchronized public int compare(ClobData a, ClobData b) {
if (a.getId() == b.getId()) {
return 0;
}
return compareText(a.getId(), b.getId());
}
synchronized int compareBytes(long aID, long bID) {
int[][] aAddresses = getBlockAddresses(aID, 0, Integer.MAX_VALUE);
int[][] bAddresses = getBlockAddresses(bID, 0, Integer.MAX_VALUE);
int aIndex = 0;
int bIndex = 0;
int aOffset = 0;
int bOffset = 0;
while (true) {
int aBlockOffset = aAddresses[aIndex][LOBS.BLOCK_ADDR] + aOffset;
int bBlockOffset = bAddresses[bIndex][LOBS.BLOCK_ADDR] + bOffset;
byte[] aBytes = getLobStore().getBlockBytes(aBlockOffset, 1);
byte[] bBytes = getLobStore().getBlockBytes(bBlockOffset, 1);
for (int i = 0; i < aBytes.length; i++) {
if (aBytes[i] == bBytes[i]) {
continue;
}
return (((int) aBytes[i]) & 0xff) > (((int) bBytes[i]) & 0xff)
? 1
: -1;
}
aOffset++;
bOffset++;
if (aOffset == aAddresses[aIndex][LOBS.BLOCK_COUNT]) {
aOffset = 0;
aIndex++;
}
if (bOffset == bAddresses[bIndex][LOBS.BLOCK_COUNT]) {
bOffset = 0;
bIndex++;
}
if (aIndex == aAddresses.length) {
break;
}
}
return 0;
}
/** @todo - word-separator and end block zero issues */
synchronized int compareText(long aID, long bID) {
Object[] data = getLobHeader(aID);
long aLength = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
data = getLobHeader(bID);
long bLength = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
int[][] aAddresses = getBlockAddresses(aID, 0, Integer.MAX_VALUE);
int[][] bAddresses = getBlockAddresses(bID, 0, Integer.MAX_VALUE);
int aIndex = 0;
int bIndex = 0;
int aOffset = 0;
int bOffset = 0;
while (true) {
int aBlockOffset = aAddresses[aIndex][LOBS.BLOCK_ADDR] + aOffset;
int bBlockOffset = bAddresses[bIndex][LOBS.BLOCK_ADDR] + bOffset;
byte[] aBytes = getLobStore().getBlockBytes(aBlockOffset, 1);
byte[] bBytes = getLobStore().getBlockBytes(bBlockOffset, 1);
long aLimit = aLength
- (aAddresses[aIndex][LOBS.BLOCK_OFFSET] + aOffset)
* lobBlockSize / 2;
if (aLimit > lobBlockSize / 2) {
aLimit = lobBlockSize / 2;
}
long bLimit = bLength
- (bAddresses[bIndex][LOBS.BLOCK_OFFSET] + bOffset)
* lobBlockSize / 2;
if (bLimit > lobBlockSize / 2) {
bLimit = lobBlockSize / 2;
}
String aString = new String(ArrayUtil.byteArrayToChars(aBytes), 0,
(int) aLimit);
String bString = new String(ArrayUtil.byteArrayToChars(bBytes), 0,
(int) bLimit);
int diff = database.collation.compare(aString, bString);
if (diff != 0) {
return diff;
}
aOffset++;
bOffset++;
if (aOffset == aAddresses[aIndex][LOBS.BLOCK_COUNT]) {
aOffset = 0;
aIndex++;
}
if (bOffset == bAddresses[bIndex][LOBS.BLOCK_COUNT]) {
bOffset = 0;
bIndex++;
}
if (aIndex == aAddresses.length) {
break;
}
}
return 0;
}
/**
* Used for SUBSTRING
*/
synchronized public Result getLob(long lobID, long offset, long length) {
throw Error.runtimeError(ErrorCode.U_S0500, "LobManager");
}
synchronized public Result createDuplicateLob(long lobID) {
Object[] data = getLobHeader(lobID);
if (data == null) {
return Result.newErrorResult(Error.error(ErrorCode.X_0F502));
}
long newLobID = getNewLobID();
Object params[] = new Object[data.length];
params[LOB_IDS.LOB_ID] = ValuePool.getLong(newLobID);
params[1] = data[1];
params[2] = data[2];
params[3] = data[3];
Result result = sysLobSession.executeCompiledStatement(createLob,
params);
if (result.isError()) {
return result;
}
long length = ((Long) data[1]).longValue();
long byteLength = length;
int lobType = ((Integer) data[1]).intValue();
if (lobType == Types.SQL_CLOB) {
byteLength *= 2;
}
int newBlockCount = (int) byteLength / lobBlockSize;
if (byteLength % lobBlockSize != 0) {
newBlockCount++;
}
createBlockAddresses(newLobID, 0, newBlockCount);
// copy the contents
int[][] sourceBlocks = getBlockAddresses(lobID, 0, Integer.MAX_VALUE);
int[][] targetBlocks = getBlockAddresses(newLobID, 0,
Integer.MAX_VALUE);
try {
copyBlockSet(sourceBlocks, targetBlocks);
} catch (HsqlException e) {
return Result.newErrorResult(e);
}
return ResultLob.newLobSetResponse(newLobID, length);
}
private void copyBlockSet(int[][] source, int[][] target) {
int sourceIndex = 0;
int targetIndex = 0;
while (true) {
int sourceOffset = source[sourceIndex][LOBS.BLOCK_OFFSET]
+ sourceIndex;
int targetOffset = target[targetIndex][LOBS.BLOCK_OFFSET]
+ targetIndex;
byte[] bytes = getLobStore().getBlockBytes(sourceOffset, 1);
getLobStore().setBlockBytes(bytes, targetOffset, 1);
sourceOffset++;
targetOffset++;
if (sourceOffset == source[sourceIndex][LOBS.BLOCK_COUNT]) {
sourceOffset = 0;
sourceIndex++;
}
if (targetOffset == target[sourceIndex][LOBS.BLOCK_COUNT]) {
targetOffset = 0;
targetIndex++;
}
if (sourceIndex == source.length) {
break;
}
}
}
synchronized public Result getChars(long lobID, long offset, int length) {
Result result = getBytes(lobID, offset * 2, length * 2);
if (result.isError()) {
return result;
}
byte[] bytes = ((ResultLob) result).getByteArray();
char[] chars = ArrayUtil.byteArrayToChars(bytes);
/*
HsqlByteArrayInputStream be = new HsqlByteArrayInputStream(bytes);
char[] chars = new char[bytes.length / 2];
try {
for (int i = 0; i < chars.length; i++) {
chars[i] = be.readChar();
}
} catch (Exception e) {
return Result.newErrorResult(e);
}
*/
return ResultLob.newLobGetCharsResponse(lobID, offset, chars);
}
synchronized public Result getBytes(long lobID, long offset, int length) {
int blockOffset = (int) (offset / lobBlockSize);
int byteBlockOffset = (int) (offset % lobBlockSize);
int blockLimit = (int) ((offset + length) / lobBlockSize);
int byteLimitOffset = (int) ((offset + length) % lobBlockSize);
if (byteLimitOffset == 0) {
byteLimitOffset = lobBlockSize;
} else {
blockLimit++;
}
int dataBytesPosition = 0;
byte[] dataBytes = new byte[length];
int[][] blockAddresses = getBlockAddresses(lobID, blockOffset,
blockLimit);
if (blockAddresses.length == 0) {
return Result.newErrorResult(Error.error(ErrorCode.X_0F502));
}
//
int i = 0;
int blockCount = blockAddresses[i][LOBS.BLOCK_COUNT]
+ blockAddresses[i][LOBS.BLOCK_OFFSET] - blockOffset;
if (blockAddresses[i][LOBS.BLOCK_COUNT]
+ blockAddresses[i][LOBS.BLOCK_OFFSET] > blockLimit) {
blockCount -= (blockAddresses[i][LOBS.BLOCK_COUNT]
+ blockAddresses[i][LOBS.BLOCK_OFFSET]
- blockLimit);
}
byte[] bytes;
try {
bytes =
getLobStore().getBlockBytes(blockAddresses[i][LOBS.BLOCK_ADDR]
+ blockOffset, blockCount);
} catch (HsqlException e) {
return Result.newErrorResult(e);
}
int subLength = lobBlockSize * blockCount - byteBlockOffset;
if (subLength > length) {
subLength = length;
}
System.arraycopy(bytes, byteBlockOffset, dataBytes, dataBytesPosition,
subLength);
dataBytesPosition += subLength;
i++;
for (; i < blockAddresses.length && dataBytesPosition < length; i++) {
blockCount = blockAddresses[i][LOBS.BLOCK_COUNT];
if (blockAddresses[i][LOBS.BLOCK_COUNT]
+ blockAddresses[i][LOBS.BLOCK_OFFSET] > blockLimit) {
blockCount -= (blockAddresses[i][LOBS.BLOCK_COUNT]
+ blockAddresses[i][LOBS.BLOCK_OFFSET]
- blockLimit);
}
try {
bytes = getLobStore().getBlockBytes(
blockAddresses[i][LOBS.BLOCK_ADDR], blockCount);
} catch (HsqlException e) {
return Result.newErrorResult(e);
}
subLength = lobBlockSize * blockCount;
if (subLength > length - dataBytesPosition) {
subLength = length - dataBytesPosition;
}
System.arraycopy(bytes, 0, dataBytes, dataBytesPosition,
subLength);
dataBytesPosition += subLength;
}
return ResultLob.newLobGetBytesResponse(lobID, offset, dataBytes);
}
synchronized public Result setBytesBA(long lobID, byte[] dataBytes,
long offset, int length) {
int blockOffset = (int) (offset / lobBlockSize);
int byteBlockOffset = (int) (offset % lobBlockSize);
int blockLimit = (int) ((offset + length) / lobBlockSize);
int byteLimitOffset = (int) ((offset + length) % lobBlockSize);
if (byteLimitOffset == 0) {
byteLimitOffset = lobBlockSize;
} else {
blockLimit++;
}
int[][] blockAddresses = getBlockAddresses(lobID, blockOffset,
blockLimit);
byte[] newBytes = new byte[(blockLimit - blockOffset) * lobBlockSize];
if (blockAddresses.length > 0) {
int blockAddress = blockAddresses[0][LOBS.BLOCK_ADDR]
+ (blockOffset
- blockAddresses[0][LOBS.BLOCK_OFFSET]);
try {
byte[] block = getLobStore().getBlockBytes(blockAddress, 1);
System.arraycopy(block, 0, newBytes, 0, lobBlockSize);
if (blockAddresses.length > 1) {
blockAddress =
blockAddresses[blockAddresses.length - 1][LOBS.BLOCK_ADDR]
+ (blockLimit
- blockAddresses[blockAddresses.length - 1][LOBS.BLOCK_OFFSET]
- 1);
block = getLobStore().getBlockBytes(blockAddress, 1);
System.arraycopy(block, 0, newBytes,
blockLimit - blockOffset - 1,
lobBlockSize);
} else if (blockLimit - blockOffset > 1) {
blockAddress = blockAddresses[0][LOBS.BLOCK_ADDR]
+ (blockLimit
- blockAddresses[0][LOBS.BLOCK_OFFSET]
- 1);
block = getLobStore().getBlockBytes(blockAddress, 1);
System.arraycopy(block, 0, newBytes,
(blockLimit - blockOffset - 1)
* lobBlockSize, lobBlockSize);
}
} catch (HsqlException e) {
return Result.newErrorResult(e);
}
// should turn into SP
divideBlockAddresses(lobID, blockOffset);
divideBlockAddresses(lobID, blockLimit);
deleteBlockAddresses(lobID, blockOffset, blockLimit);
}
createBlockAddresses(lobID, blockOffset, blockLimit - blockOffset);
System.arraycopy(dataBytes, 0, newBytes, byteBlockOffset, length);
blockAddresses = getBlockAddresses(lobID, blockOffset, blockLimit);
//
try {
for (int i = 0; i < blockAddresses.length; i++) {
getLobStore().setBlockBytes(newBytes, blockAddresses[i][0],
blockAddresses[i][1]);
}
} catch (HsqlException e) {
return Result.newErrorResult(e);
}
return ResultLob.newLobSetResponse(lobID, 0);
}
private Result setBytesIS(long lobID, InputStream inputStream,
long length) {
int blockLimit = (int) (length / lobBlockSize);
int byteLimitOffset = (int) (length % lobBlockSize);
if (byteLimitOffset == 0) {
byteLimitOffset = lobBlockSize;
} else {
blockLimit++;
}
createBlockAddresses(lobID, 0, blockLimit);
int[][] blockAddresses = getBlockAddresses(lobID, 0, blockLimit);
byte[] dataBytes = new byte[lobBlockSize];
for (int i = 0; i < blockAddresses.length; i++) {
for (int j = 0; j < blockAddresses[i][LOBS.BLOCK_COUNT]; j++) {
int localLength = lobBlockSize;
if (i == blockAddresses.length - 1
&& j == blockAddresses[i][LOBS.BLOCK_COUNT] - 1) {
localLength = byteLimitOffset;
// todo -- use block op
for (int k = localLength; k < lobBlockSize; k++) {
dataBytes[k] = 0;
}
}
try {
int count = 0;
while (localLength > 0) {
int read = inputStream.read(dataBytes, count,
localLength);
if (read == -1) {
return Result.newErrorResult(new EOFException());
}
localLength -= read;
count += read;
}
// read more
} catch (IOException e) {
// deallocate
return Result.newErrorResult(e);
}
try {
getLobStore().setBlockBytes(
dataBytes, blockAddresses[i][LOBS.BLOCK_ADDR] + j, 1);
} catch (HsqlException e) {
return Result.newErrorResult(e);
}
}
}
return ResultLob.newLobSetResponse(lobID, 0);
}
synchronized public Result setBytes(long lobID, byte[] dataBytes,
long offset) {
if (dataBytes.length == 0) {
return ResultLob.newLobSetResponse(lobID, 0);
}
Object[] data = getLobHeader(lobID);
if (data == null) {
return Result.newErrorResult(Error.error(ErrorCode.X_0F502));
}
long length = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
Result result = setBytesBA(lobID, dataBytes, offset, dataBytes.length);
if (offset + dataBytes.length > length) {
setLength(lobID, offset + dataBytes.length);
}
return result;
}
synchronized public Result setBytesForNewBlob(long lobID,
InputStream inputStream, long length) {
if (length == 0) {
return ResultLob.newLobSetResponse(lobID, 0);
}
Result result = setBytesIS(lobID, inputStream, length);
return result;
}
synchronized public Result setChars(long lobID, long offset,
char[] chars) {
if (chars.length == 0) {
return ResultLob.newLobSetResponse(lobID, 0);
}
Object[] data = getLobHeader(lobID);
if (data == null) {
return Result.newErrorResult(Error.error(ErrorCode.X_0F502));
}
long length = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
byte[] bytes = ArrayUtil.charArrayToBytes(chars);
/*
HsqlByteArrayOutputStream os =
new HsqlByteArrayOutputStream(chars.length * 2);
os.write(chars, 0, chars.length);
byte[] bytes = os.getBuffer();
*/
Result result = setBytesBA(lobID, bytes, offset * 2, chars.length * 2);
if (result.isError()) {
return result;
}
if (offset + chars.length > length) {
result = setLength(lobID, offset + chars.length);
if (result.isError()) {
return result;
}
}
return ResultLob.newLobSetResponse(lobID, 0);
}
synchronized public Result setCharsForNewClob(long lobID,
InputStream inputStream, long length) {
if (length == 0) {
return ResultLob.newLobSetResponse(lobID, 0);
}
Result result = setBytesIS(lobID, inputStream, length * 2);
if (result.isError()) {
return result;
}
return ResultLob.newLobSetResponse(lobID, 0);
}
synchronized public Result truncate(long lobID, long offset) {
Object[] data = getLobHeader(lobID);
if (data == null) {
return Result.newErrorResult(Error.error(ErrorCode.X_0F502));
}
/** @todo 1.9.0 - double offset for clob */
long length = ((Long) data[LOB_IDS.LOB_LENGTH]).longValue();
int blockOffset = (int) (offset / lobBlockSize);
ResultMetaData meta = deleteLobPartCall.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[DELETE_BLOCKS.LOB_ID] = ValuePool.getLong(lobID);
params[DELETE_BLOCKS.BLOCK_OFFSET] = new Integer(blockOffset);
params[DELETE_BLOCKS.BLOCK_LIMIT] = new Integer(Integer.MAX_VALUE);
params[DELETE_BLOCKS.TX_ID] =
ValuePool.getLong(sysLobSession.getTransactionTimestamp());
Result result =
sysLobSession.executeCompiledStatement(deleteLobPartCall, params);
setLength(lobID, offset);
return ResultLob.newLobTruncateResponse(lobID);
}
synchronized Result setLength(long lobID, long length) {
ResultMetaData meta = updateLobLength.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[UPDATE_LENGTH.LOB_LENGTH] = ValuePool.getLong(length);
params[UPDATE_LENGTH.LOB_ID] = ValuePool.getLong(lobID);
Result result = sysLobSession.executeCompiledStatement(updateLobLength,
params);
return result;
}
synchronized public Result adjustUsageCount(long lobID, int delta) {
Object[] data = getLobHeader(lobID);
int count = ((Number) data[LOB_IDS.LOB_USAGE_COUNT]).intValue();
if (count + delta == 0) {
deletedLobCount++;
}
ResultMetaData meta = updateLobUsage.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[UPDATE_USAGE.BLOCK_COUNT] = ValuePool.getInt(count + delta);
params[UPDATE_USAGE.LOB_ID] = ValuePool.getLong(lobID);
sysLobSession.sessionContext.pushDynamicArguments(params);
Result result = updateLobUsage.execute(sysLobSession);
sysLobSession.sessionContext.pop();
return result;
}
private int[][] getBlockAddresses(long lobID, int offset, int limit) {
ResultMetaData meta = getLobPart.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[GET_LOB_PART.LOB_ID] = ValuePool.getLong(lobID);
params[GET_LOB_PART.BLOCK_OFFSET] = ValuePool.getInt(offset);
params[GET_LOB_PART.BLOCK_LIMIT] = ValuePool.getInt(limit);
sysLobSession.sessionContext.pushDynamicArguments(params);
Result result = getLobPart.execute(sysLobSession);
sysLobSession.sessionContext.pop();
RowSetNavigator navigator = result.getNavigator();
int size = navigator.getSize();
int[][] blocks = new int[size][3];
for (int i = 0; i < size; i++) {
navigator.absolute(i);
Object[] data = navigator.getCurrent();
blocks[i][LOBS.BLOCK_ADDR] =
((Integer) data[LOBS.BLOCK_ADDR]).intValue();
blocks[i][LOBS.BLOCK_COUNT] =
((Integer) data[LOBS.BLOCK_COUNT]).intValue();
blocks[i][LOBS.BLOCK_OFFSET] =
((Integer) data[LOBS.BLOCK_OFFSET]).intValue();
}
navigator.close();
return blocks;
}
private void deleteBlockAddresses(long lobID, int offset, int limit) {
ResultMetaData meta = deleteLobPartCall.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[DELETE_BLOCKS.LOB_ID] = ValuePool.getLong(lobID);
params[DELETE_BLOCKS.BLOCK_OFFSET] = ValuePool.getInt(offset);
params[DELETE_BLOCKS.BLOCK_LIMIT] = ValuePool.getInt(limit);
params[DELETE_BLOCKS.TX_ID] =
ValuePool.getLong(sysLobSession.getTransactionTimestamp());
Result result =
sysLobSession.executeCompiledStatement(deleteLobPartCall, params);
}
private void divideBlockAddresses(long lobID, int offset) {
ResultMetaData meta = divideLobPartCall.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[DIVIDE_BLOCK.BLOCK_OFFSET] = ValuePool.getInt(offset);
params[DIVIDE_BLOCK.LOB_ID] = ValuePool.getLong(lobID);
Result result =
sysLobSession.executeCompiledStatement(divideLobPartCall, params);
}
private void createBlockAddresses(long lobID, int offset, int count) {
ResultMetaData meta = createLobPartCall.getParametersMetaData();
Object params[] = new Object[meta.getColumnCount()];
params[ALLOC_BLOCKS.BLOCK_COUNT] = ValuePool.getInt(count);
params[ALLOC_BLOCKS.BLOCK_OFFSET] = ValuePool.getInt(offset);
params[ALLOC_BLOCKS.LOB_ID] = ValuePool.getLong(lobID);
Result result =
sysLobSession.executeCompiledStatement(createLobPartCall, params);
}
synchronized public int getLobCount() {
sysLobSession.sessionContext.pushDynamicArguments(new Object[]{});
Result result = getLobCount.execute(sysLobSession);
sysLobSession.sessionContext.pop();
RowSetNavigator navigator = result.getNavigator();
boolean next = navigator.next();
if (!next) {
navigator.close();
return 0;
}
Object[] data = navigator.getCurrent();
return ((Number) data[0]).intValue();
}
}