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

--no commit message

--no commit message
上级 6971da7a
......@@ -37,7 +37,10 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / TODO (Build TODO)</h3><ul>
<li>PostgreSQL compatibility: SET SEARCH_PATH, SERIAL, CURRENT_USER, E'text', $1.
<li>Support for the system property baseDir. This works for embedded databases as well. The setting is supported
by the H2 Console using -Dh2.baseDir or -baseDir
</li><li>LIKE ESCAPE did not work correctly if the pattern was % or _, followed by an escape character, followed by %. Fixed.
</li><li>PostgreSQL compatibility: SET SEARCH_PATH, SERIAL, CURRENT_USER, E'text', $1.
</li><li>In some situations, when many tables with LOB columns were modified (ALTER TABLE), large objects were deleted. Fixed.
</li><li>CREATE TABLE AS SELECT .. UNION .. did not work. Fixed.
</li><li>New column ID for INFORMATION_SCHEMA.INDEXES, SEQUENCES, USERS, ROLES, RIGHTS,
......@@ -783,13 +786,14 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Priority 2</h3>
<ul>
<li>Support OSGi: http://oscar-osgi.sourceforge.net, http://incubator.apache.org/felix/index.html
</li><li>Set the database in an 'exclusive' mode (restrict to one user at a time)
</li><li>Clustering: recovery needs to becomes fully automatic. Global write lock feature.
</li><li>System table / function: cache usage
</li><li>Connection pool manager
</li><li>Set the database in an 'exclusive' mode (restrict to one user at a time)
</li><li>User defined aggregate functions
</li><li>Add a migration guide (list differences between databases)
</li><li>Optimization: automatic index creation suggestion using the trace file?
</li><li>Compression performance: don't allocate buffers, compress / expand in to out buffer
</li><li>Connection pool manager
</li><li>Start / stop server with database URL
</li><li>Rebuild index functionality (other than delete the index file)
</li><li>Don't use deleteOnExit (bug 4513817: File.deleteOnExit consumes memory)
......@@ -975,8 +979,6 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Shrink the data file without closing the database (if the end of the file is empty)
</li><li>Delay reading the row if data is not required
</li><li>Eliminate undo log records if stored on disk (just one pointer per block, not per record)
</li><li>User defined aggregate functions
</li><li>System property for base directory (h2.baseDir) in embedded mode
</li><li>Feature matrix like here: http://www.inetsoftware.de/products/jdbc/mssql/features/default.asp.
</li><li>Updatable result set on table without primary key or unique index
</li><li>Use LinkedList instead of ArrayList where applicable
......@@ -1033,6 +1035,7 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Support triggers with a string property or option: SpringTrigger, OSGITrigger
</li><li>Clustering: adding a node should be very fast and without interrupting clients (very short lock)
</li><li>Updatable result sets: DatabaseMetaData.ownUpdatesAreVisible = true
</li><li>Cache size should be in KB
</li></ul>
<h3>Not Planned</h3>
......
......@@ -24,8 +24,7 @@ import org.h2.message.TraceSystem;
*/
public class Driver implements java.sql.Driver {
// TODO server: maybe start/stop a server using DriverManager.getConnection ?
private static Driver instance = new Driver();
private static final Driver instance = new Driver();
static {
try {
......
......@@ -19,11 +19,11 @@ import org.h2.util.ObjectArray;
* @author Thomas
*/
public abstract class Command implements CommandInterface {
protected Session session;
private final String sql;
protected final Session session;
protected final Trace trace;
protected long startTime;
protected Trace trace;
private volatile boolean cancel;
private final String sql;
public abstract boolean isTransactional();
public abstract boolean isQuery();
......
......@@ -11,8 +11,8 @@ import org.h2.util.ObjectArray;
public class CommandList extends Command {
private Command command;
private String remaining;
private final Command command;
private final String remaining;
// TODO lock if possible!
......
......@@ -20,14 +20,14 @@ import org.h2.value.Transfer;
public class CommandRemote implements CommandInterface {
private final ObjectArray transferList;
private final ObjectArray parameters;
private final Trace trace;
private final String sql;
private SessionRemote session;
private ObjectArray transferList;
private int id;
private boolean isQuery;
private boolean readonly;
private ObjectArray parameters;
private Trace trace;
private String sql;
private int paramCount;
public CommandRemote(SessionRemote session, ObjectArray transferList, String sql) throws SQLException {
......
......@@ -147,6 +147,9 @@ public class Parser {
private static final int CURRENT_TIMESTAMP = 42, CURRENT_DATE = 43, CURRENT_TIME = 44, ROWNUM = 45;
private final Database database;
private final Session session;
private int[] characterTypes;
private int currentTokenType;
private String currentToken;
......@@ -159,8 +162,6 @@ public class Parser {
private Prepared prepared;
private Prepared currentPrepared;
private Select currentSelect;
private Session session;
private Database database;
private ObjectArray parameters;
private String schemaName;
private ObjectArray expected;
......
......@@ -17,9 +17,9 @@ import org.h2.util.ObjectArray;
public abstract class Prepared {
protected Session session;
protected String sql;
protected int headPos = -1;
protected Session session;
protected ObjectArray parameters;
private long modificationId;
private Command command;
......@@ -27,6 +27,11 @@ public abstract class Prepared {
protected boolean prepareAlways;
private int currentRowNumber;
public Prepared(Session session) {
this.session = session;
modificationId = session.getDatabase().getModificationMetaId();
}
public boolean needRecompile() throws SQLException {
Database db = session.getDatabase();
if(db == null) {
......@@ -42,11 +47,6 @@ public abstract class Prepared {
return false;
}
public Prepared(Session session) {
this.session = session;
modificationId = session.getDatabase().getModificationMetaId();
}
long getModificationId() {
return modificationId;
}
......
......@@ -258,9 +258,21 @@ public class AlterTableAlterColumn extends SchemaCommand {
newTable.addRow(newRow);
}
*/
String sql = "INSERT INTO " + newTable.getSQL() + "(" + columnList+") "
+ "SELECT " + columnList + " FROM " + table.getSQL();
StringBuffer buff = new StringBuffer();
buff.append("INSERT INTO ");
buff.append(newTable.getSQL());
buff.append("(");
buff.append(columnList);
buff.append(") SELECT ");
if (columnList.length() == 0) {
// special case insert into test select * from test
buff.append("*");
} else {
buff.append(columnList);
}
buff.append(" FROM ");
buff.append(table.getSQL());
String sql = buff.toString();
newTable.setCheckForeignKeyConstraints(false);
try {
execute(sql);
......
......@@ -24,9 +24,7 @@ public class PrepareProcedure extends DefineCommand {
}
public int update() throws SQLException {
Procedure proc = new Procedure();
proc.setName(procedureName);
proc.setPrepared(prepared);
Procedure proc = new Procedure(procedureName, prepared);
prepared.setParameterList(parameters);
prepared.setPrepareAlways(prepareAlways);
prepared.prepare();
......
......@@ -11,7 +11,7 @@ import org.h2.schema.Schema;
public abstract class SchemaCommand extends DefineCommand {
private Schema schema;
private final Schema schema;
public SchemaCommand(Session session, Schema schema) {
super(session);
......
......@@ -9,7 +9,7 @@ import java.io.InputStream;
public class LZFInputStream extends InputStream {
private InputStream in;
private final InputStream in;
private CompressLZF decompress = new CompressLZF();
private int pos;
private int bufferLength;
......
......@@ -12,11 +12,12 @@ import org.h2.engine.Constants;
public class LZFOutputStream extends OutputStream {
static final int MAGIC = ('H' << 24) | ('2' << 16) | ('I' << 8) | 'S';
private OutputStream out;
private byte[] buffer;
private final OutputStream out;
private final CompressLZF compress = new CompressLZF();
private final byte[] buffer;
private int pos;
private byte[] outBuffer;
private CompressLZF compress = new CompressLZF();
public LZFOutputStream(OutputStream out) throws IOException {
this.out = out;
......
......@@ -21,8 +21,8 @@ import org.h2.table.Table;
*/
public abstract class Constraint extends SchemaObject {
protected Table table;
public static final String CHECK = "CHECK", REFERENTIAL = "REFERENTIAL", UNIQUE = "UNIQUE";
protected Table table;
public Constraint(Schema schema, int id, String name, Table table) {
super(schema, id, name, Trace.CONSTRAINT);
......
......@@ -13,8 +13,8 @@ import org.h2.util.StringUtils;
public class Comment extends DbObject {
private int objectType;
private String objectName;
private final int objectType;
private final String objectName;
private String commentText;
public Comment(Database database, int id, DbObject obj) {
......
......@@ -19,16 +19,16 @@ import org.h2.util.StringUtils;
public class ConnectionInfo {
private static final HashSet KNOWN_SETTINGS = new HashSet();
private final Properties prop = new Properties();
private String originalURL;
private String url;
private Properties prop = new Properties();
private String user;
private byte[] filePasswordHash;
private byte[] userPasswordHash;
private String name;
private static HashSet KNOWN_SETTINGS = new HashSet();
private boolean remote;
private boolean ssl;
private boolean persistent;
......
......@@ -251,7 +251,15 @@ public class Constants {
public static final int CACHE_SIZE_DEFAULT = getIntSetting("h2.cacheSizeDefault", (1 << 16));
public static final int CACHE_SIZE_INDEX_SHIFT = getIntSetting("h2.cacheSizeIndexShift", 3);
public static final int CACHE_SIZE_INDEX_DEFAULT = CACHE_SIZE_DEFAULT >> CACHE_SIZE_INDEX_SHIFT;
public static String BASE_DIR = getStringSetting("h2.baseDir", null);
public static void setBaseDir(String dir) {
if(!dir.endsWith("/")) {
dir += "/";
}
BASE_DIR = dir;
}
public static boolean getBooleanSetting(String name, boolean defaultValue) {
String s = System.getProperty(name);
if(s != null) {
......
......@@ -68,27 +68,33 @@ import org.h2.value.ValueInt;
*/
public class Database implements DataHandler {
private final boolean persistent;
private final String databaseName;
private final String databaseShortName;
private final String databaseURL;
private final String cipher;
private final byte[] filePasswordHash;
private final HashMap roles = new HashMap();
private final HashMap users = new HashMap();
private final HashMap settings = new HashMap();
private final HashMap schemas = new HashMap();
private final HashMap rights = new HashMap();
private final HashMap functionAliases = new HashMap();
private final HashMap userDataTypes = new HashMap();
private final HashMap comments = new HashMap();
private final HashSet sessions = new HashSet();
private final BitField objectIds = new BitField();
private final Object lobSyncObject = new Object();
private boolean textStorage;
private String databaseName;
private String databaseShortName;
private String databaseURL;
private HashMap roles = new HashMap();
private HashMap users = new HashMap();
private HashMap settings = new HashMap();
private HashMap schemas = new HashMap();
private HashMap rights = new HashMap();
private HashMap functionAliases = new HashMap();
private HashMap userDataTypes = new HashMap();
private HashMap comments = new HashMap();
private Schema mainSchema;
private Schema infoSchema;
private int nextSessionId;
private HashSet sessions = new HashSet();
private User systemUser;
private Session systemSession;
private TableData meta;
private Index metaIdIndex;
private BitField objectIds = new BitField();
private FileLock lock;
private LogSystem log;
private WriterThread writer;
......@@ -96,9 +102,6 @@ public class Database implements DataHandler {
private boolean starting;
private DiskFile fileData, fileIndex;
private TraceSystem traceSystem;
private boolean persistent;
private String cipher;
private byte[] filePasswordHash;
private DataPage dummy;
private int fileLockMethod;
private Role publicRole;
......@@ -133,9 +136,68 @@ public class Database implements DataHandler {
private boolean optimizeReuseResults = true;
private String cacheType;
private boolean indexSummaryValid = true;
private Object lobSyncObject = new Object();
private String accessModeLog, accessModeData;
public Database(String name, ConnectionInfo ci, String cipher) throws SQLException {
this.compareMode = new CompareMode(null, null);
this.persistent = ci.isPersistent();
this.filePasswordHash = ci.getFilePasswordHash();
this.databaseName = name;
this.databaseShortName = parseDatabaseShortName();
this.cipher = cipher;
String lockMethodName = ci.removeProperty("FILE_LOCK", null);
this.accessModeLog = ci.removeProperty("ACCESS_MODE_LOG", "rw").toLowerCase();
this.accessModeData = ci.removeProperty("ACCESS_MODE_DATA", "rw").toLowerCase();
this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
this.textStorage = ci.getTextStorage();
this.databaseURL = ci.getURL();
String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null);
if(listener != null) {
if(listener.startsWith("'")) {
listener = listener.substring(1);
}
if(listener.endsWith("'")) {
listener = listener.substring(0, listener.length()-1);
}
setEventListener(listener);
}
String log = ci.getProperty(SetTypes.LOG, null);
if(log != null) {
this.logIndexChanges = log.equals("2");
}
String ignoreSummary = ci.getProperty("RECOVER", null);
if(ignoreSummary != null) {
this.recovery = true;
}
boolean closeAtVmShutdown = ci.removeProperty("DB_CLOSE_ON_EXIT", true);
int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT, TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT);
this.cacheType = StringUtils.toUpperEnglish(ci.removeProperty("CACHE_TYPE", CacheLRU.TYPE_NAME));
try {
synchronized(this) {
open(traceLevelFile, traceLevelSystemOut);
}
if(closeAtVmShutdown) {
DatabaseCloser closeOnExit = new DatabaseCloser(this, 0, true);
try {
Runtime.getRuntime().addShutdownHook(closeOnExit);
} catch(IllegalStateException e) {
// shutdown in progress - just don't register the handler
// (maybe an application wants to write something into a database at shutdown time)
}
}
} catch(Throwable e) {
if(traceSystem != null) {
traceSystem.getTrace(Trace.DATABASE).error("opening " + databaseName, e);
traceSystem.close();
}
synchronized(this) {
closeOpenFilesAndUnlock();
}
throw Message.convert(e);
}
}
public static void setInitialPowerOffCount(int count) {
initialPowerOffCount = count;
}
......@@ -340,66 +402,6 @@ public class Database implements DataHandler {
return StringUtils.toUpperEnglish(n);
}
public Database(String name, ConnectionInfo ci, String cipher) throws SQLException {
this.compareMode = new CompareMode(null, null);
this.persistent = ci.isPersistent();
this.filePasswordHash = ci.getFilePasswordHash();
this.databaseName = name;
this.databaseShortName = parseDatabaseShortName();
this.cipher = cipher;
String lockMethodName = ci.removeProperty("FILE_LOCK", null);
this.accessModeLog = ci.removeProperty("ACCESS_MODE_LOG", "rw").toLowerCase();
this.accessModeData = ci.removeProperty("ACCESS_MODE_DATA", "rw").toLowerCase();
this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
this.textStorage = ci.getTextStorage();
this.databaseURL = ci.getURL();
String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null);
if(listener != null) {
if(listener.startsWith("'")) {
listener = listener.substring(1);
}
if(listener.endsWith("'")) {
listener = listener.substring(0, listener.length()-1);
}
setEventListener(listener);
}
String log = ci.getProperty(SetTypes.LOG, null);
if(log != null) {
this.logIndexChanges = log.equals("2");
}
String ignoreSummary = ci.getProperty("RECOVER", null);
if(ignoreSummary != null) {
this.recovery = true;
}
boolean closeAtVmShutdown = ci.removeProperty("DB_CLOSE_ON_EXIT", true);
int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT, TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT);
this.cacheType = StringUtils.toUpperEnglish(ci.removeProperty("CACHE_TYPE", CacheLRU.TYPE_NAME));
try {
synchronized(this) {
open(traceLevelFile, traceLevelSystemOut);
}
if(closeAtVmShutdown) {
DatabaseCloser closeOnExit = new DatabaseCloser(this, 0, true);
try {
Runtime.getRuntime().addShutdownHook(closeOnExit);
} catch(IllegalStateException e) {
// shutdown in progress - just don't register the handler
// (maybe an application wants to write something into a database at shutdown time)
}
}
} catch(Throwable e) {
if(traceSystem != null) {
traceSystem.getTrace(Trace.DATABASE).error("opening " + databaseName, e);
traceSystem.close();
}
synchronized(this) {
closeOpenFilesAndUnlock();
}
throw Message.convert(e);
}
}
private void open(int traceLevelFile, int traceLevelSystemOut) throws SQLException {
if(persistent) {
String dataFileName = databaseName + Constants.SUFFIX_DATA_FILE;
......
......@@ -7,9 +7,10 @@ package org.h2.engine;
import java.lang.ref.WeakReference;
public class DatabaseCloser extends Thread {
private final boolean shutdownHook;
private WeakReference databaseRef;
private int delayInMillis;
private boolean shutdownHook;
DatabaseCloser(Database db, int delayInMillis, boolean shutdownHook) {
this.databaseRef = new WeakReference(db);
......
......@@ -17,54 +17,19 @@ import org.h2.util.ObjectArray;
*/
public abstract class DbObject {
public static final int TABLE_OR_VIEW=0, INDEX=1, USER=2, SEQUENCE=3, TRIGGER=4;
public static final int CONSTRAINT = 5, SETTING = 6, ROLE = 7, RIGHT = 8, FUNCTION_ALIAS = 9;
public static final int SCHEMA = 10, CONSTANT = 11;
public static final int USER_DATATYPE = 12, COMMENT = 13;
protected String comment;
static int getCreateOrder(int type) {
switch(type) {
case SETTING:
return 0;
case USER:
return 1;
case SCHEMA:
return 2;
case USER_DATATYPE:
return 3;
case SEQUENCE:
return 4;
case CONSTANT:
return 5;
case FUNCTION_ALIAS:
return 6;
case TABLE_OR_VIEW:
return 7;
case INDEX:
return 8;
case CONSTRAINT:
return 9;
case TRIGGER:
return 10;
case ROLE:
return 11;
case RIGHT:
return 12;
case COMMENT:
return 13;
default:
throw Message.getInternalError("type="+type);
}
}
private int id;
protected Database database;
protected Trace trace;
private String objectName;
private long modificationId;
private boolean temporary;
protected String comment;
protected DbObject(Database database, int id, String name, String traceModule) {
this.database = database;
......@@ -148,4 +113,39 @@ public abstract class DbObject {
return comment;
}
static int getCreateOrder(int type) {
switch(type) {
case SETTING:
return 0;
case USER:
return 1;
case SCHEMA:
return 2;
case USER_DATATYPE:
return 3;
case SEQUENCE:
return 4;
case CONSTANT:
return 5;
case FUNCTION_ALIAS:
return 6;
case TABLE_OR_VIEW:
return 7;
case INDEX:
return 8;
case CONSTRAINT:
return 9;
case TRIGGER:
return 10;
case ROLE:
return 11;
case RIGHT:
return 12;
case COMMENT:
return 13;
default:
throw Message.getInternalError("type="+type);
}
}
}
......@@ -20,8 +20,8 @@ import org.h2.util.StringUtils;
public class Engine {
// TODO use a 'engine'/'master' database to allow shut down the server, view & kill sessions and so on
private HashMap databases = new HashMap();
private static Engine instance = new Engine();
private static final Engine instance = new Engine();
private final HashMap databases = new HashMap();
private Engine() {
// don't allow others to instantiate
......
......@@ -20,12 +20,12 @@ import org.h2.value.ValueNull;
public class FunctionAlias extends DbObject {
private boolean hasConnectionParam;
private String className;
private String methodName;
private Method javaMethod;
private int paramCount;
private boolean hasConnectionParam;
private int dataType;
private final int dataType;
public FunctionAlias(Database db, int id, String name, String javaClassMethod) throws SQLException {
super(db, id, name, Trace.FUNCTION);
......
......@@ -17,6 +17,7 @@ import org.h2.value.ValueInt;
import org.h2.value.ValueString;
public class MetaRecord {
private int id;
private int objectType;
private int headPos;
......
......@@ -7,21 +7,19 @@ package org.h2.engine;
import org.h2.command.Prepared;
public class Procedure {
private String name;
private Prepared prepared;
private final String name;
private final Prepared prepared;
public void setName(String name) {
this.name = name;
public Procedure(String name, Prepared prepared) {
this.name = name;
this.prepared = prepared;
}
public String getName() {
return name;
}
public void setPrepared(Prepared prepared) {
this.prepared = prepared;
}
public Prepared getPrepared() {
return prepared;
}
......
......@@ -13,7 +13,7 @@ import org.h2.util.ObjectArray;
public class Role extends RightOwner {
private boolean system;
private final boolean system;
public Role(Database database, int id, String roleName, boolean system) {
super(database, id, roleName, Trace.USER);
......
......@@ -35,6 +35,7 @@ import org.h2.value.ValueLong;
* @author Thomas
*/
public class Session implements SessionInterface {
private User user;
private int id;
private Database database;
......
......@@ -11,6 +11,7 @@ import org.h2.message.Trace;
import org.h2.table.Table;
public class Setting extends DbObject {
private int intValue;
private String stringValue;
......
......@@ -19,10 +19,10 @@ import org.h2.util.StringUtils;
public class User extends RightOwner {
private final boolean systemUser;
private byte[] salt;
private byte[] passwordHash;
private boolean admin;
private boolean systemUser;
public User(Database database, int id, String userName, boolean systemUser) {
super(database, id, userName, Trace.USER);
......
......@@ -42,18 +42,28 @@ public class Aggregate extends Expression {
public static final int COUNT_ALL = 0, COUNT = 1, SUM = 2, MIN = 3, MAX = 4, AVG = 5;
public static final int GROUP_CONCAT = 6, STDDEV_POP = 7, STDDEV_SAMP = 8;
public static final int VAR_POP = 9, VAR_SAMP = 10, SOME = 11, EVERY = 12, SELECTIVITY = 13;
private int type;
private final Database database;
private final int type;
private final Select select;
private final boolean distinct;
private Expression on;
private Expression separator;
private ObjectArray orderList;
private SortOrder sort;
private int dataType, scale;
private long precision;
private Select select;
private Database database;
private boolean distinct;
private static HashMap aggregates = new HashMap();
private static final HashMap aggregates = new HashMap();
public Aggregate(Database database, int type, Expression on, Select select, boolean distinct) {
this.database = database;
this.type = type;
this.on = on;
this.select = select;
this.distinct = distinct;
}
static {
addAggregate("COUNT", COUNT);
......@@ -85,14 +95,6 @@ public class Aggregate extends Expression {
return type == null ? -1 : type.intValue();
}
public Aggregate(Database database, int type, Expression on, Select select, boolean distinct) {
this.database = database;
this.type = type;
this.on = on;
this.select = select;
this.distinct = distinct;
}
public void setOrder(ObjectArray orderBy) {
this.orderList = orderBy;
}
......
......@@ -19,7 +19,7 @@ import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
public class AggregateData {
private int aggregateType;
private final int aggregateType;
private long count;
private ValueHashMap distinctValues;
private Value value;
......
......@@ -18,8 +18,8 @@ import org.h2.value.Value;
public class Alias extends Expression {
private final String alias;
private Expression expr;
private String alias;
public Alias(Expression expression, String alias) {
this.expr = expression;
......
......@@ -25,7 +25,7 @@ import org.h2.value.ValueString;
public class CompareLike extends Condition {
private CompareMode compareMode;
private final CompareMode compareMode;
private Expression left;
private Expression right;
private Expression escape;
......@@ -253,6 +253,7 @@ public class CompareLike extends Condition {
throw Message.getSQLException(Message.LIKE_ESCAPE_ERROR_1, StringUtils.addAsterisk(p, i));
}
type = MATCH;
lastAny = false;
} else if (c == '%') {
if(lastAny) {
continue;
......
......@@ -21,15 +21,19 @@ import org.h2.value.ValueNull;
* @author Thomas
*/
public class Comparison extends Condition {
public static final int EQUAL = 0, BIGGER_EQUAL = 1, BIGGER = 2, SMALLER_EQUAL = 3,
SMALLER = 4, NOT_EQUAL = 5, IS_NULL = 6, IS_NOT_NULL = 7;
// TODO refactor: comparison: there never is a comparison 'false', the constant is used only for index conditions
// TODO refactor: comparison: a comparison is never 'false'; the constant is used only for index conditions
public static final int FALSE = 8;
private final Database database;
private final int compareType;
private Expression left;
private Expression right;
private int compareType;
private int dataType = -1;
private Database database;
public Comparison(Session session, int compareType, Expression left, Expression right) {
this.database = session.getDatabase();
......
......@@ -19,9 +19,12 @@ import org.h2.value.ValueNull;
* @author Thomas
*/
public class ConditionAndOr extends Condition {
// TODO optimization: we could extend (ID=1 AND ID=B) to (ID=1 AND ID=B AND B=1)
public static final int AND = 0, OR = 1;
private int andOrType;
private final int andOrType;
private Expression left, right;
public ConditionAndOr(int andOrType, Expression left, Expression right) {
......
......@@ -20,7 +20,7 @@ import org.h2.value.ValueBoolean;
public class ConditionExists extends Condition {
private Query query;
private final Query query;
public ConditionExists(Query query) {
this.query = query;
......
......@@ -23,9 +23,10 @@ import org.h2.value.ValueNull;
*/
public class ConditionIn extends Condition {
private Database database;
private final Database database;
private Expression left;
private ObjectArray values;
private final ObjectArray values;
private Value min, max;
private int queryLevel;
......
......@@ -921,6 +921,10 @@ public class JdbcConnection extends TraceObject implements Connection {
session = new SessionRemote().createSession(ci);
} else {
SessionInterface si = (SessionInterface) ClassUtils.loadClass("org.h2.engine.Session").newInstance();
String baseDir = Constants.BASE_DIR;
if(baseDir != null) {
ci.setBaseDir(baseDir);
}
session = si.createSession(ci);
}
trace = session.getTrace();
......
......@@ -58,6 +58,9 @@ public class TcpServerThread implements Runnable {
String db = transfer.readString();
String originalURL = transfer.readString();
String baseDir = server.getBaseDir();
if(baseDir == null) {
baseDir = Constants.BASE_DIR;
}
ConnectionInfo ci = new ConnectionInfo(db);
if(baseDir != null) {
ci.setBaseDir(baseDir);
......
......@@ -167,8 +167,9 @@ public class WebServer implements Service {
ssl = Boolean.valueOf(args[++i]).booleanValue();
} else if("-webAllowOthers".equals(args[i])) {
allowOthers = Boolean.valueOf(args[++i]).booleanValue();
// } else if("-baseDir".equals(args[i])) {
// String baseDir = args[++i];
} else if("-baseDir".equals(args[i])) {
String baseDir = args[++i];
Constants.setBaseDir(baseDir);
}
}
// if(driverList != null) {
......
......@@ -717,6 +717,7 @@ public class MetaTable extends Table {
add(rows, new String[]{"h2.maxFileRetry", "" + Constants.MAX_FILE_RETRY});
add(rows, new String[]{"h2.lobCloseBetweenReads", "" + Constants.LOB_CLOSE_BETWEEN_READS});
add(rows, new String[]{"h2.allowBigDecimalExtensions", "" + Constants.ALLOW_BIG_DECIMAL_EXTENSIONS});
add(rows, new String[]{"h2.baseDir", "" + Constants.BASE_DIR});
break;
}
case TYPE_INFO: {
......
......@@ -59,7 +59,7 @@ public class Server implements Runnable {
System.out.println("-ftpWritePassword <password> (default: " + FtpServer.DEFAULT_WRITE_PASSWORD+")");
System.out.println("-log [true|false]");
System.out.println("-baseDir <directory>");
System.out.println("-baseDir <directory> (sets the base directory for H2 databases)");
System.out.println("-ifExists [true|false] (only existing databases may be opened)");
}
......
......@@ -94,26 +94,20 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
/*
public static void Constants.setBaseDir(String baseDir) throws IOException.
System property h2.baseDir (default: empty meaning current working directory).
Can be set to ~ (meaning the user directory) or any directory (is created if it does not yet exist).
java -Dh2.baseDir=/tmp/data org.h2.tools.Server
System.setProperty would work as well, needs to be set before connecting to any database.
whats faster, stringBuffer.append.append.append or buff.append buff.append...
maybe replace StringBuffer with own class (StringCreator)
Test with newest Hibernate
storages should be an inthashmap
SELECT * FROM DUAL WHERE 'a_z' LIKE '%=_%' ESCAPE '=';
SELECT * FROM DUAL WHERE 'a_z' LIKE '%\_%';
Test with newest Hibernate
-- SET client_encoding = 'UTF8';
-- SET check_function_bodies = false;
-- SET client_min_messages = warning;
-- CREATE PROCEDURAL LANGUAGE plperl;
-- CREATE PROCEDURAL LANGUAGE plpgsql;
--SET search_path = public, pg_catalog;
--SET default_tablespace = '';
--SET default_with_oids = false;
--id serial NOT NULL,
pg_catalog with views
......@@ -123,12 +117,10 @@ The unique object identifier of a row. PostgreSQL automatically adds this 4-byte
ctid (tuple identifier)
The identifier which describes the physical location of the tuple within the database. A pair of numbers are represented by the ctid: the block number, and tuple index within that block.
make sure INDEX_LOOKUP_NEW = is true by default.
Test Console (batch, javaw, different platforms)
test with openoffice (metadata changes)
set read-committed as the default
testHalt
......@@ -200,8 +192,6 @@ CREATE TABLE TEST( ID BIGINT PRIMARY KEY, CREATED TIMESTAMP);
INSERT INTO TEST VALUES(1, '2007-01-01 00:00:00');
SELECT * FROM TEST;
Cache size in KB
*/
/*
......
create table test();
insert into test values();
ALTER TABLE TEST ADD ID INTEGER;
select count(*) from test;
> 1;
drop table test;
select * from dual where 'a_z' like '%=_%' escape '=';
> 1;
create table test as select 1 from dual union all select 2 from dual;
drop table test;
......
......@@ -19,8 +19,9 @@ public class TestPattern extends TestBase {
test(comp, "B", "%_");
test(comp, "A", "A%");
test(comp, "A", "A%%");
test(comp, "A_A", "%\\_%");
for(int i=0; i<10; i++) {
for(int i=0; i<10000; i++) {
String pattern=getRandomPattern();
String value=getRandomValue();
test(comp, value, pattern);
......@@ -28,21 +29,18 @@ public class TestPattern extends TestBase {
}
void test(CompareLike comp, String value, String pattern) throws Exception {
// TODO test: need another regexp implementation (this one doesn't work for gcj)
// String regexp = initPatternRegexp(pattern);
// boolean resultRegexp = value.matches(regexp);
// boolean result =
comp.test(pattern, value, 'X');
// if(result != resultRegexp) {
// throw new Exception("Error: >"+value+"< LIKE >"+pattern+"< result="+result+" resultReg="+resultRegexp);
// }
String regexp = initPatternRegexp(pattern, '\\');
boolean resultRegexp = value.matches(regexp);
boolean result = comp.test(pattern, value, '\\');
if(result != resultRegexp) {
error("Error: >"+value+"< LIKE >"+pattern+"< result="+result+" resultReg="+resultRegexp);
}
}
static String getRandomValue() {
StringBuffer buff = new StringBuffer();
int len = (int)(Math.random() * 10);
String s = "ABCDEFGHIJKLMNOP";
String s = "AB_%\\";
for(int i=0; i<len; i++) {
buff.append(s.charAt((int)(Math.random()*s.length())));
}
......@@ -52,40 +50,45 @@ public class TestPattern extends TestBase {
static String getRandomPattern() {
StringBuffer buff = new StringBuffer();
int len = (int)(Math.random() * 4);
//String s = "ABC%_\\";
String s = "AB_";
String s = "A%_\\";
for(int i=0; i<len; i++) {
buff.append(s.charAt((int)(Math.random()*s.length())));
char c = s.charAt((int)(Math.random()*s.length()));
if((c == '_' || c == '%') && Math.random() > 0.5) {
buff.append('\\');
} else if(c=='\\') {
buff.append(c);
}
buff.append(c);
}
return buff.toString();
}
// private String initPatternRegexp(String pattern) {
// int len = pattern.length();
// StringBuffer buff = new StringBuffer();
// for (int i = 0; i < len; i++) {
// char c = pattern.charAt(i);
// if (escape != null && escape.charValue() == c) {
// if (i >= len) {
// throw Message.internal("escape can't be last char");
// }
// c = pattern.charAt(++i);
// buff.append('\\');
// buff.append(c);
// } else if (c == '%') {
// buff.append(".*");
// } else if (c == '_') {
// buff.append('.');
// } else if(c=='\\'){
// buff.append("\\\\");
// } else {
// buff.append(c);
// }
// // TODO regexp: there are other chars that need escaping
// }
// String regexp = buff.toString();
//// System.out.println("regexp = " + regexp);
// return regexp;
// }
private String initPatternRegexp(String pattern, char escape) throws Exception {
int len = pattern.length();
StringBuffer buff = new StringBuffer();
for (int i = 0; i < len; i++) {
char c = pattern.charAt(i);
if (escape == c) {
if (i >= len) {
error("escape can't be last char");
}
c = pattern.charAt(++i);
buff.append('\\');
buff.append(c);
} else if (c == '%') {
buff.append(".*");
} else if (c == '_') {
buff.append('.');
} else if(c=='\\'){
buff.append("\\\\");
} else {
buff.append(c);
}
// TODO regexp: there are other chars that need escaping
}
String regexp = buff.toString();
// System.out.println("regexp = " + regexp);
return regexp;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论