提交 43214cca authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 2531186c
...@@ -37,13 +37,10 @@ ...@@ -37,13 +37,10 @@
<target name="clean" depends="init"> <target name="clean" depends="init">
<mkdir dir="bin"/> <mkdir dir="bin"/>
<mkdir dir="odbc"/>
<mkdir dir="docs"/> <mkdir dir="docs"/>
<delete includeemptydirs="true"> <delete includeemptydirs="true">
<fileset dir="." includes="*.sql,*.txt,*.lock,**/*.db,node*"/> <fileset dir="." includes="*.sql,*.txt,*.lock,**/*.db,node*"/>
<fileset dir="bin" includes="**/*" excludes="**/*.bat,**/*.sh"/> <fileset dir="bin" includes="**/*" excludes="**/*.bat,**/*.sh"/>
<fileset dir="odbc" includes="*.a,*.def,"/>
<fileset dir="odbc" includes="*.def,h2odbcTest.exe"/>
<fileset dir="docs" includes="**/*"/> <fileset dir="docs" includes="**/*"/>
</delete> </delete>
<delete file="src/tools/org/h2/tools/code/CodeSwitch.class"/> <delete file="src/tools/org/h2/tools/code/CodeSwitch.class"/>
...@@ -251,7 +248,7 @@ ...@@ -251,7 +248,7 @@
<fileset dir="src/main"/> <fileset dir="src/main"/>
</copy> </copy>
<java classname="org.h2.test.coverage.Coverage" classpath="bin" dir="bin" fork="true"> <java classname="org.h2.test.coverage.Coverage" classpath="bin" dir="bin" fork="true">
<arg line="-e org/h2/web -e org/h2/server/Odbc -e org/h2/jdbcx -e org/h2/bnf -r org/h2"/> <arg line="-e org/h2/web -e org/h2/jdbcx -e org/h2/bnf -r org/h2"/>
</java> </java>
<javac target="${jdk}" source="${jdk}" executable="${javac}" srcdir="bin" destdir="bin" debug="true" includes="org/h2/**"/> <javac target="${jdk}" source="${jdk}" executable="${javac}" srcdir="bin" destdir="bin" debug="true" includes="org/h2/**"/>
<java classname="org.h2.test.TestAll" fork="true" classpath="bin" dir="bin"> <java classname="org.h2.test.TestAll" fork="true" classpath="bin" dir="bin">
...@@ -279,7 +276,6 @@ ...@@ -279,7 +276,6 @@
<fileset dir=".." includes="h2/ant-build.properties"/> <fileset dir=".." includes="h2/ant-build.properties"/>
<fileset dir=".." includes="h2/bin/**/*"/> <fileset dir=".." includes="h2/bin/**/*"/>
<fileset dir=".." includes="h2/docs/**/*"/> <fileset dir=".." includes="h2/docs/**/*"/>
<fileset dir=".." includes="h2/odbc/**/*"/>
<fileset dir=".." includes="h2/service/**/*"/> <fileset dir=".." includes="h2/service/**/*"/>
<fileset dir=".." includes="h2/src/**/*"/> <fileset dir=".." includes="h2/src/**/*"/>
</zip> </zip>
......
...@@ -311,48 +311,86 @@ If successful, a command prompt window will pop up and disappear immediately. If ...@@ -311,48 +311,86 @@ If successful, a command prompt window will pop up and disappear immediately. If
<br /><a name="odbc_driver"></a> <br /><a name="odbc_driver"></a>
<h2>ODBC Driver</h2> <h2>ODBC Driver</h2>
The ODBC driver of this database is currently not very stable and only tested superficially This database does not come with its own ODBC driver at this time,
with a few applications (OpenOffice 2.0, Microsoft Excel and Microsoft Access) and but it supports the PostgreSQL network protocol.
data types (INT and VARCHAR), and should not be used for production applications. Therefore, the PostgreSQL ODBC driver can be used.
Only a Windows version of the driver is available at this time. Support for the PostgreSQL network protocol is quite new and should be viewed
as experimental. It should not be used for production applications.
<h3>ODBC Installation</h3> <h3>ODBC Installation</h3>
<p> <p>
Before the ODBC driver can be used, it needs to be installed. To do this, First, the ODBC driver must be installed.
double click on h2odbcSetup.exe. If you do this the first time, it will ask you to locate the Any recent PostgreSQL ODBC driver should work, however version 8.2.4 or newer is recommended.
driver dll (h2odbc.dll). If you already installed it, the ODBC administration dialog will open The Windows version of the PostgreSQL ODBC driver is available at
where you can create new or modify existing data sources. <a href="http://www.postgresql.org/ftp/odbc/versions/msi">http://www.postgresql.org/ftp/odbc/versions/msi</a>.
When you create a new H2 ODBC data source, a dialog window will appear
and ask for the database settings:
</p> </p>
<img src="odbcDataSource.png" alt="ODBC Configuration" />
<h3>Log Option</h3>
The driver is able to log operations to a file.
To enable logging, the log file name must be set in the registry under the key
CURRENT_USER/Software/H2/ODBC/LogFile. This key will only be read when the
driver starts, so you need to make sure all applications that may use the driver
are closed before changing this setting.
If this registry entry is not found when the driver starts, logging is disabled.
A sample registry key file may look like this:
<pre>
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\H2\ODBC] <h3>Starting the Server</h3>
"LogFile"="C:\\temp\\h2odbc.txt" <p>
After installing the ODBC driver, start the H2 Server using the command line:
<pre>
java -cp h2.jar org.h2.tools.Server
</pre>
The PG Server (PG for PostgreSQL protocol) is started as well.
By default, databases are stored in the current working directory where the server is started.
Use -baseDir to save databases in another directory, for example the user home directory:
<pre>
java -cp h2.jar org.h2.tools.Server -baseDir ~
</pre> </pre>
The PG server can be started and stopped from within a Java application as follows:
<pre>
Server server = Server.createPgServer(new String[]{"-baseDir", "~"});
server.start();
...
server.stop();
</pre>
By default, only connections from localhost are allowed. To allow remote connections, use
<code>-pgAllowOthers true</code> when starting the server.
</p>
<h3>ODBC Configuration</h3>
<p>
After installing the driver, a new Data Source must be added. In Windows,
run <code>odbcad32.exe</code> to open the Data Source Administrator. Then click on 'Add...'
and select the PostgreSQL Unicode driver. Then click 'Finish'.
You will be able to change the connection properties:
</p>
<table>
<tr><th>Property</th><th>Example</th><th>Remarks</th></th>
<tr><td>Data Source</td><td>H2 Test</td><td>The name of the ODBC Data Source</td></tr>
<tr><td>Database</td><td>test</td>
<td>
The database name. Only simple names are supported at this time; <br />
relative or absolute path are not supported in the database name. <br />
By default, the database is stored in the current working directory <br />
where the Server is started except when the -baseDir setting is used. <br />
The name must be at least 3 characters.
</td></tr>
<tr><td>Server</td><td>localhost</td><td>The server name or IP address.<br />By default, only remote connections are allowed</td></tr>
<tr><td>User Name</td><td>sa</td><td>The database user name.</td></tr>
<tr><td>SSL Mode</td><td>disabled</td><td>At this time, SSL is not supported.</td></tr>
<tr><td>Port</td><td>5435</td><td>The port where the PG Server is listening.</td></tr>
<tr><td>Password</td><td>sa</td><td>The database password.</td></tr>
</table>
<p>
Afterwards, you may use this data source.
</p>
<h3>PG Protocol Support Limitations</h3>
<p>
At this time, only a subset of the PostgreSQL network protocol is implemented.
Also, there may be compatibility problems on the SQL level, with the catalog, or with text encoding.
Problems are fixed as they are found.
Currently, statements can not be cancelled when using the PG protocol.
</p>
<h3>Security Considerations</h3> <h3>Security Considerations</h3>
Currently, the ODBC does not encrypt the password before sending it over TCP/IP to the server. Currently, the PG Server does not support challenge response or encrypt passwords.
This may be a problem if an attacker can listen to the data transferred between the ODBC client This may be a problem if an attacker can listen to the data transferred between the ODBC driver
and the server, because the password is readable to the attacker. and the server, because the password is readable to the attacker.
Also, it is currently not possible to use encrypted SSL connections. Also, it is currently not possible to use encrypted SSL connections.
The password for a data source is stored unencrypted in the registry.
Therefore the ODBC driver should not be used where security is important. Therefore the ODBC driver should not be used where security is important.
<h3>Uninstalling</h3>
To uninstall the ODBC driver, double click on h2odbcUninstall.exe. This will uninstall the driver.
<br /><a name="acid"></a> <br /><a name="acid"></a>
<h2>ACID</h2> <h2>ACID</h2>
In the database world, ACID stands for: In the database world, ACID stands for:
......
...@@ -39,7 +39,9 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -39,7 +39,9 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3> <h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / 2007-TODO</h3><ul> <h3>Version 1.0 / 2007-TODO</h3><ul>
<li>The default value for h2.defaultMaxMemoryUndo is now 50000. <li>
</li><li>The experimental H2 ODBC driver has been removed.
</li><li>The default value for h2.defaultMaxMemoryUndo is now 50000.
This avoids out of memory problems when using very large transactions, This avoids out of memory problems when using very large transactions,
however large transactions are slower because they are buffered to disk. however large transactions are slower because they are buffered to disk.
To disable, use -Dh2.defaultMaxMemoryUndo=2000000000. To disable, use -Dh2.defaultMaxMemoryUndo=2000000000.
......
...@@ -119,8 +119,6 @@ Section "All" ...@@ -119,8 +119,6 @@ Section "All"
File /r /x CVS /x .cvsignore ..\..\bin\*.* File /r /x CVS /x .cvsignore ..\..\bin\*.*
SetOutPath "$INSTDIR\docs" SetOutPath "$INSTDIR\docs"
File /r /x CVS /x .cvsignore ..\..\docs\*.* File /r /x CVS /x .cvsignore ..\..\docs\*.*
SetOutPath "$INSTDIR\odbc"
File /r /x CVS /x .cvsignore ..\..\odbc\*.*
SetOutPath "$INSTDIR\service" SetOutPath "$INSTDIR\service"
File /r /x CVS /x .cvsignore /x .svn ..\..\service\*.* File /r /x CVS /x .cvsignore /x .svn ..\..\service\*.*
SetOutPath "$INSTDIR" SetOutPath "$INSTDIR"
......
...@@ -50,7 +50,6 @@ import org.h2.message.TraceSystem; ...@@ -50,7 +50,6 @@ import org.h2.message.TraceSystem;
* - table of contents * - table of contents
* - Switch off auto-build * - Switch off auto-build
* - ant all * - ant all
* - Make sure odbc files are the
* - Copy the pdf file to h2/docs * - Copy the pdf file to h2/docs
* - Make sure the build files are removed * - Make sure the build files are removed
* - ant zip * - ant zip
......
...@@ -58,7 +58,6 @@ import org.h2.value.ValueUuid; ...@@ -58,7 +58,6 @@ import org.h2.value.ValueUuid;
*/ */
public class Function extends Expression implements FunctionCall { public class Function extends Expression implements FunctionCall {
// TODO functions: ODBC TIMESTAMPDIFF
// TODO functions: add function hashcode(value) // TODO functions: add function hashcode(value)
public static final int ABS = 0, ACOS = 1, ASIN = 2, ATAN = 3, ATAN2 = 4, public static final int ABS = 0, ACOS = 1, ASIN = 2, ATAN = 3, ATAN2 = 4,
......
...@@ -10,6 +10,14 @@ import org.h2.value.Value; ...@@ -10,6 +10,14 @@ import org.h2.value.Value;
* @author Thomas * @author Thomas
*/ */
public class LinearHashEntry { public class LinearHashEntry {
// private LinearHashEntry(int home, int hash, Value key, int value) {
// this.home = home;
// this.hash = hash;
// this.key = key;
// this.value = value;
// }
public int home; public int home;
public int hash; public int hash;
public Value key; public Value key;
......
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.server;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import org.h2.message.TraceSystem;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
/**
* @author Thomas
*/
public class OdbcServer implements Service {
public static final int DEFAULT_PORT = 9083; // also in the docs
private int port = OdbcServer.DEFAULT_PORT;
private boolean stop;
private boolean log;
private ServerSocket serverSocket;
private HashSet running = new HashSet();
private String baseDir;
private String url;
private boolean allowOthers;
private boolean ifExists;
private Thread listenerThread;
boolean getLog() {
return log;
}
void log(String s) {
if (log) {
System.out.println(s);
}
}
synchronized void remove(OdbcServerThread t) {
running.remove(t);
}
void logError(Exception e) {
if (log) {
e.printStackTrace();
}
}
public void init(String[] args) throws Exception {
port = DEFAULT_PORT;
for (int i = 0; i < args.length; i++) {
String a = args[i];
if (a.equals("-log")) {
log = Boolean.valueOf(args[++i]).booleanValue();
} else if (a.equals("-odbcPort")) {
port = MathUtils.decodeInt(args[++i]);
} else if (a.equals("-baseDir")) {
baseDir = args[++i];
} else if (a.equals("-odbcAllowOthers")) {
allowOthers = Boolean.valueOf(args[++i]).booleanValue();
} else if (a.equals("-ifExists")) {
ifExists = Boolean.valueOf(args[++i]).booleanValue();
}
}
org.h2.Driver.load();
url = "odbc://localhost:" + port;
}
public String getURL() {
return url;
}
boolean allow(Socket socket) {
if(allowOthers) {
return true;
}
return NetUtils.isLoopbackAddress(socket);
}
public void start() throws SQLException {
serverSocket = NetUtils.createServerSocket(port, false);
}
public void listen() {
listenerThread = Thread.currentThread();
String threadName = listenerThread.getName();
try {
while (!stop) {
Socket s = serverSocket.accept();
if(!allow(s)) {
log("Connection not allowed");
s.close();
} else {
OdbcServerThread c = new OdbcServerThread(s, this);
running.add(c);
Thread thread = new Thread(c);
thread.setName(threadName+" thread");
c.setThread(thread);
thread.start();
}
}
} catch (Exception e) {
if(!stop) {
e.printStackTrace();
}
}
}
public void stop() {
// TODO server: share code between web and tcp servers
if(!stop) {
stop = true;
if(serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
// TODO log exception
e.printStackTrace();
}
serverSocket = null;
}
if(listenerThread != null) {
try {
listenerThread.join(1000);
} catch (InterruptedException e) {
TraceSystem.traceThrowable(e);
}
}
}
// TODO server: using a boolean 'now' argument? a timeout?
ArrayList list = new ArrayList(running);
for(int i=0; i<list.size(); i++) {
OdbcServerThread c = (OdbcServerThread) list.get(i);
c.close();
try {
c.getThread().join(100);
} catch(Exception e) {
// TODO log exception
e.printStackTrace();
}
}
}
public boolean isRunning() {
if(serverSocket == null) {
return false;
}
try {
Socket s = NetUtils.createSocket(InetAddress.getLocalHost(), serverSocket.getLocalPort(), false);
s.close();
return true;
} catch(Exception e) {
return false;
}
}
public String getBaseDir() {
return baseDir;
}
public boolean getAllowOthers() {
return allowOthers;
}
public String getType() {
return "ODBC";
}
public boolean getIfExists() {
return ifExists;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.server;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.HashMap;
import org.h2.engine.ConnectionInfo;
import org.h2.message.Message;
import org.h2.util.ObjectUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
import org.h2.value.DataType;
/**
* @author Thomas
*/
public class OdbcServerThread implements Runnable {
private OdbcServer server;
private Socket socket;
private Connection conn;
private DatabaseMetaData meta;
private boolean stop;
private int cacheId;
private Object cache;
private OdbcTransfer transfer;
private Thread thread;
private BufferedOutputStream outBuff;
private HashMap object = new HashMap();
private int nextId;
OdbcServerThread(Socket socket, OdbcServer server) {
this.server = server;
this.socket = socket;
}
private int addObject(Object o) {
int id = nextId++;
server.log("addObj "+id+" "+o);
object.put(ObjectUtils.getInteger(id), o);
cacheId = id;
cache = o;
return id;
}
private void freeObject(int id) {
if (cacheId == id) {
cacheId = -1;
cache = null;
}
object.remove(ObjectUtils.getInteger(id));
}
private Object getObject(int id) {
if (id == cacheId) {
server.log("getObj "+id+" "+cache);
return cache;
}
server.log("getObj "+id+" "+object.get(ObjectUtils.getInteger(id)));
return object.get(ObjectUtils.getInteger(id));
}
public void run() {
try {
server.log("Connect");
InputStream ins = socket.getInputStream();
OutputStream outs = socket.getOutputStream();
DataInputStream in;
in = new DataInputStream(new BufferedInputStream(ins,
OdbcTransfer.BUFFER_SIZE));
outBuff = new BufferedOutputStream(outs, OdbcTransfer.BUFFER_SIZE);
DataOutputStream out = new DataOutputStream(outBuff);
transfer = new OdbcTransfer(in, out);
outBuff.flush();
while (!stop) {
process();
outBuff.flush();
}
server.log("Disconnect");
} catch (Exception e) {
server.logError(e);
}
}
public void close() {
try {
stop = true;
conn.close();
socket.close();
server.log("Close");
} catch(Exception e) {
server.logError(e);
}
conn = null;
socket = null;
server.remove(this);
}
private void sendError(Throwable e) throws IOException {
SQLException s = Message.convert(e);
server.log("Exception "+s);
s.printStackTrace();
transfer.writeByte((byte)'E');
}
private void processResultSet(ResultSet rs) throws IOException, SQLException {
int id = addObject(rs);
transfer.writeInt(id);
ResultSetMetaData m = rs.getMetaData();
int columnCount = m.getColumnCount();
transfer.writeInt(columnCount);
for(int i=0; i<columnCount; i++) {
transfer.writeInt(mapType(m.getColumnType(i+1)));
transfer.writeString(m.getTableName(i+1));
transfer.writeString(m.getColumnLabel(i+1));
transfer.writeInt(m.getPrecision(i+1));
transfer.writeInt(m.getScale(i+1));
transfer.writeInt(m.getColumnDisplaySize(i+1));
}
}
private void setParameter(PreparedStatement prep, int index, int type) throws SQLException, IOException {
switch(type) {
case Types.NULL: {
// fake: use Integer data type for now
prep.setNull(index, Types.INTEGER);
break;
}
case Types.INTEGER: {
int value = transfer.readInt();
server.log(" index="+index+" int="+value);
prep.setInt(index, value);
break;
}
case Types.VARCHAR: {
String value = transfer.readString();
server.log(" index="+index+" string="+value);
prep.setString(index, value);
break;
}
default:
throw Message.getInternalError("unexpected data type "+type);
}
}
private void setParameters(PreparedStatement prep) throws SQLException, IOException {
while(true) {
int x = transfer.readByte();
if(x == '0') {
break;
} else if(x=='1') {
int index = transfer.readInt();
int type = transfer.readInt();
setParameter(prep, index+1, type);
} else {
throw Message.getInternalError("unexpected "+x);
}
}
}
private void processMeta() throws IOException {
int operation = transfer.readByte();
server.log("meta op="+(char)operation);
switch(operation) {
case 'B': {
String catalog = transfer.readString();
String schema = transfer.readString();
String table = transfer.readString();
if(table ==null || table.length()==0) {
table = "%";
}
int scope = transfer.readInt();
boolean nullable = transfer.readBoolean();
try {
ResultSet rs = meta.getBestRowIdentifier(catalog, schema, table, scope, nullable);
processResultSet(rs);
} catch(Throwable e) {
sendError(e);
}
break;
}
case 'C': {
// String catalog = transfer.readString();
String schemaPattern = transfer.readString();
String tableNamePattern = transfer.readString();
String columnNamePattern = transfer.readString();
if(tableNamePattern ==null || tableNamePattern.length()==0) {
tableNamePattern = "%";
}
if(columnNamePattern ==null || columnNamePattern.length()==0) {
columnNamePattern = "%";
}
PreparedStatement prep = null;
try {
prep = conn.prepareStatement("SELECT "
+ "TABLE_CATALOG TABLE_CAT, "
+ "TABLE_SCHEMA TABLE_SCHEM, "
+ "TABLE_NAME, "
+ "COLUMN_NAME, "
+ "DATA_TYPE, "
+ "TYPE_NAME, "
+ "CHARACTER_MAXIMUM_LENGTH COLUMN_SIZE, "
+ "CHARACTER_MAXIMUM_LENGTH BUFFER_LENGTH, "
+ "CAST(NUMERIC_SCALE AS SMALLINT) DECIMAL_DIGITS, " // different in JDBC
+ "CAST(10 AS SMALLINT) NUM_PREC_RADIX, " // different in JDBC
+ "CAST(NULLABLE AS SMALLINT) NULLABLE, " // different in JDBC
+ "'' REMARKS, "
+ "COLUMN_DEFAULT COLUMN_DEF, "
+ "CAST(DATA_TYPE AS SMALLINT) SQL_DATA_TYPE, " // different in JDBC
+ "CAST(0 AS SMALLINT) SQL_DATETIME_SUB, " // different in JDBC
+ "CHARACTER_OCTET_LENGTH CHAR_OCTET_LENGTH, "
+ "ORDINAL_POSITION ORDINAL_POSITION, "
+ "NULLABLE IS_NULLABLE "
+ "FROM INFORMATION_SCHEMA.COLUMNS "
+ "WHERE TABLE_SCHEMA LIKE ? "
+ "AND TABLE_NAME LIKE ? "
+ "AND COLUMN_NAME LIKE ? "
+ "ORDER BY TABLE_SCHEM, TABLE_NAME, ORDINAL_POSITION");
prep.setString(1, schemaPattern == null ? "%" : schemaPattern);
prep.setString(2, tableNamePattern == null ? "%" : tableNamePattern);
prep.setString(3, columnNamePattern == null ? "%" : columnNamePattern);
// ResultSet rs = meta.getColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern);
ResultSet rs = prep.executeQuery();
processResultSet(rs);
} catch(SQLException e) {
sendError(e);
} finally {
JdbcUtils.closeSilently(prep);
}
break;
}
case 'D': {
String where;
if(transfer.readByte()=='A') {
where= "";
} else {
int type = transfer.readInt();
where = " WHERE TYPE="+type+" ";
}
Statement stat = null;
try {
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT "
+ "TYPE_NAME, "
+ "DATA_TYPE, "
+ "PRECISION COLUMN_SIZE, "
+ "PREFIX LITERAL_PREFIX, "
+ "PREFIX LITERAL_SUFFIX, "
+ "PARAMS CREATE_PARAMS, "
+ "CAST(" +DatabaseMetaData.typeNullable + " AS SMALLINT) NULLABLE, "
+ "CAST(1 AS SMALLINT) CASE_SENSITIVE, " // TODO metadata: check if this is ok
+ "CAST(1 AS SMALLINT) SEARCHABLE, " // TODO metadata: check if this is ok
+ "CAST(0 AS SMALLINT) UNSIGNED_ATTRIBUTE, " // TODO metadata: check if this is ok
+ "CAST(0 AS SMALLINT) FIXED_PREC_SCALE, " // TODO metadata: check if this is ok
+ "CAST(0 AS SMALLINT) AUTO_UNIQUE_VALUE, " // TODO metadata: check if this is ok
+ "TYPE_NAME LOCAL_TYPE_NAME, "
+ "MINIMUM_SCALE, "
+ "MAXIMUM_SCALE, "
+ "DATA_TYPE SQL_DATA_TYPE, "
+ "CAST(1 AS SMALLINT) SQL_DATETIME_SUB, " // TODO metadata: check if this is ok
+ "RADIX NUM_PREC_RADIX, "
+ "CAST(0 AS SMALLINT) INTERVAL_PRECISION "
+ "FROM INFORMATION_SCHEMA.TYPE_INFO "
+ where
+ "ORDER BY DATA_TYPE, POS");
processResultSet(rs);
} catch(SQLException e) {
sendError(e);
} finally {
JdbcUtils.closeSilently(stat);
}
break;
}
case 'I': {
// String catalog = transfer.readString();
String schemaPattern = transfer.readString();
String tableNamePattern = transfer.readString();
if(tableNamePattern==null || tableNamePattern.length()==0) {
tableNamePattern = "%";
}
// boolean unique = transfer.readBoolean();
// boolean approximate = transfer.readBoolean();
PreparedStatement prep = null;
try {
//ResultSet rs = meta.getIndexInfo(catalog, schemaPattern, tableNamePattern, unique, approximate);
prep = conn.prepareStatement("SELECT "
+ "TABLE_CATALOG TABLE_CAT, "
+ "TABLE_SCHEMA TABLE_SCHEM, "
+ "TABLE_NAME, "
+ "CAST(NON_UNIQUE AS SMALLINT) NON_UNIQUE, "
+ "TABLE_CATALOG INDEX_QUALIFIER, "
+ "INDEX_NAME, "
+ "CAST("+DatabaseMetaData.tableIndexOther + " AS SMALLINT) TYPE, "
+ "ORDINAL_POSITION, "
+ "COLUMN_NAME, "
+ "'A' ASC_OR_DESC, "
+ "CARDINALITY, "
+ "0 PAGES, "
+ "'' FILTER_CONDITION "
+ "FROM INFORMATION_SCHEMA.INDEXES "
+ "WHERE CATALOG_NAME LIKE ? "
+ "AND TABLE_NAME LIKE ? "
+ "ORDER BY NON_UNIQUE, TYPE, TABLE_SCHEM, INDEX_NAME, ORDINAL_POSITION");
prep.setString(1, schemaPattern);
prep.setString(2, tableNamePattern);
ResultSet rs = prep.executeQuery();
processResultSet(rs);
} catch(SQLException e) {
sendError(e);
} finally {
JdbcUtils.closeSilently(prep);
}
break;
}
case 'N': {
String sql = transfer.readString();
try {
sql = conn.nativeSQL(sql);
} catch(SQLException e) {
sendError(e);
}
transfer.writeString(sql);
break;
}
case 'T': {
String catalog = transfer.readString();
String schema = transfer.readString();
String table = transfer.readString();
String tableTypes = transfer.readString();
server.log(" catalog="+catalog+" schema="+schema+" table="+table+" tableTypes="+tableTypes);
ResultSet rs;
String[] types = null;
PreparedStatement prep = null;
try {
if(catalog.equals("%") && schema.length()==0 && table.length()==0) {
server.log(" allCatalogs");
prep = conn.prepareStatement("SELECT "
+ "CATALOG_NAME TABLE_CAT, "
+ "NULL TABLE_SCHEM, "
+ "NULL TABLE_NAME, "
+ "NULL TABLE_TYPE, "
+ "'' REMARKS "
+ "FROM INFORMATION_SCHEMA.CATALOGS");
rs = prep.executeQuery();
} else if(catalog.length()==0 && schema.equals("%") && table.length()==0) {
server.log(" allSchemas");
prep = conn.prepareStatement("SELECT "
+ "CATALOG_NAME TABLE_CAT, "
+ "SCHEMA_NAME TABLE_SCHEM, "
+ "NULL TABLE_NAME, "
+ "NULL TABLE_TYPE, "
+ "'' REMARKS "
+ "FROM INFORMATION_SCHEMA.SCHEMATA");
rs = prep.executeQuery();
} else if(catalog.length()==0 && schema.length()==0 && table.length()==0 && tableTypes.equals("%")) {
server.log(" allTableTypes");
prep = conn.prepareStatement("SELECT "
+ "NULL TABLE_CAT, "
+ "NULL TABLE_SCHEM, "
+ "NULL TABLE_NAME, "
+ "TYPE TABLE_TYPE, "
+ "'' REMARKS "
+ "FROM INFORMATION_SCHEMA.TABLE_TYPES");
rs = prep.executeQuery();
} else {
server.log(" getTables");
if(tableTypes.equals("%") || tableTypes.length()==0) {
types = null;
} else {
types = StringUtils.arraySplit(tableTypes, ',', false);
for(int i=0; i<types.length; i++) {
String t = StringUtils.toUpperEnglish(types[i]);
if(t.startsWith("\'")) {
t = t.substring(1, t.length()-2);
}
types[i] = t;
}
}
server.log("getTables "+catalog+" "+schema+" "+table);
if(table.length() == 0) {
table = null;
}
rs = meta.getTables(catalog, schema, table, types);
}
processResultSet(rs);
} catch(SQLException e) {
sendError(e);
} finally {
JdbcUtils.closeSilently(prep);
}
break;
}
case 'V': {
String catalog = transfer.readString();
String schema = transfer.readString();
String table = transfer.readString();
if(table ==null || table.length()==0) {
table = "%";
}
try {
ResultSet rs = meta.getVersionColumns(catalog, schema, table);
// PreparedStatement prep = conn.prepareStatement("SELECT "
// + "CAST(NULL AS INT) SCOPE, "
// + "NULL COLUMN_NAME, "
// + "CAST(NULL AS INT) DATA_TYPE, "
// + "NULL TYPE_NAME, "
// + "CAST(NULL AS INT) COLUMN_SIZE, "
// + "CAST(NULL AS INT) BUFFER_LENGTH, "
// + "CAST(NULL AS INT) DECIMAL_DIGITS, "
// + "CAST(NULL AS INT) PSEUDO_COLUMN "
// + "FROM SYSTEM_TABLES "
// + "WHERE 1=0");
// ResultSet rs = prep.executeQuery();
processResultSet(rs);
} catch(SQLException e) {
sendError(e);
}
break;
}
default:
server.log("meta operation? " + (char)operation);
}
}
private void process() throws IOException {
int operation = transfer.readByte();
if(operation == -1) {
stop = true;
return;
}
server.log("op="+(char)operation);
switch(operation) {
case 'A': {
try {
int op = transfer.readByte();
switch(op) {
case '0':
server.log("autoCommit false");
conn.setAutoCommit(false);
break;
case '1':
server.log("autoCommit true");
conn.setAutoCommit(true);
break;
case 'C':
server.log("commit");
conn.commit();
break;
case 'R':
server.log("rollback");
conn.rollback();
break;
default:
server.log("operation? " + (char)operation);
}
} catch(SQLException e) {
sendError(e);
}
break;
}
case 'C':
server.log("connect");
String db = transfer.readString();
server.log(" db="+db);
String user = transfer.readString();
server.log(" user="+user);
String password = transfer.readString();
server.log(" password="+password);
String baseDir = server.getBaseDir();
ConnectionInfo ci = new ConnectionInfo(db);
if(baseDir != null) {
ci.setBaseDir(baseDir);
}
if(server.getIfExists()) {
ci.setProperty("IFEXISTS", "TRUE");
}
String dbName = ci.getDatabaseName();
try {
conn = DriverManager.getConnection("jdbc:h2:" + dbName, user, password);
meta = conn.getMetaData();
transfer.writeByte((byte)'O');
} catch(SQLException e) {
sendError(e);
}
break;
case 'E': {
String sql = transfer.readString();
server.log("<"+sql+">");
try {
int params = getParametersCount(sql);
if(params > 0) {
// it is a prepared statement
PreparedStatement prep = conn.prepareStatement(sql);
int id = addObject(prep);
transfer.writeByte((byte)'O');
transfer.writeInt(id);
transfer.writeInt(params);
} else {
Statement stat = null;
try {
stat = conn.createStatement();
boolean isResultSet = stat.execute(sql);
if(isResultSet) {
transfer.writeByte((byte)'R');
ResultSet rs = stat.getResultSet();
processResultSet(rs);
} else {
transfer.writeByte((byte)'U');
transfer.writeInt(stat.getUpdateCount());
}
} finally {
JdbcUtils.closeSilently(stat);
}
}
} catch(SQLException e) {
sendError(e);
}
break;
}
case 'F': {
int id = transfer.readInt();
server.log("free "+id);
freeObject(id);
break;
}
case 'G': {
int objectId = transfer.readInt();
ResultSet rs = (ResultSet)getObject(objectId);
try {
boolean hasNext = rs.next();
if(hasNext) {
transfer.writeByte((byte)'1');
ResultSetMetaData m = rs.getMetaData();
int columnCount = m.getColumnCount();
for(int i=0; i<columnCount; i++) {
write(m, rs, i);
}
} else {
transfer.writeByte((byte)'0');
}
} catch(SQLException e) {
sendError(e);
}
break;
}
case 'M':
processMeta();
break;
case 'P': {
String sql = transfer.readString();
server.log("<"+sql+">");
try {
PreparedStatement prep = conn.prepareStatement(sql);
int id = addObject(prep);
transfer.writeByte((byte)'O');
transfer.writeInt(id);
int params = getParametersCount(sql);
transfer.writeInt(params);
} catch(SQLException e) {
sendError(e);
}
break;
}
case 'Q': {
// executePrepared
int id = transfer.readInt();
PreparedStatement prep = (PreparedStatement)getObject(id);
try {
setParameters(prep);
boolean isResultSet = prep.execute();
if(isResultSet) {
transfer.writeByte((byte)'R');
ResultSet rs = prep.getResultSet();
processResultSet(rs);
} else {
transfer.writeByte((byte)'U');
transfer.writeInt(prep.getUpdateCount());
}
} catch(SQLException e) {
sendError(e);
}
break;
}
case 'X':
stop = true;
break;
default:
server.log("operation? " + (char)operation);
}
}
private void write(ResultSetMetaData m, ResultSet rs, int i) throws IOException {
try {
int type = mapType(m.getColumnType(i+1));
switch(type) {
case Types.SMALLINT:
case Types.INTEGER: {
int value = rs.getInt(i+1);
if(rs.wasNull()) {
transfer.writeBoolean(true);
} else {
transfer.writeBoolean(false);
transfer.writeInt(value);
}
break;
}
case Types.NULL:
break;
case Types.VARCHAR:
transfer.writeString(rs.getString(i+1));
break;
default:
throw Message.getInternalError("unsupported data type "+type);
}
} catch(SQLException e) {
sendError(e);
}
}
int mapType(int sqlType) {
switch(sqlType) {
case Types.SMALLINT:
case Types.INTEGER:
case Types.NULL:
case Types.VARCHAR:
return sqlType;
case Types.TINYINT:
case Types.BIT:
case DataType.TYPE_BOOLEAN:
return Types.INTEGER;
case Types.BIGINT:
case Types.BINARY:
case Types.BLOB:
case Types.CHAR:
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.JAVA_OBJECT:
case Types.LONGVARBINARY:
case Types.LONGVARCHAR:
case Types.NUMERIC:
case Types.OTHER:
case Types.REAL:
case Types.VARBINARY:
return Types.VARCHAR;
default:
throw Message.getInternalError("sqlType "+sqlType);
}
}
int getParametersCount(String sql) throws SQLException {
if (sql == null || sql.indexOf('?') < 0) {
return 0;
}
int len = sql.length();
int param = 0;
for (int i = 0; i < len; i++) {
try {
char c = sql.charAt(i);
switch (c) {
case '\'': {
int j = sql.indexOf('\'', i + 1);
if (j < 0) {
throw Message.getSyntaxError(sql, i);
}
i = j;
break;
}
case '"': {
int j = sql.indexOf('"', i + 1);
if (j < 0) {
throw Message.getSyntaxError(sql, i);
}
i = j;
break;
}
case '/': {
if (sql.charAt(i + 1) == '*') {
// block comment
int j = sql.indexOf("*/", i + 2);
if (j < 0) {
throw Message.getSyntaxError(sql, i);
}
i = j + 1;
} else if (sql.charAt(i + 1) == '/') {
// single line comment
i += 2;
while (i < len && (c = sql.charAt(i)) != '\r'
&& c != '\n') {
i++;
}
}
break;
}
case '-':
if (sql.charAt(i + 1) == '-') {
// single line comment
i += 2;
while (i < len && (c = sql.charAt(i)) != '\r'
&& c != '\n') {
i++;
}
}
break;
case '?':
param++;
break;
}
} catch (ArrayIndexOutOfBoundsException e) {
throw Message.getSyntaxError(sql, i);
}
}
return param;
}
public void setThread(Thread thread) {
this.thread = thread;
}
public Thread getThread() {
return thread;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* @author Thomas
*/
public class OdbcTransfer {
static final int BUFFER_SIZE = 1024;
private DataInputStream in;
private DataOutputStream out;
OdbcTransfer(DataInputStream in, DataOutputStream out) {
this.in = in;
this.out = out;
}
OdbcTransfer writeBoolean(boolean x) throws IOException {
writeInt(x ? 1 : 0);
return this;
}
OdbcTransfer writeOk() throws IOException {
writeBoolean(true);
return this;
}
boolean readBoolean() throws IOException {
return readInt() == 1;
}
OdbcTransfer writeByte(byte x) throws IOException {
out.write(x);
return this;
}
int readByte() throws IOException {
return in.read();
}
OdbcTransfer writeShort(short x) throws IOException {
return writeInt(x);
}
short readShort() throws IOException {
return (short) readInt();
}
OdbcTransfer writeInt(int i) throws IOException {
out.writeInt(i);
return this;
}
int readInt() throws IOException {
return in.readInt();
}
OdbcTransfer writeLong(long i) throws IOException {
out.writeLong(i);
return this;
}
long readLong() throws IOException {
return in.readLong();
}
OdbcTransfer writeFloat(float i) throws IOException {
out.writeFloat(i);
return this;
}
float readFloat() throws IOException {
return in.readFloat();
}
OdbcTransfer writeDouble(double i) throws IOException {
out.writeDouble(i);
return this;
}
double readDouble() throws IOException {
return in.readDouble();
}
OdbcTransfer writeString(String s) throws IOException {
if (s == null) {
out.writeInt(-1);
} else {
out.writeInt(s.length());
for(int i=0; i<s.length(); i++) {
out.write(s.charAt(i));
}
}
return this;
}
String readString() throws IOException {
int len = in.readInt();
if (len == -1) {
return null;
}
char[] chars = new char[len];
for(int i=0; i<len; i++) {
chars[i] = (char)in.readByte();
}
return new String(chars);
}
OdbcTransfer writeDate(java.sql.Date x) throws IOException {
if (x == null) {
writeString(null);
} else {
writeString(x.toString());
}
return this;
}
OdbcTransfer writeTime(java.sql.Time x) throws IOException {
if (x == null) {
writeString(null);
} else {
writeString(x.toString());
}
return this;
}
OdbcTransfer writeTimestamp(java.sql.Timestamp x) throws IOException {
if (x == null) {
writeString(null);
} else {
writeString(x.toString());
}
return this;
}
java.sql.Date readDate() throws IOException {
String s = readString();
if (s == null) {
return null;
}
return java.sql.Date.valueOf(s);
}
java.sql.Time readTime() throws IOException {
String s = readString();
if (s == null) {
return null;
}
return java.sql.Time.valueOf(s);
}
java.sql.Timestamp readTimestamp() throws IOException {
String s = readString();
if (s == null) {
return null;
}
return java.sql.Timestamp.valueOf(s);
}
OdbcTransfer writeByteArray(byte[] data) throws IOException {
if (data == null) {
writeInt(-1);
} else {
writeInt(data.length);
}
out.write(data);
return this;
}
byte[] readByteArray() throws IOException {
int len = readInt();
if (len == -1) {
return null;
}
byte[] b = new byte[len];
in.readFully(b);
return b;
}
OdbcTransfer writeIntArray(int[] s) throws IOException {
if (s == null) {
writeInt(-1);
} else {
writeInt(s.length);
for (int i = 0; i < s.length; i++) {
writeInt(s[i]);
}
}
return this;
}
int[] readIntArray() throws IOException {
int len = readInt();
if (len == -1) {
return null;
}
int[] s = new int[len];
for (int i = 0; i < len; i++) {
s[i] = readInt();
}
return s;
}
OdbcTransfer writeStringArray(String[] s) throws IOException {
if (s == null) {
writeInt(-1);
} else {
writeInt(s.length);
for (int i = 0; i < s.length; i++) {
writeString(s[i]);
}
}
return this;
}
String[] readStringArray() throws IOException {
int len = readInt();
if (len == -1) {
return null;
}
String[] s = new String[len];
for (int i = 0; i < len; i++) {
s[i] = readString();
}
return s;
}
// buffer - cannot be null
OdbcTransfer writeBuffer(byte[] buffer) throws IOException {
out.write(buffer);
return this;
}
}
...@@ -38,11 +38,25 @@ public class PgServer implements Service { ...@@ -38,11 +38,25 @@ public class PgServer implements Service {
private boolean allowOthers; private boolean allowOthers;
private boolean ifExists; private boolean ifExists;
public static void main(String[] args) throws Exception { public void init(String[] args) throws Exception {
PgServer app = new PgServer(); port = DEFAULT_PORT;
app.init(args); for (int i = 0; i < args.length; i++) {
app.start(); String a = args[i];
app.listen(); if (a.equals("-log")) {
log = Boolean.valueOf(args[++i]).booleanValue();
} else if (a.equals("-pgPort")) {
port = MathUtils.decodeInt(args[++i]);
} else if (a.equals("-baseDir")) {
baseDir = args[++i];
} else if (a.equals("-pgAllowOthers")) {
allowOthers = Boolean.valueOf(args[++i]).booleanValue();
} else if (a.equals("-ifExists")) {
ifExists = Boolean.valueOf(args[++i]).booleanValue();
}
}
org.h2.Driver.load();
url = "pg://localhost:" + port;
// log = true;
} }
boolean getLog() { boolean getLog() {
...@@ -65,29 +79,6 @@ public class PgServer implements Service { ...@@ -65,29 +79,6 @@ public class PgServer implements Service {
} }
} }
public void init(String[] args) throws Exception {
port = DEFAULT_PORT;
for (int i = 0; i < args.length; i++) {
String a = args[i];
if (a.equals("-log")) {
log = Boolean.valueOf(args[++i]).booleanValue();
} else if (a.equals("-pgPort")) {
port = MathUtils.decodeInt(args[++i]);
} else if (a.equals("-baseDir")) {
baseDir = args[++i];
} else if (a.equals("-pgAllowOthers")) {
allowOthers = Boolean.valueOf(args[++i]).booleanValue();
} else if (a.equals("-ifExists")) {
ifExists = Boolean.valueOf(args[++i]).booleanValue();
}
}
org.h2.Driver.load();
url = "pg://localhost:" + port;
int testing;
log = true;
}
public String getURL() { public String getURL() {
return url; return url;
} }
...@@ -129,7 +120,7 @@ public class PgServer implements Service { ...@@ -129,7 +120,7 @@ public class PgServer implements Service {
} }
public void stop() { public void stop() {
// TODO server: share code between web and tcp servers // TODO server: combine with tcp server
if(!stop) { if(!stop) {
stop = true; stop = true;
if(serverSocket != null) { if(serverSocket != null) {
...@@ -243,7 +234,7 @@ public class PgServer implements Service { ...@@ -243,7 +234,7 @@ public class PgServer implements Service {
} }
public static boolean hasDatabasePrivilege(int id, String privilege) { public static boolean hasDatabasePrivilege(int id, String privilege) {
return false; return true;
} }
public static boolean hasTablePrivilege(String table, String privilege) { public static boolean hasTablePrivilege(String table, String privilege) {
......
...@@ -26,7 +26,6 @@ import java.sql.Statement; ...@@ -26,7 +26,6 @@ import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Hashtable;
import java.util.Properties; import java.util.Properties;
import org.h2.Driver; import org.h2.Driver;
...@@ -37,7 +36,7 @@ import org.h2.util.ScriptReader; ...@@ -37,7 +36,7 @@ import org.h2.util.ScriptReader;
* This class implements a subset of the PostgreSQL protocol as described here: * This class implements a subset of the PostgreSQL protocol as described here:
* http://developer.postgresql.org/pgdocs/postgres/protocol.html * http://developer.postgresql.org/pgdocs/postgres/protocol.html
* The PostgreSQL catalog is described here: * The PostgreSQL catalog is described here:
* http://www.postgresql.org/docs/7.4/static/view-pg-user.html * http://www.postgresql.org/docs/7.4/static/catalogs.html
* @author Thomas * @author Thomas
*/ */
...@@ -117,17 +116,6 @@ public class PgServerThread implements Runnable { ...@@ -117,17 +116,6 @@ public class PgServerThread implements Runnable {
} }
private void error(String message, Exception e) { private void error(String message, Exception e) {
int todoDocumentLimitations;
/*
Limitations:
- The database name can not contains the path (~ or directory).
Workaround: use -baseDir when starting the server.
- SSL is not supported.
- Statements can not be cancelled.
- Metadata is static.
*/
if(e != null) { if(e != null) {
server.logError(e); server.logError(e);
} }
...@@ -154,8 +142,7 @@ Limitations: ...@@ -154,8 +142,7 @@ Limitations:
server.log("Init"); server.log("Init");
int version = readInt(); int version = readInt();
if(version == 80877102) { if(version == 80877102) {
int todoSupport; server.log("CancelRequest (not supported)");
server.log("CancelRequest");
server.log(" pid: "+readInt()); server.log(" pid: "+readInt());
server.log(" key: "+readInt()); server.log(" key: "+readInt());
error("CancelRequest", null); error("CancelRequest", null);
...@@ -382,7 +369,6 @@ Limitations: ...@@ -382,7 +369,6 @@ Limitations:
private String getSQL(String s) { private String getSQL(String s) {
String lower = s.toLowerCase(); String lower = s.toLowerCase();
int todo;
if(lower.startsWith("show max_identifier_length")) { if(lower.startsWith("show max_identifier_length")) {
s = "CALL 63"; s = "CALL 63";
} else if(lower.startsWith("set client_encoding to")) { } else if(lower.startsWith("set client_encoding to")) {
...@@ -458,8 +444,7 @@ Limitations: ...@@ -458,8 +444,7 @@ Limitations:
if(text) { if(text) {
s = new String(d2, getEncoding()); s = new String(d2, getEncoding());
} else { } else {
int testing; server.logError(new SQLException("Binary format not supported"));
System.out.println("binary format!");
s = new String(d2, getEncoding()); s = new String(d2, getEncoding());
} }
} catch(Exception e) { } catch(Exception e) {
...@@ -483,6 +468,7 @@ Limitations: ...@@ -483,6 +468,7 @@ Limitations:
writeString(e.getMessage()); writeString(e.getMessage());
write('D'); write('D');
writeString(e.toString()); writeString(e.toString());
write(0);
sendMessage(); sendMessage();
} }
...@@ -534,7 +520,7 @@ Limitations: ...@@ -534,7 +520,7 @@ Limitations:
writeString(names[i].toLowerCase()); writeString(names[i].toLowerCase());
writeInt(0); // object ID writeInt(0); // object ID
writeShort(0); // attribute number of the column writeShort(0); // attribute number of the column
writeInt(getType(types[i])); // data type writeInt(types[i]); // data type
writeShort(getTypeSize(types[i])); // pg_type.typlen writeShort(getTypeSize(types[i])); // pg_type.typlen
writeInt(getModifier(types[i])); // pg_attribute.atttypmod writeInt(getModifier(types[i])); // pg_attribute.atttypmod
writeShort(0); // text writeShort(0); // text
...@@ -546,15 +532,6 @@ Limitations: ...@@ -546,15 +532,6 @@ Limitations:
} }
} }
private int getType(int type) {
int testing;
// switch(type) {
// case Types.VARCHAR:
// return 19;
// }
return type;
}
private int getTypeSize(int type) { private int getTypeSize(int type) {
switch(type) { switch(type) {
case Types.VARCHAR: case Types.VARCHAR:
...@@ -590,8 +567,18 @@ Limitations: ...@@ -590,8 +567,18 @@ Limitations:
} }
private void initDb() throws SQLException { private void initDb() throws SQLException {
int todoUseVersionOnlyInitWhenRequired; ResultSet rs = conn.getMetaData().getTables(null, "PG_CATALOG", "PG_VERSION", null);
boolean tableFound = rs.next();
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
if(tableFound) {
rs = stat.executeQuery("SELECT VERION FROM PG_CATALOG.PG_VERSION");
if(rs.next()) {
if(rs.getInt(1) == 1) {
// already installed
return;
}
}
}
Reader r = new InputStreamReader(getClass().getResourceAsStream("pg_catalog.sql")); Reader r = new InputStreamReader(getClass().getResourceAsStream("pg_catalog.sql"));
r = new BufferedReader(r); r = new BufferedReader(r);
ScriptReader reader = new ScriptReader(r); ScriptReader reader = new ScriptReader(r);
...@@ -604,52 +591,12 @@ Limitations: ...@@ -604,52 +591,12 @@ Limitations:
} }
reader.close(); reader.close();
ResultSet rs = stat.executeQuery("SELECT OID FROM PG_CATALOG.PG_TYPE"); rs = stat.executeQuery("SELECT OID FROM PG_CATALOG.PG_TYPE");
while(rs.next()) { while(rs.next()) {
types.add(new Integer(rs.getInt(1))); types.add(new Integer(rs.getInt(1)));
} }
} }
// private void sendResultSet(ResultSet rs) throws SQLException, IOException {
// ResultSetMetaData meta = rs.getMetaData();
// int columnCount = meta.getColumnCount();
// //
// startMessage('T');
// writeShort(columnCount);
// for(int i=0; i<columnCount; i++) {
// writeString(meta.getColumnName(i + 1));
// writeInt(0); // table id
// writeShort(0); // column id
// writeInt(0); // data type id
// writeShort(26); // data type size (see pg_type.typlen)
// writeInt(4); // type modifier (see pg_attribute.atttypmod)
// writeShort(0); // format code 0=text, 1=binary
// }
// sendMessage();
// while(rs.next()) {
// // DataRow
// startMessage('D');
// writeShort(columnCount);
// for(int i=0; i<columnCount; i++) {
// String v = rs.getString(i + 1);
// if(v == null) {
// writeInt(-1);
// } else {
// byte[] data = v.getBytes();
// writeInt(data.length);
// write(data);
// }
// }
// sendMessage();
// }
//
// // CommandComplete
// startMessage('C');
// writeString("SELECT");
// sendMessage();
// sendReadyForQuery('I');
// }
public void close() { public void close() {
try { try {
stop = true; stop = true;
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
drop schema if exists pg_catalog; drop schema if exists pg_catalog;
create schema pg_catalog; create schema pg_catalog;
create table pg_version as select 1 as version;
set search_path = PUBLIC, pg_catalog; set search_path = PUBLIC, pg_catalog;
create view pg_catalog.pg_roles -- (oid, rolname, rolcreaterole, rolcreatedb) create view pg_catalog.pg_roles -- (oid, rolname, rolcreaterole, rolcreatedb)
...@@ -87,7 +89,9 @@ from information_schema.indexes; ...@@ -87,7 +89,9 @@ from information_schema.indexes;
create table pg_catalog.pg_proc( create table pg_catalog.pg_proc(
oid int, oid int,
proname varchar_ignorecase proname varchar_ignorecase,
prorettype int,
pronamespace int
); );
create table pg_catalog.pg_trigger( create table pg_catalog.pg_trigger(
......
...@@ -31,7 +31,7 @@ import org.h2.util.IOUtils; ...@@ -31,7 +31,7 @@ import org.h2.util.IOUtils;
import org.h2.util.StartBrowser; import org.h2.util.StartBrowser;
/** /**
* This tool starts the H2 Console (web-) server, as well as the TCP and ODBC server. * This tool starts the H2 Console (web-) server, as well as the TCP and PG server.
* For JDK 1.6, a system tray icon is created, for platforms that support it. * For JDK 1.6, a system tray icon is created, for platforms that support it.
* Otherwise, a small window opens. * Otherwise, a small window opens.
* *
......
...@@ -12,7 +12,6 @@ import java.sql.SQLException; ...@@ -12,7 +12,6 @@ import java.sql.SQLException;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.server.OdbcServer;
import org.h2.server.Service; import org.h2.server.Service;
import org.h2.server.TcpServer; import org.h2.server.TcpServer;
import org.h2.server.ftp.FtpServer; import org.h2.server.ftp.FtpServer;
...@@ -52,10 +51,6 @@ public class Server implements Runnable { ...@@ -52,10 +51,6 @@ public class Server implements Runnable {
System.out.println("-pgPort <port> (default: " + PgServer.DEFAULT_PORT+")"); System.out.println("-pgPort <port> (default: " + PgServer.DEFAULT_PORT+")");
System.out.println("-pgAllowOthers [true|false]"); System.out.println("-pgAllowOthers [true|false]");
System.out.println("-odbc (start the ODBC Server)");
System.out.println("-odbcPort <port> (default: " + OdbcServer.DEFAULT_PORT+")");
System.out.println("-odbcAllowOthers [true|false]");
System.out.println("-ftp (start the FTP Server)"); System.out.println("-ftp (start the FTP Server)");
System.out.println("-ftpPort <port> (default: " + Constants.DEFAULT_FTP_PORT+")"); System.out.println("-ftpPort <port> (default: " + Constants.DEFAULT_FTP_PORT+")");
System.out.println("-ftpDir <directory> (default: " + FtpServer.DEFAULT_ROOT+", use jdbc:... to access a database)"); System.out.println("-ftpDir <directory> (default: " + FtpServer.DEFAULT_ROOT+", use jdbc:... to access a database)");
...@@ -74,7 +69,7 @@ public class Server implements Runnable { ...@@ -74,7 +69,7 @@ public class Server implements Runnable {
/** /**
* 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",...
* By default, -tcp, -web, -browser and -odbc are started. * By default, -tcp, -web, -browser and -pg are started.
* If there is a problem starting a service, the program terminates with an exit code of 1. * If there is a problem starting a service, the program terminates with an exit code of 1.
* The following options are supported: * The following options are supported:
* <ul> * <ul>
...@@ -83,7 +78,6 @@ public class Server implements Runnable { ...@@ -83,7 +78,6 @@ public class Server implements Runnable {
* </li><li>-tcp (start the TCP Server) * </li><li>-tcp (start the TCP Server)
* </li><li>-tcpShutdown {url} (shutdown the running TCP Server, URL example: tcp://localhost:9094) * </li><li>-tcpShutdown {url} (shutdown the running TCP Server, URL example: tcp://localhost:9094)
* </li><li>-pg (start the PG Server) * </li><li>-pg (start the PG Server)
* </li><li>-odbc (start the ODBC Server)
* </li><li>-browser (start a browser and open a page to connect to the Web Server) * </li><li>-browser (start a browser and open a page to connect to the Web Server)
* </li><li>-log [true|false] (enable or disable logging) * </li><li>-log [true|false] (enable or disable logging)
* </li><li>-baseDir {directory} (sets the base directory for database files; not for H2 Console) * </li><li>-baseDir {directory} (sets the base directory for database files; not for H2 Console)
...@@ -102,8 +96,6 @@ public class Server implements Runnable { ...@@ -102,8 +96,6 @@ public class Server implements Runnable {
* </li><li>-tcpShutdownForce [true|false] (don't wait for other connections to close) * </li><li>-tcpShutdownForce [true|false] (don't wait for other connections to close)
* </li><li>-pgPort {port} (the port of PG Server, default: 5435) * </li><li>-pgPort {port} (the port of PG Server, default: 5435)
* </li><li>-pgAllowOthers [true|false] (enable/disable remote connections) * </li><li>-pgAllowOthers [true|false] (enable/disable remote connections)
* </li><li>-odbcPort {port} (the port of ODBC Server, default: 9083)
* </li><li>-odbcAllowOthers [true|false] (enable/disable remote connections)
* </li><li>-ftpPort {port} * </li><li>-ftpPort {port}
* </li><li>-ftpDir {directory} * </li><li>-ftpDir {directory}
* </li><li>-ftpRead {readUserName} * </li><li>-ftpRead {readUserName}
...@@ -122,7 +114,7 @@ public class Server implements Runnable { ...@@ -122,7 +114,7 @@ public class Server implements Runnable {
} }
private int run(String[] args) throws SQLException { private int run(String[] args) throws SQLException {
boolean tcpStart = false, odbcStart = false, pgStart = false, webStart = false, ftpStart = false; boolean tcpStart = false, pgStart = false, webStart = false, ftpStart = false;
boolean browserStart = false; boolean browserStart = false;
boolean tcpShutdown = false, tcpShutdownForce = false; boolean tcpShutdown = false, tcpShutdownForce = false;
String tcpPassword = ""; String tcpPassword = "";
...@@ -136,9 +128,6 @@ public class Server implements Runnable { ...@@ -136,9 +128,6 @@ public class Server implements Runnable {
} else if(a.equals("-web")) { } else if(a.equals("-web")) {
startDefaultServers = false; startDefaultServers = false;
webStart = true; webStart = true;
} else if(a.equals("-odbc")) {
startDefaultServers = false;
odbcStart = true;
} else if(a.equals("-tcp")) { } else if(a.equals("-tcp")) {
startDefaultServers = false; startDefaultServers = false;
tcpStart = true; tcpStart = true;
...@@ -165,7 +154,6 @@ public class Server implements Runnable { ...@@ -165,7 +154,6 @@ public class Server implements Runnable {
if(startDefaultServers) { if(startDefaultServers) {
tcpStart = true; tcpStart = true;
pgStart = true; pgStart = true;
odbcStart = false;
webStart = true; webStart = true;
browserStart = true; browserStart = true;
} }
...@@ -196,17 +184,6 @@ public class Server implements Runnable { ...@@ -196,17 +184,6 @@ public class Server implements Runnable {
} }
System.out.println(pg.getStatus()); System.out.println(pg.getStatus());
} }
if(odbcStart) {
Server odbc = createOdbcServer(args);
try {
odbc.start();
} catch(SQLException e) {
// ignore (status is displayed)
e.printStackTrace();
exitCode = EXIT_ERROR;
}
System.out.println(odbc.getStatus());
}
if(webStart) { if(webStart) {
Server web = createWebServer(args); Server web = createWebServer(args);
try { try {
...@@ -341,15 +318,6 @@ public class Server implements Runnable { ...@@ -341,15 +318,6 @@ public class Server implements Runnable {
return new Server("H2 TCP Server", new TcpServer(), args); return new Server("H2 TCP Server", new TcpServer(), args);
} }
/**
* Create a new ODBC server, but does not start it yet.
* @param args
* @return the server
*/
public static Server createOdbcServer(String[] args) throws SQLException {
return new Server("H2 ODBC Server", new OdbcServer(), args);
}
/** /**
* Create a new PG server, but does not start it yet. * Create a new PG server, but does not start it yet.
* @param args * @param args
......
...@@ -17,7 +17,7 @@ public class Cache2Q implements Cache { ...@@ -17,7 +17,7 @@ public class Cache2Q implements Cache {
public static final String TYPE_NAME = "TQ"; public static final String TYPE_NAME = "TQ";
private static final int MAIN = 1, IN = 2, OUT = 3; private static final int MAIN = 1, IN = 2, OUT = 3;
private final static int PERCENT_IN = 20, PERCENT_OUT = 50; private static final int PERCENT_IN = 20, PERCENT_OUT = 50;
private final CacheWriter writer; private final CacheWriter writer;
private int maxSize; private int maxSize;
...@@ -160,7 +160,11 @@ public class Cache2Q implements Cache { ...@@ -160,7 +160,11 @@ public class Cache2Q implements Cache {
int i=0; int i=0;
ObjectArray changed = new ObjectArray(); ObjectArray changed = new ObjectArray();
while (((sizeIn*4 > maxIn*3) || (sizeOut*4 > maxOut*3) || (sizeMain*4 > maxMain*3)) && recordCount > Constants.CACHE_MIN_RECORDS) { while (((sizeIn*4 > maxIn*3) || (sizeOut*4 > maxOut*3) || (sizeMain*4 > maxMain*3)) && recordCount > Constants.CACHE_MIN_RECORDS) {
if(i++ >= recordCount) { i++;
if(i == recordCount) {
writer.flushLog();
}
if(i >= recordCount * 2) {
// can't remove any record, because the log is not written yet // can't remove any record, because the log is not written yet
// hopefully this does not happen too much, but it could happen theoretically // hopefully this does not happen too much, but it could happen theoretically
// TODO log this // TODO log this
......
...@@ -90,23 +90,17 @@ public class CacheLRU implements Cache { ...@@ -90,23 +90,17 @@ public class CacheLRU implements Cache {
int i=0; int i=0;
ObjectArray changed = new ObjectArray(); ObjectArray changed = new ObjectArray();
while (sizeMemory*4 > maxSize*3 && recordCount > Constants.CACHE_MIN_RECORDS) { while (sizeMemory*4 > maxSize*3 && recordCount > Constants.CACHE_MIN_RECORDS) {
CacheObject last = head.next;
i++; i++;
if(i == recordCount) { if(i == recordCount) {
int testing;
int todoCopyTo2Q;
System.out.println("flush log");
writer.flushLog(); writer.flushLog();
} }
if(i >= recordCount * 2) { if(i >= recordCount * 2) {
// can't remove any record, because the log is not written yet // can't remove any record, because the log is not written yet
// hopefully this does not happen too much, but it could happen theoretically // hopefully this does not happen too much, but it could happen theoretically
// TODO log this // TODO log this
System.out.println("can not shrink cache");
break; break;
} }
CacheObject last = head.next;
if(Constants.CHECK && last == head) { if(Constants.CHECK && last == head) {
throw Message.getInternalError("try to remove head"); throw Message.getInternalError("try to remove head");
} }
......
...@@ -230,7 +230,6 @@ public class DataType { ...@@ -230,7 +230,6 @@ public class DataType {
} }
Value.getOrder(i); Value.getOrder(i);
} }
// TODO data types: try to support other types as well (longvarchar for odbc/access,...) - maybe map them to regular types?
} }
private static void add(int type, int sqlType, String jdbc, DataType dataType, String[] names, int memory) { private static void add(int type, int sqlType, String jdbc, DataType dataType, String[] names, int memory) {
......
...@@ -201,7 +201,7 @@ CREATE TABLE TEST( ID BIGINT PRIMARY KEY, CREATED TIMESTAMP); ...@@ -201,7 +201,7 @@ CREATE TABLE TEST( ID BIGINT PRIMARY KEY, CREATED TIMESTAMP);
INSERT INTO TEST VALUES(1, '2007-01-01 00:00:00'); INSERT INTO TEST VALUES(1, '2007-01-01 00:00:00');
SELECT * FROM TEST; SELECT * FROM TEST;
Server: use one listener (detect if the request comes from an ODBC or TCP client). Server: use one listener (detect if the request comes from an PG or TCP client).
PMD PMD
......
db2 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa db1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
xdb2 = H2 (XTEA), org.h2.Driver, jdbc:h2:data/test_xtea;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=XTEA, sa, sa 123
xdb3 = H2 (AES), org.h2.Driver, jdbc:h2:data/test_aes;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=AES, sa, sa 123
xdb4 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;write_mode_log=rws;write_delay=0, sa, sa
db1 = H2_PG, org.postgresql.Driver, jdbc:postgresql://localhost:5435/h2test, sa, sa #xdb2 = H2 (XTEA), org.h2.Driver, jdbc:h2:data/test_xtea;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=XTEA, sa, sa 123
xdb2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa #xdb3 = H2 (AES), org.h2.Driver, jdbc:h2:data/test_aes;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=AES, sa, sa 123
#xdb4 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;write_mode_log=rws;write_delay=0, sa, sa
#xdb5 = H2_PG, org.postgresql.Driver, jdbc:postgresql://localhost:5435/h2test, sa, sa
db2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa
db3 = Derby, org.apache.derby.jdbc.EmbeddedDriver, jdbc:derby:data/test;create=true, sa, sa db3 = Derby, org.apache.derby.jdbc.EmbeddedDriver, jdbc:derby:data/test;create=true, sa, sa
db4 = H2, org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa db4 = H2, org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
...@@ -30,5 +30,4 @@ oracle.datetime = TIMESTAMP ...@@ -30,5 +30,4 @@ oracle.datetime = TIMESTAMP
#test3 = org.h2.test.bench.BenchB #test3 = org.h2.test.bench.BenchB
test1 = org.h2.test.bench.BenchC test1 = org.h2.test.bench.BenchC
#size = 400 size = 400
size = 10
\ No newline at end of file
...@@ -7,7 +7,6 @@ package org.h2.test.db; ...@@ -7,7 +7,6 @@ package org.h2.test.db;
import java.sql.Connection; import java.sql.Connection;
import java.sql.Statement; import java.sql.Statement;
import org.h2.engine.Constants;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Restore; import org.h2.tools.Restore;
......
call timestamp '2007-07-26 18:44:26.109000 +02:00'; select timestamp '2007-07-26 18:44:26.109000 +02:00';
> 1; > 2007-07-26 18:44:26.109;
create table test(id int primary key); create table test(id int primary key);
begin; begin;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论