提交 3d2cd051 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 77fe8768
org.h2.Driver
\ No newline at end of file
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
/**
* @author Thomas
*/
public class BitField {
private long[] data = new long[10];
private static final int ADDRESS_BITS = 6;
private static final int BITS = 64;
private static final int ADDRESS_MASK = BITS - 1;
public int getLastSetBit() {
int i = (data.length << ADDRESS_BITS) - 1;
while(i >= 0) {
if(get(i)) {
return i;
}
i--;
}
return -1;
}
public int nextSetBit(int fromIndex) {
int i = fromIndex >> ADDRESS_BITS;
int max = data.length;
int maxAddress = data.length << ADDRESS_BITS;
for(; i<max; i++) {
if(data[i] == 0) {
continue;
}
for(int j=Math.max(fromIndex, i << ADDRESS_BITS); j<maxAddress; j++) {
if(get(j)) {
return j;
}
}
}
return -1;
}
public int nextClearBit(int fromIndex) {
int i = fromIndex >> ADDRESS_BITS;
int max = data.length;
for(; i<max; i++) {
if(data[i] == -1) {
continue;
}
for(int j=Math.max(fromIndex, i << ADDRESS_BITS); ; j++) {
if(!get(j)) {
return j;
}
}
}
return fromIndex;
}
public long getLong(int i) {
int addr = getAddress(i);
if(addr >= data.length) {
return 0;
}
return data[addr];
}
public boolean get(int i) {
int addr = getAddress(i);
if(addr >= data.length) {
return false;
}
return (data[addr] & getBitMask(i)) != 0;
}
public void set(int i) {
int addr = getAddress(i);
checkCapacity(addr);
data[addr] |= getBitMask(i);
}
public void clear(int i) {
int addr = getAddress(i);
if(addr >= data.length) {
return;
}
data[addr] &= ~getBitMask(i);
}
private int getAddress(int i) {
return i >> ADDRESS_BITS;
}
private long getBitMask(int i) {
return 1L << (i & ADDRESS_MASK);
}
private void checkCapacity(int size) {
while (size >= data.length) {
int newSize = data.length == 0 ? 1 : data.length * 2;
long[] d = new long[newSize];
System.arraycopy(data, 0, d, 0, data.length);
data = d;
}
}
/*
private BitSet data = new BitSet();
public boolean get(int i) {
return data.get(i);
}
public void set(int i) {
data.set(i);
}
public void clear(int i) {
data.clear(i);
}
*/
public void setRange(int start, int len, boolean value) {
for(int end = start + len; start < end; start++) {
set(start, value);
}
}
private void set(int i, boolean value) {
if(value) {
set(i);
} else {
clear(i);
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
import org.h2.message.Message;
public class ByteUtils {
private static final char[] HEX = "0123456789abcdef".toCharArray();
public static int readInt(byte[] buff, int pos) {
return (buff[pos++]<< 24) + ((buff[pos++] & 0xff) << 16)
+ ((buff[pos++] & 0xff) << 8) + (buff[pos++] & 0xff);
}
public static long readLong(byte[] buff, int pos) {
return ((long)(readInt(buff, pos)) << 32) + (readInt(buff, pos+4) & 0xffffffffL);
}
public static int indexOf(byte[] bytes, byte[] pattern, int start) {
if (pattern.length == 0) {
return start;
}
if (start > bytes.length) {
return -1;
}
int last = bytes.length - pattern.length + 1;
next:
for(;start < last; start++) {
for(int i=0; i<pattern.length; i++) {
if(bytes[start + i] != pattern[i]) {
continue next;
}
}
return start;
}
return -1;
}
public static byte[] convertStringToBytes(String s) throws SQLException {
int len = s.length();
if (len % 2 == 1) {
throw Message.getSQLException(Message.HEX_STRING_ODD_1, s);
}
len /= 2;
byte[] buff = new byte[len];
try {
for (int i = 0; i < len; i++) {
buff[i] = (byte) ((Character.digit(s.charAt(i+i), 16) << 4) | (Character.digit(s.charAt(i+i+1), 16)));
}
} catch (NumberFormatException e) {
throw Message.getSQLException(Message.HEX_STRING_WRONG_1, s);
}
return buff;
}
public static int getByteArrayHash(byte[] value) {
int h = 1;
for (int i = 0; i < value.length;) {
h = 31 * h + value[i++];
}
return h;
}
public static String convertBytesToString(byte[] value) {
return convertBytesToString(value, value.length);
}
public static String convertBytesToString(byte[] value, int len) {
char[] buff = new char[len+len];
char[] hex = HEX;
for (int i = 0; i < len; i++) {
int c = value[i] & 0xff;
buff[i+i] = hex[c >> 4];
buff[i+i+1] = hex[c & 0xf];
}
return new String(buff);
}
public static boolean compareSecure(byte[] test, byte[] good) {
if((test==null) || (good==null)) {
return (test == null) && (good == null);
}
if(test.length != good.length) {
return false;
}
if(test.length == 0) {
return true;
}
// silly loop: this should help a little against timing attacks
boolean correct = true, correct2 = false;
for(int i=0; i<good.length; i++) {
if(test[i] != good[i]) {
correct = false;
} else {
correct2 = true;
}
}
return correct && correct2;
}
public static void clear(byte[] buff) {
for(int i=0; i<buff.length; i++) {
buff[i] = 0;
}
}
public static int compareNotNull(byte[] data1, byte[] data2) {
int len = Math.min(data1.length, data2.length);
for (int i = 0; i < len; i++) {
byte b = data1[i];
byte b2 = data2[i];
if (b != b2) {
return b > b2 ? 1 : -1;
}
}
int c = data1.length - data2.length;
return c == 0 ? 0 : (c < 0 ? -1 : 1);
}
public static String convertToBinString(byte[] buff) {
char[] chars = new char[buff.length];
for(int i=0; i<buff.length; i++) {
chars[i] = (char) (buff[i] & 0xff);
}
return new String(chars);
}
public static byte[] convertBinStringToBytes(String data) {
byte[] buff = new byte[data.length()];
for(int i=0; i<data.length(); i++) {
buff[i] = (byte) (data.charAt(i) & 0xff);
}
return buff;
}
public static byte[] copy(byte[] source, byte[] target) {
int len = source.length;
if(len > target.length) {
target = new byte[len];
}
System.arraycopy(source, 0, target, 0, len);
return target;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
public interface Cache {
ObjectArray getAllChanged();
void clear();
CacheObject get(int pos);
void put(CacheObject r) throws SQLException;
CacheObject update(int pos, CacheObject record) throws SQLException;
void remove(int pos);
CacheObject find(int i);
void setMaxSize(int value) throws SQLException;
String getTypeName();
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
/**
* For details about the 2Q algorithm, see http://www.vldb.org/conf/1994/P439.PDF.
* However, items are moved from 'in' queue and move to the 'main' queue if the are referenced again.
*/
public class Cache2Q implements Cache {
public static String TYPE_NAME = "TQ";
private static final int MAIN = 1, IN = 2, OUT = 3;
private int maxSize;
private CacheWriter writer;
private int percentIn = 20, percentOut = 50;
private int maxMain, maxIn, maxOut;
private CacheObject headMain = new CacheHead();
private CacheObject headIn = new CacheHead();
private CacheObject headOut = new CacheHead();
private int sizeMain, sizeIn, sizeOut, sizeRecords;
private int len;
private CacheObject[] values;
private int mask;
public Cache2Q(CacheWriter writer, int maxSize) {
this.writer = writer;
this.maxSize = maxSize;
this.len = maxSize / 2;
MathUtils.checkPowerOf2(len);
this.mask = len - 1;
clear();
}
public void clear() {
headMain.next = headMain.previous = headMain;
headIn.next = headIn.previous = headIn;
headOut.next = headOut.previous = headOut;
values = new CacheObject[len];
sizeIn = sizeOut = sizeMain = 0;
sizeRecords = 0;
recalcMax();
}
void setPercentIn(int percent) {
percentIn = percent;
recalcMax();
}
void setPercentOut(int percent) {
percentOut = percent;
recalcMax();
}
private void recalcMax() {
maxMain = maxSize;
maxIn = maxSize * percentIn / 100;
maxOut = maxSize * percentOut / 100;
}
private void addToFront(CacheObject head, CacheObject rec) {
if(Constants.CHECK) {
if(rec == head) {
throw Message.getInternalError("try to move head");
}
if(rec.next != null || rec.previous != null) {
throw Message.getInternalError("already linked");
}
}
rec.next = head;
rec.previous = head.previous;
rec.previous.next = rec;
head.previous = rec;
}
private void removeFromList(CacheObject rec) {
if(Constants.CHECK && (rec instanceof CacheHead && rec.cacheQueue != OUT)) {
throw Message.getInternalError();
}
rec.previous.next = rec.next;
rec.next.previous = rec.previous;
// TODO cache: mystery: why is this required? needs more memory if we don't do this
rec.next = null;
rec.previous = null;
}
public CacheObject get(int pos) {
CacheObject r = findCacheObject(pos);
if(r==null) {
return null;
}
if(r.cacheQueue == MAIN) {
removeFromList(r);
addToFront(headMain, r);
} else if(r.cacheQueue == OUT) {
return null;
} else if(r.cacheQueue == IN) {
removeFromList(r);
sizeIn -= r.getBlockCount();
sizeMain += r.getBlockCount();
r.cacheQueue = MAIN;
addToFront(headMain, r);
}
return r;
}
private CacheObject findCacheObject(int pos) {
CacheObject rec = values[pos & mask];
while(rec != null && rec.getPos() != pos) {
rec = rec.chained;
}
return rec;
}
private CacheObject removeCacheObject(int pos) {
int index = pos & mask;
CacheObject rec = values[index];
if(rec == null) {
return null;
}
if(rec.getPos() == pos) {
values[index] = rec.chained;
} else {
CacheObject last;
do {
last = rec;
rec = rec.chained;
if(rec == null) {
return null;
}
} while(rec.getPos() != pos);
last.chained = rec.chained;
}
sizeRecords--;
if(Constants.CHECK) {
rec.chained = null;
}
return rec;
}
public void remove(int pos) {
CacheObject r = removeCacheObject(pos);
if(r != null) {
removeFromList(r);
if(r.cacheQueue == MAIN) {
sizeMain -= r.getBlockCount();
} else if(r.cacheQueue == IN) {
sizeIn -= r.getBlockCount();
}
}
}
private void removeOld() throws SQLException {
if((sizeIn < maxIn) && (sizeOut < maxOut) && (sizeMain < maxMain)) {
return;
}
int i=0;
ObjectArray changed = new ObjectArray();
while (((sizeIn*4 > maxIn*3) || (sizeOut*4 > maxOut*3) || (sizeMain*4 > maxMain*3)) && sizeRecords > Constants.CACHE_MIN_RECORDS) {
if(i++ >= sizeRecords) {
// can't remove any record, because the log is not written yet
// hopefully this does not happen too much, but it could happen theoretically
// TODO log this
break;
}
if (sizeIn > maxIn) {
CacheObject r = headIn.next;
if(!r.canRemove()) {
removeFromList(r);
addToFront(headIn, r);
continue;
}
sizeIn -= r.getBlockCount();
int pos = r.getPos();
removeCacheObject(pos);
removeFromList(r);
if(r.isChanged()) {
changed.add(r);
}
r = new CacheHead();
r.setPos(pos);
r.cacheQueue = OUT;
putCacheObject(r);
addToFront(headOut, r);
sizeOut++;
if (sizeOut >= maxOut) {
r = headOut.next;
sizeOut--;
removeCacheObject(r.getPos());
removeFromList(r);
}
} else {
CacheObject r = headMain.next;
if(!r.canRemove()) {
removeFromList(r);
addToFront(headMain, r);
continue;
}
sizeMain -= r.getBlockCount();
removeCacheObject(r.getPos());
removeFromList(r);
if(r.isChanged()) {
changed.add(r);
}
}
}
if(changed.size() > 0) {
CacheObject.sort(changed);
for(i=0; i<changed.size(); i++) {
CacheObject rec = (CacheObject) changed.get(i);
writer.writeBack(rec);
}
}
}
public ObjectArray getAllChanged() {
ObjectArray list = new ObjectArray();
for(CacheObject o = headMain.next; o != headMain; o = o.next) {
if(o.isChanged()) {
list.add(o);
}
}
for(CacheObject o = headIn.next; o != headIn; o = o.next) {
if(o.isChanged()) {
list.add(o);
}
}
CacheObject.sort(list);
return list;
}
public CacheObject find(int pos) {
CacheObject o = findCacheObject(pos);
if(o != null && o.cacheQueue != OUT) {
return o;
}
return null;
}
private void putCacheObject(CacheObject rec) {
if(Constants.CHECK) {
for(int i=0; i<rec.getBlockCount(); i++) {
CacheObject old = find(rec.getPos() + i);
if(old != null) {
throw Message.getInternalError("try to add a record twice i="+i);
}
}
}
int index = rec.getPos() & mask;
rec.chained = values[index];
values[index] = rec;
sizeRecords++;
}
public void put(CacheObject rec) throws SQLException {
int pos = rec.getPos();
CacheObject r = findCacheObject(pos);
if(r != null) {
if(r.cacheQueue == OUT) {
removeCacheObject(pos);
removeFromList(r);
removeOld();
rec.cacheQueue = MAIN;
putCacheObject(rec);
addToFront(headMain, rec);
sizeMain += rec.getBlockCount();
}
} else if(sizeMain < maxMain) {
removeOld();
rec.cacheQueue = MAIN;
putCacheObject(rec);
addToFront(headMain, rec);
sizeMain += rec.getBlockCount();
} else {
removeOld();
rec.cacheQueue = IN;
putCacheObject(rec);
addToFront(headIn, rec);
sizeIn += rec.getBlockCount();
}
}
public CacheObject update(int pos, CacheObject rec) throws SQLException {
CacheObject old = find(pos);
if(old == null || old.cacheQueue == OUT) {
put(rec);
} else {
if(old == rec) {
if(rec.cacheQueue == MAIN) {
removeFromList(rec);
addToFront(headMain, rec);
}
}
}
return old;
}
public void setMaxSize(int newSize) throws SQLException {
maxSize = newSize < 0 ? 0 : newSize;
recalcMax();
removeOld();
}
public String getTypeName() {
return TYPE_NAME;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
import org.h2.store.DataPage;
public class CacheHead extends CacheObject {
public int getByteCount(DataPage dummy) throws SQLException {
return 0;
}
public void write(DataPage buff) throws SQLException {
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
/**
* Special behavior of the cache: You are not allowed to add the same record
* twice.
*
* @author Thomas
*/
public class CacheLRU implements Cache {
public static String TYPE_NAME = "LRU";
private int len;
private int maxSize;
private CacheObject[] values;
private int mask;
private CacheWriter writer;
private int sizeRecords;
private int sizeBlocks;
private CacheObject head = new CacheHead();
public CacheLRU(CacheWriter writer, int maxSize) {
this.writer = writer;
this.len = maxSize / 2;
this.mask = len - 1;
MathUtils.checkPowerOf2(len);
this.maxSize = maxSize;
clear();
}
public void put(CacheObject rec) throws SQLException {
if(Constants.CHECK) {
for(int i=0; i<rec.getBlockCount(); i++) {
CacheObject old = find(rec.getPos() + i);
if(old != null) {
throw Message.getInternalError("try to add a record twice i="+i);
}
}
}
int index = rec.getPos() & mask;
rec.chained = values[index];
values[index] = rec;
sizeRecords++;
sizeBlocks += rec.getBlockCount();
addToFront(rec);
removeOld();
}
public CacheObject update(int pos, CacheObject rec) throws SQLException {
CacheObject old = find(pos);
if(old == null) {
put(rec);
} else {
if(Constants.CHECK) {
if(old!=rec) {
throw Message.getInternalError("old != record old="+old+" new="+rec);
}
}
removeFromLinkedList(rec);
addToFront(rec);
}
return old;
}
private void removeOld() throws SQLException {
if(sizeBlocks < maxSize) {
return;
}
int i=0;
ObjectArray changed = new ObjectArray();
while (sizeBlocks*4 > maxSize*3 && sizeRecords > Constants.CACHE_MIN_RECORDS) {
CacheObject last = head.next;
if(i++ >= sizeRecords) {
// can't remove any record, because the log is not written yet
// hopefully this does not happen too much, but it could happen theoretically
// TODO log this
break;
}
if(Constants.CHECK && last == head) {
throw Message.getInternalError("try to remove head");
}
// we are not allowed to remove it if the log is not yet written
// (because we need to log before writing the data)
// also, can't write it if the record is pinned
if(!last.canRemove()) {
removeFromLinkedList(last);
addToFront(last);
continue;
}
remove(last.getPos());
if (last.isChanged()) {
changed.add(last);
}
}
if(changed.size() > 0) {
CacheObject.sort(changed);
for(i=0; i<changed.size(); i++) {
CacheObject rec = (CacheObject) changed.get(i);
writer.writeBack(rec);
}
}
}
private void addToFront(CacheObject rec) {
if(Constants.CHECK && rec == head) {
throw Message.getInternalError("try to move head");
}
rec.next = head;
rec.previous = head.previous;
rec.previous.next = rec;
head.previous = rec;
}
private void removeFromLinkedList(CacheObject rec) {
if(Constants.CHECK && rec == head) {
throw Message.getInternalError("try to remove head");
}
rec.previous.next = rec.next;
rec.next.previous = rec.previous;
// TODO cache: mystery: why is this required? needs more memory if we don't do this
rec.next = null;
rec.previous = null;
}
public void remove(int pos) {
int index = pos & mask;
CacheObject rec = values[index];
if(rec == null) {
return;
}
if(rec.getPos() == pos) {
values[index] = rec.chained;
} else {
CacheObject last;
do {
last = rec;
rec = rec.chained;
if(rec == null) {
return;
}
} while(rec.getPos() != pos);
last.chained = rec.chained;
}
sizeRecords--;
sizeBlocks -= rec.getBlockCount();
removeFromLinkedList(rec);
if(Constants.CHECK) {
rec.chained = null;
if(find(pos) != null) {
throw Message.getInternalError("not removed!");
}
}
}
public CacheObject find(int pos) {
CacheObject rec = values[pos & mask];
while(rec != null && rec.getPos() != pos) {
rec = rec.chained;
}
return rec;
}
public CacheObject get(int pos) {
CacheObject rec = find(pos);
if(rec != null) {
removeFromLinkedList(rec);
addToFront(rec);
}
return rec;
}
// private void testConsistency() {
// int s = size;
// HashSet set = new HashSet();
// for(int i=0; i<values.length; i++) {
// Record rec = values[i];
// if(rec == null) {
// continue;
// }
// set.add(rec);
// while(rec.chained != null) {
// rec = rec.chained;
// set.add(rec);
// }
// }
// Record rec = head.next;
// while(rec != head) {
// set.add(rec);
// rec = rec.next;
// }
// rec = head.previous;
// while(rec != head) {
// set.add(rec);
// rec = rec.previous;
// }
// if(set.size() != size) {
// System.out.println("size="+size+" but el.size="+set.size());
// }
// }
public ObjectArray getAllChanged() {
// if(Database.CHECK) {
// testConsistency();
// }
// TODO cache: should probably use the LRU list
ObjectArray list = new ObjectArray();
for (int i = 0; i < len; i++) {
CacheObject rec = values[i];
while (rec != null) {
if(rec.isChanged()) {
list.add(rec);
if(list.size() >= sizeRecords) {
if(Constants.CHECK) {
if(list.size() > sizeRecords) {
throw Message.getInternalError("cache chain error");
}
} else {
break;
}
}
}
rec = rec.chained;
}
}
return list;
}
public void clear() {
head.next = head.previous = head;
values = new CacheObject[len];
sizeRecords = 0;
sizeBlocks = 0;
}
public void setMaxSize(int newSize) throws SQLException {
maxSize = newSize < 0 ? 0 : newSize;
removeOld();
}
public String getTypeName() {
return TYPE_NAME;
}
}
// Unmaintained reference code (very old)
//import java.util.Iterator;
//import java.util.LinkedHashMap;
//import java.util.Map;
//
//public class Cache extends LinkedHashMap {
//
// final static int MAX_SIZE = 1 << 10;
// private CacheWriter writer;
//
// public Cache(CacheWriter writer) {
// super(16, (float) 0.75, true);
// this.writer = writer;
// }
//
// protected boolean removeEldestEntry(Map.Entry eldest) {
// if(size() <= MAX_SIZE) {
// return false;
// }
// Record entry = (Record) eldest.getValue();
// if(entry.getDeleted()) {
// return true;
// }
// if(entry.isChanged()) {
// try {
////System.out.println("cache write "+entry.getPos());
// writer.writeBack(entry);
// } catch(SQLException e) {
// // TODO cache: printStackTrace not needed if we use our own hashtable
// e.printStackTrace();
// }
// }
// return true;
// }
//
// public void put(Record rec) {
// put(new Integer(rec.getPos()), rec);
// }
//
// public Record get(int pos) {
// return (Record)get(new Integer(pos));
// }
//
// public void remove(int pos) {
// remove(new Integer(pos));
// }
//
// public ObjectArray getAllChanged() {
// Iterator it = values().iterator();
// ObjectArray list = new ObjectArray();
// while(it.hasNext()) {
// Record rec = (Record)it.next();
// if(rec.isChanged()) {
// list.add(rec);
// }
// }
// return list;
// }
//}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.util.Comparator;
import org.h2.engine.Constants;
import org.h2.message.Message;
public abstract class CacheObject {
private boolean changed;
public CacheObject previous, next, chained;
public int cacheQueue;
private int blockCount;
private int pos;
public static void sort(ObjectArray recordList) {
recordList.sort(new Comparator() {
public int compare(Object a, Object b) {
int pa = ((CacheObject) a).getPos();
int pb = ((CacheObject) b).getPos();
return pa==pb ? 0 : (pa<pb ? -1 : 1);
}
});
}
public void setBlockCount(int size) {
this.blockCount = size;
}
public int getBlockCount() {
return blockCount;
}
public void setPos(int pos) {
if(Constants.CHECK && (previous!=null || next!=null || chained!=null)) {
throw Message.getInternalError("setPos too late");
}
this.pos = pos;
}
public int getPos() {
return pos;
}
public boolean isChanged() {
return changed;
}
public void setChanged(boolean b) {
changed = b;
}
public boolean isPinned() {
return false;
}
public boolean canRemove() {
return true;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
/**
* @author Thomas
*/
public interface CacheWriter {
void writeBack(CacheObject entry) throws SQLException;
}
package org.h2.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
/**
* The InputStreamReader may read some more bytes than required.
* If this is a problem, use this class
*/
public class ExactUTF8InputStreamReader extends Reader {
private InputStream in;
public ExactUTF8InputStreamReader(InputStream in) {
this.in = in;
}
public void close() throws IOException {
}
public int read(char[] cbuf, int off, int len) throws IOException {
for(int i=0; i<len; i++, off++) {
int x = in.read();
if(x < 0) {
return i == 0 ? -1 : i;
}
x = x & 0xff;
if(x < 0x80) {
cbuf[off] = (char)x;
} else if(x >= 0xe0) {
cbuf[off] = (char)(((x & 0xf) << 12) + ((in.read() & 0x3f) << 6) + (in.read() & 0x3f));
} else {
cbuf[off] = (char)(((x & 0x1f) << 6) + (in.read() & 0x3f));
}
}
return len;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Properties;
import org.h2.engine.Constants;
import org.h2.message.Message;
import org.h2.message.TraceSystem;
/**
* @author Thomas
*/
public class FileUtils {
public static HashMap memoryFiles = new HashMap();
private static final String MEMORY_PREFIX = "inmemory:";
private static final String MEMORY_PREFIX_2 = "mem:";
// TODO detection of 'case in sensitive filesystem' could maybe implemented using some other means
private static final boolean isCaseInsensitiveFileSystem = (File.separatorChar == '\\');
// TODO gcj: use our own UTF-8 encoder
public static RandomAccessFile openRandomAccessFile(String fileName, String mode) throws IOException {
try {
return new RandomAccessFile(fileName, mode);
} catch(IOException e) {
freeMemoryAndFinalize();
return new RandomAccessFile(fileName, mode);
}
}
public static void setLength(RandomAccessFile file, long newLength) throws IOException {
try {
file.setLength(newLength);
} catch(IOException e) {
long length = file.length();
if(newLength < length) {
throw e;
} else {
long pos = file.getFilePointer();
file.seek(length);
long remaining = newLength - length;
int maxSize = 1024 * 1024;
int block = (int)Math.min(remaining, maxSize);
byte[] buffer = new byte[block];
while(remaining > 0) {
int write = (int) Math.min(remaining, maxSize);
file.write(buffer, 0, write);
remaining -= write;
}
file.seek(pos);
}
}
}
public static FileWriter openFileWriter(String fileName, boolean append) throws IOException {
try {
return new FileWriter(fileName, append);
} catch(IOException e) {
freeMemoryAndFinalize();
return new FileWriter(fileName, append);
}
}
public static boolean fileStartsWith(String fileName, String prefix) {
if(isCaseInsensitiveFileSystem) {
fileName = StringUtils.toUpperEnglish(fileName);
prefix = StringUtils.toUpperEnglish(prefix);
}
return fileName.startsWith(prefix);
}
public static FileOutputStream openFileOutputStream(File file) throws IOException {
try {
return new FileOutputStream(file);
} catch(IOException e) {
freeMemoryAndFinalize();
return new FileOutputStream(file);
}
}
private static void freeMemoryAndFinalize() {
long mem = Runtime.getRuntime().freeMemory();
for(int i=0; i<16; i++) {
System.gc();
long now = Runtime.getRuntime().freeMemory();
Runtime.getRuntime().runFinalization();
if(now == mem) {
break;
}
mem = now;
}
}
public static void rename(String oldName, String newName) throws SQLException {
if(isInMemory(oldName)) {
MemoryFile f = getMemoryFile(oldName);
f.setName(newName);
memoryFiles.put(newName, f);
return;
}
File oldFile = new File(oldName);
File newFile = new File(newName);
if(oldFile.getName().equals(newFile.getName())) {
throw Message.getInternalError("rename file old=new");
}
if(oldFile.exists() && !newFile.exists()) {
for(int i=0; i<16; i++) {
boolean ok = oldFile.renameTo(newFile);
if(ok) {
return;
}
wait(i);
}
}
throw Message.getSQLException(Message.FILE_RENAME_FAILED_2, new String[]{oldName, newName}, null);
}
public static synchronized Properties loadProperties(File file) throws IOException {
Properties prop = new Properties();
if(file.exists()) {
FileInputStream in = new FileInputStream(file);
try {
prop.load(in);
} finally {
in.close();
}
}
return prop;
}
public static boolean getBooleanProperty(Properties prop, String key, boolean def) {
String value = prop.getProperty(key, ""+def);
try {
return Boolean.valueOf(value).booleanValue();
} catch(Exception e) {
TraceSystem.traceThrowable(e);
return def;
}
}
public static int getIntProperty(Properties prop, String key, int def) {
String value = prop.getProperty(key, ""+def);
try {
return MathUtils.decodeInt(value);
} catch(Exception e) {
TraceSystem.traceThrowable(e);
return def;
}
}
public static void createDirs(String fileName) throws SQLException {
File f = new File(fileName);
if(!f.exists()) {
String parent = f.getParent();
if(parent == null) {
return;
}
File dir = new File(parent);
for(int i=0; i<16; i++) {
if(dir.exists() || dir.mkdirs()) {
return;
}
wait(i);
}
throw Message.getSQLException(Message.FILE_CREATION_FAILED_1, parent);
}
}
public static boolean createNewFile(String fileName) throws SQLException {
if(isInMemory(fileName)) {
if(exists(fileName)) {
return false;
}
// creates the file (not thread safe)
getMemoryFile(fileName);
return true;
}
File file = new File(fileName);
for(int i=0; i<8; i++) {
try {
return file.createNewFile();
} catch (IOException e) {
// TODO file lock: check if 'access denied' exceptions are really a concurrent access problem
wait(i);
}
}
// TODO GCJ: it seems gcj throws 'CreateFile failed' if the file already exists?!
return false;
// TODO is this message used elsewhere?
// throw Message.getSQLException(Message.FILE_CREATION_FAILED_1, fileName);
}
public static void delete(String fileName) throws SQLException {
if(isInMemory(fileName)) {
memoryFiles.remove(fileName);
return;
}
File file = new File(fileName);
if(file.exists()) {
for(int i=0; i<16; i++) {
boolean ok = file.delete();
if(ok) {
return;
}
wait(i);
}
throw Message.getSQLException(Message.FILE_DELETE_FAILED_1, fileName);
}
}
private static void wait(int i) {
if(i > 8) {
System.gc();
}
try {
// sleep at most 256 ms
Thread.sleep(i * i);
} catch (InterruptedException e) {
// ignore
}
}
public static String getFileName(String name) throws SQLException {
String separator = System.getProperty("file.separator");
String path = getParent(name) + separator;
String fullFileName = normalize(name);
if(!fullFileName.startsWith(path)) {
throw Message.getInternalError("file utils error: " + fullFileName+" does not start with "+path);
}
String fileName = fullFileName.substring(path.length());
return fileName;
}
public static File getFileInUserHome(String filename) {
String userDir = System.getProperty("user.home");
if(userDir == null) {
return new File(filename);
}
return new File(userDir, filename);
}
public static String normalize(String fileName) throws SQLException {
if(isInMemory(fileName)) {
return fileName;
}
File f = new File(fileName);
try {
return f.getCanonicalPath();
} catch (IOException e) {
throw Message.convert(e);
}
}
public static void tryDelete(String fileName) {
if(isInMemory(fileName)) {
memoryFiles.remove(fileName);
return;
}
new File(fileName).delete();
}
public static boolean isReadOnly(String fileName) {
if(isInMemory(fileName)) {
return false;
}
File f = new File(fileName);
return f.exists() && !f.canWrite();
}
public static boolean exists(String fileName) {
if(isInMemory(fileName)) {
return memoryFiles.get(fileName) != null;
}
return new File(fileName).exists();
}
public static MemoryFile getMemoryFile(String fileName) {
MemoryFile m = (MemoryFile)memoryFiles.get(fileName);
if(m == null) {
m = new MemoryFile(fileName);
memoryFiles.put(fileName, m);
}
return m;
}
public static long length(String fileName) {
if(isInMemory(fileName)) {
return getMemoryFile(fileName).length();
}
return new File(fileName).length();
}
public static boolean isInMemory(String fileName) {
return fileName.startsWith(MEMORY_PREFIX) || fileName.startsWith(MEMORY_PREFIX_2);
}
public static String createTempFile(String name, String suffix, boolean deleteOnExit) throws IOException {
name += ".";
if(isInMemory(name)) {
for(int i=0;; i++) {
String n = name + i + suffix;
if(!exists(n)) {
// creates the file (not thread safe)
getMemoryFile(n);
return n;
}
}
}
String prefix = new File(name).getName();
File dir = new File(name).getAbsoluteFile().getParentFile();
File f = File.createTempFile(prefix, suffix, dir);
if(deleteOnExit) {
f.deleteOnExit();
}
return f.getCanonicalPath();
}
// public static void deleteOnExit(String temp) {
// if(isInMemory(temp)) {
// // nothing to do
// return;
// }
// new File(temp).deleteOnExit();
// }
public static String getParent(String fileName) {
if(isInMemory(fileName)) {
return MEMORY_PREFIX;
}
return new File(fileName).getParent();
}
public static String[] listFiles(String path) throws SQLException {
if(isInMemory(path)) {
String[] list = new String[memoryFiles.size()];
MemoryFile[] l = new MemoryFile[memoryFiles.size()];
memoryFiles.values().toArray(l);
for(int i=0; i<list.length; i++) {
list[i] = l[i].getName();
}
return list;
}
try {
File[] files = new File(path).listFiles();
if(files == null) {
return new String[0];
}
String[] list = new String[files.length];
for(int i=0; i<files.length; i++) {
list[i] = files[i].getCanonicalPath();
}
return list;
} catch (IOException e) {
throw Message.convert(e);
}
}
public static boolean isDirectory(String fileName) {
if(isInMemory(fileName)) {
// TODO inmemory: currently doesn't support directories
return false;
}
return new File(fileName).isDirectory();
}
public static void copy(String original, String copy) throws SQLException {
FileOutputStream out = null;
FileInputStream in = null;
try {
out = openFileOutputStream(new File(copy));
in = new FileInputStream(new File(original));
byte[] buffer = new byte[Constants.IO_BUFFER_SIZE];
while(true) {
int len = in.read(buffer);
if(len < 0) {
break;
}
out.write(buffer, 0, len);
}
} catch(IOException e) {
IOUtils.closeSilently(in);
IOUtils.closeSilently(out);
throw Message.convert(e);
}
}
public static void deleteRecursive(String fileName) throws SQLException {
if(FileUtils.isDirectory(fileName)) {
String[] list = FileUtils.listFiles(fileName);
for(int i=0; list != null && i<list.length; i++) {
deleteRecursive(list[i]);
}
}
FileUtils.delete(fileName);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
public abstract class HashBase {
protected int mask, len, size, deletedCount, level;
protected boolean zeroKey;
private int maxSize, minSize, maxDeleted;
private static final int MAX_LOAD = 90;
public HashBase() {
reset(2);
}
public int size() {
return size + (zeroKey ? 1 : 0);
}
protected void checkSizePut() throws SQLException {
if(deletedCount > size) {
rehash(level);
}
if(size + deletedCount >= maxSize) {
rehash(level+1);
}
}
protected void checkSizeRemove() throws SQLException {
if(size < minSize && level>0) {
rehash(level-1);
} else if(deletedCount > maxDeleted) {
rehash(level);
}
}
protected abstract void rehash(int newLevel) throws SQLException;
protected void reset(int newLevel) {
minSize = size * 3 / 4;
size = 0;
level = newLevel;
len = 2 << level;
mask = len - 1;
maxSize = (int)(len * MAX_LOAD / 100L);
deletedCount = 0;
maxDeleted = 20 + len / 2;
}
protected int getIndex(int hash) {
return hash & mask;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
public class IOUtils {
private static final int BUFFER_BLOCK_SIZE = 4 * 1024;
public static void closeSilently(OutputStream out) {
if(out != null) {
try {
out.close();
} catch(IOException e) {
// ignore
}
}
}
public static void skipFully(InputStream in, long skip) throws IOException {
while(skip > 0) {
skip -= in.skip(skip);
}
}
public static void skipFully(Reader reader, long skip) throws IOException {
while(skip > 0) {
skip -= reader.skip(skip);
}
}
public static long copyAndClose(InputStream in, OutputStream out) throws IOException {
try {
return copyAndCloseInput(in, out);
} finally {
out.close();
}
}
public static long copyAndCloseInput(InputStream in, OutputStream out) throws IOException {
int written = 0;
try {
byte[] buffer = new byte[4 * 1024];
while(true) {
int len = in.read(buffer);
if(len < 0) {
break;
}
out.write(buffer, 0, len);
written += len;
}
} finally {
in.close();
}
return written;
}
public static long copyAndCloseInput(Reader in, Writer out) throws IOException {
long written = 0;
try {
char[] buffer = new char[4 * 1024];
while(true) {
int len = in.read(buffer);
if(len < 0) {
break;
}
out.write(buffer, 0, len);
written += len;
}
} finally {
in.close();
}
return written;
}
public static void closeSilently(InputStream in) {
if(in != null) {
try {
in.close();
} catch(IOException e) {
// ignore
}
}
}
public static void closeSilently(Reader reader) {
if(reader != null) {
try {
reader.close();
} catch(IOException e) {
// ignore
}
}
}
public static void closeSilently(Writer writer) {
if(writer != null) {
try {
writer.flush();
writer.close();
} catch(IOException e) {
// ignore
}
}
}
public static byte[] readBytesAndClose(InputStream in, int length) throws IOException {
try {
if(length <= 0) {
length = Integer.MAX_VALUE;
}
int block = Math.min(BUFFER_BLOCK_SIZE, length);
ByteArrayOutputStream out=new ByteArrayOutputStream(block);
byte[] buff=new byte[block];
while(length > 0) {
int len = Math.min(block, length);
len = in.read(buff, 0, len);
if(len < 0) {
break;
}
out.write(buff, 0, len);
length -= len;
}
return out.toByteArray();
} finally {
in.close();
}
}
public static String readStringAndClose(Reader in, int length) throws IOException {
if(length <= 0) {
length = Integer.MAX_VALUE;
}
int block = Math.min(BUFFER_BLOCK_SIZE, length);
StringWriter out=new StringWriter(length == Integer.MAX_VALUE ? block : length);
char[] buff=new char[block];
while(length > 0) {
int len = Math.min(block, length);
len = in.read(buff, 0, len);
if(len < 0) {
break;
}
out.write(buff, 0, len);
length -= len;
}
in.close();
return out.toString();
}
public static int readFully(InputStream in, byte[] buffer, int max) throws IOException {
int off = 0, len = Math.min(max, buffer.length);
if(len == 0) {
return 0;
}
while (true) {
int l = len - off;
if (l <= 0) {
break;
}
l = in.read(buffer, off, l);
if (l < 0) {
break;
}
off += l;
}
return off < 0 ? -1 : off;
}
public static int readFully(Reader in, char[] buffer, int max) throws IOException {
int off = 0, len = Math.min(max, buffer.length);
if(len == 0) {
return 0;
}
while (true) {
int l = len - off;
if (l <= 0) {
break;
}
l = in.read(buffer, off, l);
if (l < 0) {
break;
}
off += l;
}
return off < 0 ? -1 : off;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import org.h2.engine.Constants;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class IntArray {
private int[] data;
private int size;
private int hash;
public IntArray() {
data = new int[10];
}
public IntArray(int[] data) {
this.data = data;
size = data.length;
}
public static int[] clone(int[] array) {
if(array == null) {
return null;
}
int[] copy = new int[array.length];
System.arraycopy(array, 0, copy, 0, array.length);
return copy;
}
public static boolean equals(int[] a, int[] b) {
if(a == null || b == null) {
return a == b;
}
if(a.length != b.length) {
return false;
}
for(int i=0; i<a.length; i++) {
if(a[i] != b[i]) {
return false;
}
}
return true;
}
public void add(int value) {
checkCapacity();
data[size++] = value;
}
public int get(int i) {
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
}
return data[i];
}
public int remove(int i) {
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
}
int value = data[i];
System.arraycopy(data, i + 1, data, i, size - i - 1);
size--;
return value;
}
private void checkCapacity() {
if (size >= data.length) {
int[] d = new int[data.length * 2];
System.arraycopy(data, 0, d, 0, data.length);
data = d;
}
}
public void add(int i, int value) {
if (Constants.CHECK && i > size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
}
checkCapacity();
if (i == size) {
add(value);
} else {
System.arraycopy(data, i, data, i + 1, size - i);
data[i] = value;
size++;
}
}
public void set(int i, int value) {
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
}
data[i] = value;
}
public boolean equals(Object obj) {
IntArray other = (IntArray) obj;
if(hashCode() != other.hashCode() || size != other.size) {
return false;
}
for(int i=0; i<size; i++) {
if(data[i] != other.data[i]) {
return false;
}
}
return true;
}
public int hashCode() {
if (hash != 0) {
return hash;
}
int h = size + 1;
for(int i=0; i<size; i++) {
h = h * 31 + data[i];
}
hash = h;
return h;
}
public int size() {
return size;
}
public void addValueSorted(int value) {
int l = 0, r = size;
while(l < r) {
int i = (l + r) >>> 1;
int d = data[i];
if(d == value) {
return;
} else if(d > value) {
r = i;
} else {
l = i + 1;
}
}
add(l, value);
}
// public void addValueSorted(int value) {
// int l = 0, r = size - 1;
// while(l <= r) {
// int i = (l + r) >>> 1;
// int d = data[i];
// if(d == value) {
// return;
// } else if(d > value) {
// r = i - 1;
// } else {
// l = i + 1;
// }
// }
// add(l, value);
// }
public void removeValue(int value) {
for(int i=0; i<size; i++) {
if(data[i] == value) {
remove(i);
return;
}
}
throw Message.getInternalError();
}
public int findNextValueIndex(int value) {
int l = 0, r = size;
while(l < r) {
int i = (l + r) >>> 1;
int d = data[i];
if(d >= value) {
r = i;
} else {
l = i + 1;
}
}
return l;
// for(int i=0; i<size; i++) {
// if(data[i] >= value) {
// return i;
// }
// }
// return size;
}
public void sort() {
for (int i = 1, j; i < size(); i++) {
int t = get(i);
for (j = i - 1; j >= 0 && (get(j) > t); j--) {
set(j + 1, get(j));
}
set(j + 1, t);
}
}
public void toArray(int[] array) {
System.arraycopy(data, 0, array, 0, size);
}
// ArrayList data = new ArrayList();
//
// public IntArray() {
// }
//
// public IntArray(int[] data) {
// for (int i = 0; i < data.length; i++) {
// this.data.add(new Integer(data[i]));
// }
// }
//
// public void add(int value) {
// this.data.add(new Integer(value));
// }
//
// public int get(int i) {
// return ((Integer) data.get(i)).intValue();
// }
//
// public void remove(int i) {
// data.remove(i);
// }
//
// public void add(int i, int value) {
// data.add(i, new Integer(value));
// }
//
// public void set(int i, int value) {
// data.set(i, new Integer(value));
// }
//
// public int size() {
// return data.size();
// }
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
import org.h2.message.Message;
/**
* An empty record has key=0 and value=0
* A deleted record has key=0 and value=DELETED
* value NOT_FOUND: -1; this value cannot be stored in the map (however 0 can be stored)
* @author Thomas
*/
public class IntIntHashMap extends HashBase {
public static final int NOT_FOUND = -1;
private static final int DELETED = 1;
private int[] keys;
private int[] values;
private int zeroValue;
protected void reset(int newLevel) {
super.reset(newLevel);
keys = new int[len];
values = new int[len];
}
public void put(int key, int value) {
if(key==0) {
zeroKey = true;
zeroValue = value;
}
try {
checkSizePut();
} catch(SQLException e) {
// in fact, it is never thrown
// TODO hash: maybe optimize it
}
int index = getIndex(key);
int plus = 1;
int deleted = -1;
do {
int k = keys[index];
if(k==0) {
if(values[index] != DELETED) {
// found an empty record
if(deleted>=0) {
index = deleted;
deletedCount--;
}
size++;
keys[index] = key;
values[index] = value;
return;
}
// found a deleted record
if(deleted<0) {
deleted = index;
}
} else if(k==key) {
// update existing
values[index] = value;
return;
}
index = (index + plus++) & mask;
} while(plus <= len);
// no space
throw Message.getInternalError("hashmap is full");
}
public void remove(int key) {
if(key == 0) {
zeroKey = false;
return;
}
try {
checkSizeRemove();
} catch(SQLException e) {
// in fact, it is never thrown
// TODO hash: maybe optimize it
}
int index = getIndex(key);
int plus = 1;
do {
int k = keys[index];
if(k==key) {
// found the record
keys[index] = 0;
values[index] = DELETED;
deletedCount++;
size--;
return;
} else if(k==0 && values[index] == 0) {
// found an empty record
return;
}
index = (index + plus++) & mask;
k = keys[index];
} while(plus <= len);
// not found
}
protected void rehash(int newLevel) {
int[] oldKeys = keys;
int[] oldValues = values;
reset(newLevel);
for(int i=0; i<oldKeys.length; i++) {
int k = oldKeys[i];
if(k != 0) {
put(k, oldValues[i]);
}
}
}
public int get(int key) {
if(key == 0) {
return zeroKey ? zeroValue : NOT_FOUND;
}
int index = getIndex(key);
int plus = 1;
do {
int k = keys[index];
if(k==0 && values[index]==0) {
// found an empty record
return NOT_FOUND;
} else if(k==key) {
// found it
return values[index];
}
index = (index + plus++) & mask;
} while(plus <= len);
return NOT_FOUND;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.math.BigDecimal;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class MathUtils {
// with blockSizePowerOf2 8: 0 > 0; 1..8 > 8, 9..16 > 16, ...
public static int roundUp(int x, int blockSizePowerOf2) {
return (x + blockSizePowerOf2 - 1) & (-blockSizePowerOf2);
}
public static void checkPowerOf2(int len) {
if((len & (len-1)) != 0 && len > 0) {
throw Message.getInternalError("not a power of 2: " + len);
}
}
public static long scaleUp50Percent(long start, long min, long blockSize) {
while(start < min) {
start += start / 2;
start += start % blockSize;
}
return start;
}
public static BigDecimal setScale(BigDecimal bd, int scale) throws SQLException {
if(scale > Constants.BIGDECIMAL_SCALE_MAX) {
throw Message.getInvalidValueException(""+scale, "scale");
} else if(scale < 0) {
throw Message.getInvalidValueException(""+scale, "scale");
}
return bd.setScale(scale, BigDecimal.ROUND_HALF_UP);
}
public static byte decodeByte(String s) {
return Byte.decode(s).byteValue();
}
public static short decodeShort(String s) {
return Short.decode(s).shortValue();
}
public static int decodeInt(String s) {
return Integer.decode(s).intValue();
}
public static long decodeLong(String s) {
return Long.decode(s).longValue();
}
public static int convertLongToInt(long l) {
if(l<=Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
} else if(l>=Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
} else {
return (int) l;
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
public class MemoryFile {
private String name;
private int length;
private byte[] data;
private int pos;
private byte[] magic;
MemoryFile(String name) {
this.name = name;
data = new byte[16];
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long length() {
return length;
}
public void setLength(long l) {
length = (int)l;
}
public void seek(long pos) {
this.pos = (int)pos;
}
public void write(byte[] b, int off, int len) {
if(pos+len > length) {
length = pos+len;
}
if(pos+len > data.length) {
byte[] n = new byte[length*2];
System.arraycopy(data, 0, n, 0, data.length);
data = n;
}
System.arraycopy(b, off, data, pos, len);
pos += len;
}
public void readFully(byte[] b, int off, int len) {
if(pos+len > length) {
length = pos+len;
}
if(pos+len > data.length) {
byte[] n = new byte[length*2];
System.arraycopy(data, 0, n, 0, data.length);
data = n;
}
System.arraycopy(data, pos, b, off, len);
pos += len;
}
public long getFilePointer() {
return pos;
}
public void setMagic(byte[] magic) {
this.magic = magic;
}
public byte[] getMagic() {
return magic;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
public class MemoryUtils {
private static long lastGC;
private static int GC_DELAY = 50;
private static int MAX_GC = 8;
public static int getMemoryUsed() {
collectGarbage();
Runtime rt = Runtime.getRuntime();
long mem = rt.totalMemory() - rt.freeMemory();
return (int) (mem >> 10);
}
public static int getMemoryFree() {
collectGarbage();
Runtime rt = Runtime.getRuntime();
long mem = rt.freeMemory();
return (int) (mem >> 10);
}
private static synchronized void collectGarbage() {
Runtime runtime = Runtime.getRuntime();
long total = runtime.totalMemory();
long time = System.currentTimeMillis();
if (lastGC + GC_DELAY < time) {
for (int i = 0; i < MAX_GC; i++) {
runtime.gc();
long now = runtime.totalMemory();
if (now == total) {
lastGC = System.currentTimeMillis();
break;
}
total = now;
}
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.security.SecureSocketFactory;
public class NetUtils {
public static Socket createLoopbackSocket(int port, boolean ssl) throws IOException, SQLException {
InetAddress address = InetAddress.getByName("127.0.0.1");
return createSocket(address, port, ssl);
}
public static Socket createSocket(InetAddress address, int port, boolean ssl) throws IOException, SQLException {
if (ssl) {
SecureSocketFactory f = SecureSocketFactory.getInstance();
return f.createSocket(address, port);
} else {
return new Socket(address, port);
}
}
public static ServerSocket createServerSocket(int port, boolean ssl) throws SQLException {
try {
return createServerSocketTry(port, ssl);
} catch(SQLException e) {
// try again
return createServerSocketTry(port, ssl);
}
}
private static ServerSocket createServerSocketTry(int port, boolean ssl) throws SQLException {
// TODO server sockets: maybe automatically open the next port if this is in use?
// TODO server sockets: maybe a parameter to allow anonymous ssl?
try {
if(ssl) {
SecureSocketFactory f = SecureSocketFactory.getInstance();
return f.createServerSocket(port);
} else {
return new ServerSocket(port);
}
} catch(BindException be) {
throw Message.getSQLException(Message.EXCEPTION_OPENING_PORT_1, new String[]{""+port}, be);
} catch(IOException e) {
throw Message.convert(e);
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import org.h2.engine.Constants;
/**
* @author Thomas
*/
public class ObjectArray {
private static final int SIZE_INIT = 4, SIZE_SHRINK = 256;
private Object[] data;
private int size;
public ObjectArray() {
this(SIZE_INIT);
}
public ObjectArray(int size) {
data = new Object[size > 1 ? size : 1];
}
public ObjectArray(Object[] data) {
this.data = data;
size = data.length;
}
public ObjectArray(Collection collection) {
// TODO lib: Collection should not be required
size = collection.size();
data = new Object[size];
Iterator it = collection.iterator();
for(int i=0; i<size; i++) {
data[i] = it.next();
}
}
public void add(Object value) {
ensureCapacity(size);
data[size++] = value;
}
public Object get(int i) {
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
}
return data[i];
}
public Object remove(int i) {
// TODO performance: the app should (where possible) remove from end to start, to avoid O(n^2)
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
}
Object value = data[i];
System.arraycopy(data, i + 1, data, i, size - i - 1);
size--;
data[size] = null;
// TODO optimization / lib: could shrink ObjectArray on element remove
return value;
}
public void removeRange(int from, int to) {
if (Constants.CHECK && (to > size || from > to)) {
throw new ArrayIndexOutOfBoundsException("to=" + to + " from="+from+" size=" + size);
}
System.arraycopy(data, to, data, from, size - to);
size -= to - from;
for(int i=size + (to-from) - 1; i>=size; i--) {
data[i] = null;
}
}
public void setSize(int i) {
ensureCapacity(i);
this.size = i;
}
private void ensureCapacity(int i) {
while (i >= data.length) {
Object[] d = new Object[data.length * 2];
System.arraycopy(data, 0, d, 0, data.length);
data = d;
}
}
public void add(int i, Object value) {
if (Constants.CHECK && i > size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
}
ensureCapacity(size);
if (i == size) {
add(value);
} else {
System.arraycopy(data, i, data, i + 1, size - i);
data[i] = value;
size++;
}
}
public void set(int i, Object value) {
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
}
data[i] = value;
}
public int size() {
return size;
}
public void toArray(Object[] array) {
for(int i=0; i<size; i++) {
array[i] = data[i];
}
}
public void clear() {
if(data.length > SIZE_SHRINK) {
data = new Object[SIZE_INIT];
} else {
for(int i=0; i<size; i++) {
data[i] = null;
}
}
size = 0;
}
public int indexOf(Object o) {
for(int i=0; i<size; i++) {
if(data[i] == o) {
return i;
}
}
return -1;
}
public void addAll(ObjectArray list) {
for(int i=0; i<list.size; i++) {
add(list.get(i));
}
}
private void swap(int l, int r) {
Object t = data[r];
data[r] = data[l];
data[l] = t;
}
public void sort(Comparator comp) {
sort(comp, 0, size-1);
}
private void sort(Comparator comp, int l, int r) {
int i, j;
while (r - l > 10) {
i = (r + l) >> 1;
if (comp.compare(get(l), get(r)) > 0) {
swap(l, r);
}
if (comp.compare(get(i), get(l)) < 0) {
swap(l, i);
} else if (comp.compare(get(i), get(r)) > 0) {
swap(i, r);
}
j = r - 1;
swap(i, j);
Object p = get(j);
i = l;
while (true) {
do {
++i;
} while (comp.compare(get(i), p) < 0);
do {
--j;
} while (comp.compare(get(j), p) > 0);
if (i >= j) {
break;
}
swap(i, j);
}
swap(i, r - 1);
sort(comp, l, i - 1);
l = i + 1;
}
for (i = l + 1; i <= r; i++) {
Object t = get(i);
for (j = i - 1; j >= l && (comp.compare(get(j), t) > 0); j--) {
set(j + 1, get(j));
}
set(j + 1, t);
}
}
// public void sortInsertion(Comparator comp) {
// for (int i = 1, j; i < size(); i++) {
// Object t = get(i);
// for (j = i - 1; j >= 0 && (comp.compare(get(j), t) < 0); j--) {
// set(j + 1, get(j));
// }
// set(j + 1, t);
// }
// }
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import org.h2.message.Message;
// code originally from http://www.koders.com/java/fidD3445CD11B1DC687F6B8911075E7F01E23171553.aspx
// http://www.koders.com/java/fidD3445CD11B1DC687F6B8911075E7F01E23171553.aspx
/**
* The Permutations class provides an enumeration of all permutations of an
* array of objects. Each permutation is simply an ordered list of the group.
* <p>
* For example, to see all of the ways we can select a school representative and
* an alternate from a list of 4 children, begin with an array of names::
* <blockquote>
*
* <pre>
* Object[] children = { Leonardo, Monica, Nathan, Olivia };
* </pre>
*
* </blockquote> To see all 2-permutations of these 4 names, create and use a
* Permutations enumeration: <blockquote>
*
* <pre>
*
* Permutations c = new Permutations(children, 2);
* while (c.hasMoreElements()) {
* Object[] perm = (Object[])c.nextElement();
* for (int i = 0; i &lt; perm.length; i++) {
* System.out.print(perm[i] + );
* }
* System.out.println();
* }
*
* </pre>
*
* </blockquote> This will print out: <blockquote>
*
* <pre>
*
* Leonardo Monica
* Leonardo Nathan
* Leonardo Olivia
* Monica Leonardo
* Monica Nathan
* Monica Olivia
* Nathan Leonardo
* Nathan Monica
* Nathan Olivia
* Olivia Leonardo
* Olivia Monica
* Olivia Nathan
*
* </pre>
*
* </blockquote>
*
*/
public class Permutations implements java.util.Enumeration {
private Object[] inArray;
private int n, m;
private int[] index;
private boolean hasMore = true;
/**
* Create a Permutation to enumerate through all possible lineups of the
* supplied array of Objects.
*
* @param inArray
* the group to line up
* @exception CombinatoricException
* Should never happen with this interface
*
*/
public Permutations(Object[] inArray) {
this(inArray, inArray.length);
}
/**
* Create a Permutation to enumerate through all possible lineups of the
* supplied array of Objects.
*
* @param inArray
* the group to line up
* @param m
* the number of objects to use
* @exception CombinatoricException
* if m is greater than the length of inArray, or less than
* 0.
*/
public Permutations(Object[] inArray, int m) {
this.inArray = inArray;
this.n = inArray.length;
this.m = m;
// throw exception unless n >= m >= 0
if (n < m || m < 0) {
throw Message.getInternalError("n < m or m < 0");
}
/**
* index is an array of ints that keep track of the next permutation to
* return. For example, an index on a permutation of 3 things might
* contain {1 2 0}. This index will be followed by {2 0 1} and {2 1 0}.
* Initially, the index is {0 ... n - 1}.
*/
index = new int[n];
for (int i = 0; i < n; i++) {
index[i] = i;
}
/**
* The elements from m to n are always kept ascending right to left.
* This keeps the dip in the interesting region.
*/
reverseAfter(m - 1);
}
/**
* @return true, unless we have already returned the last permutation.
*/
public boolean hasMoreElements() {
return hasMore;
}
/**
* Move the index forward a notch. The algorithm first finds the rightmost
* index that is less than its neighbor to the right. This is the dip point.
* The algorithm next finds the least element to the right of the dip that
* is greater than the dip. That element is switched with the dip. Finally,
* the list of elements to the right of the dip is reversed.
* <p>
* For example, in a permutation of 5 items, the index may be {1, 2, 4, 3,
* 0}. The dip is 2 the rightmost element less than its neighbor on its
* right. The least element to the right of 2 that is greater than 2 is 3.
* These elements are swapped, yielding {1, 3, 4, 2, 0}, and the list right
* of the dip point is reversed, yielding {1, 3, 0, 2, 4}.
* <p>
* The algorithm is from Applied Combinatorics, by Alan Tucker.
*
*/
private void moveIndex() {
// find the index of the first element that dips
int i = rightmostDip();
if (i < 0) {
hasMore = false;
return;
}
// find the least greater element to the right of the dip
int leastToRightIndex = i + 1;
for (int j = i + 2; j < n; j++) {
if (index[j] < index[leastToRightIndex] && index[j] > index[i]) {
leastToRightIndex = j;
}
}
// switch dip element with least greater element to its right
int t = index[i];
index[i] = index[leastToRightIndex];
index[leastToRightIndex] = t;
if (m - 1 > i) {
// reverse the elements to the right of the dip
reverseAfter(i);
// reverse the elements to the right of m - 1
reverseAfter(m - 1);
}
}
/**
* @return java.lang.Object, the next permutation of the original Object
* array.
* <p>
* Actually, an array of Objects is returned. The declaration must
* say just Object, because the Permutations class implements
* Enumeration, which declares that the nextElement() returns a
* plain Object. Users must cast the returned object to (Object[]).
*/
public Object nextElement() {
if (!hasMore) {
return null;
}
Object[] out = new Object[m];
for (int i = 0; i < m; i++) {
out[i] = inArray[index[i]];
}
moveIndex();
return out;
}
/**
* Reverse the index elements to the right of the specified index.
*/
private void reverseAfter(int i) {
int start = i + 1;
int end = n - 1;
while (start < end) {
int t = index[start];
index[start] = index[end];
index[end] = t;
start++;
end--;
}
}
/**
* @return int the index of the first element from the right that is less
* than its neighbor on the right.
*/
private int rightmostDip() {
for (int i = n - 2; i >= 0; i--) {
if (index[i] < index[i + 1]) {
return i;
}
}
return -1;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
public class RandomUtils {
private static SecureRandom secureRandom;
private static Random random;
static {
try {
secureRandom = SecureRandom.getInstance("SHA1PRNG");
random = new Random(secureRandom.nextLong());
} catch (NoSuchAlgorithmException e) {
// random is null if the algorithm is not found
// TODO log exception
random = new Random();
}
}
public static long getSecureLong() {
if(secureRandom == null) {
byte[] buff = SecureRandom.getSeed(8);
return ByteUtils.readLong(buff, 0);
}
return secureRandom.nextLong();
}
public static byte[] getSecureBytes(int len) {
if(secureRandom == null) {
return SecureRandom.getSeed(len);
}
if(len <= 0) {
len = 1;
}
byte[] buff = new byte[len];
secureRandom.nextBytes(buff);
return buff;
}
public static int nextInt(int max) {
return random.nextInt(max);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class ReaderInputStream extends InputStream {
private Reader reader;
private int pos;
private int remaining;
private char[] chars;
private byte[] buffer;
private ByteArrayOutputStream out;
private OutputStreamWriter writer;
public ReaderInputStream(Reader reader) throws SQLException {
chars = new char[Constants.IO_BUFFER_SIZE];
this.reader = reader;
out = new ByteArrayOutputStream(Constants.IO_BUFFER_SIZE);
try {
writer = new OutputStreamWriter(out, Constants.UTF8);
} catch (UnsupportedEncodingException e) {
throw Message.convert(e);
}
}
private void fillBuffer() throws IOException {
if(remaining == 0) {
pos = 0;
remaining = reader.read(chars, 0, Constants.IO_BUFFER_SIZE);
if(remaining < 0) {
return;
}
// String s = new String(chars, 0, remaining);
// try {
// buffer = StringUtils.asBytes(s);
// } catch(SQLException e) {
// throw new IOException(e.toString());
// }
writer.write(chars, 0, remaining);
writer.flush();
buffer = out.toByteArray();
remaining = buffer.length;
out.reset();
}
}
public int read() throws IOException {
if(remaining == 0) {
fillBuffer();
}
if(remaining < 0) {
return -1;
}
remaining--;
return buffer[pos++] & 0xff;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
class ResourceData {
public static void load() {
// dummy implementation
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.HashMap;
public class Resources {
private static final HashMap FILES = new HashMap();
static {
ResourceData.load();
}
public static void main(String[] args) throws Exception {
String inDir = args.length > 0 ? args[0] : null;
String outDir = args.length > 1 ? args[1] : null;
new Resources().run(inDir, outDir);
}
void run(String outDir, String inDir) throws Exception {
if(outDir == null) {
outDir = "bin";
}
if(inDir == null) {
inDir = "src/main";
}
if(new File(outDir + "/org/h2/util").exists()) {
String file = outDir + "/org/h2/util/ResourceData.java";
PrintWriter out = new PrintWriter(new FileWriter(file));
out.println("package org.h2.util;");
out.println("// Do not change this code manually");
out.println("// This code is generated by " + getClass().getName());
out.println("class ResourceData {");
out.println(" public static void load() {");
generate(out, inDir+"/org/h2/res", "org.h2");
generate(out, inDir+"/org/h2/server/web/res", "org.h2.server.web");
out.println(" }");
out.println("}");
out.close();
}
}
void generate(PrintWriter out, String inDir, String packageName) throws Exception {
File dir = new File(inDir);
String[] list = dir.list();
for(int i=0; list != null && i<list.length; i++) {
File f = new File(dir, list[i]);
if(!f.isFile()) {
continue;
}
if(list[i].endsWith(".java")) {
continue;
}
String name = "/" + packageName.replace('.', '/') + "/res/" + f.getName();
// System.out.println(name+": "+f.length());
FileInputStream in = new FileInputStream(f);
byte[] buffer = IOUtils.readBytesAndClose(in, 0);
String s = ByteUtils.convertToBinString(buffer);
out.print(" Resources.add(" + StringUtils.quoteJavaString(name) + ", ");
out.print("new String[]{");
do {
String s2;
if(s.length() < 65000) {
s2 = s;
s = null;
} else {
s2 = s.substring(0, 65000);
s = s.substring(65000);
}
out.print(StringUtils.quoteJavaString(s2));
out.println(", ");
} while(s != null);
out.println("});");
}
}
static void add(String name, String[] data) {
StringBuffer buff = new StringBuffer();
for(int i=0; i<data.length; i++) {
buff.append(data[i]);
}
FILES.put(name, ByteUtils.convertBinStringToBytes(buff.toString()));
}
public static byte[] get(String name) throws IOException {
byte[] data;
if(FILES.size() == 0) {
// TODO web: security (check what happens with files like 'lpt1.txt' on windows)
InputStream in = Resources.class.getResourceAsStream(name);
if(in == null) {
data = null;
} else {
data = IOUtils.readBytesAndClose(in, 0);
}
} else {
data = (byte[]) FILES.get(name);
}
return data == null ? new byte[0] : data;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;
import org.h2.message.Message;
public class ScriptReader {
private Reader reader;
private boolean end;
private boolean insideRemark;
private boolean blockRemark;
private boolean skipRemarks;
public ScriptReader(Reader reader) {
this.reader = reader;
}
private int read() throws SQLException {
try {
return reader.read();
} catch (IOException e) {
throw Message.convert(e);
}
}
public String readStatement() throws SQLException {
if(end) {
return null;
}
StringBuffer buff = new StringBuffer();
int c = read();
while(true) {
if(c<0) {
end = true;
return buff.length() == 0 ? null : buff.toString();
} else if(c==';') {
break;
}
switch (c) {
case '\'':
buff.append((char)c);
while(true) {
c = read();
if(c<0) {
break;
}
buff.append((char)c);
if(c=='\'') {
break;
}
}
c = read();
break;
case '"':
buff.append((char)c);
while(true) {
c = read();
if(c<0) {
break;
}
buff.append((char)c);
if(c=='\"') {
break;
}
}
c = read();
break;
case '/': {
int last = c;
c = read();
if(c=='*') {
// block comment
insideRemark = true;
blockRemark = true;
if(!skipRemarks) {
buff.append((char)last);
buff.append((char)c);
}
while(true) {
c = read();
if(c<0) {
break;
}
if(!skipRemarks) {
buff.append((char)c);
}
if(c=='*') {
c = read();
if(c<0) {
break;
}
if(!skipRemarks) {
buff.append((char)c);
}
if(c == '/') {
insideRemark = false;
break;
}
}
}
c = read();
} else if (c == '/') {
// single line comment
insideRemark = true;
blockRemark = false;
if(!skipRemarks) {
buff.append((char)last);
buff.append((char)c);
}
while(true) {
c = read();
if(c<0) {
break;
}
if(!skipRemarks) {
buff.append((char)c);
}
if(c=='\r' || c=='\n') {
insideRemark = false;
break;
}
}
c = read();
} else {
buff.append((char)last);
}
break;
}
case '-': {
int last = c;
c = read();
if(c=='-') {
// single line comment
insideRemark = true;
blockRemark = false;
if(!skipRemarks) {
buff.append((char)last);
buff.append((char)c);
}
while(true) {
c = read();
if(c<0) {
break;
}
if(!skipRemarks) {
buff.append((char)c);
}
if(c=='\r' || c=='\n') {
insideRemark = false;
break;
}
}
c = read();
} else {
buff.append((char)last);
}
break;
}
default:
buff.append((char)c);
c = read();
}
}
return buff.toString();
}
public boolean isInsideRemark() {
return insideRemark;
}
public boolean isBlockRemark() {
return blockRemark;
}
public void setSkipRemarks(boolean skipRemarks) {
this.skipRemarks = skipRemarks;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.util.LinkedHashMap;
import java.util.Map;
public class SmallLRUCache extends LinkedHashMap {
private static final long serialVersionUID = 3643268440910181829L;
private int size;
public SmallLRUCache(int size) {
this.size = size;
}
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > size;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import org.h2.message.Message;
public class SmallMap {
private HashMap map = new HashMap();
private Object cache;
private int cacheId;
private int lastId;
private int maxElements;
public SmallMap(int maxElements) {
this.maxElements = maxElements;
}
public int addObject(int id, Object o) {
if(map.size() > maxElements * 2) {
Iterator it = map.keySet().iterator();
while(it.hasNext()) {
Integer k = (Integer) it.next();
if(k.intValue() + maxElements < lastId) {
it.remove();
}
}
}
if(id > lastId) {
lastId = id;
}
map.put(new Integer(id), o);
cacheId = id;
cache = o;
return id;
}
public void freeObject(int id) {
if (cacheId == id) {
cacheId = -1;
cache = null;
}
map.remove(new Integer(id));
}
public Object getObject(int id, boolean ifAvailable) throws SQLException {
if (id == cacheId) {
return cache;
}
Object obj = map.get(new Integer(id));
if(obj == null && !ifAvailable) {
throw Message.getSQLException(Message.OBJECT_CLOSED);
}
return obj;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.IOException;
public class StartBrowser {
public static void openURL(String url) {
String osName = System.getProperty("os.name");
try {
if(osName.startsWith("Windows")) {
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
} else if(osName.startsWith("Mac OS X")) {
// Runtime.getRuntime().exec("open -a safari " + url);
// Runtime.getRuntime().exec("open " + url + "/index.html");
Runtime.getRuntime().exec("open " + url);
} else {
System.out.println("Please open a browser and go to "+ url);
}
} catch (IOException e) {
System.out.println("Failed to start a browser to open the url " + url);
e.printStackTrace();
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.lang.ref.WeakReference;
import org.h2.engine.Constants;
public class StringCache {
private static final boolean ENABLED = true;
private static WeakReference weakCache = new WeakReference(null);
// testing: cacheHit / miss are public!
// public static int cacheHit = 0, cacheMiss = 0;
// 4703
// public static String get(String s) {
// if (s == null) {
// return s;
// } else if (s.length() == 0) {
// return "";
// }
// if (!Constants.USE_OBJECT_CACHE || !ENABLED || s.length() > MAX_CACHE_SIZE / 10) {
// return s;
// }
// int hash = s.hashCode();
// int index = hash & (Constants.OBJECT_CACHE_SIZE - 1);
// String cached = cache[index];
// if (cached != null) {
// if (s.equals(cached)) {
// // cacheHit++;
// return cached;
// }
// }
// // cacheMiss++;
// replace(index, s);
// return s;
// }
// 3500
// public static String get(String s) {
// return s;
// }
// 3906
public static String get(String s) {
if (!Constants.USE_OBJECT_CACHE || !ENABLED) {
return s;
}
if (s == null) {
return s;
} else if (s.length() == 0) {
return "";
}
String[] cache = (String[]) weakCache.get();
int hash = s.hashCode();
if (cache == null) {
cache = new String[Constants.OBJECT_CACHE_SIZE];
weakCache = new WeakReference(cache);
}
int index = hash & (Constants.OBJECT_CACHE_SIZE - 1);
String cached = cache[index];
if (cached != null) {
if (s.equals(cached)) {
// cacheHit++;
return cached;
}
}
cache[index] = s;
return s;
}
public static String getNew(String s) {
if (!Constants.USE_OBJECT_CACHE || !ENABLED) {
return s;
}
if (s == null) {
return s;
} else if (s.length() == 0) {
return "";
}
String[] cache = (String[]) weakCache.get();
int hash = s.hashCode();
if (cache == null) {
cache = new String[Constants.OBJECT_CACHE_SIZE];
weakCache = new WeakReference(cache);
}
int index = hash & (Constants.OBJECT_CACHE_SIZE - 1);
String cached = cache[index];
if (cached != null) {
if (s.equals(cached)) {
// cacheHit++;
return cached;
}
}
s = new String(s);
cache[index] = s;
return s;
}
public static void clearCache() {
weakCache = new WeakReference(null);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import org.h2.engine.Constants;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class StringUtils {
// TODO hack for gcj
//#GCJHACK private static final Class[] gcjClasses = {
//#GCJHACK gnu.gcj.convert.Input_ASCII.class,
//#GCJHACK gnu.gcj.convert.Input_UTF8.class,
//#GCJHACK gnu.gcj.convert.Input_8859_1.class,
//#GCJHACK gnu.gcj.convert.Output_ASCII.class,
//#GCJHACK gnu.gcj.convert.Output_UTF8.class,
//#GCJHACK gnu.gcj.convert.UnicodeToBytes.class,
//#GCJHACK gnu.gcj.convert.BytesToUnicode.class,
//#GCJHACK gnu.java.locale.Calendar.class,
//#GCJHACK gnu.java.locale.LocaleInformation.class,
//#GCJHACK gnu.java.locale.LocaleInformation_de.class,
//#GCJHACK java.util.GregorianCalendar.class,
//#GCJHACK };
public static boolean equals(String a, String b) {
if(a==null) {
return b==null;
}
return a.equals(b);
}
public static String toUpperEnglish(String s) {
return s.toUpperCase(Locale.ENGLISH);
}
public static String toLowerEnglish(String s) {
return s.toLowerCase(Locale.ENGLISH);
}
public static String getDefaultCharset() {
return System.getProperty("file.encoding");
}
public static String quoteStringSQL(String s) {
StringBuffer buff = new StringBuffer(s.length()+2);
buff.append('\'');
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '\'') {
buff.append(c);
} else if(c < ' ' || c > 127) {
// need to start from the beginning because maybe there was a \ that was not quoted
return "STRINGDECODE(" + quoteStringSQL(javaEncode(s)) + ")";
}
buff.append(c);
}
buff.append('\'');
return buff.toString();
}
public static String javaEncode(String s) {
StringBuffer buff = new StringBuffer(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
// case '\b':
// // BS backspace
// // not supported in properties files
// buff.append("\\b");
// break;
case '\t':
// HT horizontal tab
buff.append("\\t");
break;
case '\n':
// LF linefeed
buff.append("\\n");
break;
case '\f':
// FF form feed
buff.append("\\f");
break;
case '\r':
// CR carriage return
buff.append("\\r");
break;
case '"':
// double quote
buff.append("\\\"");
break;
case '\\':
// backslash
buff.append("\\\\");
break;
default:
int ch = (c & 0xffff);
if (ch >= ' ' && (ch < 0x80)) {
buff.append(c);
// not supported in properties files
// } else if(ch < 0xff) {
// buff.append("\\");
// // make sure it's three characters (0x200 is octal 1000)
// buff.append(Integer.toOctalString(0x200 | ch).substring(1));
} else {
buff.append("\\u");
// make sure it's four characters
buff.append(Integer.toHexString(0x10000 | ch).substring(1));
}
}
}
return buff.toString();
}
public static String addAsterix(String s, int index) {
if (s != null && index < s.length()) {
s = s.substring(0, index) + "[*]" + s.substring(index);
}
return s;
}
private static SQLException getFormatException(String s, int i) {
return Message.getSQLException(Message.STRING_FORMAT_ERROR_1, addAsterix(s, i));
}
public static String javaDecode(String s) throws SQLException {
StringBuffer buff = new StringBuffer(s.length());
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == '"') {
break;
} else if(c=='\\') {
if(i >= s.length()) {
throw getFormatException(s, s.length()-1);
}
c = s.charAt(++i);
switch(c) {
case 't':
buff.append('\t');
break;
case 'r':
buff.append('\r');
break;
case 'n':
buff.append('\n');
break;
case 'b':
buff.append('\b');
break;
case 'f':
buff.append('\f');
break;
case '"':
buff.append('"');
break;
case '\\':
buff.append('\\');
break;
case 'u': {
try {
c = (char)(Integer.parseInt(s.substring(i+1, i+5), 16));
} catch(NumberFormatException e) {
throw getFormatException(s, i);
}
i += 4;
buff.append(c);
break;
}
default:
if(c >= '0' && c <= '9') {
try {
c = (char)(Integer.parseInt(s.substring(i, i+3), 8));
} catch(NumberFormatException e) {
throw getFormatException(s, i);
}
i += 2;
buff.append(c);
} else {
throw getFormatException(s, i);
}
}
} else {
buff.append(c);
}
}
return buff.toString();
}
public static String quoteJavaString(String s) {
if(s==null) {
return "null";
} else {
return "\"" + javaEncode(s) + "\"";
}
}
public static byte[] utf8Encode(String s) throws SQLException {
try {
// TODO UTF8: String.getBytes("UTF-8") only returns 1 byte for 0xd800-0xdfff
return s.getBytes(Constants.UTF8);
} catch (UnsupportedEncodingException e) {
throw Message.convert(e);
}
}
public static String utf8Decode(byte[] utf8) {
try {
return new String(utf8, Constants.UTF8);
} catch (UnsupportedEncodingException e) {
throw Message.convertToInternal(e);
}
}
public static String utf8Decode(byte[] bytes, int offset, int length) {
try {
return new String(bytes, offset, length, Constants.UTF8);
} catch (UnsupportedEncodingException e) {
throw Message.convertToInternal(e);
}
}
public static String quoteJavaStringArray(String[] array) {
if(array == null) {
return "null";
}
StringBuffer buff = new StringBuffer();
buff.append("new String[]{");
for(int i=0; i<array.length; i++) {
if(i>0) {
buff.append(", ");
}
buff.append(quoteJavaString(array[i]));
}
buff.append("}");
return buff.toString();
}
public static String quoteJavaIntArray(int[] array) {
if(array == null) {
return "null";
}
StringBuffer buff = new StringBuffer(2*array.length);
buff.append("new int[]{");
for(int i=0; i<array.length; i++) {
if(i>0) {
buff.append(", ");
}
buff.append(array[i]);
}
buff.append("}");
return buff.toString();
}
public static String enclose(String s) {
if(s.startsWith("(")) {
return s;
} else {
return "(" + s + ")";
}
}
public static String unEnclose(String s) {
if(s.startsWith("(") && s.endsWith(")")) {
return s.substring(1, s.length()-1);
} else {
return s;
}
}
public static String urlEncode(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return s;
}
// byte[] utf = utf8Encode(s);
// StringBuffer buff = new StringBuffer(utf.length);
// for(int i=0; i<utf.length; i++) {
//
// buff.append()
// }
}
public static String urlDecode(String encoded) throws SQLException {
byte[] buff = new byte[encoded.length()];
int j=0;
for(int i=0; i<encoded.length(); i++) {
char ch = encoded.charAt(i);
if(ch=='+') {
buff[j++] = ' ';
} else if(ch=='%') {
buff[j++] = (byte)Integer.parseInt(encoded.substring(i+1,i+3),16);
i+=2;
} else {
if(Constants.CHECK && (ch > 127 || ch < ' ')) {
throw new IllegalArgumentException("unexpected char " + (int)ch + " decoding " + encoded);
}
buff[j++] = (byte)ch;
}
}
String s = utf8Decode(buff, 0, j);
return s;
}
public static String[] arraySplit(String s, char separatorChar, boolean trim) {
if(s==null) {
return null;
}
if(s.length()==0) {
return new String[0];
}
ArrayList list = new ArrayList();
StringBuffer buff=new StringBuffer(s.length());
for(int i=0;i<s.length();i++) {
char c=s.charAt(i);
if(c==separatorChar) {
String e = buff.toString();
list.add(trim ? e.trim() : e);
buff.setLength(0);
} else if(c=='\\' && i<s.length()-1) {
buff.append(s.charAt(++i));
} else {
buff.append(c);
}
}
String e = buff.toString();
list.add(trim ? e.trim() : e);
String[] array = new String[list.size()];
list.toArray(array);
return array;
}
public static String arrayCombine(String[] list, char separatorChar) {
StringBuffer buff=new StringBuffer();
for(int i=0;i<list.length;i++) {
if(i>0) {
buff.append(separatorChar);
}
String s=list[i];
if(s==null) {
s = "";
}
for(int j=0;j<s.length();j++) {
char c=s.charAt(j);
if(c=='\\' || c==separatorChar) {
buff.append('\\');
}
buff.append(c);
}
}
return buff.toString();
}
/**
* Formats a date using a format string
*/
public static String formatDateTime(Date date, String format, String locale, String timezone) throws SQLException {
SimpleDateFormat sdf = getDateFormat(format, locale, timezone);
return sdf.format(date);
}
/**
* Parses a date using a format string
*/
public static Date parseDateTime(String date, String format, String locale, String timezone) throws SQLException {
SimpleDateFormat sdf = getDateFormat(format, locale, timezone);
try {
return sdf.parse(date);
} catch(ParseException e) {
throw Message.getSQLException(Message.PARSE_ERROR_1, date);
}
}
private static SimpleDateFormat getDateFormat(String format, String locale, String timezone) throws SQLException {
try {
SimpleDateFormat df;
if(locale == null) {
df = new SimpleDateFormat(format);
} else {
Locale l = new Locale(locale);
df = new SimpleDateFormat(format, l);
}
if(timezone != null) {
df.setTimeZone(TimeZone.getTimeZone(timezone));
}
return df;
} catch(Exception e) {
throw Message.getSQLException(Message.PARSE_ERROR_1, format + "/" + locale + "/" + timezone);
}
}
/**
* Creates an XML attribute of the form name="value".
* A single space is prepended to the name,
* so that multiple attributes can be concatenated.
* @param name
* @param value
* @return the attribute
*/
public static String xmlAttr(String name, String value) {
return " " + name + "=\"" + xmlText(value) + "\"";
}
/**
* Create an XML node with optional attributes and content.
* The data is indented with 4 spaces if it contains a newline character.
*
* @param name the element name
* @param attributes the attributes (may be null)
* @param content the content (may be null)
* @return the node
*/
public static String xmlNode(String name, String attributes, String content) {
String start = attributes == null ? name : name + attributes;
if(content == null) {
return "<" + start + "/>\n";
} else {
if(content.indexOf('\n') >= 0) {
content = "\n" + indent(content);
}
return "<" + start + ">" + content + "</" + name + ">\n";
}
}
/**
* Indents a string with 4 spaces.
* @param s the string
* @return the indented string
*/
public static String indent(String s) {
return indent(s, 4);
}
/**
* Indents a string with spaces.
* @param s the string
* @param spaces the number of spaces
* @return the indented string
*/
public static String indent(String s, int spaces) {
StringBuffer buff = new StringBuffer(s.length() + spaces);
for(int i=0; i < s.length(); ) {
for(int j=0; j<spaces; j++) {
buff.append(' ');
}
int n = s.indexOf('\n', i);
n = n < 0 ? s.length() : n+1;
buff.append(s.substring(i, n));
i = n;
}
if(!s.endsWith("\n")) {
buff.append('\n');
}
return buff.toString();
}
/**
* Escapes a comment.
* If the data contains '--', it is converted to '- -'.
* The data is indented with 4 spaces if it contains a newline character.
*
* @param data
* @return <!-- data -->
*/
public static String xmlComment(String data) {
int idx = 0;
while(true) {
idx = data.indexOf("--", idx);
if(idx<0) {
break;
}
data = data.substring(0, idx + 1) + " " + data.substring(idx + 1);
}
// must have a space at the beginning and at the end,
// otherwise the data must not contain '-' as the first/last character
if(data.indexOf('\n') >= 0) {
return "<!--\n" + indent(data) + "-->\n";
} else {
return "<!-- " + data + " -->\n";
}
}
/**
* Converts the data to a CDATA element.
* If the data contains ']]>', it is escaped as a text element.
* @param data
* @return <![CDATA[data]]>
*/
public static String xmlCData(String data) {
if(data.indexOf("]]>") >= 0) {
return xmlText(data);
}
boolean newline = data.endsWith("\n");
data = "<![CDATA[" + data + "]]>";
return newline ? data + "\n" : data;
}
/**
* Returns <?xml version="1.0"?>
* @return <?xml version="1.0"?>
*/
public static String xmlStartDoc() {
return "<?xml version=\"1.0\"?>\n";
}
/**
* Escapes an XML text element.
*
* @param text
* @return the escaped text
*/
public static String xmlText(String text) {
StringBuffer buff = new StringBuffer(text.length());
for(int i=0; i<text.length(); i++) {
char ch = text.charAt(i);
switch(ch) {
case '<':
buff.append("&lt;");
break;
case '>':
buff.append("&gt;");
break;
case '&':
buff.append("&amp;");
break;
case '\'':
buff.append("&apos;");
break;
case '\"':
buff.append("&quot;");
break;
case '\r':
case '\n':
case '\t':
buff.append(ch);
break;
default:
if(ch < ' ' || ch > 127) {
buff.append("&#x");
buff.append(Integer.toHexString(ch));
buff.append(';');
} else {
buff.append(ch);
}
}
}
return buff.toString();
}
public static String replaceAll(String s, String before, String after) {
int index = 0;
while(true) {
int next = s.indexOf(before, index);
if(next < 0) {
return s;
}
s = s.substring(0, next) + after + s.substring(next+before.length());
index = next + after.length();
}
}
public static String quoteIdentifier(String s) {
StringBuffer buff = new StringBuffer("\"");
for(int i=0; i<s.length(); i++) {
char c = s.charAt(i);
if(c == '"') {
buff.append(c);
}
buff.append(c);
}
return buff.append('\"').toString();
}
}
package org.h2.util;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.HashMap;
import org.h2.engine.Constants;
import org.h2.message.Message;
public class TempFileDeleter {
private static ReferenceQueue queue = new ReferenceQueue();
private static HashMap refMap = new HashMap();
public static synchronized Reference addFile(String fileName, Object file) {
PhantomReference ref = new PhantomReference(file, queue);
refMap.put(ref, fileName);
deleteUnused();
return ref;
}
public static synchronized void deleteFile(Reference ref, String fileName) {
if(ref != null) {
String f2 = (String) refMap.remove(ref);
if(Constants.CHECK && f2 != null && fileName != null && !f2.equals(fileName)) {
throw Message.getInternalError("f2:"+f2+" f:"+fileName);
}
}
if(fileName != null && FileUtils.exists(fileName)) {
try {
FileUtils.delete(fileName);
} catch(Exception e) {
// TODO log such errors?
}
}
deleteUnused();
}
public static void deleteUnused() {
while(true) {
Reference ref = queue.poll();
if(ref == null) {
break;
}
deleteFile(ref, null);
}
}
public static void stopAutoDelete(Reference ref, String fileName) {
if(ref != null) {
String f2 = (String) refMap.remove(ref);
if(Constants.CHECK && (f2 == null || !f2.equals(fileName))) {
throw Message.getInternalError("f2:"+f2+" f:"+fileName);
}
}
deleteUnused();
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.sql.Clob;
import java.sql.Date;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.jdbc.JdbcBlob;
import org.h2.jdbc.JdbcClob;
import org.h2.jdbc.JdbcConnection;
import org.h2.jdbc.JdbcSQLException;
import org.h2.message.Message;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
/**
* @author Thomas
*/
public class TypeConverter {
public static Object getDefaultForPrimitiveType(Class clazz) {
if(clazz == Boolean.TYPE) {
return Boolean.FALSE;
} else if(clazz == Byte.TYPE) {
return new Byte((byte)0);
} else if(clazz == Character.TYPE) {
return new Character((char)0);
} else if(clazz == Short.TYPE) {
return new Short((short)0);
} else if(clazz == Integer.TYPE) {
return new Integer(0);
} else if(clazz == Long.TYPE) {
return new Long(0);
} else if(clazz == Float.TYPE) {
return new Float(0);
} else if(clazz == Double.TYPE) {
return new Double(0);
} else {
throw Message.getInternalError("primitive="+ clazz.toString());
}
}
public static byte[] serialize(Object obj) throws SQLException {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(obj);
return out.toByteArray();
} catch(Throwable e) {
throw Message.getSQLException(Message.SERIALIZATION_FAILED, null, e);
}
}
public static Object deserialize(byte[] data) throws SQLException {
try {
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is = new ObjectInputStream(in);
Object obj = is.readObject();
return obj;
} catch(Throwable e) {
throw Message.getSQLException(Message.DESERIALIZATION_FAILED, null, e);
}
}
public static Reader getReader(InputStream in) throws SQLException {
try {
// InputStreamReader may read some more bytes
return in == null ? null : new InputStreamReader(in, Constants.UTF8);
} catch (UnsupportedEncodingException e) {
throw Message.convert(e);
}
}
public static InputStream getInputStream(String s) throws SQLException {
return new ByteArrayInputStream(StringUtils.utf8Encode(s));
}
public static InputStream getInputStream(Reader x) throws SQLException {
return x == null ? null : new ReaderInputStream(x);
}
public static Reader getReader(String s) {
return new StringReader(s);
}
public static Date convertDateToCalendar(Date x, Calendar calendar) throws SQLException {
return x == null ? x : new Date(getLocalTime(x, calendar));
}
public static Time convertTimeToCalendar(Time x, Calendar calendar) throws SQLException {
return x == null ? x : new Time(getLocalTime(x, calendar));
}
public static Timestamp convertTimestampToCalendar(Timestamp x, Calendar calendar) throws SQLException {
if(x != null) {
Timestamp y = new Timestamp(getLocalTime(x, calendar));
// fix the nano seconds
y.setNanos(x.getNanos());
x = y;
}
return x;
}
public static Value convertDateToUniversal(Date x, Calendar source) throws SQLException {
return ValueDate.get(new Date(TypeConverter.getUniversalTime(source, x)));
}
public static Value convertTimeToUniversal(Time x, Calendar source) throws SQLException {
return ValueTime.get(new Time(TypeConverter.getUniversalTime(source, x)));
}
public static Value convertTimestampToUniversal(Timestamp x, Calendar source) throws SQLException {
Timestamp y = new Timestamp(TypeConverter.getUniversalTime(source, x));
// fix the nano seconds
y.setNanos(x.getNanos());
return ValueTimestamp.get(y);
}
private static long getUniversalTime(Calendar source, java.util.Date x) throws SQLException {
if(source == null) {
throw Message.getInvalidValueException("calendar", null);
}
source = (Calendar)source.clone();
Calendar universal=Calendar.getInstance();
source.setTime(x);
convertTime(source, universal);
return universal.getTime().getTime();
}
private static long getLocalTime(java.util.Date x, Calendar target) throws SQLException {
if(target == null) {
throw Message.getInvalidValueException("calendar", null);
}
target = (Calendar)target.clone();
Calendar local=Calendar.getInstance();
local.setTime(x);
convertTime(local, target);
return target.getTime().getTime();
}
private static void convertTime(Calendar from, Calendar to) {
to.set(Calendar.YEAR, from.get(Calendar.YEAR));
to.set(Calendar.MONTH, from.get(Calendar.MONTH));
to.set(Calendar.DAY_OF_MONTH, from.get(Calendar.DAY_OF_MONTH));
to.set(Calendar.HOUR_OF_DAY, from.get(Calendar.HOUR_OF_DAY));
to.set(Calendar.MINUTE, from.get(Calendar.MINUTE));
to.set(Calendar.SECOND, from.get(Calendar.SECOND));
to.set(Calendar.MILLISECOND, from.get(Calendar.MILLISECOND));
}
public static Reader getAsciiReader(InputStream x) throws SQLException {
try {
return x == null ? null : new InputStreamReader(x, "US-ASCII");
} catch (UnsupportedEncodingException e) {
throw Message.convert(e);
}
}
public static Object convertTo(SessionInterface session, JdbcConnection conn, Value v, Class paramClass) throws JdbcSQLException {
if(paramClass == java.sql.Blob.class) {
return new JdbcBlob(session, conn, v, 0);
} else if(paramClass == Clob.class) {
return new JdbcClob(session, conn, v, 0);
} else {
throw Message.getUnsupportedException();
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.h2.engine.SessionInterface;
import org.h2.message.Message;
import org.h2.result.ResultInterface;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueNull;
public class UpdatableRow {
private SessionInterface session;
private Connection conn;
private DatabaseMetaData meta;
private ResultInterface result;
private int columnCount;
private String schemaName;
private String tableName;
private ObjectArray key;
private boolean isUpdatable;
public UpdatableRow(Connection conn, ResultInterface result, SessionInterface session) throws SQLException {
this.conn = conn;
this.meta = conn.getMetaData();
this.result = result;
this.session = session;
columnCount = result.getVisibleColumnCount();
for(int i=0; i<columnCount; i++) {
String t = result.getTableName(i);
String s = result.getSchemaName(i);
if(t == null || s == null) {
return;
}
if(tableName == null) {
tableName = t;
} else if(!tableName.equals(t)) {
return;
}
if(schemaName == null) {
schemaName = s;
} else if(!schemaName.equals(s)) {
return;
}
}
ResultSet rs = meta.getTables(null, schemaName, tableName, new String[]{"TABLE"});
if(!rs.next()) {
return;
}
if(rs.getString("SQL")==null) {
// system table
return;
}
key = new ObjectArray();
rs = meta.getPrimaryKeys(null, schemaName, tableName);
while(rs.next()) {
key.add(rs.getString("COLUMN_NAME"));
}
if(key.size() == 0) {
return;
}
isUpdatable = true;
}
public boolean isUpdatable() {
return isUpdatable;
}
void initKey() throws SQLException {
}
private int getColumnIndex(String columnName) throws SQLException {
for(int i=0; i<columnCount; i++) {
String col = result.getColumnName(i);
if(col.equals(columnName)) {
return i;
}
}
throw Message.getSQLException(Message.COLUMN_NOT_FOUND_1, columnName);
}
private void appendColumnList(StringBuffer buff, boolean set) {
for(int i=0; i<columnCount; i++) {
if(i>0) {
buff.append(", ");
}
String col = result.getColumnName(i);
buff.append(StringUtils.quoteIdentifier(col));
if(set) {
buff.append("=? ");
}
}
}
private void appendKeyCondition(StringBuffer buff) {
buff.append(" WHERE ");
for(int i=0; i<key.size(); i++) {
if(i>0) {
buff.append(" AND ");
}
buff.append(StringUtils.quoteIdentifier((String)key.get(i)));
buff.append("=?");
}
}
private void setKey(PreparedStatement prep, int start, Value[] current) throws SQLException {
for(int i=0; i<key.size(); i++) {
String col = (String) key.get(i);
int idx = getColumnIndex(col);
Value v = current[idx];
v.set(prep, start+i);
}
}
// public boolean isRowDeleted(Value[] row) throws SQLException {
// StringBuffer buff = new StringBuffer();
// buff.append("SELECT COUNT(*) FROM ");
// buff.append(StringUtils.quoteIdentifier(tableName));
// appendKeyCondition(buff);
// PreparedStatement prep = conn.prepareStatement(buff.toString());
// setKey(prep, 1, row);
// ResultSet rs = prep.executeQuery();
// rs.next();
// return rs.getInt(1) == 0;
// }
public void refreshRow(Value[] row) throws SQLException {
Value[] newRow = readRow(row);
for(int i=0; i<columnCount; i++) {
row[i] = newRow[i];
}
}
private void appendTableName(StringBuffer buff) {
if(schemaName != null && schemaName.length()>0) {
buff.append(StringUtils.quoteIdentifier(schemaName));
buff.append('.');
}
buff.append(StringUtils.quoteIdentifier(tableName));
}
private Value[] readRow(Value[] row) throws SQLException {
StringBuffer buff = new StringBuffer();
buff.append("SELECT ");
appendColumnList(buff, false);
buff.append(" FROM ");
appendTableName(buff);
appendKeyCondition(buff);
PreparedStatement prep = conn.prepareStatement(buff.toString());
setKey(prep, 1, row);
ResultSet rs = prep.executeQuery();
if(!rs.next()) {
throw Message.getSQLException(Message.NO_DATA_AVAILABLE);
}
Value[] newRow = new Value[columnCount];
for(int i=0; i<columnCount; i++) {
int type = result.getColumnType(i);
// TODO lob: support updatable rows
newRow[i] = DataType.readValue(session, rs, i+1, type);
}
return newRow;
}
public void deleteRow(Value[] current) throws SQLException {
StringBuffer buff = new StringBuffer();
buff.append("DELETE FROM ");
appendTableName(buff);
appendKeyCondition(buff);
PreparedStatement prep = conn.prepareStatement(buff.toString());
setKey(prep, 1, current);
int count = prep.executeUpdate();
if(count != 1) {
throw Message.getSQLException(Message.NO_DATA_AVAILABLE);
}
}
public void updateRow(Value[] current, Value[] updateRow) throws SQLException {
StringBuffer buff = new StringBuffer();
buff.append("UPDATE ");
appendTableName(buff);
buff.append(" SET ");
appendColumnList(buff, true);
// TODO updatable result set: we could add all current values to the where clause
// - like this optimistic ('no') locking is possible
appendKeyCondition(buff);
PreparedStatement prep = conn.prepareStatement(buff.toString());
int j = 1;
for(int i=0; i<columnCount; i++) {
Value v = updateRow[i];
if(v == null) {
v = current[i];
}
v.set(prep, j++);
}
setKey(prep, j, current);
prep.execute();
}
public void insertRow(Value[] row) throws SQLException {
StringBuffer buff = new StringBuffer();
buff.append("INSERT INTO ");
appendTableName(buff);
buff.append("(");
appendColumnList(buff, false);
buff.append(")VALUES(");
for(int i=0; i<columnCount; i++) {
if(i>0) {
buff.append(",");
}
buff.append("?");
}
buff.append(")");
PreparedStatement prep = conn.prepareStatement(buff.toString());
for(int i=0; i<columnCount; i++) {
Value v = row[i];
if(v == null) {
v = ValueNull.INSTANCE;
}
v.set(prep, i+1);
}
prep.executeUpdate();
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.store.DataHandler;
import org.h2.value.Value;
import org.h2.value.ValueNull;
public class ValueHashMap extends HashBase {
private Value[] keys;
private Object[] values;
private DataHandler database;
public ValueHashMap(DataHandler database) {
this.database = database;
}
protected void reset(int newLevel) {
super.reset(newLevel);
keys = new Value[len];
values = new Object[len];
}
protected void rehash(int newLevel) throws SQLException {
Value[] oldKeys = keys;
Object[] oldValues = values;
reset(newLevel);
for(int i=0; i<oldKeys.length; i++) {
Value k = oldKeys[i];
if(k != null && k != ValueNull.DELETED) {
put(k, oldValues[i]);
}
}
}
private int getIndex(Value key) {
return key.hashCode() & mask;
}
public void put(Value key, Object value) throws SQLException {
checkSizePut();
int index = getIndex(key);
int plus = 1;
int deleted = -1;
do {
Value k = keys[index];
if(k==null) {
// found an empty record
if(deleted>=0) {
index = deleted;
deletedCount--;
}
size++;
keys[index] = key;
values[index] = value;
return;
} else if(k==ValueNull.DELETED) {
// found a deleted record
if(deleted<0) {
deleted = index;
}
} else if(database.compareTypeSave(k, key)==0) {
// update existing
values[index] = value;
return;
}
index = (index + plus++) & mask;
} while(plus <= len);
// no space
throw Message.getInternalError("hashmap is full");
}
public void remove(Value key) throws SQLException {
checkSizeRemove();
int index = getIndex(key);
int plus = 1;
do {
Value k = keys[index];
if(k==null) {
// found an empty record
return;
} else if(k==ValueNull.DELETED) {
// found a deleted record
} else if(database.compareTypeSave(k, key)==0) {
// found the record
keys[index] = ValueNull.DELETED;
values[index] = null;
deletedCount++;
size--;
return;
}
index = (index + plus++) & mask;
k = keys[index];
} while(plus <= len);
// not found
}
public Object get(Value key) throws SQLException {
int index = getIndex(key);
int plus = 1;
do {
Value k = keys[index];
if(k==null) {
// found an empty record
return null;
} else if(k == ValueNull.DELETED) {
// found a deleted record
} else if(database.compareTypeSave(k, key)==0) {
// found it
return values[index];
}
index = (index + plus++) & mask;
} while(plus <= len);
return null;
}
public ObjectArray keys() {
ObjectArray list = new ObjectArray(size);
for(int i=0; i<keys.length; i++) {
Value k = keys[i];
if(k != null && k != ValueNull.DELETED) {
list.add(k);
}
}
return list;
}
public ObjectArray values() {
ObjectArray list = new ObjectArray(size);
for(int i=0; i<keys.length; i++) {
Value k = keys[i];
if(k != null && k != ValueNull.DELETED) {
list.add(values[i]);
}
}
return list;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.text.Collator;
import java.util.Locale;
import org.h2.util.StringUtils;
public class CompareMode {
public static final String OFF = "OFF";
private String name = OFF;
private Collator collator;
public CompareMode(Collator collator, String name) {
this.collator = collator;
if(name != null) {
this.name = name;
}
}
public int compareString(String a, String b, boolean ignoreCase) {
if(collator == null) {
if(ignoreCase) {
return a.compareToIgnoreCase(b);
}
return a.compareTo(b);
}
if(ignoreCase) {
// this is locale sensitive
a = a.toUpperCase();
b = b.toUpperCase();
}
int comp = collator.compare(a, b);
return comp;
}
public static String getName(Locale l) {
Locale english = Locale.ENGLISH;
String name = l.getDisplayLanguage(english) + ' ' + l.getDisplayCountry(english) + ' ' + l.getVariant();
name = StringUtils.toUpperEnglish(name.trim().replace(' ', '_'));
return name;
}
public static Collator getCollator(String name) {
Locale[] locales = Collator.getAvailableLocales();
for(int i=0; i<locales.length; i++) {
Locale locale = locales[i];
if(name.equalsIgnoreCase(locale.toString()) || name.equalsIgnoreCase(getName(locale))) {
return Collator.getInstance(locale);
}
}
return null;
}
public String getName() {
return name;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.HashMap;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.message.Message;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.util.TypeConverter;
public class DataType {
private static ObjectArray types = new ObjectArray();
private static HashMap typesByName = new HashMap();
private static DataType[] typesByValueType = new DataType[Value.TYPE_COUNT];
public int type;
public String name;
public int sqlType;
public String jdbc;
// how closely the data type maps to the corresponding JDBC SQL type (low is best)
public int order;
public int maxPrecision;
public int minScale, maxScale;
public boolean decimal;
public String prefix, suffix;
public String params;
public boolean autoInc;
public boolean caseSensitive;
public boolean supportsPrecision, supportsScale;
public long defaultPrecision;
public int defaultScale;
public boolean hidden;
static {
add(Value.NULL, Types.NULL, "Null",
new DataType(),
new String[]{"NULL"}
);
add(Value.BOOLEAN, Types.BOOLEAN, "Boolean",
createDecimal(ValueBoolean.PRECISION, ValueBoolean.PRECISION, 0, false, false),
new String[]{"BOOLEAN", "BIT", "BOOL"}
);
add(Value.BYTE, Types.TINYINT, "Byte",
createDecimal(ValueByte.PRECISION, ValueByte.PRECISION, 0, false, false),
new String[]{"TINYINT"}
);
add(Value.SHORT, Types.SMALLINT, "Short",
createDecimal(ValueShort.PRECISION, ValueShort.PRECISION, 0, false, false),
new String[]{"SMALLINT", "YEAR", "INT2"}
);
add(Value.INT, Types.INTEGER, "Int",
createDecimal(ValueInt.PRECISION, ValueInt.PRECISION, 0, false, false),
new String[]{"INTEGER", "INT", "MEDIUMINT", "INT4", "SIGNED"}
);
add(Value.LONG, Types.BIGINT, "Long",
createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, false, false),
new String[]{"BIGINT", "INT8"}
);
add(Value.LONG, Types.BIGINT, "Long",
createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, false, true),
new String[]{"IDENTITY"}
);
add(Value.DECIMAL, Types.DECIMAL, "BigDecimal",
createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, true, false),
new String[]{"DECIMAL", "DEC"}
// TODO value: are NaN, Inf, -Inf,... supported as well?
);
add(Value.DECIMAL, Types.NUMERIC, "BigDecimal",
createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, true, false),
new String[]{"NUMERIC", "NUMBER"}
// TODO value: are NaN, Inf, -Inf,... supported as well?
);
add(Value.DOUBLE, Types.DOUBLE, "Double",
createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, false, false),
new String[] { "DOUBLE", "DOUBLE PRECISION" }
);
add(Value.DOUBLE, Types.FLOAT, "Double",
createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, false, false),
new String[] {"FLOAT", "FLOAT8" }
// TODO value: show min and max values, E format if supported
);
add(Value.FLOAT, Types.REAL, "Float",
createDecimal(ValueFloat.PRECISION, ValueFloat.PRECISION, 0, false, false),
new String[] {"REAL", "FLOAT4"}
);
add(Value.TIME, Types.TIME, "Time",
createDate(ValueTime.PRECISION, "TIME", 0),
new String[]{"TIME"}
// TODO value: min / max for time
);
add(Value.DATE, Types.DATE, "Date",
createDate(ValueDate.PRECISION, "DATE", 0),
new String[]{"DATE"}
// TODO value: min / max for date
);
add(Value.TIMESTAMP, Types.TIMESTAMP, "Timestamp",
createDate(ValueTimestamp.PRECISION, "TIMESTAMP", ValueTimestamp.DEFAULT_SCALE),
new String[]{"TIMESTAMP", "DATETIME", "SMALLDATETIME"}
// TODO value: min / max for timestamp
);
add(Value.BYTES, Types.VARBINARY, "Bytes",
createString(false),
new String[]{"VARBINARY"}
);
add(Value.BYTES, Types.BINARY, "Bytes",
createString(false),
new String[]{"BINARY", "RAW", "BYTEA", "LONG RAW"}
);
add(Value.UUID, Types.BINARY, "Bytes",
createString(false),
new String[]{"UUID"}
);
add(Value.BYTES, Types.LONGVARBINARY, "Bytes",
createString(false),
new String[]{"LONGVARBINARY"}
);
add(Value.JAVA_OBJECT, Types.OTHER, "Object",
createString(false),
new String[]{"OTHER", "OBJECT", "JAVA_OBJECT"}
);
add(Value.STRING, Types.VARCHAR, "String",
createString(true),
new String[]{"VARCHAR", "VARCHAR2", "NVARCHAR", "NVARCHAR2", "VARCHAR_CASESENSITIVE"}
);
add(Value.STRING, Types.LONGVARCHAR, "String",
createString(true),
new String[]{"LONGVARCHAR"}
);
add(Value.STRING, Types.CHAR, "String",
createString(true),
new String[]{"CHAR", "CHARACTER", "NCHAR"}
);
add(Value.STRING_IGNORECASE, Types.VARCHAR, "String",
createString(false),
new String[]{"VARCHAR_IGNORECASE"}
);
add(Value.BLOB, Types.BLOB, "Bytes",
createString(false),
new String[]{"BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "IMAGE", "OID"}
);
add(Value.CLOB, Types.CLOB, "String",
createString(true),
new String[]{"CLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "NTEXT", "NCLOB"}
);
// TODO data types: try to support other types as well (longvarchar for odbc/access,...) - maybe map them to regular types?
}
private static void add(int type, int sqlType, String jdbc, DataType dataType, String[] names) {
for(int i=0; i<names.length; i++) {
DataType dt = new DataType();
dt.type = type;
dt.sqlType = sqlType;
dt.jdbc = jdbc;
dt.name = names[i];
dt.autoInc = dataType.autoInc;
dt.decimal = dataType.decimal;
dt.maxPrecision = dataType.maxPrecision;
dt.maxScale = dataType.maxScale;
dt.minScale = dataType.minScale;
dt.params = dataType.params;
dt.prefix = dataType.prefix;
dt.suffix = dataType.suffix;
dt.supportsPrecision = dataType.supportsPrecision;
dt.supportsScale = dataType.supportsScale;
dt.defaultPrecision = dataType.defaultPrecision;
dt.defaultScale = dataType.defaultScale;
dt.caseSensitive = dataType.caseSensitive;
dt.hidden = i > 0;
for(int j=0; j<types.size(); j++) {
DataType t2 = (DataType) types.get(j);
if(t2.sqlType == dt.sqlType) {
dt.order++;
}
}
typesByName.put(dt.name, dt);
if(typesByValueType[type] == null) {
typesByValueType[type] = dt;
}
types.add(dt);
}
}
public static String getJdbcString(int type) {
return typesByValueType[type].jdbc;
}
private static DataType createDecimal(int maxPrecision, int defaultPrecision, int defaultScale, boolean needsPrecisionAndScale, boolean autoInc) {
DataType dataType = new DataType();
dataType.maxPrecision = maxPrecision;
dataType.defaultPrecision = defaultPrecision;
dataType.defaultScale = defaultScale;
if(needsPrecisionAndScale) {
dataType.params = "PRECISION,SCALE";
dataType.supportsPrecision = true;
dataType.supportsScale = true;
}
dataType.decimal = true;
dataType.autoInc = autoInc;
return dataType;
}
private static DataType createDate(int precision, String prefix, int scale) {
DataType dataType = new DataType();
dataType.prefix = prefix + " '";
dataType.suffix = "'";
dataType.maxPrecision = precision;
dataType.supportsScale = scale != 0;
dataType.maxScale = scale;
dataType.defaultPrecision = precision;
dataType.defaultScale = scale;
return dataType;
}
private static DataType createString(boolean caseSensitive) {
DataType dataType = new DataType();
dataType.prefix = "'";
dataType.suffix = "'";
dataType.params = "LENGTH";
dataType.caseSensitive = caseSensitive;
dataType.supportsPrecision = true;
dataType.maxPrecision = Integer.MAX_VALUE;
dataType.defaultPrecision = Integer.MAX_VALUE;
return dataType;
}
public static ObjectArray getTypes() {
return types;
}
public static Value readValue(SessionInterface session, ResultSet rs, int columnIndex, int type) throws SQLException {
Value v;
switch(type) {
case Value.NULL: {
return ValueNull.INSTANCE;
}
case Value.BYTES: {
byte[] buff = rs.getBytes(columnIndex);
v = buff==null ? (Value)ValueNull.INSTANCE : ValueBytes.get(buff);
break;
}
case Value.UUID: {
byte[] buff = rs.getBytes(columnIndex);
v = buff==null ? (Value)ValueNull.INSTANCE : ValueUuid.get(buff);
break;
}
case Value.BOOLEAN: {
boolean value = rs.getBoolean(columnIndex);
v = rs.wasNull() ? (Value)ValueNull.INSTANCE : ValueBoolean.get(value);
break;
}
case Value.BYTE: {
byte value = rs.getByte(columnIndex);
v = rs.wasNull() ? (Value)ValueNull.INSTANCE : ValueByte.get(value);
break;
}
case Value.DATE: {
Date value = rs.getDate(columnIndex);
v = value == null ? (Value)ValueNull.INSTANCE : ValueDate.get(value);
break;
}
case Value.TIME: {
Time value = rs.getTime(columnIndex);
v = value == null ? (Value)ValueNull.INSTANCE : ValueTime.get(value);
break;
}
case Value.TIMESTAMP: {
Timestamp value = rs.getTimestamp(columnIndex);
v = value == null ? (Value)ValueNull.INSTANCE : ValueTimestamp.get(value);
break;
}
case Value.DECIMAL: {
BigDecimal value = rs.getBigDecimal(columnIndex);
v = value == null ? (Value)ValueNull.INSTANCE : ValueDecimal.get(value);
break;
}
case Value.DOUBLE: {
double value = rs.getDouble(columnIndex);
v = rs.wasNull() ? (Value)ValueNull.INSTANCE : ValueDouble.get(value);
break;
}
case Value.FLOAT: {
float value = rs.getFloat(columnIndex);
v = rs.wasNull() ? (Value)ValueNull.INSTANCE : ValueFloat.get(value);
break;
}
case Value.INT: {
int value = rs.getInt(columnIndex);
v = rs.wasNull() ? (Value)ValueNull.INSTANCE : ValueInt.get(value);
break;
}
case Value.LONG: {
long value = rs.getLong(columnIndex);
v = rs.wasNull() ? (Value)ValueNull.INSTANCE : ValueLong.get(value);
break;
}
case Value.SHORT: {
short value = rs.getShort(columnIndex);
v = rs.wasNull() ? (Value)ValueNull.INSTANCE : ValueShort.get(value);
break;
}
case Value.STRING_IGNORECASE:
case Value.STRING: {
String s = rs.getString(columnIndex);
v = (s == null) ? (Value)ValueNull.INSTANCE : ValueString.get(s);
break;
}
case Value.CLOB: {
if(session == null) {
v = ValueLob.createSmallLob(Value.CLOB, StringUtils.utf8Encode(rs.getString(columnIndex)));
} else {
Reader in = rs.getCharacterStream(columnIndex);
v = (in == null) ? (Value)ValueNull.INSTANCE : ValueLob.createClob(in, -1, session.getDataHandler());
}
break;
}
case Value.BLOB: {
if(session == null) {
v = ValueLob.createSmallLob(Value.BLOB, rs.getBytes(columnIndex));
} else {
InputStream in = rs.getBinaryStream(columnIndex);
v = (in == null) ? (Value)ValueNull.INSTANCE : ValueLob.createBlob(in, -1, session.getDataHandler());
}
break;
}
case Value.JAVA_OBJECT: {
byte[] buff = rs.getBytes(columnIndex);
v = buff==null ? (Value)ValueNull.INSTANCE : ValueJavaObject.get(buff);
break;
}
default:
throw Message.getInternalError("type="+type);
}
return v;
}
public static String getTypeClassName(int type) {
switch(type) {
case Value.BOOLEAN:
return Boolean.class.getName(); // "java.lang.Boolean";
case Value.BYTE:
return Byte.class.getName(); // "java.lang.Byte";
case Value.SHORT:
return Short.class.getName(); // "java.lang.Short";
case Value.INT:
return Integer.class.getName(); // "java.lang.Integer";
case Value.LONG:
return Long.class.getName(); // "java.lang.Long";
case Value.DECIMAL:
return BigDecimal.class.getName(); // "java.math.BigDecimal";
case Value.TIME:
return Time.class.getName(); // "java.sql.Time";
case Value.DATE:
return Date.class.getName(); // "java.sql.Date";
case Value.TIMESTAMP:
return Timestamp.class.getName(); // "java.sql.Timestamp";
case Value.BYTES:
case Value.UUID:
return byte[].class.getName(); // "[B", not "byte[]";
case Value.STRING:
case Value.STRING_IGNORECASE:
return String.class.getName(); // "java.lang.String";
case Value.BLOB:
return java.sql.Blob.class.getName(); // "java.sql.Blob";
case Value.CLOB:
return java.sql.Clob.class.getName(); // "java.sql.Clob";
case Value.DOUBLE:
return Double.class.getName(); // "java.lang.Double";
case Value.FLOAT:
return Float.class.getName(); // "java.lang.Float";
case Value.NULL:
return null;
case Value.JAVA_OBJECT:
return Object.class.getName(); // "java.lang.Object";
default:
throw Message.getInternalError("type="+type);
}
}
public static DataType getDataType(int type) {
DataType dt = typesByValueType[type];
if(dt == null) {
dt = typesByValueType[Value.NULL];
}
return dt;
}
public static int convertTypeToSQLType(int type) {
return getDataType(type).sqlType;
}
public static int convertSQLTypeToValueType(int sqlType) throws SQLException {
switch(sqlType) {
case Types.VARCHAR:
case Types.CHAR:
case Types.LONGVARCHAR:
return Value.STRING;
case Types.NUMERIC:
case Types.DECIMAL:
return Value.DECIMAL;
case Types.BIT:
case Types.BOOLEAN:
return Value.BOOLEAN;
case Types.INTEGER:
return Value.INT;
case Types.SMALLINT:
return Value.SHORT;
case Types.TINYINT:
return Value.BYTE;
case Types.BIGINT:
return Value.LONG;
case Types.REAL:
return Value.FLOAT;
case Types.DOUBLE:
case Types.FLOAT:
return Value.DOUBLE;
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
return Value.BYTES;
case Types.OTHER:
case Types.JAVA_OBJECT:
return Value.JAVA_OBJECT;
case Types.DATE:
return Value.DATE;
case Types.TIME:
return Value.TIME;
case Types.TIMESTAMP:
return Value.TIMESTAMP;
case Types.BLOB:
return Value.BLOB;
case Types.CLOB:
return Value.CLOB;
case Types.NULL:
return Value.NULL;
default:
throw Message.getSQLException(Message.UNKNOWN_DATA_TYPE_1, ""+sqlType);
}
}
public static int getTypeFromClass(Class x) throws SQLException {
// TODO refactor: too many if/else in functions, can reduce!
if(x==null) {
return Value.NULL;
}
if(ResultSet.class.isAssignableFrom(x)) {
return Value.RESULT_SET;
} else if(String.class.isAssignableFrom(x)) {
return Value.STRING;
} else if(BigDecimal.class.isAssignableFrom(x)) {
return Value.DECIMAL;
} else if(Boolean.class.isAssignableFrom(x) || boolean.class.isAssignableFrom(x)) {
return Value.BOOLEAN;
} else if(Byte.class.isAssignableFrom(x) || byte.class.isAssignableFrom(x)) {
return Value.BYTE;
} else if(Short.class.isAssignableFrom(x) || short.class.isAssignableFrom(x)) {
return Value.SHORT;
} else if(Integer.class.isAssignableFrom(x) || int.class.isAssignableFrom(x)) {
return Value.INT;
} else if(Character.class.isAssignableFrom(x) || char.class.isAssignableFrom(x)) {
throw Message.getSQLException(Message.DATA_CONVERSION_ERROR_1, "char (not supported)");
} else if(Long.class.isAssignableFrom(x) || long.class.isAssignableFrom(x)) {
return Value.LONG;
} else if(Float.class.isAssignableFrom(x) || float.class.isAssignableFrom(x)) {
return Value.FLOAT;
} else if(Double.class.isAssignableFrom(x) || double.class.isAssignableFrom(x)) {
return Value.DOUBLE;
} else if(byte[].class.isAssignableFrom(x)) {
return Value.BYTES;
} else if(Date.class.isAssignableFrom(x)) {
return Value.DATE;
} else if(Time.class.isAssignableFrom(x)) {
return Value.TIME;
} else if(Timestamp.class.isAssignableFrom(x)) {
return Value.TIMESTAMP;
} else if(java.util.Date.class.isAssignableFrom(x)) {
return Value.DATE;
} else if(java.io.Reader.class.isAssignableFrom(x)) {
return Value.CLOB;
} else if(java.sql.Clob.class.isAssignableFrom(x)) {
return Value.CLOB;
} else if(java.io.InputStream.class.isAssignableFrom(x)) {
return Value.BLOB;
} else if(java.sql.Blob.class.isAssignableFrom(x)) {
return Value.BLOB;
} else if(Object[].class.isAssignableFrom(x)) {
return Value.ARRAY;
} else if(Void.TYPE==x) {
return Value.NULL;
} else {
return Value.JAVA_OBJECT;
}
}
public static Value convertToValue(SessionInterface session, Object x, int type) throws SQLException {
if(x==null) {
return ValueNull.INSTANCE;
}
if(type == Value.JAVA_OBJECT) {
// serialize JAVA_OBJECTs, even if the type is known
if(Constants.SERIALIZE_JAVA_OBJECTS) {
return ValueJavaObject.get(TypeConverter.serialize(x));
}
}
if(x instanceof String) {
return ValueString.get((String)x);
} else if(x instanceof BigDecimal) {
return ValueDecimal.get((BigDecimal)x);
} else if(x instanceof Boolean) {
return ValueBoolean.get(((Boolean)x).booleanValue());
} else if(x instanceof Byte) {
return ValueByte.get(((Byte)x).byteValue());
} else if(x instanceof Short) {
return ValueShort.get(((Short)x).shortValue());
} else if(x instanceof Integer) {
return ValueInt.get(((Integer)x).intValue());
} else if(x instanceof Long) {
return ValueLong.get(((Long)x).longValue());
} else if(x instanceof Float) {
return ValueFloat.get(((Float)x).floatValue());
} else if(x instanceof Double) {
return ValueDouble.get(((Double)x).doubleValue());
} else if(x instanceof byte[]) {
return ValueBytes.get((byte[])x);
} else if(x instanceof Date) {
return ValueDate.get((Date)x);
} else if(x instanceof Time) {
return ValueTime.get((Time)x);
} else if(x instanceof Timestamp) {
return ValueTimestamp.get((Timestamp)x);
} else if(x instanceof java.util.Date) {
return ValueDate.get(new Date(((java.util.Date)x).getTime()));
} else if(x instanceof java.io.Reader) {
return ValueLob.createClob((java.io.Reader)x, -1, session.getDataHandler());
} else if(x instanceof java.sql.Clob) {
return ValueLob.createClob(((java.sql.Clob)x).getCharacterStream(), -1, session.getDataHandler());
} else if(x instanceof java.io.InputStream) {
return ValueLob.createBlob((java.io.InputStream)x, -1, session.getDataHandler());
} else if(x instanceof java.sql.Blob) {
return ValueLob.createBlob(((java.sql.Blob)x).getBinaryStream(), -1, session.getDataHandler());
} else if(x instanceof ResultSet) {
return ValueResultSet.get((ResultSet)x);
} else if(x instanceof Object[]) {
// (a.getClass().isArray());
// (a.getClass().getComponentType().isPrimitive());
Object[] o = (Object[]) x;
int len = o.length;
Value[] v = new Value[len];
for(int i=0; i<len; i++) {
v[i] = convertToValue(session, o[i], type);
}
return ValueArray.get(v);
} else {
if(Constants.SERIALIZE_JAVA_OBJECTS) {
return ValueJavaObject.get(TypeConverter.serialize(x));
} else {
throw Message.getSQLException(Message.UNKNOWN_DATA_TYPE_1, x.getClass().getName());
}
}
}
public static DataType getTypeByName(String s) {
return (DataType) typesByName.get(s);
}
public static boolean isLargeObject(int type) {
if(type == Value.BLOB || type == Value.CLOB) {
return true;
}
return false;
}
public static boolean supportsAdd(int type) {
switch(type) {
case Value.BYTE:
case Value.DECIMAL:
case Value.DOUBLE:
case Value.FLOAT:
case Value.INT:
case Value.LONG:
case Value.SHORT:
return true;
}
return false;
}
public static java.util.Date parseDateTime(String s, int type, int errorCode) throws SQLException {
if (s == null) {
return null;
}
try {
int timeStart;
if(type == Value.TIME) {
timeStart = 0;
} else {
timeStart = s.indexOf(' ') + 1;
if(timeStart <= 0) {
// PostgreSQL compatibility
timeStart = s.indexOf('T') + 1;
}
}
int year = 1970, month = 1, day = 1;
if (type != Value.TIME) {
int s1 = s.indexOf('-');
int s2 = s.indexOf('-', s1 + 1);
if(s1 <= 0 || s2 <= s1) {
throw Message.getSQLException(errorCode, s);
}
year = Integer.parseInt(s.substring(0, s1));
month = Integer.parseInt(s.substring(s1 + 1, s2));
int end = timeStart == 0 ? s.length() : timeStart - 1;
day = Integer.parseInt(s.substring(s2 + 1, end));
}
int hour = 0, minute = 0, second = 0, nano = 0;
if (type != Value.DATE) {
int s1 = s.indexOf(':', timeStart);
int s2 = s.indexOf(':', s1 + 1);
int s3 = s.indexOf('.', s2 + 1);
if(s1 <= 0 || s2 <= s1) {
throw Message.getSQLException(errorCode, s);
}
hour = Integer.parseInt(s.substring(timeStart, s1));
minute = Integer.parseInt(s.substring(s1 + 1, s2));
if (s3 < 0) {
second = Integer.parseInt(s.substring(s2 + 1));
} else {
second = Integer.parseInt(s.substring(s2 + 1, s3));
String n = (s + "000000000").substring(s3 + 1, s3 + 10);
nano = Integer.parseInt(n);
}
}
Calendar c = Calendar.getInstance();
c.setLenient(false);
long time;
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month - 1); // january is 0
c.set(Calendar.DAY_OF_MONTH, day);
c.set(Calendar.HOUR_OF_DAY, hour);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.SECOND, second);
if(type != Value.TIMESTAMP) {
c.set(Calendar.MILLISECOND, nano / 1000000);
}
time = c.getTime().getTime();
switch(type) {
case Value.DATE:
return new java.sql.Date(time);
case Value.TIME:
return new java.sql.Time(time);
case Value.TIMESTAMP: {
Timestamp ts = new Timestamp(time);
ts.setNanos(nano);
return ts;
}
default:
throw Message.getInternalError("type:"+type);
}
} catch(IllegalArgumentException e) {
throw Message.getSQLException(errorCode, s);
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* */
package org.h2.value;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.math.BigDecimal;
import java.net.Socket;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.message.Message;
import org.h2.message.TraceSystem;
import org.h2.tools.SimpleResultSet;
import org.h2.util.ExactUTF8InputStreamReader;
import org.h2.util.IOUtils;
import org.h2.util.StringCache;
/**
* @author Thomas
*/
public class Transfer {
private static final int BUFFER_SIZE = 16 * 1024;
private static final int LOB_MAGIC = 0x1234;
protected Socket socket;
protected DataInputStream in;
protected DataOutputStream out;
private Exception stackTrace = new Exception();
private SessionInterface session;
public Transfer(SessionInterface session) {
this.session = session;
}
public void finalize() {
if (!Constants.RUN_FINALIZERS) {
return;
}
if(socket != null) {
throw Message.getInternalError("not closed", stackTrace);
}
}
public void setSocket(Socket s) {
socket = s;
}
public void init() throws IOException {
in = new DataInputStream(new BufferedInputStream(socket.getInputStream(), Transfer.BUFFER_SIZE));
out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream(), Transfer.BUFFER_SIZE));
}
public void flush() throws IOException {
out.flush();
}
public Transfer writeBoolean(boolean x) throws IOException {
out.writeByte((byte)(x ? 1 : 0));
return this;
}
public boolean readBoolean() throws IOException {
return in.readByte() == 1;
}
public Transfer writeByte(byte x) throws IOException {
out.writeByte(x);
return this;
}
public byte readByte() throws IOException {
return in.readByte();
}
public Transfer writeInt(int i) throws IOException {
out.writeInt(i);
return this;
}
public int readInt() throws IOException {
return in.readInt();
}
public Transfer writeLong(long i) throws IOException {
out.writeLong(i);
return this;
}
public long readLong() throws IOException {
return in.readLong();
}
public Transfer writeDouble(double i) throws IOException {
out.writeDouble(i);
return this;
}
public Transfer writeFloat(float i) throws IOException {
out.writeFloat(i);
return this;
}
public double readDouble() throws IOException {
return in.readDouble();
}
public float readFloat() throws IOException {
return in.readFloat();
}
public Transfer writeString(String s) throws IOException {
if (s == null) {
out.writeInt(-1);
} else {
int len = s.length();
out.writeInt(len);
for(int i=0; i<len; i++) {
out.writeChar(s.charAt(i));
}
}
return this;
}
public String readString() throws IOException {
int len = in.readInt();
if (len == -1) {
return null;
}
// TODO optimize: StringBuffer is synchronized, maybe use a char array (but that means more memory)
StringBuffer buff = new StringBuffer(len);
for(int i=0; i<len; i++) {
buff.append(in.readChar());
}
String s = buff.toString();
s = StringCache.get(s);
return s;
}
public Transfer writeBytes(byte[] data) throws IOException {
if(data == null) {
writeInt(-1);
} else {
writeInt(data.length);
out.write(data);
}
return this;
}
public byte[] readBytes() throws IOException {
int len = readInt();
if(len == -1) {
return null;
}
byte[] b = new byte[len];
in.readFully(b);
return b;
}
public void close() {
if(socket != null) {
try {
out.flush();
if(socket != null) {
socket.close();
}
} catch(IOException e) {
TraceSystem.traceThrowable(e);
} finally {
socket = null;
}
}
}
public void writeValue(Value v) throws IOException, SQLException {
int type = v.getType();
writeInt(type);
switch(type) {
case Value.NULL:
break;
case Value.BYTES:
case Value.JAVA_OBJECT:
writeBytes(v.getBytes());
break;
case Value.UUID: {
ValueUuid uuid = (ValueUuid) v;
writeLong(uuid.getHigh());
writeLong(uuid.getLow());
break;
}
case Value.BOOLEAN:
writeBoolean(v.getBoolean().booleanValue());
break;
case Value.BYTE:
writeByte(v.getByte());
break;
case Value.TIME:
writeLong(v.getTime().getTime());
break;
case Value.DATE:
writeLong(v.getDate().getTime());
break;
case Value.TIMESTAMP: {
Timestamp ts = v.getTimestamp();
writeLong(ts.getTime());
writeInt(ts.getNanos());
break;
}
case Value.DECIMAL:
writeString(v.getString());
break;
case Value.DOUBLE:
writeDouble(v.getDouble());
break;
case Value.FLOAT:
writeFloat(v.getFloat());
break;
case Value.INT:
writeInt(v.getInt());
break;
case Value.LONG:
writeLong(v.getLong());
break;
case Value.SHORT:
writeInt(v.getShort());
break;
case Value.STRING:
case Value.STRING_IGNORECASE:
writeString(v.getString());
break;
case Value.BLOB: {
long length = v.getPrecision();
if(Constants.CHECK && length < 0) {
throw Message.getInternalError("length: " + length);
}
writeLong(length);
InputStream in = v.getInputStream();
long written = IOUtils.copyAndCloseInput(in, out);
if(Constants.CHECK && written != length) {
Message.getInternalError("length:" + length + " written:" + written);
}
writeInt(LOB_MAGIC);
break;
}
case Value.CLOB: {
long length = v.getPrecision();
if(Constants.CHECK && length < 0) {
throw Message.getInternalError("length: " + length);
}
writeLong(length);
Reader reader = v.getReader();
Writer writer = new OutputStreamWriter(out, Constants.UTF8);
long written = IOUtils.copyAndCloseInput(reader, writer);
if(Constants.CHECK && written != length) {
Message.getInternalError("length:" + length + " written:" + written);
}
writer.flush();
writeInt(LOB_MAGIC);
break;
}
case Value.ARRAY: {
Value[] list = ((ValueArray)v).getList();
writeInt(list.length);
for(int i=0; i<list.length; i++) {
writeValue(list[i]);
}
break;
}
case Value.RESULT_SET: {
ResultSet rs = ((ValueResultSet)v).getResultSet();
rs.beforeFirst();
ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
writeInt(columnCount);
for(int i=0; i<columnCount; i++) {
writeString(meta.getColumnName(i + 1));
writeInt(meta.getColumnType(i + 1));
writeInt(meta.getPrecision(i + 1));
writeInt(meta.getScale(i + 1));
}
while(rs.next()) {
writeBoolean(true);
for(int i=0; i<columnCount; i++) {
int t = DataType.convertSQLTypeToValueType(meta.getColumnType(i + 1));
Value val = DataType.readValue(session, rs, i+1, t);
writeValue(val);
}
}
writeBoolean(false);
rs.beforeFirst();
break;
}
default:
throw Message.getInternalError("type="+type);
}
}
public Value readValue() throws IOException, SQLException {
int type = readInt();
switch(type) {
case Value.NULL:
return ValueNull.INSTANCE;
case Value.BYTES:
return ValueBytes.get(readBytes());
case Value.UUID:
return ValueUuid.get(readLong(), readLong());
case Value.JAVA_OBJECT:
return ValueJavaObject.get(readBytes());
case Value.BOOLEAN:
return ValueBoolean.get(readBoolean());
case Value.BYTE:
return ValueByte.get(readByte());
case Value.DATE:
return ValueDate.get(new Date(readLong()));
case Value.TIME:
return ValueTime.get(new Time(readLong()));
case Value.TIMESTAMP: {
Timestamp ts = new Timestamp(readLong());
ts.setNanos(readInt());
return ValueTimestamp.get(ts);
}
case Value.DECIMAL:
return ValueDecimal.get(new BigDecimal(readString()));
case Value.DOUBLE:
return ValueDouble.get(readDouble());
case Value.FLOAT:
return ValueFloat.get(readFloat());
case Value.INT:
return ValueInt.get(readInt());
case Value.LONG:
return ValueLong.get(readLong());
case Value.SHORT:
return ValueShort.get((short)readInt());
case Value.STRING:
return ValueString.get(readString());
case Value.STRING_IGNORECASE:
return ValueStringIgnoreCase.get(readString());
case Value.BLOB: {
long length = readLong();
ValueLob v = ValueLob.createBlob(in, length, session.getDataHandler());
if(readInt() != LOB_MAGIC) {
throw Message.getSQLException(Message.CONNECTION_BROKEN);
}
return v;
}
case Value.CLOB: {
long length = readLong();
ValueLob v = ValueLob.createClob(new ExactUTF8InputStreamReader(in), length, session.getDataHandler());
if(readInt() != LOB_MAGIC) {
throw Message.getSQLException(Message.CONNECTION_BROKEN);
}
return v;
}
case Value.ARRAY: {
int len = readInt();
Value[] list = new Value[len];
for(int i=0; i<len; i++) {
list[i] = readValue();
}
return ValueArray.get(list);
}
case Value.RESULT_SET: {
SimpleResultSet rs = new SimpleResultSet();
int columns = readInt();
for(int i=0; i<columns; i++) {
rs.addColumn(readString(), readInt(), readInt(), readInt());
}
while(true) {
if(!readBoolean()) {
break;
}
Object[] o = new Object[columns];
for(int i=0; i<columns; i++) {
o[i] = readValue().getObject();
}
rs.addRow(o);
}
return ValueResultSet.get(rs);
}
default:
throw Message.getInternalError("type="+type);
}
}
public Socket getSocket() {
return socket;
}
public void setSession(SessionInterface session) {
this.session = session;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Reader;
import java.lang.ref.WeakReference;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import org.h2.engine.Constants;
import org.h2.engine.Mode;
import org.h2.message.Message;
import org.h2.store.DataHandler;
import org.h2.tools.SimpleResultSet;
import org.h2.util.ByteUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
import org.h2.util.TypeConverter;
/**
* @author Thomas
*/
public abstract class Value {
// TODO value: float is missing
// remember to keep the order
public static final int UNKNOWN = -1;
public static final int NULL = 0, BOOLEAN = 1, BYTE = 2, SHORT = 3, INT = 4, LONG = 5, DECIMAL = 6;
public static final int DOUBLE = 7, FLOAT = 8, TIME = 9, DATE = 10, TIMESTAMP = 11, BYTES = 12;
public static final int STRING = 13, STRING_IGNORECASE = 14, BLOB = 15, CLOB = 16;
public static final int ARRAY = 17, RESULT_SET = 18, JAVA_OBJECT = 19, UUID = 20;
public static final int TYPE_COUNT = UUID + 1;
private static WeakReference weakCache = new WeakReference(null);
// private static int cacheCleaner = 0;
// testing: cacheHit / miss are public!
// public static int cacheHit = 0, cacheMiss = 0;
// private static Value[] cache = new Value[Constants.OBJECT_CACHE_SIZE];
private static final BigDecimal MAX_LONG_DECIMAL = new BigDecimal("" + Long.MAX_VALUE);
private static final BigDecimal MIN_LONG_DECIMAL = new BigDecimal("" + Long.MIN_VALUE);
static Value cache(Value v) {
if (Constants.USE_OBJECT_CACHE) {
Value[] cache = (Value[]) weakCache.get();
int hash = v.hashCode();
if (cache == null) {
cache = new Value[Constants.OBJECT_CACHE_SIZE];
weakCache = new WeakReference(cache);
}
int index = hash & (Constants.OBJECT_CACHE_SIZE - 1);
Value cached = cache[index];
if (cached != null) {
if (cached.getType() == v.getType() && v.isEqual(cached)) {
// cacheHit++;
return cached;
}
}
// cacheMiss++;
// cache[cacheCleaner] = null;
// cacheCleaner = (cacheCleaner + 1) & (Constants.OBJECT_CACHE_SIZE - 1);
cache[index] = v;
}
return v;
}
public abstract String getSQL();
public abstract int getType();
public abstract long getPrecision();
public abstract int getDisplaySize();
public abstract String getString() throws SQLException;
// public abstract String getJavaString();
protected abstract int compareSecure(Value v, CompareMode mode) throws SQLException;
protected abstract boolean isEqual(Value v);
public abstract Object getObject() throws SQLException;
public abstract void set(PreparedStatement prep, int parameterIndex) throws SQLException;
public Boolean getBoolean() throws SQLException {
return ((ValueBoolean) convertTo(Value.BOOLEAN)).getBoolean();
}
public Date getDate() throws SQLException {
return ((ValueDate) convertTo(Value.DATE)).getDate();
}
public Time getTime() throws SQLException {
return ((ValueTime) convertTo(Value.TIME)).getTime();
}
public Timestamp getTimestamp() throws SQLException {
return ((ValueTimestamp) convertTo(Value.TIMESTAMP)).getTimestamp();
}
public byte[] getBytes() throws SQLException {
return ((ValueBytes) convertTo(Value.BYTES)).getBytes();
}
public byte getByte() throws SQLException {
return ((ValueByte) convertTo(Value.BYTE)).getByte();
}
public short getShort() throws SQLException {
return ((ValueShort) convertTo(Value.SHORT)).getShort();
}
public BigDecimal getBigDecimal() throws SQLException {
return ((ValueDecimal) convertTo(Value.DECIMAL)).getBigDecimal();
}
public double getDouble() throws SQLException {
return ((ValueDouble) convertTo(Value.DOUBLE)).getDouble();
}
public float getFloat() throws SQLException {
return ((ValueFloat) convertTo(Value.FLOAT)).getFloat();
}
public int getInt() throws SQLException {
return ((ValueInt) convertTo(Value.INT)).getInt();
}
public long getLong() throws SQLException {
return ((ValueLong) convertTo(Value.LONG)).getLong();
}
public InputStream getInputStream() throws SQLException {
return new ByteArrayInputStream(getBytes());
}
public Reader getReader() throws SQLException {
return TypeConverter.getReader(getString());
}
public Value add(Value v) throws SQLException {
throw Message.getUnsupportedException();
}
public int getSignum() throws SQLException {
throw Message.getUnsupportedException();
}
public Value negate() throws SQLException {
throw Message.getUnsupportedException();
}
public Value subtract(Value v) throws SQLException {
throw Message.getUnsupportedException();
}
public Value divide(Value v) throws SQLException {
throw Message.getUnsupportedException();
}
public Value multiply(Value v) throws SQLException {
throw Message.getUnsupportedException();
}
public static int getHigherOrder(int t1, int t2) throws SQLException {
if(t1 == t2) {
if(t1 == Value.UNKNOWN) {
throw Message.getSQLException(Message.UNKNOWN_DATA_TYPE_1, "?, ?");
}
return t1;
}
int type = Math.max(t1, t2);
switch(type) {
case Value.STRING:
case Value.STRING_IGNORECASE: {
int b = Math.min(t1, t2);
switch(b) {
case Value.BLOB:
case Value.BYTES:
case Value.DATE:
case Value.JAVA_OBJECT:
case Value.TIME:
case Value.TIMESTAMP:
case Value.UUID:
return b;
}
}
}
return type;
}
public Value convertTo(int type) throws SQLException {
// converting NULL done in ValueNull
// converting BLOB to CLOB and vice versa is done in ValueLob
if (getType() == type) {
return this;
}
// decimal conversion
switch (type) {
case BOOLEAN: {
switch (getType()) {
case BYTE:
case SHORT:
case INT:
case LONG:
case DECIMAL:
case DOUBLE:
case FLOAT:
return ValueBoolean.get(getSignum() != 0);
case TIME:
case DATE:
case TIMESTAMP:
case BYTES:
case JAVA_OBJECT:
case UUID:
throw Message.getSQLException(Message.DATA_CONVERSION_ERROR_1, getString());
}
break;
}
case BYTE: {
switch (getType()) {
case BOOLEAN:
return ValueByte.get(getBoolean().booleanValue() ? (byte)1 : (byte)0);
case SHORT:
return ValueByte.get(convertToByte(getShort()));
case INT:
return ValueByte.get(convertToByte(getInt()));
case LONG:
return ValueByte.get(convertToByte(getLong()));
case DECIMAL:
return ValueByte.get(convertToByte(convertToLong(getBigDecimal())));
case DOUBLE:
return ValueByte.get(convertToByte(convertToLong(getDouble())));
case FLOAT:
return ValueByte.get(convertToByte(convertToLong(getFloat())));
}
break;
}
case SHORT: {
switch (getType()) {
case BOOLEAN:
return ValueShort.get(getBoolean().booleanValue() ? (short)1 : (short)0);
case BYTE:
return ValueShort.get(getByte());
case INT:
return ValueShort.get(convertToShort(getInt()));
case LONG:
return ValueShort.get(convertToShort(getLong()));
case DECIMAL:
return ValueShort.get(convertToShort(convertToLong(getBigDecimal())));
case DOUBLE:
return ValueShort.get(convertToShort(convertToLong(getDouble())));
case FLOAT:
return ValueShort.get(convertToShort(convertToLong(getFloat())));
}
break;
}
case INT: {
switch (getType()) {
case BOOLEAN:
return ValueInt.get(getBoolean().booleanValue() ? 1 : 0);
case BYTE:
return ValueInt.get(getByte());
case SHORT:
return ValueInt.get(getShort());
case LONG:
return ValueInt.get(convertToInt(getLong()));
case DECIMAL:
return ValueInt.get(convertToInt(convertToLong(getBigDecimal())));
case DOUBLE:
return ValueInt.get(convertToInt(convertToLong(getDouble())));
case FLOAT:
return ValueInt.get(convertToInt(convertToLong(getFloat())));
}
break;
}
case LONG: {
switch (getType()) {
case BOOLEAN:
return ValueLong.get(getBoolean().booleanValue() ? 1 : 0);
case BYTE:
return ValueLong.get(getByte());
case SHORT:
return ValueLong.get(getShort());
case INT:
return ValueLong.get(getInt());
case DECIMAL:
return ValueLong.get(convertToLong(getBigDecimal()));
case DOUBLE:
return ValueLong.get(convertToLong(getDouble()));
case FLOAT:
return ValueLong.get(convertToLong(getFloat()));
}
break;
}
case DECIMAL: {
// convert to string is required for JDK 1.4
switch (getType()) {
case BOOLEAN:
return ValueDecimal.get(new BigDecimal(getBoolean().booleanValue() ? "1" : "0"));
case BYTE:
return ValueDecimal.get(new BigDecimal("" + getByte()));
case SHORT:
return ValueDecimal.get(new BigDecimal("" + getShort()));
case INT:
return ValueDecimal.get(new BigDecimal("" + getInt()));
case LONG:
return ValueDecimal.get(new BigDecimal("" + getLong()));
case DOUBLE: {
double d = getDouble();
if(Double.isInfinite(d) || Double.isNaN(d)) {
throw Message.getSQLException(Message.DATA_CONVERSION_ERROR_1, ""+d);
}
return ValueDecimal.get(new BigDecimal(d));
}
case FLOAT: {
float f = getFloat();
if(Float.isInfinite(f) || Float.isNaN(f)) {
throw Message.getSQLException(Message.DATA_CONVERSION_ERROR_1, ""+f);
}
return ValueDecimal.get(new BigDecimal(f));
}
}
break;
}
case DOUBLE: {
switch (getType()) {
case BOOLEAN:
return ValueDouble.get(getBoolean().booleanValue() ? 1 : 0);
case BYTE:
return ValueDouble.get(getByte());
case SHORT:
return ValueDouble.get(getShort());
case INT:
return ValueDouble.get(getInt());
case LONG:
return ValueDouble.get(getLong());
case DECIMAL:
return ValueDouble.get(getBigDecimal().doubleValue());
case FLOAT:
return ValueDouble.get(getFloat());
}
break;
}
case FLOAT: {
switch (getType()) {
case BOOLEAN:
return ValueFloat.get(getBoolean().booleanValue() ? 1 : 0);
case BYTE:
return ValueFloat.get(getByte());
case SHORT:
return ValueFloat.get(getShort());
case INT:
return ValueFloat.get(getInt());
case LONG:
return ValueFloat.get(getLong());
case DECIMAL:
return ValueFloat.get(getBigDecimal().floatValue());
case DOUBLE:
return ValueFloat.get((float)getDouble());
}
break;
}
case DATE: {
switch (getType()) {
case TIME:
return ValueDate.get(new Date(getTime().getTime()));
case TIMESTAMP:
return ValueDate.get(new Date(getTimestamp().getTime()));
}
break;
}
case TIME: {
switch (getType()) {
case DATE:
return ValueTime.get(new Time(getDate().getTime()));
case TIMESTAMP:
return ValueTime.get(new Time(getTimestamp().getTime()));
}
break;
}
case TIMESTAMP: {
switch (getType()) {
case TIME:
return ValueTimestamp.get(new Timestamp(getTime().getTime()));
case DATE:
return ValueTimestamp.get(new Timestamp(getDate().getTime()));
}
break;
}
case BYTES: {
switch(getType()) {
case JAVA_OBJECT:
case BLOB:
case UUID:
return ValueBytes.get(getBytes());
}
break;
}
case JAVA_OBJECT: {
switch(getType()) {
case BYTES:
case BLOB:
return ValueBytes.get(getBytes());
}
break;
}
case BLOB: {
switch(getType()) {
case BYTES:
return ValueLob.createSmallLob(Value.BLOB, getBytes());
}
break;
}
case UUID: {
switch(getType()) {
case BYTES:
return ValueUuid.get(getBytes());
}
}
}
// conversion by parsing the string value
String s = getString();
try {
switch (type) {
case NULL:
return ValueNull.INSTANCE;
case BOOLEAN: {
if (s.equalsIgnoreCase("true") || s.equalsIgnoreCase("t") || s.equalsIgnoreCase("yes") || s.equalsIgnoreCase("y")) {
return ValueBoolean.get(true);
} else if (s.equalsIgnoreCase("false") || s.equalsIgnoreCase("f") || s.equalsIgnoreCase("no") || s.equalsIgnoreCase("n")) {
return ValueBoolean.get(false);
} else {
// convert to a number, and if it is not 0 then it is true
return ValueBoolean.get(new BigDecimal(s).signum() != 0);
}
}
case BYTE:
return ValueByte.get(MathUtils.decodeByte(s.trim()));
case SHORT:
return ValueShort.get(MathUtils.decodeShort(s.trim()));
case INT:
return ValueInt.get(MathUtils.decodeInt(s.trim()));
case LONG:
return ValueLong.get(MathUtils.decodeLong(s.trim()));
case DECIMAL:
return ValueDecimal.get(new BigDecimal(s.trim()));
case TIME:
return ValueTime.get(ValueTime.parseTime(s.trim()));
case DATE:
return ValueDate.get(ValueDate.parseDate(s.trim()));
case TIMESTAMP:
return ValueTimestamp.get(ValueTimestamp.parseTimestamp(s.trim()));
case BYTES:
return ValueBytes.get(ByteUtils.convertStringToBytes(s.trim()));
case JAVA_OBJECT:
return ValueJavaObject.get(ByteUtils.convertStringToBytes(s.trim()));
case STRING:
return ValueString.get(s);
case STRING_IGNORECASE:
return ValueStringIgnoreCase.get(s);
case DOUBLE:
return ValueDouble.get(Double.parseDouble(s.trim()));
case FLOAT:
return ValueFloat.get(Float.parseFloat(s.trim()));
case CLOB:
return ValueLob.createSmallLob(CLOB, StringUtils.utf8Encode(s));
case BLOB:
return ValueLob.createSmallLob(BLOB, ByteUtils.convertStringToBytes(s.trim()));
case ARRAY:
return ValueArray.get(new Value[]{ValueString.get(s)});
case RESULT_SET: {
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("X", Types.VARCHAR, s.length(), 0);
rs.addRow(new String[]{s});
return ValueResultSet.get(rs);
}
case UUID:
return ValueUuid.get(s);
default:
throw Message.getInternalError("type=" + type);
}
} catch (NumberFormatException e) {
throw Message.getSQLException(Message.DATA_CONVERSION_ERROR_1, new String[] { s }, e);
}
}
public final int compareTypeSave(Value v, CompareMode mode) throws SQLException {
if (this == ValueNull.INSTANCE) {
return v == ValueNull.INSTANCE ? 0 : -1;
} else if (v == ValueNull.INSTANCE) {
return 1;
}
return compareSecure(v, mode);
}
public final boolean compareEqual(Value v) throws SQLException {
if (this == ValueNull.INSTANCE) {
return v == ValueNull.INSTANCE;
} else if (v == ValueNull.INSTANCE) {
return false;
}
if (getType() == v.getType()) {
return isEqual(v);
}
int t2 = Value.getHigherOrder(getType(), v.getType());
return convertTo(t2).isEqual(v.convertTo(t2));
}
public final int compareTo(Value v, CompareMode mode) throws SQLException {
if (this == ValueNull.INSTANCE) {
return v == ValueNull.INSTANCE ? 0 : -1;
} else if (v == ValueNull.INSTANCE) {
return 1;
}
if (getType() == v.getType()) {
return compareSecure(v, mode);
}
int t2 = Value.getHigherOrder(getType(), v.getType());
return convertTo(t2).compareSecure(v.convertTo(t2), mode);
}
public int getScale() {
return 0;
}
public Value convertScale(boolean onlyToSmallerScale, int targetScale) throws SQLException {
return this;
}
public Value convertPrecision(long precision) throws SQLException {
return this;
}
private byte convertToByte(long x) throws SQLException {
if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) {
throw Message.getSQLException(Message.NUMERIC_VALUE_OUT_OF_RANGE);
}
return (byte) x;
}
private short convertToShort(long x) throws SQLException {
if (x > Short.MAX_VALUE || x < Short.MIN_VALUE) {
throw Message.getSQLException(Message.NUMERIC_VALUE_OUT_OF_RANGE);
}
return (short) x;
}
private int convertToInt(long x) throws SQLException {
if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE) {
throw Message.getSQLException(Message.NUMERIC_VALUE_OUT_OF_RANGE);
}
return (int) x;
}
private long convertToLong(double x) throws SQLException {
if (x > Long.MAX_VALUE || x < Long.MIN_VALUE) {
// TODO document that +Infinity, -Infinity throw an exception and NaN returns 0
throw Message.getSQLException(Message.NUMERIC_VALUE_OUT_OF_RANGE);
}
if(Mode.getCurrentMode().roundWhenConvertToLong) {
return Math.round(x);
} else {
return (long) x;
}
}
private long convertToLong(BigDecimal x) throws SQLException {
if (x.compareTo(MAX_LONG_DECIMAL) > 0 || x.compareTo(Value.MIN_LONG_DECIMAL) < 0) {
throw Message.getSQLException(Message.NUMERIC_VALUE_OUT_OF_RANGE);
}
if(Mode.getCurrentMode().roundWhenConvertToLong) {
return Math.round(x.doubleValue());
} else {
return x.longValue();
}
}
public Value link(DataHandler handler, int tableId) throws SQLException {
return this;
}
public void unlink(DataHandler handler) throws SQLException {
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.message.Message;
public class ValueArray extends Value {
private Value[] values;
private int hash;
public static ValueArray get(Value[] list) {
return new ValueArray(list);
}
private ValueArray(Value[] list) {
this.values = list;
}
public int hashCode() {
if (hash != 0) {
return hash;
}
int h = 1;
for (int i = 0; i < values.length;) {
h = h * 31 + values[i++].hashCode();
}
return hash = h;
}
public Value[] getList() {
return values;
}
public int compareTo(ValueArray other, CompareMode mode) throws SQLException {
for (int i = 0; i < values.length; i++) {
Value v1 = values[i];
Value v2 = other.values[i];
int comp = v1.compareTo(v2, mode);
if (comp != 0) {
return comp;
}
}
return 0;
}
public int getType() {
return Value.ARRAY;
}
public long getPrecision() {
return 0;
}
public String getString() throws SQLException {
StringBuffer buff = new StringBuffer();
buff.append('(');
for (int i = 0; i < values.length; i++) {
if (i > 0) {
buff.append(", ");
}
buff.append(values[i].getString());
}
buff.append(')');
return buff.toString();
}
// public String getJavaString() {
// StringBuffer buff = new StringBuffer();
// buff.append('{');
// for (int i = 0; i < values.length; i++) {
// if (i > 0) {
// buff.append(", ");
// }
// buff.append(values[i].getJavaString());
// }
// buff.append('}');
// return buff.toString();
// }
protected int compareSecure(Value o, CompareMode mode) throws SQLException {
ValueArray v = (ValueArray) o;
if (values == v.values) {
return 0;
}
if (values.length != v.values.length) {
return values.length > v.values.length ? 1 : -1;
}
for (int i = 0; i < values.length; i++) {
Value v1 = values[i];
Value v2 = v.values[i];
int c;
if (v1 == ValueNull.INSTANCE) {
c = v2 == ValueNull.INSTANCE ? 0 : -1;
} else if (v2 == ValueNull.INSTANCE) {
c = 1;
} else {
c = v1.compareSecure(v2, mode);
}
if (c != 0) {
return c;
}
}
return 0;
}
public Object getObject() throws SQLException {
Object[] list = new Object[values.length];
for (int i = 0; i < values.length; i++) {
list[i] = values[i].getObject();
}
return list;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
throw Message.getUnsupportedException();
}
public String getSQL() {
StringBuffer buff = new StringBuffer();
buff.append('(');
for (int i = 0; i < values.length; i++) {
if (i > 0) {
buff.append(", ");
}
buff.append(values[i].getSQL());
}
buff.append(')');
return buff.toString();
}
public int getDisplaySize() {
int size = 0;
for(int i=0; i<values.length; i++) {
size += values[i].getDisplaySize();
}
return size;
}
protected boolean isEqual(Value o) {
if(!(o instanceof ValueArray)) {
return false;
}
ValueArray v = (ValueArray) o;
if (values == v.values) {
return true;
}
if (values.length != v.values.length) {
return false;
}
for (int i = 0; i < values.length; i++) {
if(!values[i].isEqual(v.values[i])) {
return false;
}
}
return true;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author Thomas
*/
public class ValueBoolean extends Value {
public static final int PRECISION = 1;
private Boolean value;
private static final ValueBoolean TRUE = new ValueBoolean(true);
private static final ValueBoolean FALSE = new ValueBoolean(false);
private ValueBoolean(boolean value) {
this.value = Boolean.valueOf(""+value);
}
public int getType() {
return Value.BOOLEAN;
}
public String getSQL() {
return getString();
}
public String getString() {
return value.booleanValue() ? "TRUE" : "FALSE";
}
public Value negate() throws SQLException {
return value.booleanValue() ? FALSE : TRUE;
}
public Boolean getBoolean() {
return value;
}
protected int compareSecure(Value o, CompareMode mode) {
boolean v2 = ((ValueBoolean) o).value.booleanValue();
boolean v = value.booleanValue();
return (v == v2) ? 0 : (v ? 1 : -1);
}
public long getPrecision() {
return PRECISION;
}
public int hashCode() {
return value.booleanValue() ? 1 : 0;
}
public Object getObject() {
return value;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setBoolean(parameterIndex, value.booleanValue());
}
public static ValueBoolean get(boolean b) {
return b ? TRUE : FALSE;
}
// public String getJavaString() {
// return value.booleanValue() ? "true" : "false";
// }
public int getDisplaySize() {
return "FALSE".length();
}
protected boolean isEqual(Value v) {
return v instanceof ValueBoolean && value == ((ValueBoolean)v).value;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
public class ValueByte extends Value {
public static final int PRECISION = 3;
private byte value;
private ValueByte(byte value) {
this.value = value;
}
public Value add(Value v) throws SQLException {
ValueByte other = (ValueByte) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(value + other.value);
}
return ValueByte.get((byte) (value + other.value));
}
private ValueByte checkRange(int value) throws SQLException {
if(value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
throw Message.getSQLException(Message.OVERFLOW_FOR_TYPE_1, DataType.getDataType(Value.BYTE).name);
} else {
return ValueByte.get((byte)value);
}
}
public int getSignum() {
return value == 0 ? 0 : (value < 0 ? -1 : 1);
}
public Value negate() throws SQLException {
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(-(int)value);
}
return ValueByte.get((byte) (-value));
}
public Value subtract(Value v) throws SQLException {
ValueByte other = (ValueByte) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(value - other.value);
}
return ValueByte.get((byte) (value - other.value));
}
public Value multiply(Value v) throws SQLException {
ValueByte other = (ValueByte) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(value * other.value);
}
return ValueByte.get((byte) (value * other.value));
}
public Value divide(Value v) throws SQLException {
ValueByte other = (ValueByte) v;
if (other.value == 0) {
throw Message.getSQLException(Message.DIVISION_BY_ZERO_1, getSQL());
}
return ValueByte.get((byte) (value / other.value));
}
public String getSQL() {
return getString();
}
public int getType() {
return Value.BYTE;
}
public byte getByte() {
return value;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueByte v = (ValueByte) o;
if (value == v.value) {
return 0;
}
return value > v.value ? 1 : -1;
}
public String getString() {
return String.valueOf(value);
}
public long getPrecision() {
return PRECISION;
}
public int hashCode() {
return value;
}
public Object getObject() {
return new Byte(value);
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setByte(parameterIndex, value);
}
public static ValueByte get(byte i) {
return (ValueByte) Value.cache(new ValueByte(i));
}
// public String getJavaString() {
// return "(byte)" + toString();
// }
public int getDisplaySize() {
return PRECISION;
}
protected boolean isEqual(Value v) {
return v instanceof ValueByte && value == ((ValueByte)v).value;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import org.h2.engine.Constants;
/**
* @author Thomas
*/
public class ValueBytes extends ValueBytesBase {
private static final ValueBytes EMPTY = new ValueBytes(new byte[0]);
protected ValueBytes(byte[] v) {
super(v);
}
public static ValueBytes get(byte[] b) {
if (b.length == 0) {
return EMPTY;
}
ValueBytes obj = new ValueBytes(b);
if (b.length > Constants.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
}
return (ValueBytes) Value.cache(obj);
}
public int getType() {
return Value.BYTES;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.util.ByteUtils;
abstract class ValueBytesBase extends Value {
private byte[] value;
private int hash;
protected ValueBytesBase(byte[] v) {
this.value = v;
}
public String getSQL() {
return "X'" + getString() + "'";
}
public byte[] getBytes() {
return value;
}
protected int compareSecure(Value v, CompareMode mode) {
byte[] v2 = ((ValueBytesBase) v).value;
return ByteUtils.compareNotNull(value, v2);
}
public String getString() {
return ByteUtils.convertBytesToString(value);
}
public long getPrecision() {
return value.length;
}
public int hashCode() {
if (hash == 0) {
hash = ByteUtils.getByteArrayHash(value);
}
return hash;
}
public Object getObject() {
return getBytes();
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setBytes(parameterIndex, value);
}
public int getDisplaySize() {
return value.length * 2;
}
protected boolean isEqual(Value v) {
return v instanceof ValueBytesBase && ByteUtils.compareNotNull(value, ((ValueBytesBase)v).value) == 0;
}
// public String getJavaString() {
// return "ByteUtils.convertStringToBytes(\"" + toString() + "\")";
//}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Calendar;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class ValueDate extends Value {
public static final int PRECISION = 8;
private Date value;
private ValueDate(Date value) {
// this class is mutable - must copy the object
Calendar cal = Calendar.getInstance();
cal.setTime(value);
// TODO gcj: required so that the millis are calculated?
cal.get(Calendar.YEAR);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.HOUR_OF_DAY, 0);
this.value = new Date(cal.getTime().getTime());
}
public static Date parseDate(String s) throws SQLException {
return (Date) DataType.parseDateTime(s, Value.DATE, Message.DATE_CONSTANT_1);
}
public Date getDate() {
// this class is mutable - must copy the object
return (Date)value.clone();
}
public String getSQL() {
return "DATE '" + getString() + "'";
}
public int getType() {
return Value.DATE;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueDate v = (ValueDate) o;
int c = value.compareTo(v.value);
return c == 0 ? 0 : (c < 0 ? -1 : 1);
}
public String getString() {
return value.toString();
}
public long getPrecision() {
return PRECISION;
}
public int hashCode() {
return value.hashCode();
}
public Object getObject() {
// this class is mutable - must copy the object
return getDate();
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setDate(parameterIndex, value);
}
public static ValueDate get(Date date) {
return (ValueDate) Value.cache(new ValueDate(date));
}
// public String getJavaString() {
// return "Date.valueOf(\"" + toString() + "\")";
// }
public int getDisplaySize() {
return "2001-01-01".length();
}
protected boolean isEqual(Value v) {
return v instanceof ValueDate && value.equals(((ValueDate)v).value);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.util.MathUtils;
/**
* @author Thomas
*/
public class ValueDecimal extends Value {
// TODO doc: document differences for BigDecimal 1.5 <> 1.4
private BigDecimal value;
private String valueString;
private int precision;
private static final BigDecimal DEC_ZERO = new BigDecimal("0");
private static final BigDecimal DEC_ONE = new BigDecimal("1");
private static final ValueDecimal ZERO = new ValueDecimal(DEC_ZERO);
private static final ValueDecimal ONE = new ValueDecimal(DEC_ONE);
public static final int DEFAULT_PRECISION = 65535;
public static final int DEFAULT_SCALE = 32767;
private static final int DIVIDE_SCALE_ADD = 25;
private ValueDecimal(BigDecimal value) {
if (value == null) {
throw new NullPointerException();
}
this.value = value;
}
public Value add(Value v) {
ValueDecimal dec = (ValueDecimal) v;
return ValueDecimal.get(value.add(dec.value));
}
public Value subtract(Value v) {
ValueDecimal dec = (ValueDecimal) v;
return ValueDecimal.get(value.subtract(dec.value));
}
public Value negate() {
return ValueDecimal.get(value.negate());
}
public Value multiply(Value v) {
ValueDecimal dec = (ValueDecimal) v;
return ValueDecimal.get(value.multiply(dec.value));
}
public Value divide(Value v) throws SQLException {
ValueDecimal dec = (ValueDecimal) v;
// TODO value: divide decimal: rounding?
if (dec.value.signum() == 0) {
throw Message.getSQLException(Message.DIVISION_BY_ZERO_1, getSQL());
}
BigDecimal bd = value.divide(dec.value, value.scale()+DIVIDE_SCALE_ADD, BigDecimal.ROUND_HALF_DOWN);
if(bd.signum()==0) {
bd = DEC_ZERO;
} else if(bd.scale()>0) {
if(!bd.unscaledValue().testBit(0)) {
String s = bd.toString();
int i=s.length() - 1;
while(i>=0 && s.charAt(i) == '0') {
i--;
}
if(i < s.length() - 1) {
s = s.substring(0, i+1);
bd = new BigDecimal(s);
}
}
}
return ValueDecimal.get(bd);
}
public String getSQL() {
return getString();
}
public int getType() {
return Value.DECIMAL;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueDecimal v = (ValueDecimal) o;
int c = value.compareTo(v.value);
return c == 0 ? 0 : (c < 0 ? -1 : 1);
}
public int getSignum() {
return value.signum();
}
public BigDecimal getBigDecimal() {
return value;
}
public String getString() {
if(valueString == null) {
valueString = value.toString();
}
return valueString;
}
public long getPrecision() {
if(precision == 0) {
precision = value.unscaledValue().abs().toString().length();
}
return precision;
}
public int getScale() {
return value.scale();
}
public int hashCode() {
return value.hashCode();
}
public Object getObject() {
return value;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setBigDecimal(parameterIndex, value);
}
public Value convertScale(boolean onlyToSmallerScale, int targetScale) throws SQLException {
if (value.scale() == targetScale) {
return this;
}
if (onlyToSmallerScale || targetScale >= DEFAULT_SCALE) {
if (value.scale() < targetScale) {
return this;
}
}
BigDecimal bd = MathUtils.setScale(value, targetScale);
return ValueDecimal.get(bd);
}
public Value convertPrecision(long precision) throws SQLException {
if (getPrecision() <= precision) {
return this;
}
throw Message.getSQLException(Message.VALUE_TOO_LARGE_FOR_PRECISION_1, "" + precision);
}
public static ValueDecimal get(BigDecimal dec) {
if (DEC_ZERO.equals(dec)) {
return ZERO;
} else if (DEC_ONE.equals(dec)) {
return ONE;
}
// TODO value optimization: find a way to find out size of bigdecimal,
// check max cache size
return (ValueDecimal) Value.cache(new ValueDecimal(dec));
}
// public String getJavaString() {
// return toString();
// }
public int getDisplaySize() {
// TODO displaySize: this is probably very slow
return getString().length();
}
protected boolean isEqual(Value v) {
return v instanceof ValueDecimal && value.equals(((ValueDecimal)v).value);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class ValueDouble extends Value {
public static final int PRECISION = 17;
private double value;
private static final double DOUBLE_ZERO = 0.0;
private static final double DOUBLE_ONE = 1.0;
private static final ValueDouble ZERO = new ValueDouble(DOUBLE_ZERO);
private static final ValueDouble ONE = new ValueDouble(DOUBLE_ONE);
private ValueDouble(double value) {
this.value = value;
}
public Value add(Value v) {
ValueDouble v2 = (ValueDouble) v;
return ValueDouble.get(value + v2.value);
}
public Value subtract(Value v) {
ValueDouble v2 = (ValueDouble) v;
return ValueDouble.get(value - v2.value);
}
public Value negate() {
return ValueDouble.get(-value);
}
public Value multiply(Value v) {
ValueDouble v2 = (ValueDouble) v;
return ValueDouble.get(value * v2.value);
}
public Value divide(Value v) throws SQLException {
ValueDouble v2 = (ValueDouble) v;
if (v2.value == 0.0) {
throw Message.getSQLException(Message.DIVISION_BY_ZERO_1, getSQL());
}
return ValueDouble.get(value / v2.value);
}
public String getSQL() {
return getString();
}
public int getType() {
return Value.DOUBLE;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueDouble v = (ValueDouble) o;
if (value == v.value) {
return 0;
}
return value > v.value ? 1 : -1;
}
public int getSignum() {
return value == 0 ? 0 : (value < 0 ? -1 : 1);
}
public double getDouble() {
return value;
}
public String getString() {
return String.valueOf(value);
}
public long getPrecision() {
return PRECISION;
}
public int getScale() {
// TODO value: what is the scale of a double?
return 0;
}
public int hashCode() {
long hash = Double.doubleToLongBits(value);
return (int) (hash ^ (hash >> 32));
}
public Object getObject() {
return new Double(value);
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setDouble(parameterIndex, value);
}
public static ValueDouble get(double d) {
if (DOUBLE_ZERO == d) {
return ZERO;
} else if (DOUBLE_ONE == d) {
return ONE;
}
return (ValueDouble) Value.cache(new ValueDouble(d));
}
// public String getJavaString() {
// return getString();
// }
public int getDisplaySize() {
return PRECISION + 2;
}
protected boolean isEqual(Value v) {
return v instanceof ValueDouble && value == ((ValueDouble)v).value;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class ValueFloat extends Value {
public static final int PRECISION = 7;
private float value;
private static final float FLOAT_ZERO = 0.0F;
private static final float FLOAT_ONE = 1.0F;
private static final ValueFloat ZERO = new ValueFloat(FLOAT_ZERO);
private static final ValueFloat ONE = new ValueFloat(FLOAT_ONE);
private ValueFloat(float value) {
this.value = value;
}
public Value add(Value v) {
ValueFloat v2 = (ValueFloat) v;
return ValueFloat.get(value + v2.value);
}
public Value subtract(Value v) {
ValueFloat v2 = (ValueFloat) v;
return ValueFloat.get(value - v2.value);
}
public Value negate() {
return ValueFloat.get(-value);
}
public Value multiply(Value v) {
ValueFloat v2 = (ValueFloat) v;
return ValueFloat.get(value * v2.value);
}
public Value divide(Value v) throws SQLException {
ValueFloat v2 = (ValueFloat) v;
if (v2.value == 0.0) {
throw Message.getSQLException(Message.DIVISION_BY_ZERO_1, getSQL());
}
return ValueFloat.get(value / v2.value);
}
public String getSQL() {
return getString();
}
public int getType() {
return Value.FLOAT;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueFloat v = (ValueFloat) o;
if (value == v.value) {
return 0;
}
return value > v.value ? 1 : -1;
}
public int getSignum() {
return value == 0 ? 0 : (value < 0 ? -1 : 1);
}
public float getFloat() {
return value;
}
public String getString() {
return String.valueOf(value);
}
public long getPrecision() {
return PRECISION;
}
public int getScale() {
// TODO value: what is the scale of a float?
return 0;
}
public int hashCode() {
long hash = Float.floatToIntBits(value);
return (int) (hash ^ (hash >> 32));
}
public Object getObject() {
return new Float(value);
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setFloat(parameterIndex, value);
}
public static ValueFloat get(float d) {
if (FLOAT_ZERO == d) {
return ZERO;
} else if (FLOAT_ONE == d) {
return ONE;
}
return (ValueFloat) Value.cache(new ValueFloat(d));
}
// public String getJavaString() {
// return getString();
// }
public int getDisplaySize() {
return PRECISION + 2;
}
protected boolean isEqual(Value v) {
return v instanceof ValueFloat && value == ((ValueFloat)v).value;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class ValueInt extends Value {
public static final int PRECISION = 10;
private int value;
private static final int STATIC_SIZE = 100;
private static final int DYNAMIC_SIZE = 256; // must be a power of 2
// TODO check performance of final static!
private static ValueInt[] staticCache;
private static ValueInt[] dynamicCache;
static {
staticCache = new ValueInt[STATIC_SIZE];
dynamicCache = new ValueInt[DYNAMIC_SIZE];
for (int i = 0; i < STATIC_SIZE; i++) {
staticCache[i] = new ValueInt(i);
}
}
public static ValueInt get(int i) {
if (i >= 0 && i < STATIC_SIZE) {
return staticCache[i];
}
ValueInt v = dynamicCache[i & DYNAMIC_SIZE - 1];
if (v == null || v.value != i) {
v = new ValueInt(i);
dynamicCache[i & DYNAMIC_SIZE - 1] = v;
}
return v;
}
private ValueInt(int value) {
this.value = value;
}
public Value add(Value v) throws SQLException {
ValueInt other = (ValueInt) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange((long)value + (long)other.value);
}
return ValueInt.get(value + other.value);
}
private ValueInt checkRange(long value) throws SQLException {
if(value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
throw Message.getSQLException(Message.OVERFLOW_FOR_TYPE_1, DataType.getDataType(Value.INT).name);
} else {
return ValueInt.get((int)value);
}
}
public int getSignum() {
return value == 0 ? 0 : (value < 0 ? -1 : 1);
}
public Value negate() throws SQLException {
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(-(long)value);
}
return ValueInt.get(-value);
}
public Value subtract(Value v) throws SQLException {
ValueInt other = (ValueInt) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange((long)value - (long)other.value);
}
return ValueInt.get(value - other.value);
}
public Value multiply(Value v) throws SQLException {
ValueInt other = (ValueInt) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange((long)value * (long)other.value);
}
return ValueInt.get(value * other.value);
}
public Value divide(Value v) throws SQLException {
ValueInt other = (ValueInt) v;
if (other.value == 0) {
throw Message.getSQLException(Message.DIVISION_BY_ZERO_1, getSQL());
}
return ValueInt.get(value / other.value);
}
public String getSQL() {
return getString();
}
public int getType() {
return Value.INT;
}
public int getInt() {
return value;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueInt v = (ValueInt) o;
if (value == v.value) {
return 0;
}
return value > v.value ? 1 : -1;
}
public String getString() {
return String.valueOf(value);
}
public long getPrecision() {
return PRECISION;
}
public int hashCode() {
return value;
}
public Object getObject() {
return new Integer(value);
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setInt(parameterIndex, value);
}
// public String getJavaString() {
// return getString();
// }
public int getDisplaySize() {
return PRECISION;
}
protected boolean isEqual(Value v) {
return v instanceof ValueInt && value == ((ValueInt)v).value;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import org.h2.engine.Constants;
public class ValueJavaObject extends ValueBytesBase {
private static final ValueJavaObject EMPTY = new ValueJavaObject(new byte[0]);
protected ValueJavaObject(byte[] v) {
super(v);
}
public static ValueJavaObject get(byte[] b) {
if (b.length == 0) {
return EMPTY;
}
ValueJavaObject obj = new ValueJavaObject(b);
if (b.length > Constants.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
}
return (ValueJavaObject) Value.cache(obj);
}
public int getType() {
return Value.JAVA_OBJECT;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream;
import org.h2.store.FileStoreOutputStream;
import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.RandomUtils;
import org.h2.util.StringUtils;
import org.h2.util.TypeConverter;
/**
* @author Thomas
*/
public class ValueLob extends Value {
// TODO lob: concatenate function for blob and clob (to create a large blob from pieces)
// and a getpart function (to get it in pieces) and make sure a file is created!
private int type;
private long precision;
private DataHandler handler;
private int tableId;
private int objectId;
private String fileName;
private boolean linked;
private byte[] small;
private int hash;
private boolean compression;
private FileStore tempFile;
private ValueLob(int type, DataHandler handler, String fileName, int tableId, int objectId, boolean linked, long precision, boolean compression) {
this.type = type;
this.handler = handler;
this.fileName = fileName;
this.tableId = tableId;
this.objectId = objectId;
this.linked = linked;
this.precision = precision;
this.compression = compression;
}
private static ValueLob copy(ValueLob lob) {
ValueLob copy = new ValueLob(lob.type, lob.handler, lob.fileName, lob.tableId, lob.objectId, lob.linked, lob.precision, lob.compression);
copy.small = lob.small;
copy.hash = lob.hash;
return copy;
}
private ValueLob(int type, byte[] small) throws SQLException {
this.type = type;
this.small = small;
if(small != null) {
if(type == Value.BLOB) {
this.precision = small.length;
} else {
this.precision = getString().length();
}
}
}
public static ValueLob createSmallLob(int type, byte[] small) throws SQLException {
return new ValueLob(type, small);
}
private static String getFileName(DataHandler handler, int tableId, int objectId) {
if (Constants.CHECK && tableId == 0 && objectId == 0) {
throw Message.getInternalError("0 LOB");
}
if(Constants.LOB_FILES_IN_DIRECTORIES) {
String table = tableId < 0 ? ".temp" : ".t" + tableId;
return getFileNamePrefix(handler.getDatabasePath(), objectId) + table + Constants.SUFFIX_LOB_FILE;
} else {
return handler.getDatabasePath() + "." + tableId + "." + objectId + Constants.SUFFIX_LOB_FILE;
}
}
public static ValueLob open(int type, DataHandler handler, int tableId, int objectId, long precision, boolean compression) {
String fileName = getFileName(handler, tableId, objectId);
return new ValueLob(type, handler, fileName, tableId, objectId, true, precision, compression);
}
// public static ValueLob createClobFromReader(Reader in, long length) throws SQLException {
// try {
// String s = IOUtils.readStringAndClose(in, (int)length);
// byte[] buff = StringUtils.utf8Encode(s);
// return new ValueLob(CLOB, buff);
// } catch (IOException e) {
// throw Message.convert(e);
// }
// }
// public static ValueLob createBlobFromInputStream(InputStream in, long length) throws SQLException {
// try {
// byte[] buff = IOUtils.readBytesAndClose(in, (int)length);
// return new ValueLob(BLOB, buff);
// } catch (IOException e) {
// throw Message.convert(e);
// }
// }
public static ValueLob createClob(Reader in, long length, DataHandler handler) throws SQLException {
try {
boolean compress = handler.getLobCompressionAlgorithm(Value.CLOB) != null;
long remaining = Long.MAX_VALUE;
if (length >= 0 && length < remaining) {
remaining = length;
}
int len = getBufferSize(handler, compress, remaining);
char[] buff = new char[len];
len = IOUtils.readFully(in, buff, len);
len = len < 0 ? 0 : len;
if (len <= handler.getMaxLengthInplaceLob()) {
byte[] small = StringUtils.utf8Encode(new String(buff, 0, len));
return ValueLob.createSmallLob(Value.CLOB, small);
}
ValueLob lob = new ValueLob(Value.CLOB, null);
lob.createFromReader(buff, len, in, remaining, handler);
return lob;
} catch (IOException e) {
throw Message.convert(e);
}
}
private static int getBufferSize(DataHandler handler, boolean compress, long remaining) {
int bufferSize = compress ? Constants.IO_BUFFER_SIZE_COMPRESS : Constants.IO_BUFFER_SIZE;
while(bufferSize < remaining && bufferSize <= handler.getMaxLengthInplaceLob()) {
// the buffer size must be bigger than the inplace lob, otherwise we can't
// know if it must be stored in-place or not
bufferSize += Constants.IO_BUFFER_SIZE;
}
bufferSize = (int) Math.min(remaining, bufferSize);
return bufferSize;
}
private void createFromReader(char[] buff, int len, Reader in, long remaining, DataHandler handler) throws SQLException {
try {
FileStoreOutputStream out = initLarge(handler);
boolean compress = handler.getLobCompressionAlgorithm(Value.CLOB) != null;
try {
while (true) {
precision += len;
byte[] b = StringUtils.utf8Encode(new String(buff, 0, len));
out.write(b, 0, b.length);
remaining -= len;
if (remaining <= 0) {
break;
}
len = getBufferSize(handler, compress, remaining);
len = IOUtils.readFully(in, buff, len);
if (len <= 0) {
break;
}
}
} finally {
out.close();
}
} catch (IOException e) {
throw Message.convert(e);
}
}
private static String getFileNamePrefix(String path, int objectId) {
String name;
int f = objectId % Constants.LOB_FILES_PER_DIRECTORY;
if(f > 0) {
name = File.separator + objectId;
} else {
name = "";
}
objectId /= Constants.LOB_FILES_PER_DIRECTORY;
while(objectId > 0) {
f = objectId % Constants.LOB_FILES_PER_DIRECTORY;
name = File.separator + f + Constants.SUFFIX_LOBS_DIRECTORY + name;
objectId /= Constants.LOB_FILES_PER_DIRECTORY;
}
name = path + Constants.SUFFIX_LOBS_DIRECTORY + name;
return name;
}
private static int getNewObjectId(String path) throws SQLException {
int objectId;
objectId = 0;
while(true) {
String dir = getFileNamePrefix(path, objectId);
String[] list = FileUtils.listFiles(dir);
int fileCount = 0;
boolean[] used = new boolean[Constants.LOB_FILES_PER_DIRECTORY];
for(int i=0; i<list.length; i++) {
String name = list[i];
if(name.endsWith(".db")) {
name = name.substring(name.lastIndexOf(File.separatorChar) + 1);
String n = name.substring(0, name.indexOf('.'));
int id;
try {
id = Integer.parseInt(n);
} catch(NumberFormatException e) {
id = -1;
}
if(id > 0) {
fileCount++;
used[id % Constants.LOB_FILES_PER_DIRECTORY] = true;
}
}
}
int fileId = -1;
if(fileCount < Constants.LOB_FILES_PER_DIRECTORY) {
for(int i=1; i<Constants.LOB_FILES_PER_DIRECTORY; i++) {
if(!used[i]) {
fileId = i;
break;
}
}
}
if(fileId > 0) {
objectId += fileId;
break;
} else {
if(objectId > Integer.MAX_VALUE / Constants.LOB_FILES_PER_DIRECTORY) {
// this directory path is full: start from zero
// (this can happen only theoretically, for example if the random number generator is broken)
objectId = 0;
} else {
// start with 1 (otherwise we don't know the number of directories)
int dirId = RandomUtils.nextInt(Constants.LOB_FILES_PER_DIRECTORY - 1) + 1;
objectId = objectId * Constants.LOB_FILES_PER_DIRECTORY;
objectId += dirId * Constants.LOB_FILES_PER_DIRECTORY;
}
}
}
return objectId;
}
public static ValueLob createBlob(InputStream in, long length, DataHandler handler) throws SQLException {
try {
long remaining = Long.MAX_VALUE;
boolean compress = handler.getLobCompressionAlgorithm(Value.BLOB) != null;
if (length >= 0 && length < remaining) {
remaining = length;
}
int len = getBufferSize(handler, compress, remaining);
byte[] buff = new byte[len];
len = IOUtils.readFully(in, buff, len);
len = len < 0 ? 0 : len;
if (len <= handler.getMaxLengthInplaceLob()) {
byte[] small = new byte[len];
System.arraycopy(buff, 0, small, 0, len);
return ValueLob.createSmallLob(Value.BLOB, small);
}
ValueLob lob = new ValueLob(Value.BLOB, null);
lob.createFromStream(buff, len, in, remaining, handler);
return lob;
} catch (IOException e) {
throw Message.convert(e);
}
}
private FileStoreOutputStream initLarge(DataHandler handler) throws IOException, SQLException {
this.handler = handler;
this.tableId = 0;
this.linked = false;
this.precision = 0;
this.small = null;
this.hash = 0;
String compressionAlgorithm = handler.getLobCompressionAlgorithm(type);
this.compression = compressionAlgorithm != null;
synchronized(handler) {
if(Constants.LOB_FILES_IN_DIRECTORIES) {
objectId = getNewObjectId(handler.getDatabasePath());
fileName = getFileNamePrefix(handler.getDatabasePath(), objectId) + ".temp.db";
} else {
objectId = handler.allocateObjectId(false, true);
fileName = handler.createTempFile();
}
tempFile = handler.openFile(fileName, false);
tempFile.autoDelete();
}
FileStoreOutputStream out = new FileStoreOutputStream(tempFile, handler, compressionAlgorithm);
return out;
}
private void createFromStream(byte[] buff, int len, InputStream in, long remaining, DataHandler handler) throws SQLException {
try {
FileStoreOutputStream out = initLarge(handler);
boolean compress = handler.getLobCompressionAlgorithm(Value.BLOB) != null;
try {
while (true) {
precision += len;
out.write(buff, 0, len);
remaining -= len;
if (remaining <= 0) {
break;
}
len = getBufferSize(handler, compress, remaining);
len = IOUtils.readFully(in, buff, len);
if (len <= 0) {
break;
}
}
} finally {
out.close();
}
} catch (IOException e) {
throw Message.convert(e);
}
}
public Value convertTo(int t) throws SQLException {
if (t == type) {
return this;
} else if (t == Value.CLOB) {
ValueLob copy = ValueLob.createClob(getReader(), -1, handler);
return copy;
} else if(t == Value.BLOB) {
ValueLob copy = ValueLob.createBlob(getInputStream(), -1, handler);
return copy;
}
return super.convertTo(t);
}
public void unlink(DataHandler handler) throws SQLException {
if (linked && fileName != null) {
String temp;
if(Constants.LOB_FILES_IN_DIRECTORIES) {
temp = getFileName(handler, -1, objectId);
} else {
// just to get a filename - an empty file will be created
temp = handler.createTempFile();
}
// delete the temp file
// TODO could there be a race condition? maybe another thread creates the file again?
FileUtils.delete(temp);
// rename the current file to the temp file
FileUtils.rename(fileName, temp);
// FileUtils.deleteOnExit(temp);
tempFile = FileStore.open(handler, temp, null);
tempFile.autoDelete();
tempFile.closeSilently();
fileName = temp;
linked = false;
}
}
public Value link(DataHandler handler, int tabId) throws SQLException {
if(fileName == null) {
this.tableId = tabId;
return this;
}
if(linked) {
ValueLob copy = ValueLob.copy(this);
if(Constants.LOB_FILES_IN_DIRECTORIES) {
copy.objectId = getNewObjectId(handler.getDatabasePath());
} else {
copy.objectId = handler.allocateObjectId(false, true);
}
copy.tableId = tabId;
String live = getFileName(handler, copy.tableId, copy.objectId);
FileUtils.copy(fileName, live);
copy.fileName = live;
copy.linked = true;
return copy;
}
if (!linked) {
this.tableId = tabId;
String live = getFileName(handler, tableId, objectId);
tempFile.stopAutoDelete();
tempFile = null;
FileUtils.rename(fileName, live);
fileName = live;
linked = true;
}
return this;
}
public int getTableId() {
return tableId;
}
public int getObjectId() {
return objectId;
}
public int getType() {
return type;
}
public long getPrecision() {
return precision;
}
public String getString() throws SQLException {
int len = precision > Integer.MAX_VALUE || precision == 0 ? Integer.MAX_VALUE : (int)precision;
try {
if (type == Value.CLOB) {
if (small != null) {
return StringUtils.utf8Decode(small);
}
return IOUtils.readStringAndClose(getReader(), len);
} else {
byte[] buff;
if (small != null) {
buff = small;
} else {
buff = IOUtils.readBytesAndClose(getInputStream(), len);
}
return ByteUtils.convertBytesToString(buff);
}
} catch (IOException e) {
throw Message.convert(e);
}
}
public byte[] getBytes() throws SQLException {
if (small != null) {
return small;
}
try {
return IOUtils.readBytesAndClose(getInputStream(), Integer.MAX_VALUE);
} catch (IOException e) {
throw Message.convert(e);
}
}
public int hashCode() {
if (hash == 0) {
try {
hash = ByteUtils.getByteArrayHash(getBytes());
} catch(SQLException e) {
// TODO hash code for lob: should not ignore exception
}
}
return hash;
}
protected int compareSecure(Value v, CompareMode mode) throws SQLException {
if(type == Value.CLOB) {
int c = getString().compareTo(v.getString());
return c == 0 ? 0 : (c < 0 ? -1 : 1);
} else {
byte[] v2 = v.getBytes();
return ByteUtils.compareNotNull(getBytes(), v2);
}
}
public Object getObject() throws SQLException {
if(type == Value.CLOB) {
return getReader();
} else {
return getInputStream();
}
}
public Reader getReader() throws SQLException {
return TypeConverter.getReader(getInputStream());
}
public InputStream getInputStream() throws SQLException {
if (fileName == null) {
return new ByteArrayInputStream(small);
}
FileStore store = handler.openFile(fileName, true);
return new BufferedInputStream(new FileStoreInputStream(store, handler, compression), Constants.IO_BUFFER_SIZE);
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
long p = getPrecision();
// TODO test if setBinaryStream with -1 works for other databases a well
if(p > Integer.MAX_VALUE || p <= 0) {
p = -1;
}
if(type == Value.BLOB) {
prep.setBinaryStream(parameterIndex, getInputStream(), (int)p);
} else {
prep.setCharacterStream(parameterIndex, getReader(), (int)p);
}
}
public String getSQL() {
try {
String s;
if(type == Value.CLOB) {
s = getString();
return StringUtils.quoteStringSQL(s);
} else {
byte[] buff = getBytes();
s = ByteUtils.convertBytesToString(buff);
return "X'" + s + "'";
}
} catch(SQLException e) {
throw Message.convertToInternal(e);
}
}
public byte[] getSmall() {
return small;
}
// public String getJavaString() {
// // TODO value: maybe use another trick (at least the size should be ok?)
// return StringUtils.quoteJavaString(getSQL());
// }
public int getDisplaySize() {
// TODO displaysize of lob?
return 40;
}
protected boolean isEqual(Value v) {
try {
return compareSecure(v, null) == 0;
} catch(SQLException e) {
// TODO exceptions: improve concept, maybe remove throws SQLException almost everywhere
throw Message.getInternalError("compare", e);
}
}
public void convertToFileIfRequired(DataHandler handler) throws SQLException {
if(Constants.AUTO_CONVERT_LOB_TO_FILES && small != null && small.length > handler.getMaxLengthInplaceLob()) {
boolean compress = handler.getLobCompressionAlgorithm(type) != null;
int len = getBufferSize(handler, compress, Long.MAX_VALUE);
int tabId = tableId;
if(type == Value.BLOB) {
createFromStream(new byte[len], 0, getInputStream(), Long.MAX_VALUE, handler);
} else {
createFromReader(new char[len], 0, getReader(), Long.MAX_VALUE, handler);
}
Value v2 = link(handler, tabId);
if(Constants.CHECK && v2 != this) {
throw Message.getInternalError();
}
}
}
public static void removeAllForTable(DataHandler handler, int tableId) throws SQLException {
if(Constants.LOB_FILES_IN_DIRECTORIES) {
String dir = getFileNamePrefix(handler.getDatabasePath(), 0);
removeAllForTable(handler, dir, tableId);
} else {
String prefix = handler.getDatabasePath();
String dir = FileUtils.getParent(prefix);
String[] list = FileUtils.listFiles(dir);
for(int i=0; i<list.length; i++) {
String name = list[i];
if(name.startsWith(prefix+"." + tableId) && name.endsWith(".lob.db")) {
FileUtils.delete(name);
}
}
}
}
private static void removeAllForTable(DataHandler handler, String dir, int tableId) throws SQLException {
String[] list = FileUtils.listFiles(dir);
for(int i=0; i<list.length; i++) {
if(FileUtils.isDirectory(list[i])) {
removeAllForTable(handler, list[i], tableId);
} else {
String name = list[i];
if(name.endsWith(".t" + tableId + ".lob.db")) {
FileUtils.delete(name);
}
}
}
}
public boolean useCompression() {
return compression;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.math.BigInteger;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
public class ValueLong extends Value {
private long value;
public static final int PRECISION = 19;
private static final int STATIC_SIZE = 10;
private static ValueLong[] cache;
private static final BigInteger MIN = new BigInteger("" + Long.MIN_VALUE);
private static final BigInteger MAX = new BigInteger("" + Long.MAX_VALUE);
static {
cache = new ValueLong[STATIC_SIZE];
for (int i = 0; i < STATIC_SIZE; i++) {
cache[i] = new ValueLong(i);
}
}
private ValueLong(long value) {
this.value = value;
}
public Value add(Value v) throws SQLException {
ValueLong other = (ValueLong) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
long result = value + other.value;
int sv = value == 0 ? 0 : (value < 0 ? -1 : 1);
int so = other.value == 0 ? 0 : (other.value < 0 ? -1 : 1);
int sr = result == 0 ? 0 : (result < 0 ? -1 : 1);
// if the operands have different signs overflow can not occur
// if the operands have the same sign, and the result has a different sign, then it is an overflow
// it can not be an overflow when one of the operands is 0
if(sv!=so || sr==so || sv==0 || so==0) {
return ValueLong.get(result);
}
throw getOverflow();
}
return ValueLong.get(value + other.value);
}
public int getSignum() {
return value == 0 ? 0 : (value < 0 ? -1 : 1);
}
public Value negate() throws SQLException {
if(Constants.OVERFLOW_EXCEPTIONS) {
if(value == Long.MIN_VALUE) {
throw getOverflow();
}
}
return ValueLong.get(-value);
}
private SQLException getOverflow() {
return Message.getSQLException(Message.OVERFLOW_FOR_TYPE_1, DataType.getDataType(Value.LONG).name);
}
public Value subtract(Value v) throws SQLException {
ValueLong other = (ValueLong) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
int sv = value == 0 ? 0 : (value < 0 ? -1 : 1);
int so = other.value == 0 ? 0 : (other.value < 0 ? -1 : 1);
// if the operands have the same sign, then overflow can not occur
// if the second operand is 0, then overflow can not occur
if(sv==so || so==0) {
return ValueLong.get(value - other.value);
}
// now, if the other value is Long.MIN_VALUE, it must be an overflow
// x - Long.MIN_VALUE overflows for x>=0
return add(other.negate());
}
return ValueLong.get(value - other.value);
}
private boolean isInteger(long a) {
return a >= Integer.MIN_VALUE && a <= Integer.MAX_VALUE;
}
public Value multiply(Value v) throws SQLException {
ValueLong other = (ValueLong) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
long result = value * other.value;
if(value == 0 || value == 1 || other.value == 0 || other.value == 1) {
return ValueLong.get(result);
}
if(isInteger(value) && isInteger(other.value)) {
return ValueLong.get(result);
}
// just checking one case is not enough: Long.MIN_VALUE * -1
// probably this is correct but I'm not sure
// if(result / value == other.value && result / other.value == value) {
// return ValueLong.get(result);
//}
BigInteger bv = new BigInteger("" + value);
BigInteger bo = new BigInteger("" + other.value);
BigInteger br = bv.multiply(bo);
if(br.compareTo(MIN) < 0 || br.compareTo(MAX) > 0) {
throw getOverflow();
}
return ValueLong.get(br.longValue());
}
return ValueLong.get(value * other.value);
}
public Value divide(Value v) throws SQLException {
ValueLong other = (ValueLong) v;
if (other.value == 0) {
throw Message.getSQLException(Message.DIVISION_BY_ZERO_1, getSQL());
}
return ValueLong.get(value / other.value);
}
public String getSQL() {
return getString();
}
public int getType() {
return Value.LONG;
}
public long getLong() {
return value;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueLong v = (ValueLong) o;
if (value == v.value) {
return 0;
}
return value > v.value ? 1 : -1;
}
public String getString() {
return String.valueOf(value);
}
public long getPrecision() {
return PRECISION;
}
public int hashCode() {
return (int) (value ^ (value >> 32));
}
public Object getObject() {
return new Long(value);
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setLong(parameterIndex, value);
}
public static ValueLong get(long i) {
if (i >= 0 && i < STATIC_SIZE) {
return cache[(int) i];
}
return (ValueLong) Value.cache(new ValueLong(i));
}
// public String getJavaString() {
// return getString();
// }
public int getDisplaySize() {
return PRECISION;
}
protected boolean isEqual(Value v) {
return v instanceof ValueLong && value == ((ValueLong)v).value;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class ValueNull extends Value {
public static final ValueNull INSTANCE = new ValueNull();
public static final ValueNull DELETED = new ValueNull();
public static final int PRECISION = 1;
private ValueNull() {
}
public String getSQL() {
return "NULL";
}
public int getType() {
return Value.NULL;
}
public String getString() {
return null;
}
public Boolean getBoolean() throws SQLException {
return null;
}
public Date getDate() throws SQLException {
return null;
}
public Time getTime() throws SQLException {
return null;
}
public Timestamp getTimestamp() throws SQLException {
return null;
}
public byte[] getBytes() throws SQLException {
return null;
}
public byte getByte() throws SQLException {
return 0;
}
public short getShort() throws SQLException {
return 0;
}
public BigDecimal getBigDecimal() throws SQLException {
return null;
}
public double getDouble() throws SQLException {
return 0.0;
}
public float getFloat() throws SQLException {
return 0.0F;
}
public int getInt() throws SQLException {
return 0;
}
public long getLong() throws SQLException {
return 0;
}
public InputStream getInputStream() throws SQLException {
return null;
}
public Reader getReader() throws SQLException {
return null;
}
public Value convertTo(int type) throws SQLException {
return this;
}
protected int compareSecure(Value v, CompareMode mode) {
throw Message.getInternalError("compare null");
}
public long getPrecision() {
return PRECISION;
}
public int hashCode() {
return 0;
}
public Object getObject() {
return null;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setNull(parameterIndex, DataType.convertTypeToSQLType(Value.NULL));
}
// public String getJavaString() {
// return "null";
// }
public int getDisplaySize() {
return "null".length();
}
protected boolean isEqual(Value v) {
return v == ValueNull.INSTANCE;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.tools.SimpleResultSet;
public class ValueResultSet extends Value {
private ResultSet result;
public static ValueResultSet get(ResultSet rs) throws SQLException {
ValueResultSet val = new ValueResultSet();
val.result = rs;
return val;
}
public static ValueResultSet getCopy(ResultSet rs, int maxrows) throws SQLException {
ValueResultSet val = new ValueResultSet();
ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
SimpleResultSet simple = new SimpleResultSet();
val.result = simple;
for(int i=0; i<columnCount; i++) {
String name = meta.getColumnLabel(i+1);
int sqlType = meta.getColumnType(i+1);
int precision = meta.getPrecision(i+1);
int scale = meta.getScale(i+1);
simple.addColumn(name, sqlType, precision, scale);
}
for(int i=0; i<maxrows && rs.next(); i++) {
Object[] list = new Object[columnCount];
for(int j=0; j<columnCount; j++) {
list[j] = rs.getObject(j+1);
}
simple.addRow(list);
}
return val;
}
public int getType() {
return Value.RESULT_SET;
}
public long getPrecision() {
return 0;
}
public int getDisplaySize() {
// it doesn't make sense to calculate this
return 100;
}
public String getString() throws SQLException {
StringBuffer buff = new StringBuffer();
buff.append("(");
result.beforeFirst();
ResultSetMetaData meta = result.getMetaData();
int columnCount = meta.getColumnCount();
for(int i=0; result.next(); i++) {
if(i>0) {
buff.append(", ");
}
buff.append('(');
for(int j=0; j<columnCount; j++) {
if (j > 0) {
buff.append(", ");
}
int t = DataType.convertSQLTypeToValueType(meta.getColumnType(j + 1));
Value v = DataType.readValue(null, result, j+1, t);
buff.append(v.getString());
}
buff.append(')');
}
buff.append(")");
result.beforeFirst();
return buff.toString();
}
protected int compareSecure(Value v, CompareMode mode) throws SQLException {
throw Message.getUnsupportedException();
}
protected boolean isEqual(Value v) {
return false;
}
public Object getObject() throws SQLException {
return result;
}
public ResultSet getResultSet() {
return result;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
throw Message.getUnsupportedException();
}
public String getSQL() {
return "";
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
public class ValueShort extends Value {
public static final int PRECISION = 5;
private short value;
private ValueShort(short value) {
this.value = value;
}
public Value add(Value v) throws SQLException {
ValueShort other = (ValueShort) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(value + other.value);
}
return ValueShort.get((short) (value + other.value));
}
private ValueShort checkRange(int value) throws SQLException {
if(value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
throw Message.getSQLException(Message.OVERFLOW_FOR_TYPE_1, DataType.getDataType(Value.SHORT).name);
} else {
return ValueShort.get((short)value);
}
}
public int getSignum() {
return value == 0 ? 0 : (value < 0 ? -1 : 1);
}
public Value negate() throws SQLException {
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(-(int)value);
}
return ValueShort.get((short) (-value));
}
public Value subtract(Value v) throws SQLException {
ValueShort other = (ValueShort) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(value - other.value);
}
return ValueShort.get((short) (value - other.value));
}
public Value multiply(Value v) throws SQLException {
ValueShort other = (ValueShort) v;
if(Constants.OVERFLOW_EXCEPTIONS) {
return checkRange(value * other.value);
}
return ValueShort.get((short) (value * other.value));
}
public Value divide(Value v) throws SQLException {
ValueShort other = (ValueShort) v;
if (other.value == 0) {
throw Message.getSQLException(Message.DIVISION_BY_ZERO_1, getSQL());
}
return ValueShort.get((short) (value / other.value));
}
public String getSQL() {
return getString();
}
public int getType() {
return Value.SHORT;
}
public short getShort() {
return value;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueShort v = (ValueShort) o;
if (value == v.value) {
return 0;
}
return value > v.value ? 1 : -1;
}
public String getString() {
return String.valueOf(value);
}
public long getPrecision() {
return PRECISION;
}
public int hashCode() {
return value;
}
public Object getObject() {
return new Short(value);
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setShort(parameterIndex, value);
}
public static ValueShort get(short i) {
return (ValueShort) Value.cache(new ValueShort(i));
}
// public String getJavaString() {
// return getString();
// }
public int getDisplaySize() {
return PRECISION;
}
protected boolean isEqual(Value v) {
return v instanceof ValueShort && value == ((ValueShort)v).value;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import org.h2.engine.Constants;
import org.h2.util.StringCache;
/**
* @author Thomas
*/
public class ValueString extends ValueStringBase {
private static final ValueString EMPTY = new ValueString("");
protected ValueString(String value) {
super(value);
}
public int getType() {
return Value.STRING;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueString v = (ValueString) o;
return mode.compareString(value, v.value, false);
}
public int hashCode() {
// TODO hash performance: could build a quicker hash by hashing the size and a few characters
return value.hashCode();
// proposed code:
// private int hash = 0;
//
// public int hashCode() {
// int h = hash;
// if (h == 0) {
// String s = value;
// int l = s.length();
// if (l > 0) {
// if (l < 16)
// h = s.hashCode();
// else {
// h = l;
// for (int i = 1; i <= l; i <<= 1)
// h = 31 * (31 * h + s.charAt(i - 1)) + s.charAt(l - i);
// }
// hash = h;
// }
// }
// return h;
// }
}
public static ValueString get(String s) {
if (s.length() == 0) {
return EMPTY;
}
ValueString obj = new ValueString(StringCache.get(s));
if (s.length() > Constants.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
}
return (ValueString) Value.cache(obj);
// this saves memory, but is really slow
// return new ValueString(s.intern());
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
abstract class ValueStringBase extends Value {
protected String value;
protected ValueStringBase(String value) {
this.value = value;
}
public String getSQL() {
return StringUtils.quoteStringSQL(value);
}
public String getString() {
return value;
}
public long getPrecision() {
return value.length();
}
public Object getObject() {
return value;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setString(parameterIndex, value);
}
public Value convertPrecision(long precision) {
if (precision == 0 || value.length() <= precision) {
return this;
}
int p = MathUtils.convertLongToInt(precision);
return ValueString.get(value.substring(0, p));
}
// public String getJavaString() {
// return StringUtils.quoteJavaString(value);
// }
public int getDisplaySize() {
return value.length();
}
protected boolean isEqual(Value v) {
return v instanceof ValueString && value.equals(((ValueString)v).value);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import org.h2.engine.Constants;
import org.h2.util.StringCache;
public class ValueStringIgnoreCase extends ValueStringBase {
private static final ValueStringIgnoreCase EMPTY = new ValueStringIgnoreCase("");
private int hash;
protected ValueStringIgnoreCase(String value) {
super(value);
}
public int getType() {
return Value.STRING_IGNORECASE;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueStringIgnoreCase v = (ValueStringIgnoreCase) o;
return mode.compareString(value, v.value, true);
}
protected boolean isEqual(Value v) {
return v instanceof ValueStringBase && value.equalsIgnoreCase(((ValueStringBase)v).value);
}
public int hashCode() {
if (hash == 0) {
// this is locale sensitive
hash = value.toUpperCase().hashCode();
}
return hash;
}
public static ValueStringIgnoreCase get(String s) {
if (s.length() == 0) {
return EMPTY;
}
ValueStringIgnoreCase obj = new ValueStringIgnoreCase(StringCache.get(s));
if (s.length() > Constants.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
}
ValueStringIgnoreCase cache = (ValueStringIgnoreCase) Value.cache(obj);
// the cached object could have the wrong case (it would still be 'equal', but we don't like to store it)
if(cache.value.equals(s)) {
return cache;
} else {
return obj;
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Time;
import java.util.Calendar;
import org.h2.message.Message;
public class ValueTime extends Value {
public static final int PRECISION = 6;
private Time value;
private ValueTime(Time value) {
// this class is mutable - must copy the object
Calendar cal = Calendar.getInstance();
cal.setTime(value);
// TODO gcj: required so that the millis are calculated?
cal.get(Calendar.HOUR_OF_DAY);
cal.set(1970, 0, 1);
this.value = new Time(cal.getTime().getTime());
}
public static Time parseTime(String s) throws SQLException {
return (Time) DataType.parseDateTime(s, Value.TIME, Message.TIME_CONSTANT_1);
}
public Time getTime() {
// this class is mutable - must copy the object
return (Time)value.clone();
}
public String getSQL() {
return "TIME '" + getString() + "'";
}
public int getType() {
return Value.TIME;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueTime v = (ValueTime) o;
int c = value.compareTo(v.value);
return c == 0 ? 0 : (c < 0 ? -1 : 1);
}
public String getString() {
return value.toString();
}
public long getPrecision() {
return PRECISION;
}
public int hashCode() {
return value.hashCode();
}
public Object getObject() {
return getTime();
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setTime(parameterIndex, value);
}
public static ValueTime get(Time date) {
return (ValueTime) Value.cache(new ValueTime(date));
}
// public String getJavaString() {
// return "Time.valueOf(\"" + toString() + "\")";
// }
public int getDisplaySize() {
return "23:59:59".length();
}
protected boolean isEqual(Value v) {
return v instanceof ValueTime && value.equals(((ValueTime)v).value);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import org.h2.message.Message;
import org.h2.util.MathUtils;
public class ValueTimestamp extends Value {
public static final int PRECISION = 23;
public static final int DEFAULT_SCALE = 10;
private Timestamp value;
private ValueTimestamp(Timestamp value) {
// this class is mutable - must copy the object
this.value = (Timestamp)value.clone();
}
public Timestamp getTimestamp() {
// TODO performance: clone not always required
// this class is mutable - must copy the object
return (Timestamp)value.clone();
}
public String getSQL() {
return "TIMESTAMP '" + getString() + "'";
}
public static Timestamp parseTimestamp(String s) throws SQLException {
return (Timestamp) DataType.parseDateTime(s, Value.TIMESTAMP, Message.TIMESTAMP_CONSTANT_1);
}
public int getType() {
return Value.TIMESTAMP;
}
protected int compareSecure(Value o, CompareMode mode) {
ValueTimestamp v = (ValueTimestamp) o;
int c = value.compareTo(v.value);
return c == 0 ? 0 : (c < 0 ? -1 : 1);
}
public String getString() {
return value.toString();
}
public long getPrecision() {
return PRECISION;
}
public int getScale() {
return DEFAULT_SCALE;
}
public int hashCode() {
return value.hashCode();
}
public Object getObject() {
// this class is mutable - must copy the object
return getTimestamp();
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setTimestamp(parameterIndex, value);
}
public static ValueTimestamp get(Timestamp date) {
return (ValueTimestamp) Value.cache(new ValueTimestamp(date));
}
public Value convertScale(boolean onlyToSmallerScale, int targetScale) throws SQLException {
if (targetScale < 0 || targetScale > DEFAULT_SCALE) {
// TODO convertScale for Timestamps: may throw an exception?
throw Message.getInvalidValueException(""+targetScale, "scale");
}
int nanos = value.getNanos();
BigDecimal bd = new BigDecimal("" + nanos);
bd = bd.movePointLeft(9);
bd = MathUtils.setScale(bd, targetScale);
bd = bd.movePointRight(9);
int n2 = bd.intValue();
if (n2 == nanos) {
return this;
}
long t = value.getTime();
while (n2 >= 1000000000) {
t += 1000;
n2 -= 1000000000;
}
Timestamp t2 = new Timestamp(t);
t2.setNanos(n2);
return ValueTimestamp.get(t2);
}
// public String getJavaString() {
// return "Timestamp.valueOf(\"" + toString() + "\")";
// }
public int getDisplaySize() {
return "2001-01-01 23:59:59".length();
}
protected boolean isEqual(Value v) {
return v instanceof ValueTimestamp && value.equals(((ValueTimestamp)v).value);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.util.ByteUtils;
import org.h2.util.RandomUtils;
import org.h2.util.StringUtils;
public class ValueUuid extends Value {
public static final int PRECISION = 36;
private long high, low;
private ValueUuid(long high, long low) {
this.high = high;
this.low = low;
}
public int hashCode() {
return (int)((high >>> 32) ^ high ^ (low >>> 32) ^ low);
}
public static ValueUuid getNewRandom() {
long high = RandomUtils.getSecureLong();
long low = RandomUtils.getSecureLong();
high = (high & (~0xf000L)) | 0x4000L; // version 4 (random)
low = (low & 0x3fffffffffffffffL) | 0x8000000000000000L; // variant (Leach-Salz)
return new ValueUuid(high, low);
}
public static ValueUuid get(byte[] binary) {
if(binary.length < 32) {
return get(ByteUtils.convertBytesToString(binary));
}
long high = ByteUtils.readLong(binary, 0);
long low = ByteUtils.readLong(binary, 16);
return (ValueUuid) Value.cache(new ValueUuid(high, low));
}
public static ValueUuid get(long high, long low) {
return (ValueUuid) Value.cache(new ValueUuid(high, low));
}
public static ValueUuid get(String s) {
long high = 0, low = 0;
int i=0;
for(int j=0; i<s.length() && j<16; i++) {
char ch = s.charAt(i);
if(ch != '-') {
high = (high << 4) | Character.digit(ch, 16);
j++;
}
}
for(int j=0; i<s.length() && j<16; i++) {
char ch = s.charAt(i);
if(ch != '-') {
low = (low << 4) | Character.digit(ch, 16);
j++;
}
}
return (ValueUuid) Value.cache(new ValueUuid(high, low));
}
public String getSQL() {
return StringUtils.quoteStringSQL(getString());
}
public int getType() {
return Value.UUID;
}
public long getPrecision() {
return PRECISION;
}
public int getDisplaySize() {
return 0;
}
private void appendHex(StringBuffer buff, long x, int bytes) {
for (int i = bytes*8-4; i >= 0; i-=8) {
buff.append(Integer.toHexString((int)(x >> i) & 0xf));
buff.append(Integer.toHexString((int)(x >> (i-4)) & 0xf));
}
}
public String getString() {
StringBuffer buff = new StringBuffer(36);
appendHex(buff, high >> 32, 4);
buff.append('-');
appendHex(buff, high >> 16, 2);
buff.append('-');
appendHex(buff, high, 2);
buff.append('-');
appendHex(buff, low >> 48, 2);
buff.append('-');
appendHex(buff, low, 6);
return buff.toString();
}
protected int compareSecure(Value o, CompareMode mode) {
ValueUuid v = (ValueUuid) o;
if (high == v.high) {
return (low == v.low) ? 0 : (low > v.low ? 1 : -1);
} else {
return high > v.high ? 1 : -1;
}
}
protected boolean isEqual(Value v) {
return v instanceof ValueUuid && compareSecure(v, null) == 0;
}
public Object getObject() {
// TODO needs to be documented
return new long[]{high, low};
}
public byte[] getBytes() {
byte[] buff = new byte[16];
for(int i=0; i<8; i++) {
buff[i] = (byte)((high >> (8*(8-i))) & 255);
buff[8+i] = (byte)((low >> (8*(8-i))) & 255);
}
return buff;
}
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setBytes(parameterIndex, getBytes());
}
public long getHigh() {
return high;
}
public long getLow() {
return low;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论