提交 488ecc5e authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 e9b4498c
......@@ -509,6 +509,8 @@ public class Database implements DataHandler {
addMetaData(MetaTable.CONSTANTS);
addMetaData(MetaTable.DOMAINS);
addMetaData(MetaTable.TRIGGERS);
addMetaData(MetaTable.SESSIONS);
addMetaData(MetaTable.LOCKS);
starting = true;
Cursor cursor = metaIdIndex.find(systemSession, null, null);
// first, create all function aliases and sequences because
......
......@@ -30,8 +30,8 @@ import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.store.DataHandler;
import org.h2.table.Table;
import org.h2.util.ObjectUtils;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.value.Value;
import org.h2.value.ValueLong;
......@@ -73,6 +73,7 @@ public class Session implements SessionInterface {
private String currentTransactionName;
private boolean isClosed;
private boolean rollbackMode;
private long loginTime = System.currentTimeMillis();
public Session() {
}
......@@ -507,6 +508,11 @@ public class Session implements SessionInterface {
}
}
public String getCurrentCommand() {
Command c = currentCommand;
return c == null ? null : c.toString();
}
public boolean getAllowLiterals() {
return allowLiterals;
}
......@@ -603,4 +609,16 @@ public class Session implements SessionInterface {
return rollbackMode;
}
public long getLoginTime() {
return loginTime;
}
public Table[] getLocks() {
synchronized (database) {
Table[] list = new Table[locks.size()];
locks.toArray(list);
return list;
}
}
}
......@@ -72,7 +72,7 @@ public class Function extends Expression implements FunctionCall {
OCTET_LENGTH = 64, RAWTOHEX = 65, REPEAT = 66, REPLACE = 67, RIGHT = 68, RTRIM = 69, SOUNDEX = 70,
SPACE = 71, SUBSTR = 72, SUBSTRING = 73, UCASE = 74, LOWER = 75, UPPER = 76, POSITION = 77, TRIM = 78,
STRINGENCODE = 79, STRINGDECODE = 80, STRINGTOUTF8 = 81, UTF8TOSTRING = 82, XMLATTR = 83, XMLNODE = 84,
XMLCOMMENT = 85, XMLCDATA = 86, XMLSTARTDOC = 87, XMLTEXT = 88, REGEXP_REPLACE = 89;
XMLCOMMENT = 85, XMLCDATA = 86, XMLSTARTDOC = 87, XMLTEXT = 88, REGEXP_REPLACE = 89, RPAD = 90, LPAD = 91;
public static final int CURDATE = 100, CURTIME = 101, DATEADD = 102, DATEDIFF = 103, DAYNAME = 104,
DAYOFMONTH = 105, DAYOFWEEK = 106, DAYOFYEAR = 107, HOUR = 108, MINUTE = 109, MONTH = 110, MONTHNAME = 111,
......@@ -159,13 +159,9 @@ public class Function extends Expression implements FunctionCall {
addFunction("PI", PI, 0, Value.DOUBLE);
addFunction("POWER", POWER, 2, Value.DOUBLE);
addFunction("RADIANS", RADIANS, 1, Value.DOUBLE);
addFunctionNotConst("RAND", RAND, VAR_ARGS, Value.DOUBLE); // no args:
// regular
// rand;
// with one
// arg: seed
// random
// generator
// RAND without argument: get the next value
// RAND with one argument: seed the random generator
addFunctionNotConst("RAND", RAND, VAR_ARGS, Value.DOUBLE);
addFunction("ROUND", ROUND, 2, Value.DOUBLE);
addFunction("ROUNDMAGIC", ROUNDMAGIC, 1, Value.DOUBLE);
addFunction("SIGN", SIGN, 1, Value.INT);
......@@ -228,6 +224,8 @@ public class Function extends Expression implements FunctionCall {
addFunction("XMLSTARTDOC", XMLSTARTDOC, 0, Value.STRING);
addFunction("XMLTEXT", XMLTEXT, 1, Value.STRING);
addFunction("REGEXP_REPLACE", REGEXP_REPLACE, 3, Value.STRING);
addFunction("RPAD", RPAD, VAR_ARGS, Value.STRING);
addFunction("LPAD", LPAD, VAR_ARGS, Value.STRING);
// date
addFunctionNotConst("CURRENT_DATE", CURRENT_DATE, 0, Value.DATE);
......@@ -682,6 +680,10 @@ public class Function extends Expression implements FunctionCall {
return ValueString.get(StringUtils.xmlText(v0.getString()));
case REGEXP_REPLACE:
return ValueString.get(v0.getString().replaceAll(v1.getString(), v2.getString()));
case RPAD:
return ValueString.get(StringUtils.pad(v0.getString(), v1.getInt(), v2 == null ? null : v2.getString(), true));
case LPAD:
return ValueString.get(StringUtils.pad(v0.getString(), v1.getInt(), v2 == null ? null : v2.getString(), false));
// date
case DATEADD:
return ValueTimestamp.getNoCopy(dateadd(v0.getString(), v1.getInt(), v2.getTimestampNoCopy()));
......@@ -1290,6 +1292,8 @@ public class Function extends Expression implements FunctionCall {
case INSTR:
case SUBSTR:
case SUBSTRING:
case LPAD:
case RPAD:
min = 2;
max = 3;
break;
......
......@@ -2080,6 +2080,7 @@ Each character needs 16 bits.
","
BIT_LENGTH(NAME)
"
"Functions (String)","LENGTH","
{LENGTH | CHAR_LENGTH | CHARACTER_LENGTH}(string): int
","
......@@ -2088,6 +2089,7 @@ For BLOB, CLOB, BYTES and JAVA_OBJECT, the precision is used.
","
LENGTH(NAME)
"
"Functions (String)","OCTET_LENGTH","
OCTET_LENGTH(string): int
","
......@@ -2097,6 +2099,7 @@ Each character needs 2 bytes.
","
OCTET_LENGTH(NAME)
"
"Functions (String)","CHAR","
{CHAR | CHR}(int): string
","
......@@ -2104,6 +2107,7 @@ Returns the character that represents the ASCII value.
","
CHAR(65)
"
"Functions (String)","CONCAT","
CONCAT(string, string [,...]): string
","
......@@ -2111,6 +2115,7 @@ Combines strings.
","
CONCAT(NAME, '!')
"
"Functions (String)","DIFFERENCE","
DIFFERENCE(string, string): int
","
......@@ -2118,6 +2123,7 @@ Returns the difference between the sounds of two strings.
","
DIFFERENCE(T1.NAME, T2.NAME)
"
"Functions (String)","HEXTORAW","
HEXTORAW(string): string
","
......@@ -2126,6 +2132,7 @@ Converts a hex representation of a string to a string.
","
HEXTORAW(DATA)
"
"Functions (String)","RAWTOHEX","
RAWTOHEX(string): string
","
......@@ -2134,6 +2141,7 @@ Converts a string to the hex representation.
","
RAWTOHEX(DATA)
"
"Functions (String)","INSTR","
INSTR(string, searchString, [, startInt]): int
","
......@@ -2153,6 +2161,7 @@ in the original string.
","
INSERT(NAME, 1, 1, ' ')
"
"Functions (String)","LOWER","
{LOWER | LCASE}(string): string
","
......@@ -2160,6 +2169,7 @@ Converts a string to lowercase.
","
LOWER(NAME)
"
"Functions (String)","UPPER","
{UPPER | UCASE}(string): string
","
......@@ -2167,6 +2177,7 @@ Converts a string to uppercase.
","
UPPER(NAME)
"
"Functions (String)","LEFT","
LEFT(string, int): string
","
......@@ -2174,6 +2185,7 @@ Returns the leftmost number of characters.
","
LEFT(NAME, 3)
"
"Functions (String)","RIGHT","
RIGHT(string, int): string
","
......@@ -2181,6 +2193,7 @@ Returns the rightmost number of characters.
","
RIGHT(NAME, 3)
"
"Functions (String)","LOCATE","
LOCATE(searchString, string [, startInt]): int
","
......@@ -2191,6 +2204,7 @@ If position is negative, the rightmost location is returned.
","
LOCATE('.', NAME)
"
"Functions (String)","POSITION","
POSITION(searchString, string): int
","
......@@ -2199,6 +2213,27 @@ See also LOCATE.
","
POSITION('.', NAME)
"
"Functions (String)","LPAD","
LPAD(string, int[, paddingString]): string
","
Left pad the string to the specified length.
If the length is shorter than the string, it will be truncated at the end.
If the padding string is not set, spaces will be used.
","
LPAD(AMOUNT, 10, '*')
"
"Functions (String)","RPAD","
RPAD(string, int[, paddingString]): string
","
Right pad the string to the specified length.
If the length is shorter than the string, it will be truncated.
If the padding string is not set, spaces will be used.
","
RPAD(TEXT, 10, '-')
"
"Functions (String)","LTRIM","
LTRIM(string): string
","
......@@ -2206,6 +2241,7 @@ Removes all leading spaces from a string.
","
LTRIM(NAME)
"
"Functions (String)","RTRIM","
RTRIM(string): string
","
......@@ -2213,6 +2249,7 @@ Removes all trailing spaces from a string.
","
RTRIM(NAME)
"
"Functions (String)","TRIM","
TRIM([{LEADING | TRAILING | BOTH} [string] FROM]
string): string
......@@ -2775,9 +2812,11 @@ INFORMATION_SCHEMA.FUNCTION_COLUMNS contains the function columns.
INFORMATION_SCHEMA.HELP contains the reference help.
INFORMATION_SCHEMA.INDEXES contains the indexes.
INFORMATION_SCHEMA.IN_DOUBT contains all in-doubt transactions.
INFORMATION_SCHEMA.LOCKS contains all locks.
INFORMATION_SCHEMA.RIGHTS contains the rights.
INFORMATION_SCHEMA.ROLES contains the roles.
INFORMATION_SCHEMA.SCHEMATA contains the schemas.
INFORMATION_SCHEMA.SESSIONS contains the open sessions.
INFORMATION_SCHEMA.SEQUENCES contains the sequences.
INFORMATION_SCHEMA.SETTINGS contains the settings.
INFORMATION_SCHEMA.TABLES contains the tables.
......
......@@ -8,12 +8,14 @@ import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
......@@ -55,10 +57,20 @@ public class TcpServer implements Service {
private boolean allowOthers;
private boolean ifExists;
private Connection managementDb;
private PreparedStatement managementDbAdd;
private PreparedStatement managementDbRemove;
private String managementPassword = "";
private static HashMap servers = new HashMap();
private static final Map SERVERS = Collections.synchronizedMap(new HashMap());
private Thread listenerThread;
private int nextThreadId;
/**
* Get the database name of the management database.
* The management database contains a table with active sessions (SESSIONS).
*
* @param port the TCP server port
* @return the database name (usually starting with mem:)
*/
public static String getManagementDbName(int port) {
return "mem:" + Constants.MANAGEMENT_DB_PREFIX + port;
}
......@@ -74,10 +86,47 @@ public class TcpServer implements Service {
try {
stat = conn.createStatement();
stat.execute("CREATE ALIAS IF NOT EXISTS STOP_SERVER FOR \"" + TcpServer.class.getName() + ".stopServer\"");
stat.execute("CREATE TABLE IF NOT EXISTS SESSIONS(ID INT PRIMARY KEY, URL VARCHAR, USER VARCHAR, CONNECTED TIMESTAMP)");
managementDbAdd = conn.prepareStatement("INSERT INTO SESSIONS VALUES(?, ?, ?, NOW())");
managementDbRemove = conn.prepareStatement("DELETE FROM SESSIONS WHERE ID=?");
} finally {
JdbcUtils.closeSilently(stat);
}
servers.put("" + port, this);
SERVERS.put("" + port, this);
}
/**
* Get the list of ports of all running TCP server.
*
* @return the list of ports
*/
public static int[] getAllServerPorts() {
Object[] servers = SERVERS.keySet().toArray();
int[] ports = new int[servers.length];
for (int i = 0; i < servers.length; i++) {
ports[i] = Integer.parseInt(servers[i].toString());
}
return ports;
}
synchronized void addConnection(int id, String url, String user) {
try {
managementDbAdd.setInt(1, id);
managementDbAdd.setString(2, url);
managementDbAdd.setString(3, user);
managementDbAdd.execute();
} catch (SQLException e) {
TraceSystem.traceThrowable(e);
}
}
synchronized void removeConnection(int id) {
try {
managementDbRemove.setInt(1, id);
managementDbRemove.execute();
} catch (SQLException e) {
TraceSystem.traceThrowable(e);
}
}
private void stopManagementDb() {
......@@ -141,7 +190,7 @@ public class TcpServer implements Service {
try {
while (!stop) {
Socket s = serverSocket.accept();
TcpServerThread c = new TcpServerThread(s, this);
TcpServerThread c = new TcpServerThread(s, this, nextThreadId++);
running.add(c);
Thread thread = new Thread(c);
thread.setName(threadName + " thread");
......@@ -202,11 +251,11 @@ public class TcpServer implements Service {
TraceSystem.traceThrowable(e);
}
}
servers.remove("" + port);
SERVERS.remove("" + port);
}
public static synchronized void stopServer(int port, String password, int shutdownMode) {
TcpServer server = (TcpServer) servers.get("" + port);
TcpServer server = (TcpServer) SERVERS.get("" + port);
if (server == null) {
return;
}
......
......@@ -39,9 +39,11 @@ public class TcpServerThread implements Runnable {
private Transfer transfer;
private Command commit;
private SmallMap cache = new SmallMap(SysProperties.SERVER_CACHED_OBJECTS);
private int id;
public TcpServerThread(Socket socket, TcpServer server) {
public TcpServerThread(Socket socket, TcpServer server, int id) {
this.server = server;
this.id = id;
transfer = new Transfer(null);
transfer.setSocket(socket);
}
......@@ -90,6 +92,7 @@ public class TcpServerThread implements Runnable {
session = engine.getSession(ci);
transfer.setSession(session);
transfer.writeInt(SessionRemote.STATUS_OK).flush();
server.addConnection(id, originalURL, ci.getUserName());
log("Connected");
} catch (Throwable e) {
sendError(e);
......@@ -116,6 +119,7 @@ public class TcpServerThread implements Runnable {
Command rollback = session.prepareLocal("ROLLBACK");
rollback.executeUpdate();
session.close();
server.removeConnection(id);
} catch (Exception e) {
server.logError(e);
} finally {
......
......@@ -12,6 +12,7 @@ import java.io.Reader;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.Collator;
import java.util.Locale;
......@@ -66,7 +67,7 @@ public class MetaTable extends Table {
SETTINGS = 6, HELP = 7, SEQUENCES = 8, USERS = 9, ROLES = 10, RIGHTS = 11, FUNCTION_ALIASES = 12,
SCHEMATA = 13, TABLE_PRIVILEGES = 14, COLUMN_PRIVILEGES = 15, COLLATIONS = 16, VIEWS = 17, IN_DOUBT = 18,
CROSS_REFERENCES = 19, CONSTRAINTS = 20, FUNCTION_COLUMNS = 21, CONSTANTS = 22, DOMAINS = 23,
TRIGGERS = 24;
TRIGGERS = 24, SESSIONS = 25, LOCKS = 26;
private final int type;
private final int indexColumn;
......@@ -428,6 +429,26 @@ public class MetaTable extends Table {
"ID INT"
});
break;
case SESSIONS: {
setObjectName("SESSIONS");
cols = createColumns(new String[]{
"ID INT",
"USER_NAME",
"CURRENT_STATEMENT",
"LOGIN_TIME",
});
break;
}
case LOCKS: {
setObjectName("LOCKS");
cols = createColumns(new String[]{
"TABLE_SCHEMA",
"TABLE_NAME",
"SESSION_ID INT",
"LOCK_TYPE",
});
break;
}
default:
throw Message.getInternalError("type="+type);
}
......@@ -1178,6 +1199,42 @@ public class MetaTable extends Table {
}
break;
}
case SESSIONS: {
Session[] sessions = database.getSessions();
boolean admin = session.getUser().getAdmin();
for (int i = 0; i < sessions.length; i++) {
Session s = sessions[i];
if (admin || s == session) {
add(rows, new String[] {
"" + s.getId(), // ID
s.getUser().getName(), // USER_NAME
s.getCurrentCommand(), // CURRENT_COMMAND
new Timestamp(s.getLoginTime()).toString(), // LOGIN_TIME
});
}
}
break;
}
case LOCKS: {
Session[] sessions = database.getSessions();
boolean admin = session.getUser().getAdmin();
for (int i = 0; i < sessions.length; i++) {
Session s = sessions[i];
if (admin || s == session) {
Table[] locks = s.getLocks();
for (int j = 0; j < locks.length; j++) {
Table table = locks[j];
add(rows, new String[] {
table.getSchema().getName(), // TABLE_SCHEMA
table.getName(), // TABLE_NAME
"" + s.getId(), // SESSION_ID
table.isLockExclusive(s) ? "WRITE" : "READ", // LOCK_TYPE
});
}
}
}
break;
}
default:
throw Message.getInternalError("type="+type);
}
......
......@@ -135,6 +135,10 @@ public abstract class Table extends SchemaObjectBase {
public abstract void lock(Session session, boolean exclusive, boolean force) throws SQLException;
public boolean isLockExclusive(Session s) {
return false;
}
public abstract void close(Session session) throws SQLException;
public abstract void unlock(Session s);
......
......@@ -307,6 +307,10 @@ public class TableData extends Table implements RecordReader {
rowCount = 0;
}
public boolean isLockExclusive(Session s) {
return lockExclusive == s;
}
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
int lockMode = database.getLockMode();
if (lockMode == Constants.LOCK_MODE_OFF) {
......
......@@ -630,4 +630,33 @@ public class StringUtils {
return sql;
}
public static String pad(String string, int n, String padding, boolean right) {
if (n < 0) {
n = 0;
}
if (n < string.length()) {
return string.substring(0, n);
} else if (n == string.length()) {
return string;
}
char paddingChar;
if (padding == null || padding.length() == 0) {
paddingChar = ' ';
} else {
paddingChar = padding.charAt(0);
}
StringBuffer buff = new StringBuffer(n);
n -= string.length();
if (right) {
buff.append(string);
}
for (int i = 0; i < n; i++) {
buff.append(paddingChar);
}
if (!right) {
buff.append(string);
}
return buff.toString();
}
}
......@@ -151,10 +151,8 @@ java org.h2.test.TestAll timer
/*
History:
The Ukrainian translation was not working in the last release. Fixed.
Creating many tables (many hundreds) was slow. Fixed.
Opening a database with many indexes (thousands) was slow. Fixed.
write simple test for
NFORMATION_SCHEMA.SESSIONS and LOCKS
C:\temp\test\db
......
......@@ -96,6 +96,8 @@ public class TestMetaData extends TestBase {
rs.next();
check("IN_DOUBT", rs.getString("TABLE_NAME"));
rs.next();
check("LOCKS", rs.getString("TABLE_NAME"));
rs.next();
check("RIGHTS", rs.getString("TABLE_NAME"));
rs.next();
check("ROLES", rs.getString("TABLE_NAME"));
......@@ -104,6 +106,8 @@ public class TestMetaData extends TestBase {
rs.next();
check("SEQUENCES", rs.getString("TABLE_NAME"));
rs.next();
check("SESSIONS", rs.getString("TABLE_NAME"));
rs.next();
check("SETTINGS", rs.getString("TABLE_NAME"));
rs.next();
check("TABLES", rs.getString("TABLE_NAME"));
......
......@@ -362,6 +362,9 @@ public class TestCrashAPI extends TestBase {
public TestBase init(TestAll conf) throws Exception {
super.init(conf);
if (config.mvcc) {
return this;
}
if (config.logMode == 0) {
error("Log mode 0 may corrupt the db, can't test");
}
......@@ -382,6 +385,9 @@ public class TestCrashAPI extends TestBase {
}
public void test() throws Exception {
if (config.mvcc) {
return;
}
int len = getSize(2, 6);
for (int i = 0; i < len; i++) {
int seed = RandomUtils.nextInt(Integer.MAX_VALUE);
......
select lpad('string', 10, '+');
> ++++string;
select rpad('string', 10, '+');
> string++++;
select lpad('string', 10);
> string;
select count(*) from (select * from dual union select * from dual) where x = 0;
> 0;
select count(*) from (select * from (select * from dual union select * from dual)) where x = 0;
......
......@@ -19,6 +19,14 @@ public class TestStringUtils extends TestBase {
testSplit();
testJavaString();
testURL();
testPad();
}
private void testPad() throws Exception {
check("large", StringUtils.pad("larger text", 5, null, true));
check("large", StringUtils.pad("larger text", 5, null, false));
check("short+++++", StringUtils.pad("short", 10, "+", true));
check("+++++short", StringUtils.pad("short", 10, "+", false));
}
private void testXML() throws Exception {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论