提交 2a22d077 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 10b7282c
/*
* 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.store;
import java.sql.SQLException;
import org.h2.value.Value;
public interface DataHandler {
boolean getTextStorage();
String getDatabasePath();
FileStore openFile(String name, boolean mustExist) throws SQLException;
int getChecksum(byte[] data, int start, int end);
void checkPowerOff() throws SQLException;
void checkWritingAllowed() throws SQLException;
void freeUpDiskSpace() throws SQLException;
void handleInvalidChecksum() throws SQLException;
int compareTypeSave(Value a, Value b) throws SQLException;
int getMaxLengthInplaceLob();
String getLobCompressionAlgorithm(int type);
// only tempoarily, until LOB_FILES_IN_DIRECTORIES is enabled
int allocateObjectId(boolean needFresh, boolean dataFile);
String createTempFile() 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.store;
import java.sql.SQLException;
/**
* @author Thomas
*/
public class DataPageBinary extends DataPage {
// private final static boolean UTF8 = true;
public DataPageBinary(DataHandler handler, byte[] data) {
super(handler, data);
}
public void updateChecksum() {
if(CHECKSUM) {
int x = handler.getChecksum(data, 0, pos-2);
data[pos-2] = (byte)x;
}
}
public void check(int len) throws SQLException {
if(CHECKSUM) {
int x = handler.getChecksum(data, 0, len-2);
if(data[len-2] == (byte)x) {
return;
}
handler.handleInvalidChecksum();
}
}
public int getFillerLength() {
return 2;
}
public void writeByte(byte x) {
data[pos++] = x;
}
public int readByte() {
return data[pos++];
}
public void writeInt(int x) {
byte[] buff = data;
buff[pos++] = (byte) (x >> 24);
buff[pos++] = (byte) (x >> 16);
buff[pos++] = (byte) (x >> 8);
buff[pos++] = (byte) x;
}
public void setInt(int pos, int x) {
byte[] buff = data;
buff[pos] = (byte) (x >> 24);
buff[pos+1] = (byte) (x >> 16);
buff[pos+2] = (byte) (x >> 8);
buff[pos+3] = (byte) x;
}
public int readInt() {
byte[] buff = data;
return (buff[pos++]<< 24) + ((buff[pos++] & 0xff) << 16)
+ ((buff[pos++] & 0xff) << 8) + (buff[pos++] & 0xff);
}
// private static int getStringLenChar(String s) {
// return 4 + s.length() * 2;
// }
// private String readStringChar() {
// int len = ((data[pos++] & 0xff) << 24) + ((data[pos++] & 0xff) << 16) + ((data[pos++] & 0xff) << 8) + (data[pos++] & 0xff);
// char[] chars = new char[len];
// for(int i=0; i<len; i++) {
// chars[i] = (char)(((data[pos++] & 0xff) << 8) + (data[pos++] & 0xff));
// }
// return new String(chars);
// }
// private void writeStringChar(String s) {
// checkCapacity(s.length()*2+4);
// int len = s.length();
// data[pos++] = (byte) ((len >> 24) & 0xff);
// data[pos++] = (byte) ((len >> 16) & 0xff);
// data[pos++] = (byte) ((len >> 8) & 0xff);
// data[pos++] = (byte) (len & 0xff);
// for(int i=0; i<s.length(); i++) {
// int c = s.charAt(i);
// data[pos++] = (byte)(c >> 8);
// data[pos++] = (byte)c;
// }
// }
private static int getStringLenUTF8(String s) {
int plus = 4, len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (c >= 0x800) {
plus += 2;
} else if (c == 0 || c >= 0x80) {
plus++;
}
}
return len + plus;
}
private void writeStringUTF8(String s) {
int len = s.length();
checkCapacity(len*3+4);
int p = pos;
byte[] buff = data;
buff[p++] = (byte) (len >> 24);
buff[p++] = (byte) (len >> 16);
buff[p++] = (byte) (len >> 8);
buff[p++] = (byte) len;
for(int i=0; i<len; i++) {
int c = s.charAt(i);
if(c>0 && c<0x80) {
buff[p++] = (byte)c;
} else if(c>=0x800) {
buff[p++] = (byte)(0xe0 | (c >> 12));
buff[p++] = (byte)(0x80 | ((c >> 6) & 0x3f));
buff[p++] = (byte)(0x80 | (c & 0x3f));
} else {
buff[p++] = (byte)(0xc0 | (c >> 6));
buff[p++] = (byte)(0x80 | (c & 0x3f));
}
}
pos = p;
}
private String readStringUTF8() {
byte[] buff = data;
int p = pos;
int len = ((buff[p++] & 0xff) << 24) + ((buff[p++] & 0xff) << 16) + ((buff[p++] & 0xff) << 8) + (buff[p++] & 0xff);
char[] chars = new char[len];
for(int i=0; i<len; i++) {
int x = buff[p++] & 0xff;
if(x < 0x80) {
chars[i] = (char)x;
} else if(x >= 0xe0) {
chars[i] = (char)(((x & 0xf) << 12) + ((buff[p++] & 0x3f) << 6) + (buff[p++] & 0x3f));
} else {
chars[i] = (char)(((x & 0x1f) << 6) + (buff[p++] & 0x3f));
}
}
pos = p;
return new String(chars);
}
public void writeString(String s) {
// if(UTF8) {
writeStringUTF8(s);
// } else {
// writeStringChar(s);
// }
}
public String readString() {
// if(UTF8) {
return readStringUTF8();
// }
// return readStringChar();
}
public int getIntLen() {
return 4;
}
public int getLongLen(long x) {
return 8;
}
public int getStringLen(String s) {
// if(UTF8) {
return getStringLenUTF8(s);
// }
// return getStringLenChar(s);
}
public void fill(int len) {
if (pos > len) {
pos = len;
}
checkCapacity(len-pos);
pos = 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.store;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
public class DataPageText extends DataPage {
public DataPageText(DataHandler handler, byte[] data) {
super(handler, data);
}
public void setInt(int pos, int x) {
for(int i=7;i>=0;i--, x>>>= 4) {
data[i] = (byte)Character.forDigit(x & 0xf, 16);
}
}
public void updateChecksum() {
if(CHECKSUM) {
int x = handler.getChecksum(data, 0, pos-2);
data[pos-2] = (byte)('a' + (((x^(x>>4)) & 0xf)));
}
}
public void check(int len) throws SQLException {
if(CHECKSUM) {
int x = handler.getChecksum(data, 0, len-2);
if(data[len-2] == (byte)('a' + (((x^(x>>4)) & 0xf)))) {
return;
}
handler.handleInvalidChecksum();
}
}
public int getFillerLength() {
return 2;
}
public void writeInt(int x) {
if(Constants.CHECK) {
checkCapacity(8);
}
for(int i=7;i>=0;i--, x>>>= 4) {
data[pos+i] = (byte)Character.forDigit(x & 0xf, 16);
}
pos+=8;
}
public int readInt() {
int x = 0;
if(data[pos]==' ') {
pos += 8;
return 0;
}
for(int i=8, c;i>0;i--) {
x <<= 4;
x |= (c = data[pos++]) >= 'a' ? (c - 'a'+10) : (c - '0');
}
return x;
}
public int getIntLen() {
return 8;
}
public int getLongLen(long x) {
return 16;
}
public int getStringLen(String s) {
int len = 2 + s.length() + 1;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '\b':
case '\n':
case '\r':
case '\t':
case '\f':
case '"':
case '\\':
len++;
break;
default:
int ch = (c & 0xffff);
if ((ch >= ' ') && (ch <= 0xff)) {
// 0
} else {
len += 5;
}
}
}
return len;
}
public String readString() {
StringBuffer buff = new StringBuffer(32);
if(Constants.CHECK && data[pos] != '"') {
throw Message.getInternalError("\" expected");
}
pos++;
while(true) {
char x = (char)(data[pos++] & 0xff);
if(x == '"') {
break;
} else if(x=='\\') {
x = (char)data[pos++];
switch(x) {
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': {
x = 0;
for(int i=3, c;i>=0;i--) {
x <<= 4;
x |= (c = data[pos++]) >= 'a' ? (c - 'a'+10) : (c - '0');
}
buff.append(x);
break;
}
default:
throw Message.getInternalError("unexpected "+x);
}
} else {
buff.append(x);
}
}
pos++;
return buff.toString();
}
public void writeString(String s) {
checkCapacity(s.length()*6+2);
data[pos++] = '\"';
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '\t':
data[pos++] = '\\'; data[pos++] = 't';
break;
case '\r':
data[pos++] = '\\'; data[pos++] = 'r';
break;
case '\n':
data[pos++] = '\\'; data[pos++] = 'n';
break;
case '\b':
data[pos++] = '\\'; data[pos++] = 'b';
break;
case '\f':
data[pos++] = '\\'; data[pos++] = 'f';
break;
case '"':
data[pos++] = '\\'; data[pos++] = '\"';
break;
case '\\':
data[pos++] = '\\'; data[pos++] = '\\';
break;
default:
int ch = (c & 0xffff);
if ((ch >= ' ') && (ch <= 0xff)) {
data[pos++] = (byte)ch;
} else {
data[pos++] = '\\';
data[pos++] = 'u';
for(int j=3;j>=0;j--, ch>>>= 4) {
data[pos+j] = (byte)Character.forDigit(ch & 0xf, 16);
}
pos += 4;
}
}
}
data[pos++] = '\"';
data[pos++] = ' ';
}
public void fill(int len) {
if (pos > len) {
pos = len;
}
checkCapacity(len-pos);
while(pos < len) {
data[pos++] = ' ';
}
data[pos-1] = '\n';
}
}
差异被折叠。
差异被折叠。
/*
* 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.store;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.ref.Reference;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
import org.h2.security.SecureFileStore;
import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.TempFileDeleter;
public class FileStore {
public static final int HEADER_LENGTH = 3 * Constants.FILE_BLOCK_SIZE;
protected String name;
protected DataHandler handler;
private byte[] magic;
private RandomAccessFile file;
private long filePos;
private Reference autoDeleteReference;
public static FileStore open(DataHandler handler, String name, byte[] magic) throws SQLException {
return open(handler, name, magic, null, null, 0);
}
public static FileStore open(DataHandler handler, String name, byte[] magic, String cipher, byte[] key) throws SQLException {
return open(handler, name, magic, cipher, key, Constants.ENCRYPTION_KEY_HASH_ITERATIONS);
}
public static FileStore open(DataHandler handler, String name, byte[] magic, String cipher, byte[] key, int keyIterations) throws SQLException {
FileStore store;
if(FileUtils.isInMemory(name)) {
store = new MemoryFileStore(handler, name, magic);
} else if(cipher == null) {
store = new FileStore(handler, name, magic);
} else {
store = new SecureFileStore(handler, name, magic, cipher, key, keyIterations);
}
return store;
}
protected FileStore(DataHandler handler, String name, byte[] magic) throws SQLException {
this.handler = handler;
this.name = name;
this.magic = magic;
try {
FileUtils.createDirs(name);
File f = new File(name);
if(f.exists() && !f.canWrite()) {
file = FileUtils.openRandomAccessFile(name, "r");
} else {
// file = new RandomAccessFile(name, "rws");
file = FileUtils.openRandomAccessFile(name, "rw");
}
} catch(IOException e) {
throw Message.convert(e);
}
}
protected FileStore(DataHandler handler, byte[] magic) {
this.handler = handler;
this.magic = magic;
}
protected byte[] generateSalt() {
return magic;
}
protected void initKey(byte[] salt) {
// do nothing
}
protected void checkWritingAllowed() throws SQLException {
if(handler != null) {
handler.checkWritingAllowed();
}
}
protected void checkPowerOff() throws SQLException {
if(handler != null) {
handler.checkPowerOff();
}
}
public void init() throws SQLException {
int len = Constants.FILE_BLOCK_SIZE;
byte[] salt;
if(length() < HEADER_LENGTH) {
// write unencrypted
writeDirect(magic, 0, len);
salt = generateSalt();
writeDirect(salt, 0, len);
initKey(salt);
// write (maybe) encrypted
write(magic, 0, len);
} else {
// write unencrypted
seek(0);
byte[] buff = new byte[len];
readFullyDirect(buff, 0, len);
if(ByteUtils.compareNotNull(buff, magic) != 0) {
throw Message.getSQLException(Message.FILE_VERSION_ERROR_1, name);
}
salt = new byte[len];
readFullyDirect(salt, 0, len);
initKey(salt);
// read (maybe) encrypted
readFully(buff, 0, Constants.FILE_BLOCK_SIZE);
if(ByteUtils.compareNotNull(buff, magic) != 0) {
throw Message.getSQLException(Message.FILE_ENCRYPTION_ERROR_1, name);
}
}
}
public void close() throws IOException {
if(file != null) {
try {
file.close();
} finally {
file = null;
}
}
}
public void closeSilently() {
if(file != null) {
try {
file.close();
} catch(IOException e) {
file = null;
}
}
}
public void closeAndDeleteSilently() {
if(file != null) {
closeSilently();
TempFileDeleter.deleteFile(autoDeleteReference, name);
name = null;
}
}
protected void readFullyDirect(byte[] b, int off, int len) throws SQLException {
readFully(b, off, len);
}
public void readFully(byte[] b, int off, int len) throws SQLException {
if(Constants.CHECK && len < 0) {
throw Message.getInternalError("read len "+len);
}
if(Constants.CHECK && len % Constants.FILE_BLOCK_SIZE != 0) {
throw Message.getInternalError("unaligned read "+name+" len "+len);
}
checkPowerOff();
try {
file.readFully(b, off, len);
} catch (IOException e) {
throw Message.convert(e);
}
filePos += len;
}
public void seek(long pos) throws SQLException {
if(Constants.CHECK && pos % Constants.FILE_BLOCK_SIZE != 0) {
throw Message.getInternalError("unaligned seek "+name+" pos "+pos);
}
try {
if(pos != filePos) {
file.seek(pos);
filePos = pos;
}
} catch (IOException e) {
throw Message.convert(e);
}
}
protected void writeDirect(byte[] b, int off, int len) throws SQLException {
write(b, off, len);
}
public void write(byte[] b, int off, int len) throws SQLException {
if(Constants.CHECK && len < 0) {
throw Message.getInternalError("read len "+len);
}
if(Constants.CHECK && len % Constants.FILE_BLOCK_SIZE != 0) {
throw Message.getInternalError("unaligned write "+name+" len "+len);
}
checkWritingAllowed();
checkPowerOff();
try {
file.write(b, off, len);
} catch (IOException e) {
if(freeUpDiskSpace()) {
try {
file.write(b, off, len);
} catch (IOException e2) {
throw Message.convert(e2);
}
} else {
throw Message.convert(e);
}
}
filePos += len;
}
private boolean freeUpDiskSpace() throws SQLException {
if(handler == null) {
return false;
}
handler.freeUpDiskSpace();
return true;
}
public void setLength(long newLength) throws SQLException {
if(Constants.CHECK && newLength % Constants.FILE_BLOCK_SIZE != 0) {
throw Message.getInternalError("unaligned setLength "+name+" pos "+newLength);
}
checkPowerOff();
checkWritingAllowed();
try {
FileUtils.setLength(file, newLength);
} catch (IOException e) {
if(freeUpDiskSpace()) {
try {
FileUtils.setLength(file, newLength);
} catch (IOException e2) {
throw Message.convert(e2);
}
} else {
throw Message.convert(e);
}
}
}
public long length() throws SQLException {
try {
if(Constants.CHECK && file.length() % Constants.FILE_BLOCK_SIZE != 0) {
long newLength = file.length() + Constants.FILE_BLOCK_SIZE - (file.length() % Constants.FILE_BLOCK_SIZE);
FileUtils.setLength(file, newLength);
throw Message.getInternalError("unaligned file length "+name+" len "+file.length());
}
return file.length();
} catch (IOException e) {
throw Message.convert(e);
}
}
public long getFilePointer() throws SQLException {
if(Constants.CHECK2) {
try {
if(file.getFilePointer() != filePos) {
throw Message.getInternalError();
}
} catch (IOException e) {
throw Message.convert(e);
}
}
return filePos;
}
public void sync() {
try {
file.getFD().sync();
} catch(IOException e) {
// TODO log exception
}
}
public void autoDelete() {
autoDeleteReference = TempFileDeleter.addFile(name, this);
}
public void stopAutoDelete() {
TempFileDeleter.stopAutoDelete(autoDeleteReference, name);
autoDeleteReference = 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.store;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
import org.h2.tools.CompressTool;
public class FileStoreInputStream extends InputStream {
private FileStore store;
private DataPage page;
private int remaining;
private CompressTool compress;
public FileStoreInputStream(FileStore store, DataHandler handler, boolean compression) throws SQLException {
this.store = store;
if(compression) {
compress = CompressTool.getInstance();
}
page = DataPage.create(handler, Constants.FILE_BLOCK_SIZE);
try {
if(store.length() <= FileStore.HEADER_LENGTH) {
close();
} else {
fillBuffer();
}
} catch(IOException e) {
throw Message.convert(e);
}
}
public int available() {
return remaining <= 0 ? 0 : remaining;
}
public int read(byte[] buff) throws IOException {
return read(buff, 0, buff.length);
}
public int read(byte[] b, int off, int len) throws IOException {
if(len == 0) {
return 0;
}
int read = 0;
while(len > 0) {
int r = readBlock(b, off, len);
if(r < 0) {
break;
}
read += r;
off += r;
len -= r;
}
return read == 0 ? -1 : read;
}
public int readBlock(byte[] buff, int off, int len) throws IOException {
fillBuffer();
if(store == null) {
return -1;
}
int l = Math.min(remaining, len);
page.read(buff, off, l);
remaining -= l;
return l;
}
private void fillBuffer() throws IOException {
if(remaining > 0 || store==null) {
return;
}
page.reset();
try {
if(store.length() == store.getFilePointer()) {
close();
return;
}
store.readFully(page.getBytes(), 0, Constants.FILE_BLOCK_SIZE);
} catch(SQLException e) {
throw Message.convertToIOException(e);
}
page.reset();
remaining = page.readInt();
if(remaining<0) {
close();
return;
}
page.checkCapacity(remaining);
// get the length to read
if(compress != null) {
page.checkCapacity(page.getIntLen());
page.readInt();
}
page.setPos(page.length() + remaining);
page.fillAligned();
int len = page.length() - Constants.FILE_BLOCK_SIZE;
page.reset();
page.readInt();
try {
store.readFully(page.getBytes(), Constants.FILE_BLOCK_SIZE, len);
page.reset();
page.readInt();
if(compress != null) {
int uncompressed = page.readInt();
byte[] buff = new byte[remaining];
page.read(buff, 0, remaining);
page.reset();
page.checkCapacity(uncompressed);
compress.expand(buff, page.getBytes(), 0);
remaining = uncompressed;
}
} catch(SQLException e) {
throw Message.convertToIOException(e);
}
}
public void close() throws IOException {
if(store != null) {
try {
store.close();
} finally {
store = null;
}
}
}
public void finalize() {
if (!Constants.RUN_FINALIZERS) {
return;
}
try {
close();
} catch(IOException e) {
// ignore
}
}
public int read() throws IOException {
fillBuffer();
if(store == null) {
return -1;
}
int i = page.readByte() & 0xff;
remaining--;
return 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.store;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.message.Message;
import org.h2.tools.CompressTool;
public class FileStoreOutputStream extends OutputStream {
private FileStore store;
private DataPage page;
private String compressionAlgorithm;
private CompressTool compress;
public FileStoreOutputStream(FileStore store, DataHandler handler, String compressionAlgorithm) {
this.store = store;
if(compressionAlgorithm != null) {
compress = CompressTool.getInstance();
this.compressionAlgorithm = compressionAlgorithm;
}
page = DataPage.create(handler, Constants.FILE_BLOCK_SIZE);
}
public void write(byte[] buff) throws IOException {
write(buff, 0, buff.length);
}
public void write(byte[] buff, int off, int len) throws IOException {
if(len > 0) {
try {
page.reset();
if(compress != null) {
if(off != 0 || len != buff.length) {
byte[] b2 = new byte[len];
System.arraycopy(buff, off, b2, 0, len);
buff = b2;
off = 0;
}
int uncompressed = len;
buff = compress.compress(buff, compressionAlgorithm);
len = buff.length;
page.writeInt(len);
page.writeInt(uncompressed);
page.write(buff, off, len);
} else {
page.writeInt(len);
page.write(buff, off, len);
}
page.fillAligned();
store.write(page.getBytes(), 0, page.length());
} catch(SQLException e) {
throw Message.convertToIOException(e);
}
}
}
public void close() throws IOException {
if(store != null) {
try {
store.close();
} finally {
store = null;
}
}
}
public void write(int b) throws IOException {
throw new IOException("this method is not implemented");
}
}
/*
* 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.store;
import java.sql.SQLException;
import org.h2.message.Message;
public class InDoubtTransaction {
public static final int IN_DOUBT = 0, COMMIT = 1, ROLLBACK = 2;
// TODO 2-phase-commit: document sql statements and metadata table
private LogFile log;
private int sessionId;
private int pos;
private String transaction;
private int blocks;
private int state;
public InDoubtTransaction(LogFile log, int sessionId, int pos, String transaction, int blocks) {
this.log = log;
this.sessionId = sessionId;
this.pos = pos;
this.transaction = transaction;
this.blocks = blocks;
this.state = IN_DOUBT;
}
public void setState(int state) throws SQLException {
switch(state) {
case COMMIT:
log.updatePreparedCommit(true, pos, sessionId, blocks);
break;
case ROLLBACK:
log.updatePreparedCommit(false, pos, sessionId, blocks);
break;
default:
throw Message.getInternalError("state="+state);
}
this.state = state;
}
public String getState() {
switch(state) {
case IN_DOUBT:
return "IN_DOUBT";
case COMMIT:
return "COMMIT";
case ROLLBACK:
return "ROLLBACK";
default:
throw Message.getInternalError("state="+state);
}
}
public int getPos() {
return pos;
}
public int getSessionId() {
return sessionId;
}
public String getTransaction() {
return transaction;
}
}
差异被折叠。
/*
* 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.store;
public class LogRecord {
LogFile log;
int logRecordId;
int sessionId;
LogRecord(LogFile log, int logRecordId, int sessionId) {
this.log = log;
this.logRecordId = logRecordId;
this.sessionId = sessionId;
}
}
差异被折叠。
/*
* 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.store;
import java.sql.SQLException;
import org.h2.util.FileUtils;
import org.h2.util.MemoryFile;
public class MemoryFileStore extends FileStore {
private MemoryFile memFile;
public MemoryFileStore(DataHandler handler, String name, byte[] magic) throws SQLException {
super(handler, magic);
this.name = name;
memFile = FileUtils.getMemoryFile(name);
memFile.setMagic(magic);
}
public void close() {
// nothing to do
}
public long getFilePointer() {
return memFile.getFilePointer();
}
public long length() {
return memFile.length();
}
public void readFully(byte[] b, int off, int len) throws SQLException {
checkPowerOff();
memFile.readFully(b, off, len);
}
public void seek(long pos) {
memFile.seek(pos);
}
public void setLength(long newLength) throws SQLException {
checkPowerOff();
memFile.setLength(newLength);
}
public void write(byte[] b, int off, int len) throws SQLException {
checkPowerOff();
memFile.write(b, off, 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.store;
import java.sql.SQLException;
import org.h2.util.CacheObject;
/**
* @author Thomas
*/
public abstract class Record extends CacheObject {
private boolean deleted;
private int storageId;
private int lastLog = LogSystem.LOG_WRITTEN;
private int lastPos = LogSystem.LOG_WRITTEN;
public abstract int getByteCount(DataPage dummy) throws SQLException;
/**
* This method is called just before the page is written.
* If a read operation is required before writing, this needs to be done here.
* Because the data page buffer is shared for read and write operations.
* The method may read data and change the file pointer.
*/
public void prepareWrite() throws SQLException {
}
public abstract void write(DataPage buff) throws SQLException;
public boolean isEmpty() {
return false;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public boolean getDeleted() {
return deleted;
}
public void setStorageId(int storageId) {
this.storageId = storageId;
}
public int getStorageId() {
return storageId;
}
public void setLastLog(int log, int pos) {
lastLog = log;
lastPos = pos;
}
public void setLogWritten(int log, int pos) {
if(log < lastLog) {
return;
}
if(log > lastLog || pos >= lastPos) {
lastLog = LogSystem.LOG_WRITTEN;
lastPos = LogSystem.LOG_WRITTEN;
}
}
public boolean canRemove() {
if((isChanged() && !isLogWritten()) || isPinned()) {
return false;
}
return true;
}
public boolean isLogWritten() {
return lastLog == LogSystem.LOG_WRITTEN;
}
}
/*
* 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.store;
import java.sql.SQLException;
/**
* @author Thomas
*/
public interface RecordReader {
Record read(DataPage s) 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.store;
public class RedoLogRecord {
Storage storage;
int sequenceId;
int recordId;
int offset;
byte[] data;
public int getSize() {
// estimated memory size in bytes ((5 variables+myself) * 4 bytes each)
if(data == null) {
return 24;
} else {
return 28 + data.length;
}
}
}
/*
* 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.store;
public class SessionState {
int sessionId;
int lastCommitLog;
int lastCommitPos;
InDoubtTransaction inDoubtTransaction;
public boolean isCommitted(int logId, int pos) {
if(logId != lastCommitLog) {
return lastCommitLog > logId;
}
return lastCommitPos >= pos;
}
}
/*
* 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.store;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.util.BitField;
import org.h2.util.IntArray;
import org.h2.util.MathUtils;
/**
* File format:
* intfixed block size
* intfixed storage id
* record data
* byte checksum
* [bytes * fillerLength]
*
* @author Thomas
*/
public class Storage {
public static final int ALLOCATE_POS = -1;
private static final int FREE_LIST_SIZE = Math.max(1024, DiskFile.BLOCKS_PER_PAGE * 4);
private DiskFile file;
private int recordCount;
private RecordReader reader;
private IntArray freeList = new IntArray();
private IntArray pages = new IntArray();
private int id;
private Database database;
private DataPage dummy;
public Storage(Database database, DiskFile file, RecordReader reader, int id) {
this.database = database;
this.file = file;
this.reader = reader;
this.id = id;
dummy = DataPage.create(database, 0);
}
public RecordReader getRecordReader() {
return reader;
}
void incrementRecordCount() {
recordCount++;
}
public Record getRecord(int pos) throws SQLException {
return file.getRecord(pos, reader, id);
}
public Record getRecordIfStored(int pos) throws SQLException {
return file.getRecordIfStored(pos, reader, id);
}
/**
* Gets the position of the next record.
* @param record the last record (null to get the first record)
* @return -1 if no record is found, otherwise the position
*/
public int getNext(Record record) {
int next;
int lastCheckedPage;
int pageIndex = -1;
if (record == null) {
if(pages.size() == 0) {
return -1;
}
pageIndex = 0;
lastCheckedPage = pages.get(0);
next = lastCheckedPage * DiskFile.BLOCKS_PER_PAGE;
} else {
int blockCount = record.getBlockCount();
lastCheckedPage = file.getPage(record.getPos());
next = record.getPos() + blockCount;
}
BitField used = file.getUsed();
while (true) {
int page = file.getPage(next);
if(lastCheckedPage != page) {
if(pageIndex < 0) {
pageIndex = pages.findNextValueIndex(page);
} else {
pageIndex++;
}
if(pageIndex >= pages.size()) {
return -1;
}
lastCheckedPage = pages.get(pageIndex);
next = Math.max(next, DiskFile.BLOCKS_PER_PAGE * lastCheckedPage);
}
if (used.get(next)) {
return next;
}
if(used.getLong(next) == 0) {
next = MathUtils.roundUp(next+1, 64);
} else {
next++;
}
}
}
public void updateRecord(Session session, Record record) throws SQLException {
record.setDeleted(false);
file.updateRecord(session, record);
}
public void addRecord(Session session, Record record, int pos) throws SQLException {
record.setStorageId(id);
int size = file.getRecordOverhead() + record.getByteCount(dummy);
size = MathUtils.roundUp(size, DiskFile.BLOCK_SIZE);
record.setDeleted(false);
int blockCount = size / DiskFile.BLOCK_SIZE;
if(pos == ALLOCATE_POS) {
pos = allocate(blockCount);
} else {
file.setUsed(pos, blockCount);
}
record.setPos(pos);
record.setBlockCount(blockCount);
record.setChanged(true);
recordCount++;
file.addRecord(session, record);
}
public void removeRecord(Session session, int pos) throws SQLException {
Record record = getRecord(pos);
if(Constants.CHECK && record.getDeleted()) {
throw Message.getInternalError("duplicate delete " + pos);
}
record.setDeleted(true);
int blockCount = record.getBlockCount();
free(pos, blockCount);
recordCount--;
file.removeRecord(session, pos, record, blockCount);
}
public void removeRecord(Session session, int pos, int blockCount) throws SQLException {
}
private boolean isFreeAndMine(int pos, int blocks) {
BitField used = file.getUsed();
for(int i=blocks + pos -1; i>=pos; i--) {
if(file.getPageOwner(file.getPage(i)) != id || used.get(i)) {
return false;
}
}
return true;
}
public int allocate(int blockCount) throws SQLException {
if (freeList.size() > 0) {
synchronized(file) {
BitField used = file.getUsed();
for (int i = 0; i < freeList.size(); i++) {
int px = freeList.get(i);
if (used.get(px)) {
// sometime there may stay some entries in the freeList that are not free (free 2, free 1, allocate 1+2)
// these entries are removed right here
freeList.remove(i--);
} else {
if (isFreeAndMine(px, blockCount)) {
int pos = px;
freeList.remove(i--);
file.setUsed(pos, blockCount);
return pos;
}
}
}
}
}
int pos = file.allocate(this, blockCount);
file.setUsed(pos, blockCount);
return pos;
}
void free(int pos, int blockCount) {
file.free(pos, blockCount);
if (freeList.size() < FREE_LIST_SIZE) {
freeList.add(pos);
}
}
public void delete(Session session) throws SQLException {
truncate(session);
database.removeStorage(id, file);
}
// private int allocateBest(int start, int blocks) {
// while (true) {
// int p = getLastUsedPlusOne(start, blocks);
// if (p == start) {
// start = p;
// break;
// }
// start = p;
// }
// allocate(start, blocks);
// return start;
// }
public int getId() {
return id;
}
public int getRecordCount() {
return recordCount;
}
public void truncate(Session session) throws SQLException {
freeList = new IntArray();
recordCount = 0;
file.truncateStorage(session, this, pages);
}
public void setReader(RecordReader reader) {
this.reader = reader;
}
public void flushRecord(Record rec) throws SQLException {
file.writeBack(rec);
}
public void flushFile() throws SQLException {
file.flush();
}
public int getRecordOverhead() {
return file.getRecordOverhead();
}
public DiskFile getDiskFile() {
return file;
}
public void setRecordCount(int recordCount) {
this.recordCount = recordCount;
}
public void addPage(int i) {
pages.addValueSorted(i);
}
public void removePage(int i) {
pages.removeValue(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.store;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.util.ObjectArray;
public class UndoLog {
private Session session;
private Database database;
// TODO undolog entry: a chain would probably be faster & use less memory than an array
private ObjectArray records = new ObjectArray();
private FileStore file;
private DataPage rowBuff;
private int memoryUndo;
public UndoLog(Session session) {
this.session = session;
this.database = session.getDatabase();
}
public int size() {
if(Constants.CHECK && memoryUndo > records.size()) {
throw Message.getInternalError();
}
return records.size();
}
public void clear() {
records.clear();
memoryUndo = 0;
if(file != null) {
file.closeAndDeleteSilently();
file = null;
rowBuff = null;
}
}
public UndoLogRecord getAndRemoveLast() throws SQLException {
int i = records.size() - 1;
UndoLogRecord entry = (UndoLogRecord) records.get(i);
if(entry.isStored()) {
int start = Math.max(0, i-database.getMaxMemoryUndo()/2);
UndoLogRecord first = null;
for(int j=start; j<=i; j++) {
UndoLogRecord e = (UndoLogRecord) records.get(j);
if(e.isStored()) {
e.load(rowBuff, file, session);
memoryUndo++;
if(first == null) {
first = e;
}
}
}
first.seek(file);
}
records.remove(i);
memoryUndo--;
return entry;
}
public void add(UndoLogRecord entry) throws SQLException {
records.add(entry);
memoryUndo++;
if(memoryUndo > database.getMaxMemoryUndo() && database.isPersistent()) {
if(file == null) {
String fileName = database.createTempFile();
file = database.openFile(fileName, false);
file.autoDelete();
file.seek(FileStore.HEADER_LENGTH);
rowBuff = DataPage.create(database, Constants.DEFAULT_DATA_PAGE_SIZE);
}
DataPage buff = rowBuff;
for(int i=0; i<records.size(); i++) {
UndoLogRecord r = (UndoLogRecord) records.get(i);
if(!r.isStored() && r.canStore()) {
r.save(buff, file);
memoryUndo--;
}
}
}
}
}
/*
* 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.store;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.table.Table;
import org.h2.value.Value;
/**
* @author Thomas
*/
public class UndoLogRecord {
public static final short INSERT = 0, DELETE = 1;
private static final int IN_MEMORY = 0, STORED = 1, IN_MEMORY_READ_POS = 2;
private Table table;
private Row row;
private short operation;
private short state;
private int filePos;
public boolean isStored() {
return state == STORED;
}
public boolean canStore() {
return table.getUniqueIndex() != null;
}
public UndoLogRecord(Table table, short op, Row row) {
this.table = table;
this.row = row;
this.operation = op;
this.state = IN_MEMORY;
}
public void undo(Session session) throws SQLException {
switch (operation) {
case INSERT:
if(state == IN_MEMORY_READ_POS) {
Index index = table.getUniqueIndex();
Cursor cursor = index.find(session, row, row);
cursor.next();
int pos = cursor.getPos();
row.setPos(pos);
state = IN_MEMORY;
}
if(session.getDatabase().getLockMode()==Constants.LOCK_MODE_OFF) {
if(row.getDeleted()) {
// it might have been deleted by another thread
return;
}
}
try {
table.removeRow(session, row);
} catch(SQLException e) {
if(session.getDatabase().getLockMode()==Constants.LOCK_MODE_OFF && e.getErrorCode() == Message.ROW_NOT_FOUND_WHEN_DELETING_1) {
// it might have been deleted by another thread
// ignore
} else {
throw e;
}
}
break;
case DELETE:
try {
row.setPos(0);
table.addRow(session, row);
} catch(SQLException e) {
if(session.getDatabase().getLockMode()==Constants.LOCK_MODE_OFF && e.getErrorCode() == Message.DUPLICATE_KEY_1) {
// it might have been added by another thread
// ignore
} else {
throw e;
}
}
break;
default:
throw Message.getInternalError("op=" + operation);
}
}
public void save(DataPage buff, FileStore file) throws SQLException {
buff.reset();
buff.writeInt(0);
buff.writeInt(operation);
buff.writeInt(row.getColumnCount());
for (int i = 0; i < row.getColumnCount(); i++) {
buff.writeValue(row.getValue(i));
}
buff.fillAligned();
buff.setInt(0, buff.length() / Constants.FILE_BLOCK_SIZE);
buff.updateChecksum();
filePos = (int) (file.getFilePointer() / Constants.FILE_BLOCK_SIZE);
file.write(buff.getBytes(), 0, buff.length());
row = null;
state = STORED;
}
public void seek(FileStore file) throws SQLException {
file.seek(filePos * Constants.FILE_BLOCK_SIZE);
}
public void load(DataPage buff, FileStore file, Session session) throws SQLException {
int min = Constants.FILE_BLOCK_SIZE;
seek(file);
buff.reset();
file.readFully(buff.getBytes(), 0, min);
int len = buff.readInt() * Constants.FILE_BLOCK_SIZE;
buff.checkCapacity(len);
if (len - min > 0) {
file.readFully(buff.getBytes(), min, len - min);
}
buff.check(len);
int op = buff.readInt();
if (Constants.CHECK) {
if (operation != op) {
throw Message.getInternalError("operation=" + operation + " op=" + op);
}
}
int columnCount = buff.readInt();
Value[] values = new Value[columnCount];
for (int i = 0; i < columnCount; i++) {
values[i] = buff.readValue();
}
row = new Row(values);
state = IN_MEMORY_READ_POS;
}
public Table getTable() {
return table;
}
}
/*
* 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.store;
import java.lang.ref.WeakReference;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.index.BtreeIndex;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
import org.h2.util.TempFileDeleter;
public class WriterThread extends Thread {
// Thread objects are not garbage collected
// until they returned from the run() method
// (even if they where never started)
// so if the connection was not closed,
// the database object cannot get reclaimed
// by the garbage collector if we use a hard reference
private volatile WeakReference databaseRef;
private int writeDelay;
private long lastIndexFlush;
private volatile boolean stop;
private WriterThread(Database database, int writeDelay) {
this.databaseRef = new WeakReference(database);
this.writeDelay = writeDelay;
}
public void setWriteDelay(int writeDelay) {
LogSystem log = getLog();
this.writeDelay = writeDelay;
// TODO check if MIN_WRITE_DELAY is a good value
if(writeDelay < Constants.MIN_WRITE_DELAY) {
log.setFlushOnEachCommit(true);
} else {
log.setFlushOnEachCommit(false);
}
}
public static WriterThread create(Database database, int writeDelay) {
WriterThread thread = new WriterThread(database, writeDelay);
thread.setName("H2 Log Writer " + database.getShortName());
thread.setDaemon(true);
thread.start();
return thread;
}
private LogSystem getLog() {
Database database = (Database)databaseRef.get();
if(database == null) {
return null;
}
LogSystem log = database.getLog();
return log;
}
private void flushIndexes(Database database) {
long time = System.currentTimeMillis();
if(lastIndexFlush + Constants.FLUSH_INDEX_DELAY > time) {
return;
}
synchronized(database) {
ObjectArray array = database.getAllSchemaObjects(DbObject.INDEX);
for(int i=0; i<array.size(); i++) {
DbObject obj = (DbObject) array.get(i);
if(obj instanceof BtreeIndex) {
BtreeIndex idx = (BtreeIndex) obj;
if(idx.getLastChange() == 0) {
continue;
}
Table tab = idx.getTable();
if(tab.isLockedExclusively()) {
continue;
}
if(idx.getLastChange() + Constants.FLUSH_INDEX_DELAY > time) {
continue;
}
try {
idx.flush(database.getSystemSession());
} catch(SQLException e) {
database.getTrace(Trace.DATABASE).error("flush index " +idx.getName(), e);
}
}
}
}
lastIndexFlush = time;
}
public void run() {
while(!stop) {
TempFileDeleter.deleteUnused();
Database database = (Database)databaseRef.get();
if(database == null) {
break;
}
if(Constants.FLUSH_INDEX_DELAY != 0) {
flushIndexes(database);
}
LogSystem log = database.getLog();
if(log == null) {
break;
}
try {
log.flush();
} catch(SQLException e) {
TraceSystem traceSystem = database.getTraceSystem();
if(traceSystem != null) {
traceSystem.getTrace(Trace.LOG).error("flush", e);
}
}
// TODO log writer: could also flush the dirty cache when there is low activity
// wait 0 mean wait forever
int wait = writeDelay > 0 ? writeDelay : 1;
try {
Thread.sleep(wait);
} catch (InterruptedException e) {
// ignore
}
}
databaseRef = null;
}
public void stopThread() {
stop = 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.table;
import org.h2.value.Value;
public interface ColumnResolver {
String getTableAlias();
Column[] getColumns();
String getSchemaName();
Value getValue(Column column);
TableFilter getTableFilter();
}
/*
* 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.table;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.FunctionCall;
import org.h2.index.FunctionIndex;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.util.ObjectArray;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueResultSet;
public class FunctionTable extends Table {
private FunctionCall function;
public FunctionTable(Schema schema, Session session, FunctionCall function) throws SQLException {
super(schema, 0, function.getName(), false);
this.function = function;
function.optimize(session);
int type = function.getType();
if(type != Value.RESULT_SET) {
throw Message.getSQLException(Message.FUNCTION_MUST_RETURN_RESULT_SET_1, function.getName());
}
int params = function.getParameterCount();
Expression[] columnListArgs = new Expression[params];
Expression[] args = function.getArgs();
for(int i=0; i<params; i++) {
args[i] = args[i].optimize(session);
columnListArgs[i] = args[i];
}
ValueResultSet template = function.getValueForColumnList(session, columnListArgs);
if(template == null) {
throw Message.getSQLException(Message.FUNCTION_MUST_RETURN_RESULT_SET_1, function.getName());
}
ResultSet rs = template.getResultSet();
ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
Column[] cols = new Column[columnCount];
for(int i=0; i<columnCount; i++) {
cols[i] = new Column(meta.getColumnName(i+1),
DataType.convertSQLTypeToValueType(meta.getColumnType(i + 1)),
meta.getPrecision(i + 1),
meta.getScale(i + 1));
}
setColumns(cols);
}
public void lock(Session session, boolean exclusive) throws SQLException {
}
public void close(Session session) throws SQLException {
}
public void unlock(Session s) {
}
public boolean isLockedExclusively() {
return false;
}
public Index addIndex(Session session, String indexName, int indexId, Column[] cols, IndexType indexType, int headPos, String comment) throws SQLException {
throw Message.getUnsupportedException();
}
public void removeRow(Session session, Row row) throws SQLException {
throw Message.getUnsupportedException();
}
public void truncate(Session session) throws SQLException {
throw Message.getUnsupportedException();
}
public boolean canDrop() {
throw Message.getInternalError();
}
public void addRow(Session session, Row row) throws SQLException {
throw Message.getUnsupportedException();
}
public void checkSupportAlter() throws SQLException {
throw Message.getUnsupportedException();
}
public String getTableType() {
throw Message.getInternalError();
}
public Index getScanIndex(Session session) throws SQLException {
return new FunctionIndex(this, columns, function);
}
public ObjectArray getIndexes() {
return null;
}
public boolean canGetRowCount() {
return false;
}
public int getRowCount() throws SQLException {
throw Message.getInternalError();
}
public String getCreateSQL() {
return null;
}
public void checkRename() throws SQLException {
throw Message.getUnsupportedException();
}
public LocalResult getResult(Session session) throws SQLException {
function.optimize(session);
ValueResultSet value = (ValueResultSet) function.getValue(session);
return LocalResult.read(session, value.getResultSet());
}
public long getMaxDataModificationId() {
// TODO optimization: table-as-a-function currently doesn't know the last modified date
return Long.MAX_VALUE;
}
public Index getUniqueIndex() {
return 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.table;
import org.h2.index.Index;
/**
* @author Thomas
*/
public class PlanItem {
public double cost;
public Index index;
public PlanItem joinPlan;
public Index getIndex() {
return index;
}
}
差异被折叠。
/*
* 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.table;
import org.h2.value.Value;
public class SingleColumnResolver implements ColumnResolver {
private Column column;
private Value value;
SingleColumnResolver(Column column) {
this.column = column;
}
public String getTableAlias() {
return null;
}
public void setValue(Value value) {
this.value = value;
}
public Value getValue(Column column) {
return value;
}
public Column[] getColumns() {
return new Column[] { column };
}
public String getSchemaName() {
return null;
}
public TableFilter getTableFilter() {
return null;
}
}
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论