提交 e0e2869b authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 488ecc5e
...@@ -64,10 +64,11 @@ public abstract class Command implements CommandInterface { ...@@ -64,10 +64,11 @@ public abstract class Command implements CommandInterface {
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
Database database = session.getDatabase(); Database database = session.getDatabase();
Object sync = database.getMultiThreaded() ? (Object) session : (Object) database; Object sync = database.getMultiThreaded() ? (Object) session : (Object) database;
session.waitIfExclusiveModeEnabled();
synchronized (sync) { synchronized (sync) {
try { try {
database.checkPowerOff(); database.checkPowerOff();
session.setCurrentCommand(this); session.setCurrentCommand(this, startTime);
return query(maxrows); return query(maxrows);
} catch (Throwable e) { } catch (Throwable e) {
SQLException s = Message.convert(e); SQLException s = Message.convert(e);
...@@ -92,7 +93,7 @@ public abstract class Command implements CommandInterface { ...@@ -92,7 +93,7 @@ public abstract class Command implements CommandInterface {
} }
private void stop() throws SQLException { private void stop() throws SQLException {
session.setCurrentCommand(null); session.setCurrentCommand(null, 0);
if (!isTransactional()) { if (!isTransactional()) {
session.commit(true); session.commit(true);
} else if (session.getAutoCommit()) { } else if (session.getAutoCommit()) {
...@@ -115,9 +116,10 @@ public abstract class Command implements CommandInterface { ...@@ -115,9 +116,10 @@ public abstract class Command implements CommandInterface {
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
Database database = session.getDatabase(); Database database = session.getDatabase();
Object sync = database.getMultiThreaded() ? (Object) session : (Object) database; Object sync = database.getMultiThreaded() ? (Object) session : (Object) database;
session.waitIfExclusiveModeEnabled();
synchronized (sync) { synchronized (sync) {
int rollback = session.getLogId(); int rollback = session.getLogId();
session.setCurrentCommand(this); session.setCurrentCommand(this, startTime);
try { try {
database.checkPowerOff(); database.checkPowerOff();
return update(); return update();
......
...@@ -3656,6 +3656,12 @@ public class Parser { ...@@ -3656,6 +3656,12 @@ public class Parser {
Set command = new Set(session, SetTypes.MVCC); Set command = new Set(session, SetTypes.MVCC);
command.setInt(value ? 1 : 0); command.setInt(value ? 1 : 0);
return command; return command;
} else if (readIf("EXCLUSIVE")) {
readIfEqualOrTo();
boolean value = readBooleanSetting();
Set command = new Set(session, SetTypes.EXCLUSIVE);
command.setInt(value ? 1 : 0);
return command;
} else if (readIf("IGNORECASE")) { } else if (readIf("IGNORECASE")) {
readIfEqualOrTo(); readIfEqualOrTo();
boolean value = readBooleanSetting(); boolean value = readBooleanSetting();
......
...@@ -94,20 +94,17 @@ public class SelectUnion extends Query { ...@@ -94,20 +94,17 @@ public class SelectUnion extends Query {
} }
switch (unionType) { switch (unionType) {
case UNION: case UNION:
case EXCEPT:
left.setDistinct(true); left.setDistinct(true);
right.setDistinct(true); right.setDistinct(true);
result.setDistinct(); result.setDistinct();
break; break;
case UNION_ALL: case UNION_ALL:
break; break;
case EXCEPT: case INTERSECT:
result.setDistinct();
// fall through
case INTERSECT: {
left.setDistinct(true); left.setDistinct(true);
right.setDistinct(true); right.setDistinct(true);
break; break;
}
default: default:
throw Message.getInternalError("type=" + unionType); throw Message.getInternalError("type=" + unionType);
} }
......
...@@ -274,6 +274,12 @@ public class Set extends Prepared { ...@@ -274,6 +274,12 @@ public class Set extends Prepared {
database.setMaxOperationMemory(value); database.setMaxOperationMemory(value);
break; break;
} }
case SetTypes.EXCLUSIVE: {
session.getUser().checkAdmin();
int value = getIntValue();
database.setExclusiveSession(value == 1 ? session : null);
break;
}
default: default:
throw Message.getInternalError("type="+type); throw Message.getInternalError("type="+type);
} }
......
...@@ -20,10 +20,10 @@ public class SetTypes { ...@@ -20,10 +20,10 @@ public class SetTypes {
public static final int LOG = 19, THROTTLE = 20, MAX_MEMORY_UNDO = 21, MAX_LENGTH_INPLACE_LOB = 22; public static final int LOG = 19, THROTTLE = 20, MAX_MEMORY_UNDO = 21, MAX_LENGTH_INPLACE_LOB = 22;
public static final int COMPRESS_LOB = 23, ALLOW_LITERALS = 24, MULTI_THREADED = 25, SCHEMA = 26; public static final int COMPRESS_LOB = 23, ALLOW_LITERALS = 24, MULTI_THREADED = 25, SCHEMA = 26;
public static final int OPTIMIZE_REUSE_RESULTS = 27, SCHEMA_SEARCH_PATH = 28, UNDO_LOG = 29; public static final int OPTIMIZE_REUSE_RESULTS = 27, SCHEMA_SEARCH_PATH = 28, UNDO_LOG = 29;
public static final int REFERENTIAL_INTEGRITY = 30, MVCC = 31, MAX_OPERATION_MEMORY = 32; public static final int REFERENTIAL_INTEGRITY = 30, MVCC = 31, MAX_OPERATION_MEMORY = 32, EXCLUSIVE = 33;
private static ObjectArray types = new ObjectArray(); private static ObjectArray types = new ObjectArray();
static { static {
setType(IGNORECASE, "IGNORECASE"); setType(IGNORECASE, "IGNORECASE");
setType(MAX_LOG_SIZE, "MAX_LOG_SIZE"); setType(MAX_LOG_SIZE, "MAX_LOG_SIZE");
...@@ -57,6 +57,7 @@ public class SetTypes { ...@@ -57,6 +57,7 @@ public class SetTypes {
setType(REFERENTIAL_INTEGRITY, "REFERENTIAL_INTEGRITY"); setType(REFERENTIAL_INTEGRITY, "REFERENTIAL_INTEGRITY");
setType(MVCC, "MVCC"); setType(MVCC, "MVCC");
setType(MAX_OPERATION_MEMORY, "MAX_OPERATION_MEMORY"); setType(MAX_OPERATION_MEMORY, "MAX_OPERATION_MEMORY");
setType(EXCLUSIVE, "EXCLUSIVE");
} }
private static void setType(int type, String name) { private static void setType(int type, String name) {
......
...@@ -315,6 +315,7 @@ public class ErrorCode { ...@@ -315,6 +315,7 @@ public class ErrorCode {
public static final int AGGREGATE_NOT_FOUND_1 = 90132; public static final int AGGREGATE_NOT_FOUND_1 = 90132;
public static final int CANNOT_CHANGE_SETTING_WHEN_OPEN_1 = 90133; public static final int CANNOT_CHANGE_SETTING_WHEN_OPEN_1 = 90133;
public static final int ACCESS_DENIED_TO_CLASS_1 = 90134; public static final int ACCESS_DENIED_TO_CLASS_1 = 90134;
public static final int DATABASE_IS_IN_EXCLUSIVE_MODE = 90135;
/** /**
* INTERNAL * INTERNAL
......
...@@ -7,9 +7,11 @@ package org.h2.engine; ...@@ -7,9 +7,11 @@ package org.h2.engine;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
...@@ -85,7 +87,8 @@ public class Database implements DataHandler { ...@@ -85,7 +87,8 @@ public class Database implements DataHandler {
private final HashMap userDataTypes = new HashMap(); private final HashMap userDataTypes = new HashMap();
private final HashMap aggregates = new HashMap(); private final HashMap aggregates = new HashMap();
private final HashMap comments = new HashMap(); private final HashMap comments = new HashMap();
private final HashSet sessions = new HashSet(); private final Set sessions = Collections.synchronizedSet(new HashSet());
private Session exclusiveSession;
private final BitField objectIds = new BitField(); private final BitField objectIds = new BitField();
private final Object lobSyncObject = new Object(); private final Object lobSyncObject = new Object();
...@@ -772,7 +775,10 @@ public class Database implements DataHandler { ...@@ -772,7 +775,10 @@ public class Database implements DataHandler {
return getUser(name, Message.getSQLException(ErrorCode.USER_NOT_FOUND_1, name)); return getUser(name, Message.getSQLException(ErrorCode.USER_NOT_FOUND_1, name));
} }
public synchronized Session createSession(User user) { public synchronized Session createSession(User user) throws SQLException {
if (exclusiveSession != null) {
throw Message.getSQLException(ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE);
}
Session session = new Session(this, user, ++nextSessionId); Session session = new Session(this, user, ++nextSessionId);
sessions.add(session); sessions.add(session);
traceSystem.getTrace(Trace.SESSION).info("connecting #" + session.getId() + " to " + databaseName); traceSystem.getTrace(Trace.SESSION).info("connecting #" + session.getId() + " to " + databaseName);
...@@ -785,6 +791,9 @@ public class Database implements DataHandler { ...@@ -785,6 +791,9 @@ public class Database implements DataHandler {
public synchronized void removeSession(Session session) throws SQLException { public synchronized void removeSession(Session session) throws SQLException {
if (session != null) { if (session != null) {
if (exclusiveSession == session) {
exclusiveSession = null;
}
sessions.remove(session); sessions.remove(session);
if (session != systemSession) { if (session != systemSession) {
traceSystem.getTrace(Trace.SESSION).info("disconnecting #" + session.getId()); traceSystem.getTrace(Trace.SESSION).info("disconnecting #" + session.getId());
...@@ -1032,7 +1041,7 @@ public class Database implements DataHandler { ...@@ -1032,7 +1041,7 @@ public class Database implements DataHandler {
return log; return log;
} }
public synchronized Session[] getSessions() { public Session[] getSessions() {
Session[] list = new Session[sessions.size()]; Session[] list = new Session[sessions.size()];
sessions.toArray(list); sessions.toArray(list);
return list; return list;
...@@ -1624,4 +1633,12 @@ public class Database implements DataHandler { ...@@ -1624,4 +1633,12 @@ public class Database implements DataHandler {
return maxOperationMemory; return maxOperationMemory;
} }
public Session getExclusiveSession() {
return exclusiveSession;
}
public void setExclusiveSession(Session session) {
this.exclusiveSession = session;
}
} }
...@@ -73,7 +73,8 @@ public class Session implements SessionInterface { ...@@ -73,7 +73,8 @@ public class Session implements SessionInterface {
private String currentTransactionName; private String currentTransactionName;
private boolean isClosed; private boolean isClosed;
private boolean rollbackMode; private boolean rollbackMode;
private long loginTime = System.currentTimeMillis(); private long sessionStart = System.currentTimeMillis();
private long currentCommandStart;
public Session() { public Session() {
} }
...@@ -498,8 +499,9 @@ public class Session implements SessionInterface { ...@@ -498,8 +499,9 @@ public class Session implements SessionInterface {
} }
} }
public void setCurrentCommand(Command command) { public void setCurrentCommand(Command command, long startTime) {
this.currentCommand = command; this.currentCommand = command;
this.currentCommandStart = startTime;
} }
public void checkCancelled() throws SQLException { public void checkCancelled() throws SQLException {
...@@ -508,9 +510,12 @@ public class Session implements SessionInterface { ...@@ -508,9 +510,12 @@ public class Session implements SessionInterface {
} }
} }
public String getCurrentCommand() { public Command getCurrentCommand() {
Command c = currentCommand; return currentCommand;
return c == null ? null : c.toString(); }
public long getCurrentCommandStart() {
return currentCommandStart;
} }
public boolean getAllowLiterals() { public boolean getAllowLiterals() {
...@@ -609,8 +614,8 @@ public class Session implements SessionInterface { ...@@ -609,8 +614,8 @@ public class Session implements SessionInterface {
return rollbackMode; return rollbackMode;
} }
public long getLoginTime() { public long getSessionStart() {
return loginTime; return sessionStart;
} }
public Table[] getLocks() { public Table[] getLocks() {
...@@ -621,4 +626,18 @@ public class Session implements SessionInterface { ...@@ -621,4 +626,18 @@ public class Session implements SessionInterface {
} }
} }
public void waitIfExclusiveModeEnabled() {
while (true) {
Session exclusive = database.getExclusiveSession();
if (exclusive == null || exclusive == this) {
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore
}
}
}
} }
...@@ -16,6 +16,7 @@ import java.util.HashMap; ...@@ -16,6 +16,7 @@ import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import org.h2.command.Command;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.Mode; import org.h2.engine.Mode;
...@@ -85,7 +86,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -85,7 +86,7 @@ public class Function extends Expression implements FunctionCall {
public static final int IFNULL = 200, CASEWHEN = 201, CONVERT = 202, CAST = 203, COALESCE = 204, NULLIF = 205, public static final int IFNULL = 200, CASEWHEN = 201, CONVERT = 202, CAST = 203, COALESCE = 204, NULLIF = 205,
CASE = 206, NEXTVAL = 207, CURRVAL = 208, ARRAY_GET = 209, CSVREAD = 210, CSVWRITE = 211, CASE = 206, NEXTVAL = 207, CURRVAL = 208, ARRAY_GET = 209, CSVREAD = 210, CSVWRITE = 211,
MEMORY_FREE = 212, MEMORY_USED = 213, LOCK_MODE = 214, SCHEMA = 215, SESSION_ID = 216, ARRAY_LENGTH = 217, MEMORY_FREE = 212, MEMORY_USED = 213, LOCK_MODE = 214, SCHEMA = 215, SESSION_ID = 216, ARRAY_LENGTH = 217,
LINK_SCHEMA = 218, TABLE = 219, LEAST = 220, GREATEST = 221, TABLE_DISTINCT = 222; LINK_SCHEMA = 218, TABLE = 219, LEAST = 220, GREATEST = 221, TABLE_DISTINCT = 222, CANCEL_SESSION = 223;
private static final int VAR_ARGS = -1; private static final int VAR_ARGS = -1;
...@@ -287,6 +288,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -287,6 +288,7 @@ public class Function extends Expression implements FunctionCall {
addFunctionWithNull("TABLE_DISTINCT", TABLE_DISTINCT, VAR_ARGS, Value.RESULT_SET); addFunctionWithNull("TABLE_DISTINCT", TABLE_DISTINCT, VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("LEAST", LEAST, VAR_ARGS, Value.NULL); addFunctionWithNull("LEAST", LEAST, VAR_ARGS, Value.NULL);
addFunctionWithNull("GREATEST", GREATEST, VAR_ARGS, Value.NULL); addFunctionWithNull("GREATEST", GREATEST, VAR_ARGS, Value.NULL);
addFunction("CANCEL_SESSION", CANCEL_SESSION, 1, Value.BOOLEAN);
} }
private static void addFunction(String name, int type, int parameterCount, int dataType, private static void addFunction(String name, int type, int parameterCount, int dataType,
...@@ -361,41 +363,332 @@ public class Function extends Expression implements FunctionCall { ...@@ -361,41 +363,332 @@ public class Function extends Expression implements FunctionCall {
return null; return null;
} }
public Value getValueWithArgs(Session session, Expression[] args) throws SQLException { public Value getSimpleValue(Session session, Value v0, Expression[] args) throws SQLException {
if (info.nullIfParameterIsNull) { Value result;
switch (info.type) {
case ABS:
result = v0.getSignum() > 0 ? v0 : v0.negate();
break;
case ACOS:
result = ValueDouble.get(Math.acos(v0.getDouble()));
break;
case ASIN:
result = ValueDouble.get(Math.asin(v0.getDouble()));
break;
case ATAN:
result = ValueDouble.get(Math.atan(v0.getDouble()));
break;
case CEILING:
result = ValueDouble.get(Math.ceil(v0.getDouble()));
break;
case COS:
result = ValueDouble.get(Math.cos(v0.getDouble()));
break;
case COT: {
double d = Math.tan(v0.getDouble());
if (d == 0.0) {
throw Message.getSQLException(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
result = ValueDouble.get(1. / d);
break;
}
case DEGREES:
result = ValueDouble.get(Math.toDegrees(v0.getDouble()));
break;
case EXP:
result = ValueDouble.get(Math.exp(v0.getDouble()));
break;
case FLOOR:
result = ValueDouble.get(Math.floor(v0.getDouble()));
break;
case LOG:
result = ValueDouble.get(Math.log(v0.getDouble()));
break;
case LOG10:
result = ValueDouble.get(log10(v0.getDouble()));
break;
case PI:
result = ValueDouble.get(Math.PI);
break;
case RADIANS:
result = ValueDouble.get(Math.toRadians(v0.getDouble()));
break;
case RAND: {
if (v0 != null) {
session.getRandom().setSeed(v0.getInt());
}
// TODO function rand: if seed value is set,
// return a random value? probably yes
result = ValueDouble.get(session.getRandom().nextDouble());
break;
}
case ROUNDMAGIC:
result = ValueDouble.get(roundmagic(v0.getDouble()));
break;
case SIGN:
result = ValueInt.get(v0.getSignum());
break;
case SIN:
result = ValueDouble.get(Math.sin(v0.getDouble()));
break;
case SQRT:
result = ValueDouble.get(Math.sqrt(v0.getDouble()));
break;
case TAN:
result = ValueDouble.get(Math.tan(v0.getDouble()));
break;
case SECURE_RAND:
result = ValueBytes.getNoCopy(RandomUtils.getSecureBytes(v0.getInt()));
break;
case EXPAND:
result = ValueBytes.getNoCopy(CompressTool.getInstance().expand(v0.getBytesNoCopy()));
break;
case ZERO:
result = ValueInt.get(0);
break;
case RANDOM_UUID:
result = ValueUuid.getNewRandom();
break;
// string
case ASCII: {
String s = v0.getString();
if (s.length() == 0) {
result = ValueNull.INSTANCE;
} else {
result = ValueInt.get(s.charAt(0));
}
break;
}
case BIT_LENGTH:
result = ValueInt.get(16 * length(v0));
break;
case CHAR:
result = ValueString.get(String.valueOf((char) v0.getInt()));
break;
case CHAR_LENGTH:
case LENGTH:
result = ValueInt.get(length(v0));
break;
case OCTET_LENGTH:
result = ValueInt.get(2 * length(v0));
break;
case CONCAT: {
result = ValueNull.INSTANCE;
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
if (getNullOrValue(session, args, i) == ValueNull.INSTANCE) { Value v = args[i].getValue(session);
return ValueNull.INSTANCE; if (v == ValueNull.INSTANCE) {
continue;
}
if (result == ValueNull.INSTANCE) {
result = v;
} else {
result = ValueString.get(result.getString().concat(v.getString()));
} }
} }
break;
}
case HEXTORAW:
result = ValueString.get(hexToRaw(v0.getString()));
break;
case LOWER:
case LCASE:
// TODO this is locale specific, need to document or provide a way
// to set the locale
result = ValueString.get(v0.getString().toLowerCase());
break;
case RAWTOHEX:
result = ValueString.get(rawToHex(v0.getString()));
break;
case SOUNDEX:
result = ValueString.get(getSoundex(v0.getString()));
break;
case SPACE: {
// TODO DOS attacks: limit len?
int len = Math.max(0, v0.getInt());
char[] chars = new char[len];
for (int i = len - 1; i >= 0; i--) {
chars[i] = ' ';
}
result = ValueString.get(new String(chars));
break;
}
case UPPER:
case UCASE:
// TODO this is locale specific, need to document or provide a way
// to set the locale
result = ValueString.get(v0.getString().toUpperCase());
break;
case STRINGENCODE:
result = ValueString.get(StringUtils.javaEncode(v0.getString()));
break;
case STRINGDECODE:
result = ValueString.get(StringUtils.javaDecode(v0.getString()));
break;
case STRINGTOUTF8:
result = ValueBytes.getNoCopy(StringUtils.utf8Encode(v0.getString()));
break;
case UTF8TOSTRING:
result = ValueString.get(StringUtils.utf8Decode(v0.getBytesNoCopy()));
break;
case XMLCOMMENT:
result = ValueString.get(StringUtils.xmlComment(v0.getString()));
break;
case XMLCDATA:
result = ValueString.get(StringUtils.xmlCData(v0.getString()));
break;
case XMLSTARTDOC:
result = ValueString.get(StringUtils.xmlStartDoc());
break;
case XMLTEXT:
result = ValueString.get(StringUtils.xmlText(v0.getString()));
break;
case DAYNAME: {
synchronized (FORMAT_DAYNAME) {
result = ValueString.get(FORMAT_DAYNAME.format(v0.getDateNoCopy()));
}
break;
}
case DAYOFMONTH:
result = ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.DAY_OF_MONTH));
break;
case DAYOFWEEK:
result = ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.DAY_OF_WEEK));
break;
case DAYOFYEAR:
result = ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.DAY_OF_YEAR));
break;
case HOUR:
result = ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.HOUR_OF_DAY));
break;
case MINUTE:
result = ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.MINUTE));
break;
case MONTH:
result = ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.MONTH));
break;
case MONTHNAME: {
synchronized (FORMAT_MONTHNAME) {
result = ValueString.get(FORMAT_MONTHNAME.format(v0.getDateNoCopy()));
}
break;
}
case QUARTER:
result = ValueInt.get((getDatePart(v0.getTimestamp(), Calendar.MONTH) - 1) / 3 + 1);
break;
case SECOND:
result = ValueInt.get(getDatePart(v0.getTimestamp(), Calendar.SECOND));
break;
case WEEK:
result = ValueInt.get(getDatePart(v0.getTimestamp(), Calendar.WEEK_OF_YEAR));
break;
case YEAR:
result = ValueInt.get(getDatePart(v0.getTimestamp(), Calendar.YEAR));
break;
case CURDATE:
case CURRENT_DATE:
// need to normalize
result = ValueDate.get(new Date(System.currentTimeMillis()));
break;
case CURTIME:
case CURRENT_TIME:
// need to normalize
result = ValueTime.get(new Time(System.currentTimeMillis()));
break;
case NOW:
case CURRENT_TIMESTAMP: {
ValueTimestamp vt = ValueTimestamp.getNoCopy(new Timestamp(System.currentTimeMillis()));
if (v0 != null) {
Mode mode = database.getMode();
vt = (ValueTimestamp) vt.convertScale(mode.convertOnlyToSmallerScale, v0.getInt());
}
result = vt;
break;
}
case DATABASE:
result = ValueString.get(database.getShortName());
break;
case USER:
case CURRENT_USER:
result = ValueString.get(session.getUser().getName());
break;
case IDENTITY:
result = session.getLastIdentity();
break;
case AUTOCOMMIT:
result = ValueBoolean.get(session.getAutoCommit());
break;
case READONLY:
result = ValueBoolean.get(database.getReadOnly());
break;
case DATABASE_PATH: {
String path = database.getDatabasePath();
result = path == null ? (Value) ValueNull.INSTANCE : ValueString.get(path);
break;
}
case LOCK_TIMEOUT:
result = ValueInt.get(session.getLockTimeout());
break;
case CAST:
case CONVERT: {
v0 = v0.convertTo(dataType);
Mode mode = database.getMode();
v0 = v0.convertScale(mode.convertOnlyToSmallerScale, scale);
v0 = v0.convertPrecision(getPrecision());
result = v0;
break;
}
case TABLE:
result = getTable(session, args, false, false);
break;
case TABLE_DISTINCT:
result = getTable(session, args, false, true);
break;
case MEMORY_FREE:
session.getUser().checkAdmin();
result = ValueInt.get(MemoryUtils.getMemoryFree());
break;
case MEMORY_USED:
session.getUser().checkAdmin();
result = ValueInt.get(MemoryUtils.getMemoryUsed());
break;
case LOCK_MODE:
result = ValueInt.get(database.getLockMode());
break;
case SCHEMA:
result = ValueString.get(session.getCurrentSchemaName());
break;
case SESSION_ID:
result = ValueInt.get(session.getId());
break;
case IFNULL: {
result = v0 == ValueNull.INSTANCE ? args[1].getValue(session) : v0;
break;
} }
Value v0 = getNullOrValue(session, args, 0);
switch (info.type) {
case IFNULL:
return v0 == ValueNull.INSTANCE ? args[1].getValue(session) : v0;
case CASEWHEN: { case CASEWHEN: {
Expression result; Expression expr;
if (v0 == ValueNull.INSTANCE || !v0.getBoolean().booleanValue()) { if (v0 == ValueNull.INSTANCE || !v0.getBoolean().booleanValue()) {
result = args[2]; expr = args[2];
} else { } else {
result = args[1]; expr = args[1];
} }
Value v = result.getValue(session); Value v = expr.getValue(session);
v = v.convertTo(dataType); result = v.convertTo(dataType);
return v; break;
} }
case COALESCE: { case COALESCE: {
result = v0;
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
Value v = i == 0 ? v0 : args[i].getValue(session); Value v = i == 0 ? v0 : args[i].getValue(session);
if (!(v == ValueNull.INSTANCE)) { if (!(v == ValueNull.INSTANCE)) {
return v.convertTo(dataType); result = v.convertTo(dataType);
break;
} }
} }
return v0; break;
} }
case GREATEST: case GREATEST:
case LEAST: { case LEAST: {
Value result = ValueNull.INSTANCE; result = ValueNull.INSTANCE;
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
Value v = i == 0 ? v0 : args[i].getValue(session); Value v = i == 0 ? v0 : args[i].getValue(session);
if (!(v == ValueNull.INSTANCE)) { if (!(v == ValueNull.INSTANCE)) {
...@@ -412,18 +705,22 @@ public class Function extends Expression implements FunctionCall { ...@@ -412,18 +705,22 @@ public class Function extends Expression implements FunctionCall {
} }
} }
} }
return result; break;
} }
case CASE: { case CASE: {
// TODO function CASE: document & implement functionality result = null;
int i = 0; int i = 0;
for (; i < args.length; i++) { for (; i < args.length; i++) {
Value when = args[i++].getValue(session); Value when = args[i++].getValue(session);
if (Boolean.TRUE.equals(when)) { if (Boolean.TRUE.equals(when)) {
return args[i].getValue(session); result = args[i].getValue(session);
break;
} }
} }
return i < args.length ? args[i].getValue(session) : ValueNull.INSTANCE; if (result == null) {
result = i < args.length ? args[i].getValue(session) : ValueNull.INSTANCE;
}
break;
} }
case ARRAY_GET: { case ARRAY_GET: {
if (v0.getType() == Value.ARRAY) { if (v0.getType() == Value.ARRAY) {
...@@ -431,372 +728,248 @@ public class Function extends Expression implements FunctionCall { ...@@ -431,372 +728,248 @@ public class Function extends Expression implements FunctionCall {
int element = v1.getInt(); int element = v1.getInt();
Value[] list = ((ValueArray) v0).getList(); Value[] list = ((ValueArray) v0).getList();
if (element < 1 || element > list.length) { if (element < 1 || element > list.length) {
return ValueNull.INSTANCE; result = ValueNull.INSTANCE;
} else {
result = list[element - 1];
} }
return list[element - 1]; } else {
result = ValueNull.INSTANCE;
} }
return ValueNull.INSTANCE; break;
} }
case ARRAY_LENGTH: { case ARRAY_LENGTH: {
if (v0.getType() == Value.ARRAY) { if (v0.getType() == Value.ARRAY) {
Value[] list = ((ValueArray) v0).getList(); Value[] list = ((ValueArray) v0).getList();
return ValueInt.get(list.length); result = ValueInt.get(list.length);
} else {
result = ValueNull.INSTANCE;
} }
return ValueNull.INSTANCE; break;
}
case CANCEL_SESSION: {
result = ValueBoolean.get(cancelStatement(session, v0.getInt()));
break;
} }
default: default:
// ok result = null;
}
return result;
}
private boolean cancelStatement(Session session, int targetSessionId) throws SQLException {
session.getUser().checkAdmin();
Session[] sessions = session.getDatabase().getSessions();
for (int i = 0; i < sessions.length; i++) {
Session s = sessions[i];
if (s.getId() == targetSessionId) {
Command c = s.getCurrentCommand();
if (c == null) {
return false;
} else {
c.cancel();
return true;
}
}
}
return false;
}
public Value getValueWithArgs(Session session, Expression[] args) throws SQLException {
if (info.nullIfParameterIsNull) {
for (int i = 0; i < args.length; i++) {
if (getNullOrValue(session, args, i) == ValueNull.INSTANCE) {
return ValueNull.INSTANCE;
}
}
}
Value v0 = getNullOrValue(session, args, 0);
Value resultSimple = getSimpleValue(session, v0, args);
if (resultSimple != null) {
return resultSimple;
} }
Value v1 = getNullOrValue(session, args, 1); Value v1 = getNullOrValue(session, args, 1);
Value v2 = getNullOrValue(session, args, 2); Value v2 = getNullOrValue(session, args, 2);
Value v3 = getNullOrValue(session, args, 3); Value v3 = getNullOrValue(session, args, 3);
Value v4 = getNullOrValue(session, args, 4); Value v4 = getNullOrValue(session, args, 4);
Value v5 = getNullOrValue(session, args, 5); Value v5 = getNullOrValue(session, args, 5);
Value result;
switch (info.type) { switch (info.type) {
case ABS:
return v0.getSignum() > 0 ? v0 : v0.negate();
case ACOS:
return ValueDouble.get(Math.acos(v0.getDouble()));
case ASIN:
return ValueDouble.get(Math.asin(v0.getDouble()));
case ATAN:
return ValueDouble.get(Math.atan(v0.getDouble()));
case ATAN2: case ATAN2:
return ValueDouble.get(Math.atan2(v0.getDouble(), v1.getDouble())); result = ValueDouble.get(Math.atan2(v0.getDouble(), v1.getDouble()));
break;
case BITAND: case BITAND:
return ValueInt.get(v0.getInt() & v1.getInt()); result = ValueInt.get(v0.getInt() & v1.getInt());
break;
case BITOR: case BITOR:
return ValueInt.get(v0.getInt() | v1.getInt()); result = ValueInt.get(v0.getInt() | v1.getInt());
break;
case BITXOR: case BITXOR:
return ValueInt.get(v0.getInt() ^ v1.getInt()); result = ValueInt.get(v0.getInt() ^ v1.getInt());
case CEILING: break;
return ValueDouble.get(Math.ceil(v0.getDouble()));
case COS:
return ValueDouble.get(Math.cos(v0.getDouble()));
case COT: {
double d = Math.tan(v0.getDouble());
if (d == 0.0) {
throw Message.getSQLException(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
}
return ValueDouble.get(1. / d);
}
case DEGREES:
return ValueDouble.get(Math.toDegrees(v0.getDouble()));
case EXP:
return ValueDouble.get(Math.exp(v0.getDouble()));
case FLOOR:
return ValueDouble.get(Math.floor(v0.getDouble()));
case LOG:
return ValueDouble.get(Math.log(v0.getDouble()));
case LOG10:
return ValueDouble.get(log10(v0.getDouble()));
case MOD: { case MOD: {
int x = v1.getInt(); int x = v1.getInt();
if (x == 0.0) { if (x == 0.0) {
throw Message.getSQLException(ErrorCode.DIVISION_BY_ZERO_1, getSQL()); throw Message.getSQLException(ErrorCode.DIVISION_BY_ZERO_1, getSQL());
} }
return ValueInt.get(v0.getInt() % x); result = ValueInt.get(v0.getInt() % x);
break;
} }
case PI:
return ValueDouble.get(Math.PI);
case POWER: case POWER:
return ValueDouble.get(Math.pow(v0.getDouble(), v1.getDouble())); result = ValueDouble.get(Math.pow(v0.getDouble(), v1.getDouble()));
case RADIANS: break;
return ValueDouble.get(Math.toRadians(v0.getDouble()));
case RAND: {
if (v0 != null) {
session.getRandom().setSeed(v0.getInt());
}
// TODO function rand: if seed value is set, return a random value?
// probably yes
return ValueDouble.get(session.getRandom().nextDouble());
}
case ROUND: { case ROUND: {
double f = Math.pow(10., v1.getDouble()); double f = Math.pow(10., v1.getDouble());
return ValueDouble.get(Math.round(v0.getDouble() * f) / f); result = ValueDouble.get(Math.round(v0.getDouble() * f) / f);
break;
} }
case ROUNDMAGIC:
return ValueDouble.get(roundmagic(v0.getDouble()));
case SIGN:
return ValueInt.get(v0.getSignum());
case SIN:
return ValueDouble.get(Math.sin(v0.getDouble()));
case SQRT:
return ValueDouble.get(Math.sqrt(v0.getDouble()));
case TAN:
return ValueDouble.get(Math.tan(v0.getDouble()));
case TRUNCATE: { case TRUNCATE: {
double d = v0.getDouble(); double d = v0.getDouble();
int p = v1.getInt(); int p = v1.getInt();
double f = Math.pow(10., p); double f = Math.pow(10., p);
double g = d * f; double g = d * f;
return ValueDouble.get(((d < 0) ? Math.ceil(g) : Math.floor(g)) / f); result = ValueDouble.get(((d < 0) ? Math.ceil(g) : Math.floor(g)) / f);
break;
} }
case SECURE_RAND:
return ValueBytes.getNoCopy(RandomUtils.getSecureBytes(v0.getInt()));
case HASH: case HASH:
return ValueBytes.getNoCopy(getHash(v0.getString(), v1.getBytesNoCopy(), v2.getInt())); result = ValueBytes.getNoCopy(getHash(v0.getString(), v1.getBytesNoCopy(), v2.getInt()));
break;
case ENCRYPT: case ENCRYPT:
return ValueBytes.getNoCopy(encrypt(v0.getString(), v1.getBytesNoCopy(), v2.getBytesNoCopy())); result = ValueBytes.getNoCopy(encrypt(v0.getString(), v1.getBytesNoCopy(), v2.getBytesNoCopy()));
break;
case DECRYPT: case DECRYPT:
return ValueBytes.getNoCopy(decrypt(v0.getString(), v1.getBytesNoCopy(), v2.getBytesNoCopy())); result = ValueBytes.getNoCopy(decrypt(v0.getString(), v1.getBytesNoCopy(), v2.getBytesNoCopy()));
break;
case COMPRESS: { case COMPRESS: {
String algorithm = null; String algorithm = null;
if (v1 != null) { if (v1 != null) {
algorithm = v1.getString(); algorithm = v1.getString();
} }
return ValueBytes.getNoCopy(CompressTool.getInstance().compress(v0.getBytesNoCopy(), algorithm)); result = ValueBytes.getNoCopy(CompressTool.getInstance().compress(v0.getBytesNoCopy(), algorithm));
} break;
case EXPAND:
return ValueBytes.getNoCopy(CompressTool.getInstance().expand(v0.getBytesNoCopy()));
case ZERO:
return ValueInt.get(0);
case RANDOM_UUID:
return ValueUuid.getNewRandom();
// string
case ASCII: {
String s = v0.getString();
if (s.length() == 0) {
return ValueNull.INSTANCE;
}
return ValueInt.get(s.charAt(0));
}
case BIT_LENGTH:
return ValueInt.get(16 * length(v0));
case CHAR:
return ValueString.get(String.valueOf((char) v0.getInt()));
case CHAR_LENGTH:
case LENGTH:
return ValueInt.get(length(v0));
case OCTET_LENGTH:
return ValueInt.get(2 * length(v0));
case CONCAT: {
Value concat = ValueNull.INSTANCE;
for (int i = 0; i < args.length; i++) {
Value v = args[i].getValue(session);
if (v == ValueNull.INSTANCE) {
continue;
}
if (concat == ValueNull.INSTANCE) {
concat = v;
} else {
concat = ValueString.get(concat.getString().concat(v.getString()));
}
}
return concat;
} }
case DIFFERENCE: case DIFFERENCE:
return ValueInt.get(getDifference(v0.getString(), v1.getString())); result = ValueInt.get(getDifference(v0.getString(), v1.getString()));
case HEXTORAW: break;
return ValueString.get(hexToRaw(v0.getString()));
case INSERT: { case INSERT: {
if (v1 == ValueNull.INSTANCE || v2 == ValueNull.INSTANCE) { if (v1 == ValueNull.INSTANCE || v2 == ValueNull.INSTANCE) {
return v1; result = v1;
} else {
result = ValueString.get(insert(v0.getString(), v1.getInt(), v2.getInt(), v3.getString()));
} }
return ValueString.get(insert(v0.getString(), v1.getInt(), v2.getInt(), v3.getString())); break;
} }
case LOWER:
case LCASE:
// TODO this is locale specific, need to document or provide a way
// to set the locale
return ValueString.get(v0.getString().toLowerCase());
case LEFT: case LEFT:
return ValueString.get(left(v0.getString(), v1.getInt())); result = ValueString.get(left(v0.getString(), v1.getInt()));
break;
case LOCATE: { case LOCATE: {
int start = v2 == null ? 0 : v2.getInt(); int start = v2 == null ? 0 : v2.getInt();
return ValueInt.get(locate(v0.getString(), v1.getString(), start)); result = ValueInt.get(locate(v0.getString(), v1.getString(), start));
break;
} }
case INSTR: { case INSTR: {
int start = v2 == null ? 0 : v2.getInt(); int start = v2 == null ? 0 : v2.getInt();
return ValueInt.get(locate(v1.getString(), v0.getString(), start)); result = ValueInt.get(locate(v1.getString(), v0.getString(), start));
break;
} }
case RAWTOHEX:
return ValueString.get(rawToHex(v0.getString()));
case REPEAT: { case REPEAT: {
// TODO DOS attacks: limit len?
int count = Math.max(0, v1.getInt()); int count = Math.max(0, v1.getInt());
return ValueString.get(repeat(v0.getString(), count)); result = ValueString.get(repeat(v0.getString(), count));
break;
} }
case REPLACE: { case REPLACE: {
String s0 = v0 == ValueNull.INSTANCE ? "" : v0.getString(); String s0 = v0 == ValueNull.INSTANCE ? "" : v0.getString();
String s1 = v1 == ValueNull.INSTANCE ? "" : v1.getString(); String s1 = v1 == ValueNull.INSTANCE ? "" : v1.getString();
String s2 = (v2 == null || v2 == ValueNull.INSTANCE) ? "" : v2.getString(); String s2 = (v2 == null || v2 == ValueNull.INSTANCE) ? "" : v2.getString();
return ValueString.get(replace(s0, s1, s2)); result = ValueString.get(replace(s0, s1, s2));
break;
} }
case RIGHT: case RIGHT:
return ValueString.get(right(v0.getString(), v1.getInt())); result = ValueString.get(right(v0.getString(), v1.getInt()));
break;
case LTRIM: case LTRIM:
return ValueString.get(trim(v0.getString(), true, false, v1 == null ? " " : v1.getString())); result = ValueString.get(trim(v0.getString(), true, false, v1 == null ? " " : v1.getString()));
break;
case TRIM: case TRIM:
return ValueString.get(trim(v0.getString(), true, true, v1 == null ? " " : v1.getString())); result = ValueString.get(trim(v0.getString(), true, true, v1 == null ? " " : v1.getString()));
break;
case RTRIM: case RTRIM:
return ValueString.get(trim(v0.getString(), false, true, v1 == null ? " " : v1.getString())); result = ValueString.get(trim(v0.getString(), false, true, v1 == null ? " " : v1.getString()));
case SOUNDEX: break;
return ValueString.get(getSoundex(v0.getString()));
case SPACE: {
// TODO DOS attacks: limit len?
int len = Math.max(0, v0.getInt());
char[] chars = new char[len];
for (int i = len - 1; i >= 0; i--) {
chars[i] = ' ';
}
return ValueString.get(new String(chars));
}
case SUBSTR: case SUBSTR:
case SUBSTRING: { case SUBSTRING: {
String s = v0.getString(); String s = v0.getString();
int length = v2 == null ? s.length() : v2.getInt(); int length = v2 == null ? s.length() : v2.getInt();
return ValueString.get(substring(s, v1.getInt(), length)); result = ValueString.get(substring(s, v1.getInt(), length));
break;
} }
case POSITION: case POSITION:
return ValueInt.get(locate(v0.getString(), v1.getString(), 0)); result = ValueInt.get(locate(v0.getString(), v1.getString(), 0));
case UPPER: break;
case UCASE:
// TODO this is locale specific, need to document or provide a way
// to set the locale
return ValueString.get(v0.getString().toUpperCase());
case STRINGENCODE:
return ValueString.get(StringUtils.javaEncode(v0.getString()));
case STRINGDECODE:
return ValueString.get(StringUtils.javaDecode(v0.getString()));
case STRINGTOUTF8:
return ValueBytes.getNoCopy(StringUtils.utf8Encode(v0.getString()));
case UTF8TOSTRING:
return ValueString.get(StringUtils.utf8Decode(v0.getBytesNoCopy()));
case XMLATTR: case XMLATTR:
return ValueString.get(StringUtils.xmlAttr(v0.getString(), v1.getString())); result = ValueString.get(StringUtils.xmlAttr(v0.getString(), v1.getString()));
break;
case XMLNODE: { case XMLNODE: {
String attr = v1 == null ? null : v1 == ValueNull.INSTANCE ? null : v1.getString(); String attr = v1 == null ? null : v1 == ValueNull.INSTANCE ? null : v1.getString();
String content = v2 == null ? null : v2 == ValueNull.INSTANCE ? null : v2.getString(); String content = v2 == null ? null : v2 == ValueNull.INSTANCE ? null : v2.getString();
return ValueString.get(StringUtils.xmlNode(v0.getString(), attr, content)); result = ValueString.get(StringUtils.xmlNode(v0.getString(), attr, content));
break;
} }
case XMLCOMMENT:
return ValueString.get(StringUtils.xmlComment(v0.getString()));
case XMLCDATA:
return ValueString.get(StringUtils.xmlCData(v0.getString()));
case XMLSTARTDOC:
return ValueString.get(StringUtils.xmlStartDoc());
case XMLTEXT:
return ValueString.get(StringUtils.xmlText(v0.getString()));
case REGEXP_REPLACE: case REGEXP_REPLACE:
return ValueString.get(v0.getString().replaceAll(v1.getString(), v2.getString())); result = ValueString.get(v0.getString().replaceAll(v1.getString(), v2.getString()));
break;
case RPAD: case RPAD:
return ValueString.get(StringUtils.pad(v0.getString(), v1.getInt(), v2 == null ? null : v2.getString(), true)); result = ValueString.get(StringUtils.pad(v0.getString(), v1.getInt(), v2 == null ? null : v2.getString(), true));
break;
case LPAD: case LPAD:
return ValueString.get(StringUtils.pad(v0.getString(), v1.getInt(), v2 == null ? null : v2.getString(), false)); result = ValueString.get(StringUtils.pad(v0.getString(), v1.getInt(), v2 == null ? null : v2.getString(), false));
break;
// date // date
case DATEADD: case DATEADD:
return ValueTimestamp.getNoCopy(dateadd(v0.getString(), v1.getInt(), v2.getTimestampNoCopy())); result = ValueTimestamp.getNoCopy(dateadd(v0.getString(), v1.getInt(), v2.getTimestampNoCopy()));
break;
case DATEDIFF: case DATEDIFF:
return ValueLong.get(datediff(v0.getString(), v1.getTimestampNoCopy(), v2.getTimestampNoCopy())); result = ValueLong.get(datediff(v0.getString(), v1.getTimestampNoCopy(), v2.getTimestampNoCopy()));
case DAYNAME: { break;
Value result;
synchronized (FORMAT_DAYNAME) {
result = ValueString.get(FORMAT_DAYNAME.format(v0.getDateNoCopy()));
}
return result;
}
case DAYOFMONTH:
return ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.DAY_OF_MONTH));
case DAYOFWEEK:
return ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.DAY_OF_WEEK));
case DAYOFYEAR:
return ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.DAY_OF_YEAR));
case HOUR:
return ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.HOUR_OF_DAY));
case MINUTE:
return ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.MINUTE));
case MONTH:
return ValueInt.get(getDatePart(v0.getTimestampNoCopy(), Calendar.MONTH));
case MONTHNAME: {
Value v;
synchronized (FORMAT_MONTHNAME) {
v = ValueString.get(FORMAT_MONTHNAME.format(v0.getDateNoCopy()));
}
return v;
}
case QUARTER:
return ValueInt.get((getDatePart(v0.getTimestamp(), Calendar.MONTH) - 1) / 3 + 1);
case SECOND:
return ValueInt.get(getDatePart(v0.getTimestamp(), Calendar.SECOND));
case WEEK:
return ValueInt.get(getDatePart(v0.getTimestamp(), Calendar.WEEK_OF_YEAR));
case YEAR:
return ValueInt.get(getDatePart(v0.getTimestamp(), Calendar.YEAR));
case CURDATE:
case CURRENT_DATE:
// need to normalize
return ValueDate.get(new Date(System.currentTimeMillis()));
case CURTIME:
case CURRENT_TIME:
// need to normalize
return ValueTime.get(new Time(System.currentTimeMillis()));
case NOW:
case CURRENT_TIMESTAMP: {
ValueTimestamp vt = ValueTimestamp.getNoCopy(new Timestamp(System.currentTimeMillis()));
if (v0 != null) {
Mode mode = database.getMode();
vt = (ValueTimestamp) vt.convertScale(mode.convertOnlyToSmallerScale, v0.getInt());
}
return vt;
}
case EXTRACT: { case EXTRACT: {
int field = getDatePart(v0.getString()); int field = getDatePart(v0.getString());
return ValueInt.get(getDatePart(v1.getTimestamp(), field)); result = ValueInt.get(getDatePart(v1.getTimestamp(), field));
break;
} }
case FORMATDATETIME: { case FORMATDATETIME: {
if (v0 == ValueNull.INSTANCE || v1 == ValueNull.INSTANCE) { if (v0 == ValueNull.INSTANCE || v1 == ValueNull.INSTANCE) {
return ValueNull.INSTANCE; result = ValueNull.INSTANCE;
} else {
String locale = v2 == null ? null : v2 == ValueNull.INSTANCE ? null : v2.getString();
String tz = v3 == null ? null : v3 == ValueNull.INSTANCE ? null : v3.getString();
result = ValueString.get(StringUtils.formatDateTime(v0.getTimestamp(), v1.getString(), locale, tz));
} }
String locale = v2 == null ? null : v2 == ValueNull.INSTANCE ? null : v2.getString(); break;
String tz = v3 == null ? null : v3 == ValueNull.INSTANCE ? null : v3.getString();
return ValueString.get(StringUtils.formatDateTime(v0.getTimestamp(), v1.getString(), locale, tz));
} }
case PARSEDATETIME: { case PARSEDATETIME: {
if (v0 == ValueNull.INSTANCE || v1 == ValueNull.INSTANCE) { if (v0 == ValueNull.INSTANCE || v1 == ValueNull.INSTANCE) {
return ValueNull.INSTANCE; result = ValueNull.INSTANCE;
} else {
String locale = v2 == null ? null : v2 == ValueNull.INSTANCE ? null : v2.getString();
String tz = v3 == null ? null : v3 == ValueNull.INSTANCE ? null : v3.getString();
java.util.Date d = StringUtils.parseDateTime(v0.getString(), v1.getString(), locale, tz);
result = ValueTimestamp.getNoCopy(new Timestamp(d.getTime()));
} }
String locale = v2 == null ? null : v2 == ValueNull.INSTANCE ? null : v2.getString(); break;
String tz = v3 == null ? null : v3 == ValueNull.INSTANCE ? null : v3.getString();
java.util.Date d = StringUtils.parseDateTime(v0.getString(), v1.getString(), locale, tz);
return ValueTimestamp.getNoCopy(new Timestamp(d.getTime()));
}
// system
case DATABASE:
return ValueString.get(database.getShortName());
case USER:
case CURRENT_USER:
return ValueString.get(session.getUser().getName());
case IDENTITY:
return session.getLastIdentity();
case AUTOCOMMIT:
return ValueBoolean.get(session.getAutoCommit());
case READONLY:
return ValueBoolean.get(database.getReadOnly());
case DATABASE_PATH: {
String path = database.getDatabasePath();
return path == null ? (Value) ValueNull.INSTANCE : ValueString.get(path);
} }
case LOCK_TIMEOUT:
return ValueInt.get(session.getLockTimeout());
case NULLIF: case NULLIF:
return database.areEqual(v0, v1) ? ValueNull.INSTANCE : v0; result = database.areEqual(v0, v1) ? ValueNull.INSTANCE : v0;
case CAST: break;
case CONVERT: { // system
v0 = v0.convertTo(dataType);
Mode mode = database.getMode();
v0 = v0.convertScale(mode.convertOnlyToSmallerScale, scale);
v0 = v0.convertPrecision(getPrecision());
return v0;
}
case NEXTVAL: { case NEXTVAL: {
Sequence sequence = getSequence(session, v0, v1); Sequence sequence = getSequence(session, v0, v1);
SequenceValue value = new SequenceValue(sequence); SequenceValue value = new SequenceValue(sequence);
return value.getValue(session); result = value.getValue(session);
break;
} }
case CURRVAL: { case CURRVAL: {
Sequence sequence = getSequence(session, v0, v1); Sequence sequence = getSequence(session, v0, v1);
return ValueLong.get(sequence.getCurrentValue()); result = ValueLong.get(sequence.getCurrentValue());
break;
} }
case CSVREAD: { case CSVREAD: {
String fileName = v0.getString(); String fileName = v0.getString();
...@@ -810,19 +983,17 @@ public class Function extends Expression implements FunctionCall { ...@@ -810,19 +983,17 @@ public class Function extends Expression implements FunctionCall {
char fieldSeparator = csv.getFieldSeparatorRead(); char fieldSeparator = csv.getFieldSeparatorRead();
String[] columns = StringUtils.arraySplit(columnList, fieldSeparator, true); String[] columns = StringUtils.arraySplit(columnList, fieldSeparator, true);
ValueResultSet vr = ValueResultSet.get(csv.read(fileName, columns, charset)); ValueResultSet vr = ValueResultSet.get(csv.read(fileName, columns, charset));
return vr; result = vr;
break;
} }
case LINK_SCHEMA: { case LINK_SCHEMA: {
session.getUser().checkAdmin(); session.getUser().checkAdmin();
Connection conn = session.createConnection(false); Connection conn = session.createConnection(false);
ResultSet rs = LinkSchema.linkSchema(conn, v0.getString(), v1.getString(), v2.getString(), v3.getString(), ResultSet rs = LinkSchema.linkSchema(conn, v0.getString(), v1.getString(), v2.getString(), v3.getString(),
v4.getString(), v5.getString()); v4.getString(), v5.getString());
return ValueResultSet.get(rs); result = ValueResultSet.get(rs);
break;
} }
case TABLE:
return getTable(session, args, false, false);
case TABLE_DISTINCT:
return getTable(session, args, false, true);
case CSVWRITE: { case CSVWRITE: {
session.getUser().checkAdmin(); session.getUser().checkAdmin();
Connection conn = session.createConnection(false); Connection conn = session.createConnection(false);
...@@ -833,23 +1004,13 @@ public class Function extends Expression implements FunctionCall { ...@@ -833,23 +1004,13 @@ public class Function extends Expression implements FunctionCall {
Csv csv = Csv.getInstance(); Csv csv = Csv.getInstance();
setCsvDelimiterEscape(csv, fieldSeparatorWrite, fieldDelimiter, escapeCharacter); setCsvDelimiterEscape(csv, fieldSeparatorWrite, fieldDelimiter, escapeCharacter);
int rows = csv.write(conn, v0.getString(), v1.getString(), charset); int rows = csv.write(conn, v0.getString(), v1.getString(), charset);
return ValueInt.get(rows); result = ValueInt.get(rows);
break;
} }
case MEMORY_FREE:
session.getUser().checkAdmin();
return ValueInt.get(MemoryUtils.getMemoryFree());
case MEMORY_USED:
session.getUser().checkAdmin();
return ValueInt.get(MemoryUtils.getMemoryUsed());
case LOCK_MODE:
return ValueInt.get(database.getLockMode());
case SCHEMA:
return ValueString.get(session.getCurrentSchemaName());
case SESSION_ID:
return ValueInt.get(session.getId());
default: default:
throw Message.getInternalError("type=" + info.type); throw Message.getInternalError("type=" + info.type);
} }
return result;
} }
private Sequence getSequence(Session session, Value v0, Value v1) throws SQLException { private Sequence getSequence(Session session, Value v0, Value v1) throws SQLException {
......
...@@ -57,8 +57,6 @@ public class BtreeIndex extends BaseIndex implements RecordReader { ...@@ -57,8 +57,6 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
head = (BtreeHead) rec; head = (BtreeHead) rec;
} }
if (head != null && head.getConsistent()) { if (head != null && head.getConsistent()) {
int testing;
// setRoot((BtreePage) storage.getRecord(session, head.getRootPosition()));
needRebuild = false; needRebuild = false;
rowCount = table.getRowCount(session); rowCount = table.getRowCount(session);
} else { } else {
......
...@@ -155,6 +155,7 @@ ...@@ -155,6 +155,7 @@
90132=Aggregate {0} not found 90132=Aggregate {0} not found
90133=Cannot change the setting {0} when the database is already open 90133=Cannot change the setting {0} when the database is already open
90134=Access to the class {0} is denied 90134=Access to the class {0} is denied
90135=The database is open in exclusive mode; can not open additional connections
HY000=General error\: {0} HY000=General error\: {0}
HY004=Unknown data type\: {0} HY004=Unknown data type\: {0}
HYC00=Feature not supported HYC00=Feature not supported
......
...@@ -856,6 +856,19 @@ Admin rights are required to execute this command. ...@@ -856,6 +856,19 @@ Admin rights are required to execute this command.
SET DEFAULT_TABLE_TYPE MEMORY SET DEFAULT_TABLE_TYPE MEMORY
" "
"Commands (Other)","SET EXCLUSIVE","
SET EXCLUSIVE {TRUE | FALSE}
","
Switched the database to exclusive mode and back. In exclusive mode, new connections are rejected,
and operations by other connections are paused until the exclusive mode is disabled.
Only the connection that set the exclusive mode can disable it. When the connection is closed,
it is automatically disabled.
This setting is not persistent.
Admin rights are required to execute this command.
","
SET EXCLUSIVE TRUE
"
"Commands (Other)","SET IGNORECASE"," "Commands (Other)","SET IGNORECASE","
SET IGNORECASE {TRUE|FALSE} SET IGNORECASE {TRUE|FALSE}
"," ","
...@@ -2559,6 +2572,18 @@ Returns true if auto commit is switched on for this session. ...@@ -2559,6 +2572,18 @@ Returns true if auto commit is switched on for this session.
AUTOCOMMIT() AUTOCOMMIT()
" "
"Functions (System)","CANCEL_SESSION","
CANCEL_SESSION(sessionInt): boolean
","
Cancels the currently executing statement of another session.
The method only works if the multithreaded kernel is enabled (see SET MULTI_THREADED).
Returns true if the statement was cancelled, false if the session is closed
or no statement is currently executing.
Admin rights are required to execute this command.
","
CANCEL_STATEMENT(3)
"
"Functions (System)","CASEWHEN Function"," "Functions (System)","CASEWHEN Function","
CASEWHEN(boolean, aValue, bValue): value CASEWHEN(boolean, aValue, bValue): value
"," ","
......
...@@ -333,6 +333,7 @@ public class FtpControl extends Thread { ...@@ -333,6 +333,7 @@ public class FtpControl extends Thread {
} else if ("XRMD".equals(command)) { } else if ("XRMD".equals(command)) {
processRemoveDir(param); processRemoveDir(param);
} }
break;
default: default:
break; break;
} }
......
...@@ -114,6 +114,7 @@ public class WebServer implements Service { ...@@ -114,6 +114,7 @@ public class WebServer implements Service {
private ShutdownHandler shutdownHandler; private ShutdownHandler shutdownHandler;
private Thread listenerThread; private Thread listenerThread;
private boolean ifExists; private boolean ifExists;
private boolean allowScript;
byte[] getFile(String file) throws IOException { byte[] getFile(String file) throws IOException {
trace("getFile <" + file + ">"); trace("getFile <" + file + ">");
...@@ -196,6 +197,8 @@ public class WebServer implements Service { ...@@ -196,6 +197,8 @@ public class WebServer implements Service {
ssl = Boolean.valueOf(args[++i]).booleanValue(); ssl = Boolean.valueOf(args[++i]).booleanValue();
} else if ("-webAllowOthers".equals(a)) { } else if ("-webAllowOthers".equals(a)) {
allowOthers = Boolean.valueOf(args[++i]).booleanValue(); allowOthers = Boolean.valueOf(args[++i]).booleanValue();
} else if ("-webScript".equals(a)) {
allowScript = Boolean.valueOf(args[++i]).booleanValue();
} else if ("-baseDir".equals(a)) { } else if ("-baseDir".equals(a)) {
String baseDir = args[++i]; String baseDir = args[++i];
SysProperties.setBaseDir(baseDir); SysProperties.setBaseDir(baseDir);
...@@ -508,4 +511,8 @@ public class WebServer implements Service { ...@@ -508,4 +511,8 @@ public class WebServer implements Service {
this.shutdownHandler = shutdownHandler; this.shutdownHandler = shutdownHandler;
} }
public boolean getAllowScript() {
return allowScript;
}
} }
...@@ -5,13 +5,20 @@ ...@@ -5,13 +5,20 @@
package org.h2.server.web; package org.h2.server.web;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.Socket; import java.net.Socket;
import java.security.SecureClassLoader;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
...@@ -40,12 +47,12 @@ import org.h2.engine.Constants; ...@@ -40,12 +47,12 @@ import org.h2.engine.Constants;
import org.h2.jdbc.JdbcSQLException; import org.h2.jdbc.JdbcSQLException;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.tools.SimpleResultSet; import org.h2.tools.SimpleResultSet;
import org.h2.util.ObjectUtils;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.MemoryUtils; import org.h2.util.MemoryUtils;
import org.h2.util.NetUtils; import org.h2.util.NetUtils;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.util.ScriptReader; import org.h2.util.ScriptReader;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -1018,7 +1025,17 @@ class WebThread extends Thread implements DatabaseEventListener { ...@@ -1018,7 +1025,17 @@ class WebThread extends Thread implements DatabaseEventListener {
try { try {
Connection conn = session.getConnection(); Connection conn = session.getConnection();
String result; String result;
if ("@AUTOCOMMIT TRUE".equals(sql)) { if (sql.startsWith("@JAVA")) {
if (server.getAllowScript()) {
try {
result = executeJava(sql.substring("@JAVA".length()));
} catch (Throwable t) {
result = getStackTrace(0, t);
}
} else {
result = "Executing Java code is not allowed, use command line parameters -webScript true";
}
} else if ("@AUTOCOMMIT TRUE".equals(sql)) {
conn.setAutoCommit(true); conn.setAutoCommit(true);
result = "${text.result.autoCommitOn}"; result = "${text.result.autoCommitOn}";
} else if ("@AUTOCOMMIT FALSE".equals(sql)) { } else if ("@AUTOCOMMIT FALSE".equals(sql)) {
...@@ -1068,6 +1085,94 @@ class WebThread extends Thread implements DatabaseEventListener { ...@@ -1068,6 +1085,94 @@ class WebThread extends Thread implements DatabaseEventListener {
return "result.jsp"; return "result.jsp";
} }
static class DynamicClassLoader extends SecureClassLoader {
private String name;
private byte[] data;
private Class clazz;
DynamicClassLoader(String name, byte[] data) throws MalformedURLException {
super(DynamicClassLoader.class.getClassLoader());
this.name = name;
this.data = data;
}
public Class loadClass(String className) throws ClassNotFoundException {
return findClass(className);
}
public Class findClass(String className) throws ClassNotFoundException {
if (className.equals(name)) {
if (clazz == null) {
clazz = defineClass(className, data, 0, data.length);
}
return clazz;
}
try {
return findSystemClass(className);
} catch (Exception e) {
}
return super.findClass(className);
}
}
private String executeJava(String code) throws Exception {
File javaFile = new File("Java.java");
File classFile = new File("Java.class");
try {
PrintWriter out = new PrintWriter(new FileWriter(javaFile));
classFile.delete();
int endImport = code.indexOf("@CODE");
String importCode = "import java.util.*; import java.math.*; import java.sql.*;";
if (endImport >= 0) {
importCode = code.substring(0, endImport);
code = code.substring("@CODE".length() + endImport);
}
out.println(importCode);
out.println("public class Java { public static Object run() throws Throwable {" + code + "}}");
out.close();
Process p = Runtime.getRuntime().exec("javac Java.java");
InputStream processIn = p.getInputStream();
InputStream processErrorIn = p.getErrorStream();
StringBuffer buff = new StringBuffer();
while (true) {
int c = processIn.read();
if (c == -1) {
break;
}
buff.append((char) c);
}
while (true) {
int c = processErrorIn.read();
if (c == -1) {
break;
}
buff.append((char) c);
}
String error = buff.toString().trim();
if (error.length() > 0) {
throw new Exception("Error compiling: " + error.toString());
}
byte[] data = new byte[(int) classFile.length()];
DataInputStream in = new DataInputStream(new FileInputStream(classFile));
in.readFully(data);
in.close();
DynamicClassLoader cl = new DynamicClassLoader("Java", data);
Class clazz = cl.loadClass("Java");
Method[] methods = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getName().equals("run")) {
return "" + m.invoke(null, new Object[0]);
}
}
return null;
} finally {
javaFile.delete();
classFile.delete();
}
}
private String editResult() { private String editResult() {
ResultSet rs = session.result; ResultSet rs = session.result;
int row = Integer.parseInt(attributes.getProperty("row")); int row = Integer.parseInt(attributes.getProperty("row"));
...@@ -1083,7 +1188,7 @@ class WebThread extends Thread implements DatabaseEventListener { ...@@ -1083,7 +1188,7 @@ class WebThread extends Thread implements DatabaseEventListener {
} }
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) { for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
String x = attributes.getProperty("r" + row + "c" + (i + 1)); String x = attributes.getProperty("r" + row + "c" + (i + 1));
rs.updateString(i + 1, x); rs.updateString(i + 1, unescapeData(x));
} }
if (insert) { if (insert) {
rs.insertRow(); rs.insertRow();
...@@ -1611,7 +1716,7 @@ class WebThread extends Thread implements DatabaseEventListener { ...@@ -1611,7 +1716,7 @@ class WebThread extends Thread implements DatabaseEventListener {
buff.append(PageParser.escapeHtml(meta.getColumnLabel(i + 1))); buff.append(PageParser.escapeHtml(meta.getColumnLabel(i + 1)));
buff.append("</td>"); buff.append("</td>");
buff.append("<td>"); buff.append("<td>");
buff.append(PageParser.escapeHtml(rs.getString(i + 1))); buff.append(escapeData(rs.getString(i + 1)));
buff.append("</td></tr>"); buff.append("</td></tr>");
} }
} }
...@@ -1645,7 +1750,7 @@ class WebThread extends Thread implements DatabaseEventListener { ...@@ -1645,7 +1750,7 @@ class WebThread extends Thread implements DatabaseEventListener {
} }
for (int i = 0; i < columns; i++) { for (int i = 0; i < columns; i++) {
buff.append("<td>"); buff.append("<td>");
buff.append(PageParser.escapeHtml(rs.getString(i + 1))); buff.append(escapeData(rs.getString(i + 1)));
buff.append("</td>"); buff.append("</td>");
} }
buff.append("</tr>"); buff.append("</tr>");
...@@ -1713,6 +1818,26 @@ class WebThread extends Thread implements DatabaseEventListener { ...@@ -1713,6 +1818,26 @@ class WebThread extends Thread implements DatabaseEventListener {
return "index.do"; return "index.do";
} }
private String escapeData(String d) {
if (d == null) {
return "<i>null</i>";
} else if (d.startsWith("null")) {
return "<div style='display: none'>=</div>" + PageParser.escapeHtml(d);
}
return PageParser.escapeHtml(d);
}
private String unescapeData(String d) {
if (d.endsWith("null")) {
if (d.equals("null")) {
return null;
} else if (d.startsWith("=")) {
return d.substring(1);
}
}
return d;
}
private String settingRemove() { private String settingRemove() {
String setting = attributes.getProperty("name", ""); String setting = attributes.getProperty("name", "");
server.removeSetting(setting); server.removeSetting(setting);
......
/* /*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html). * Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
*/ */
addEvent(window, "load", initSort); addEvent(window, "load", initSort);
function addEvent(elm, evType, fn, useCapture) { function addEvent(elm, evType, fn, useCapture) {
...@@ -26,7 +26,7 @@ function initSort() { ...@@ -26,7 +26,7 @@ function initSort() {
} }
var tables = document.getElementsByTagName("table"); var tables = document.getElementsByTagName("table");
for (var i=0; i<tables.length; i++) { for (var i=0; i<tables.length; i++) {
table = tables[i]; table = tables[i];
if (table.rows && table.rows.length > 0) { if (table.rows && table.rows.length > 0) {
var header = table.rows[0]; var header = table.rows[0];
for(var j=0;j<header.cells.length;j++) { for(var j=0;j<header.cells.length;j++) {
...@@ -45,8 +45,8 @@ function editRow(row, session, write, undo) { ...@@ -45,8 +45,8 @@ function editRow(row, session, write, undo) {
for(i=1; i<table.rows.length; i++) { for(i=1; i<table.rows.length; i++) {
var cell = table.rows[i].cells[0]; var cell = table.rows[i].cells[0];
if(i == y) { if(i == y) {
var edit = '<img width=16 height=16 src="ico_ok.gif" onclick="javascript:editing.op.value=\'1\';editing.row.value=\''+row+'\';editing.submit()" onmouseover = "this.className =\'icon_hover\'" onmouseout = "this.className=\'icon\'" class="icon" alt="'+write+'" title="'+write+'" border="1"/>'; var edit = '<img width=16 height=16 src="ico_ok.gif" onclick="editOk('+row+')" onmouseover = "this.className =\'icon_hover\'" onmouseout = "this.className=\'icon\'" class="icon" alt="'+write+'" title="'+write+'" border="1"/>';
var undo = '<img width=16 height=16 src="ico_undo.gif" onclick="javascript:editing.op.value=\'3\';editing.row.value=\''+row+'\';editing.submit()" onmouseover = "this.className =\'icon_hover\'" onmouseout = "this.className=\'icon\'" class="icon" alt="'+undo+'" title="'+undo+'" border="1"/>'; var undo = '<img width=16 height=16 src="ico_undo.gif" onclick="editCancel('+row+')" onmouseover = "this.className =\'icon_hover\'" onmouseout = "this.className=\'icon\'" class="icon" alt="'+undo+'" title="'+undo+'" border="1"/>';
cell.innerHTML = edit + undo; cell.innerHTML = edit + undo;
} else { } else {
cell.innerHTML = ''; cell.innerHTML = '';
...@@ -56,10 +56,39 @@ function editRow(row, session, write, undo) { ...@@ -56,10 +56,39 @@ function editRow(row, session, write, undo) {
for(i=1; i<cells.length; i++) { for(i=1; i<cells.length; i++) {
var cell = cells[i]; var cell = cells[i];
var text = getInnerText(cell); var text = getInnerText(cell);
cell.innerHTML = '<input type="text" name="r'+row+'c' + i + '" value="'+text+'" size="' + (text.length+5) + '"/>'; cell.innerHTML = '<input type="text" name="r'+row+'c' + i + '" value="'+text+'" size="' + (text.length+5) + '" onkeydown="return editKeyDown(' + row + ', this, event)" />';
} }
} }
function editCancel(row) {
var editing = document.getElementById('editing');
editing.row.value = row;
editing.op.value='3';
editing.submit();
}
function editOk(row) {
var editing = document.getElementById('editing');
editing.row.value = row;
editing.op.value='1';
editing.submit();
}
function editKeyDown(row, object, event) {
var key=event.keyCode? event.keyCode : event.charCode;
if(key == 46 && event.ctrlKey) {
// ctrl + delete
object.value = 'null';
return false;
} else if(key == 13) {
editOk(row);
return false;
} else if(key == 27) {
editCancel(row);
return false;
}
}
function getInnerText(el) { function getInnerText(el) {
if (typeof el == "string") return el; if (typeof el == "string") return el;
if (typeof el == "undefined") { return el }; if (typeof el == "undefined") { return el };
......
...@@ -16,6 +16,7 @@ import java.sql.Timestamp; ...@@ -16,6 +16,7 @@ import java.sql.Timestamp;
import java.text.Collator; import java.text.Collator;
import java.util.Locale; import java.util.Locale;
import org.h2.command.Command;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.constraint.Constraint; import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintCheck; import org.h2.constraint.ConstraintCheck;
...@@ -434,8 +435,9 @@ public class MetaTable extends Table { ...@@ -434,8 +435,9 @@ public class MetaTable extends Table {
cols = createColumns(new String[]{ cols = createColumns(new String[]{
"ID INT", "ID INT",
"USER_NAME", "USER_NAME",
"CURRENT_STATEMENT", "SESSION_START",
"LOGIN_TIME", "STATEMENT",
"STATEMENT_START"
}); });
break; break;
} }
...@@ -707,6 +709,7 @@ public class MetaTable extends Table { ...@@ -707,6 +709,7 @@ public class MetaTable extends Table {
} }
} }
add(rows, new String[] { "MVCC", database.isMultiVersion() ? "TRUE" : "FALSE" }); add(rows, new String[] { "MVCC", database.isMultiVersion() ? "TRUE" : "FALSE" });
add(rows, new String[] { "EXCLUSIVE", database.getExclusiveSession() == null ? "FALSE" : "TRUE" });
add(rows, new String[] { "MODE", database.getMode().getName() }); add(rows, new String[] { "MODE", database.getMode().getName() });
add(rows, new String[] { "MULTI_THREADED", database.getMultiThreaded() ? "1" : "0"}); add(rows, new String[] { "MULTI_THREADED", database.getMultiThreaded() ? "1" : "0"});
DiskFile dataFile = database.getDataFile(); DiskFile dataFile = database.getDataFile();
...@@ -1205,11 +1208,13 @@ public class MetaTable extends Table { ...@@ -1205,11 +1208,13 @@ public class MetaTable extends Table {
for (int i = 0; i < sessions.length; i++) { for (int i = 0; i < sessions.length; i++) {
Session s = sessions[i]; Session s = sessions[i];
if (admin || s == session) { if (admin || s == session) {
Command command = s.getCurrentCommand();
add(rows, new String[] { add(rows, new String[] {
"" + s.getId(), // ID "" + s.getId(), // ID
s.getUser().getName(), // USER_NAME s.getUser().getName(), // USER_NAME
s.getCurrentCommand(), // CURRENT_COMMAND new Timestamp(s.getSessionStart()).toString(), // SESSION_START
new Timestamp(s.getLoginTime()).toString(), // LOGIN_TIME command == null ? null : command.toString(), // STATEMENT
new Timestamp(s.getCurrentCommandStart()).toString() // STATEMENT_START
}); });
} }
} }
......
...@@ -28,12 +28,12 @@ import org.h2.util.StartBrowser; ...@@ -28,12 +28,12 @@ import org.h2.util.StartBrowser;
* This tool can be used to start various database servers (listeners). * This tool can be used to start various database servers (listeners).
*/ */
public class Server implements Runnable, ShutdownHandler { public class Server implements Runnable, ShutdownHandler {
private Service service; private Service service;
private static final int EXIT_ERROR = 1; private static final int EXIT_ERROR = 1;
private Server web, tcp, pg, ftp; private Server web, tcp, pg, ftp;
private ShutdownHandler shutdownHandler; private ShutdownHandler shutdownHandler;
private void showUsage(String a, PrintStream out) { private void showUsage(String a, PrintStream out) {
if (a != null) { if (a != null) {
out.println("Unknown option: " + a); out.println("Unknown option: " + a);
...@@ -53,30 +53,30 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -53,30 +53,30 @@ public class Server implements Runnable, ShutdownHandler {
out.println("-tcp (start the TCP Server)"); out.println("-tcp (start the TCP Server)");
out.println("-tcpAllowOthers {true|false}"); out.println("-tcpAllowOthers {true|false}");
out.println("-tcpPort <port> (default: " + TcpServer.DEFAULT_PORT+")"); out.println("-tcpPort <port> (default: " + TcpServer.DEFAULT_PORT+")");
out.println("-tcpSSL {true|false}"); out.println("-tcpSSL {true|false}");
out.println("-tcpPassword {password} (the password for shutting down a TCP Server)"); out.println("-tcpPassword {password} (the password for shutting down a TCP Server)");
out.println("-tcpShutdown {url} (shutdown the TCP Server, URL example: tcp://localhost:9094)"); out.println("-tcpShutdown {url} (shutdown the TCP Server, URL example: tcp://localhost:9094)");
out.println("-tcpShutdownForce {true|false} (don't wait for other connections to close)"); out.println("-tcpShutdownForce {true|false} (don't wait for other connections to close)");
out.println(); out.println();
out.println("-pg (start the PG Server)"); out.println("-pg (start the PG Server)");
out.println("-pgAllowOthers {true|false}"); out.println("-pgAllowOthers {true|false}");
out.println("-pgPort <port> (default: " + PgServer.DEFAULT_PORT+")"); out.println("-pgPort <port> (default: " + PgServer.DEFAULT_PORT+")");
out.println(); out.println();
out.println("-ftp (start the FTP Server)"); out.println("-ftp (start the FTP Server)");
out.println("-ftpPort <port> (default: " + Constants.DEFAULT_FTP_PORT+")"); out.println("-ftpPort <port> (default: " + Constants.DEFAULT_FTP_PORT+")");
out.println("-ftpDir <directory> (default: " + FtpServer.DEFAULT_ROOT+", use jdbc:... to access a database)"); out.println("-ftpDir <directory> (default: " + FtpServer.DEFAULT_ROOT+", use jdbc:... to access a database)");
out.println("-ftpRead <readUserName> (default: " + FtpServer.DEFAULT_READ+")"); out.println("-ftpRead <readUserName> (default: " + FtpServer.DEFAULT_READ+")");
out.println("-ftpWrite <writeUserName> (default: " + FtpServer.DEFAULT_WRITE+")"); out.println("-ftpWrite <writeUserName> (default: " + FtpServer.DEFAULT_WRITE+")");
out.println("-ftpWritePassword <password> (default: " + FtpServer.DEFAULT_WRITE_PASSWORD+")"); out.println("-ftpWritePassword <password> (default: " + FtpServer.DEFAULT_WRITE_PASSWORD+")");
out.println(); out.println();
out.println("-log {true|false} (enable or disable logging, for all servers)"); out.println("-log {true|false} (enable or disable logging, for all servers)");
out.println("-baseDir <directory> (sets the base directory for H2 databases, for all servers)"); out.println("-baseDir <directory> (sets the base directory for H2 databases, for all servers)");
out.println("-ifExists {true|false} (only existing databases may be opened, for all servers)"); out.println("-ifExists {true|false} (only existing databases may be opened, for all servers)");
} }
public Server() { public Server() {
} }
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-baseDir", "/temp/data",... * The options must be split into strings like this: "-baseDir", "/temp/data",...
...@@ -114,7 +114,7 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -114,7 +114,7 @@ public class Server implements Runnable, ShutdownHandler {
* </li><li>-ftpWrite {writeUserName} * </li><li>-ftpWrite {writeUserName}
* </li><li>-ftpWritePassword {password} * </li><li>-ftpWritePassword {password}
* </li></ul> * </li></ul>
* *
* @param args the command line arguments * @param args the command line arguments
* @throws SQLException * @throws SQLException
*/ */
...@@ -150,6 +150,8 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -150,6 +150,8 @@ public class Server implements Runnable, ShutdownHandler {
i++; i++;
} else if ("-webPort".equals(a)) { } else if ("-webPort".equals(a)) {
i++; i++;
} else if ("-webScript".equals(a)) {
i++;
} else if ("-webSSL".equals(a)) { } else if ("-webSSL".equals(a)) {
i++; i++;
} else { } else {
...@@ -267,10 +269,10 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -267,10 +269,10 @@ public class Server implements Runnable, ShutdownHandler {
// ignore (status is displayed) // ignore (status is displayed)
e.printStackTrace(); e.printStackTrace();
exitCode = EXIT_ERROR; exitCode = EXIT_ERROR;
} }
out.println(web.getStatus()); out.println(web.getStatus());
// start browser anyway (even if the server is already running) // start browser anyway (even if the server is already running)
// because some people don't look at the output, // because some people don't look at the output,
// but are wondering why nothing happens // but are wondering why nothing happens
if (browserStart) { if (browserStart) {
StartBrowser.openURL(web.getURL()); StartBrowser.openURL(web.getURL());
...@@ -289,22 +291,22 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -289,22 +291,22 @@ public class Server implements Runnable, ShutdownHandler {
} }
return exitCode; return exitCode;
} }
/** /**
* Shutdown a TCP server. If force is set to false, the server will not allow new connections, * Shutdown a TCP server. If force is set to false, the server will not allow new connections,
* but not kill existing connections, instead it will stop if the last connection is closed. * but not kill existing connections, instead it will stop if the last connection is closed.
* If force is set to true, existing connections are killed. * If force is set to true, existing connections are killed.
* After calling the method with force=false, it is not possible to call it again with * After calling the method with force=false, it is not possible to call it again with
* force=true because new connections are not allowed. * force=true because new connections are not allowed.
* Example: * Example:
* <pre>Server.shutdownTcpServer("tcp://localhost:9094", password, true);</pre> * <pre>Server.shutdownTcpServer("tcp://localhost:9094", password, true);</pre>
* *
* @param url example: tcp://localhost:9094 * @param url example: tcp://localhost:9094
* @param password the password to use ("" for no password) * @param password the password to use ("" for no password)
* @param force the shutdown (don't wait) * @param force the shutdown (don't wait)
* @throws ClassNotFoundException * @throws ClassNotFoundException
* @throws SQLException * @throws SQLException
*/ */
public static void shutdownTcpServer(String url, String password, boolean force) throws SQLException { public static void shutdownTcpServer(String url, String password, boolean force) throws SQLException {
int port = Constants.DEFAULT_SERVER_PORT; int port = Constants.DEFAULT_SERVER_PORT;
int idx = url.indexOf(':', "jdbc:h2:".length()); int idx = url.indexOf(':', "jdbc:h2:".length());
...@@ -376,7 +378,7 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -376,7 +378,7 @@ public class Server implements Runnable, ShutdownHandler {
* Create a new web server, but does not start it yet. * Create a new web server, but does not start it yet.
* Example: * Example:
* <pre>Server server = Server.createWebServer(new String[]{"-log", "true"}).start();</pre> * <pre>Server server = Server.createWebServer(new String[]{"-log", "true"}).start();</pre>
* *
* @param args * @param args
* @return the server * @return the server
*/ */
...@@ -391,7 +393,7 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -391,7 +393,7 @@ public class Server implements Runnable, ShutdownHandler {
* Create a new ftp server, but does not start it yet. * Create a new ftp server, but does not start it yet.
* Example: * Example:
* <pre>Server server = Server.createFtpServer(new String[]{"-log", "true"}).start();</pre> * <pre>Server server = Server.createFtpServer(new String[]{"-log", "true"}).start();</pre>
* *
* @param args * @param args
* @return the server * @return the server
*/ */
...@@ -403,26 +405,26 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -403,26 +405,26 @@ public class Server implements Runnable, ShutdownHandler {
* Create a new TCP server, but does not start it yet. * Create a new TCP server, but does not start it yet.
* Example: * Example:
* <pre>Server server = Server.createTcpServer(new String[]{"-tcpAllowOthers", "true"}).start();</pre> * <pre>Server server = Server.createTcpServer(new String[]{"-tcpAllowOthers", "true"}).start();</pre>
* *
* @param args * @param args
* @return the server * @return the server
*/ */
public static Server createTcpServer(String[] args) throws SQLException { public static Server createTcpServer(String[] args) throws SQLException {
return new Server(new TcpServer(), args); return new Server(new TcpServer(), args);
} }
/** /**
* Create a new PG server, but does not start it yet. * Create a new PG server, but does not start it yet.
* Example: * Example:
* <pre>Server server = Server.createPgServer(new String[]{"-pgAllowOthers", "true"}).start();</pre> * <pre>Server server = Server.createPgServer(new String[]{"-pgAllowOthers", "true"}).start();</pre>
* *
* @param args * @param args
* @return the server * @return the server
*/ */
public static Server createPgServer(String[] args) throws SQLException { public static Server createPgServer(String[] args) throws SQLException {
return new Server(new PgServer(), args); return new Server(new PgServer(), args);
} }
/** /**
* Tries to start the server. * Tries to start the server.
* @return the server if successful * @return the server if successful
...@@ -473,7 +475,7 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -473,7 +475,7 @@ public class Server implements Runnable, ShutdownHandler {
/** /**
* Checks if the server is running. * Checks if the server is running.
* *
* @return if the server is running * @return if the server is running
*/ */
public boolean isRunning() { public boolean isRunning() {
...@@ -486,7 +488,7 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -486,7 +488,7 @@ public class Server implements Runnable, ShutdownHandler {
public void stop() { public void stop() {
service.stop(); service.stop();
} }
/** /**
* Gets the URL of this server. * Gets the URL of this server.
* @return the url * @return the url
...@@ -514,7 +516,7 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -514,7 +516,7 @@ public class Server implements Runnable, ShutdownHandler {
TraceSystem.traceThrowable(e); TraceSystem.traceThrowable(e);
} }
} }
/** /**
* INTERNAL * INTERNAL
*/ */
...@@ -524,7 +526,7 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -524,7 +526,7 @@ public class Server implements Runnable, ShutdownHandler {
/** /**
* INTERNAL * INTERNAL
*/ */
public void shutdown() { public void shutdown() {
if (shutdownHandler != null) { if (shutdownHandler != null) {
shutdownHandler.shutdown(); shutdownHandler.shutdown();
......
...@@ -40,6 +40,7 @@ import org.h2.test.db.TestSQLInjection; ...@@ -40,6 +40,7 @@ import org.h2.test.db.TestSQLInjection;
import org.h2.test.db.TestScript; import org.h2.test.db.TestScript;
import org.h2.test.db.TestScriptSimple; import org.h2.test.db.TestScriptSimple;
import org.h2.test.db.TestSequence; import org.h2.test.db.TestSequence;
import org.h2.test.db.TestSessionsLocks;
import org.h2.test.db.TestSpaceReuse; import org.h2.test.db.TestSpaceReuse;
import org.h2.test.db.TestSpeed; import org.h2.test.db.TestSpeed;
import org.h2.test.db.TestTempTables; import org.h2.test.db.TestTempTables;
...@@ -151,8 +152,15 @@ java org.h2.test.TestAll timer ...@@ -151,8 +152,15 @@ java org.h2.test.TestAll timer
/* /*
write simple test for test & document exclusive mode
NFORMATION_SCHEMA.SESSIONS and LOCKS test exlclusive mode (inform_sch.settings table, disallow new connections, delay operations by other,
disable when close session, disable
translate error code 90135
C:\temp\test\db C:\temp\test\db
...@@ -576,6 +584,7 @@ Features of H2 ...@@ -576,6 +584,7 @@ Features of H2
new TestRights().runTest(this); new TestRights().runTest(this);
new TestRunscript().runTest(this); new TestRunscript().runTest(this);
new TestSQLInjection().runTest(this); new TestSQLInjection().runTest(this);
new TestSessionsLocks().runTest(this);
new TestSequence().runTest(this); new TestSequence().runTest(this);
new TestSpaceReuse().runTest(this); new TestSpaceReuse().runTest(this);
new TestSpeed().runTest(this); new TestSpeed().runTest(this);
......
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.test.TestBase;
public class TestSessionsLocks extends TestBase {
public void test() throws Exception {
testCancelStatement();
testLocks();
}
private void testLocks() throws Exception {
deleteDb("sessionsLocks");
Connection conn = getConnection("sessionsLocks;MULTI_THREADED=1");
Statement stat = conn.createStatement();
ResultSet rs;
rs = stat.executeQuery("select * from information_schema.locks order by session_id");
checkFalse(rs.next());
Connection conn2 = getConnection("sessionsLocks");
Statement stat2 = conn2.createStatement();
stat2.execute("create table test(id int primary key, name varchar)");
conn2.setAutoCommit(false);
stat2.execute("insert into test values(1, 'Hello')");
rs = stat.executeQuery("select * from information_schema.locks order by session_id");
rs.next();
check("PUBLIC", rs.getString("TABLE_SCHEMA"));
check("TEST", rs.getString("TABLE_NAME"));
rs.getString("SESSION_ID");
if (config.mvcc) {
check("READ", rs.getString("LOCK_TYPE"));
} else {
check("WRITE", rs.getString("LOCK_TYPE"));
}
checkFalse(rs.next());
conn2.commit();
conn2.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
stat2.execute("SELECT * FROM TEST");
rs = stat.executeQuery("select * from information_schema.locks order by session_id");
if (!config.mvcc) {
rs.next();
check("PUBLIC", rs.getString("TABLE_SCHEMA"));
check("TEST", rs.getString("TABLE_NAME"));
rs.getString("SESSION_ID");
check("READ", rs.getString("LOCK_TYPE"));
}
checkFalse(rs.next());
conn2.commit();
rs = stat.executeQuery("select * from information_schema.locks order by session_id");
checkFalse(rs.next());
conn.close();
conn2.close();
}
public void testCancelStatement() throws Exception {
deleteDb("sessionsLocks");
Connection conn = getConnection("sessionsLocks;MULTI_THREADED=1");
Statement stat = conn.createStatement();
ResultSet rs;
rs = stat.executeQuery("select * from information_schema.sessions order by SESSION_START, ID");
rs.next();
int sessionId = rs.getInt("ID");
rs.getString("USER_NAME");
rs.getTimestamp("SESSION_START");
rs.getString("STATEMENT");
rs.getTimestamp("STATEMENT_START");
checkFalse(rs.next());
Connection conn2 = getConnection("sessionsLocks");
final Statement stat2 = conn2.createStatement();
rs = stat.executeQuery("select * from information_schema.sessions order by SESSION_START, ID");
check(rs.next());
check(sessionId, rs.getInt("ID"));
check(rs.next());
int otherId = rs.getInt("ID");
check(otherId != sessionId);
checkFalse(rs.next());
stat2.execute("set throttle 1");
final boolean[] done = new boolean[1];
Runnable runnable = new Runnable() {
public void run() {
try {
stat2.execute("select count(*) from system_range(1, 10000000) t1, system_range(1, 10000000) t2");
new Error("Unexpected success").printStackTrace();
} catch (SQLException e) {
done[0] = true;
}
}
};
new Thread(runnable).start();
while (true) {
Thread.sleep(1000);
rs = stat.executeQuery("CALL CANCEL_SESSION(" + otherId + ")");
rs.next();
if (rs.getBoolean(1)) {
Thread.sleep(100);
check(done[0]);
break;
} else {
System.out.println("no statement is executing yet");
}
}
conn2.close();
conn.close();
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论