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

--no commit message

--no commit message
上级 973875f3
......@@ -19,6 +19,7 @@ import org.h2.jdbc.JdbcConnection;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.store.DataHandler;
import org.h2.store.InDoubtTransaction;
......@@ -268,8 +269,12 @@ public class Session implements SessionInterface {
}
locks.add(table);
}
public void log(Table table, short type, Row row) throws SQLException {
log(new UndoLogRecord(table, type, row));
}
public void log(UndoLogRecord log) throws SQLException {
private void log(UndoLogRecord log) throws SQLException {
// called _after_ the row was inserted successfully into the table,
// otherwise rollback will try to rollback a not-inserted row
if(Constants.CHECK) {
......
......@@ -94,7 +94,7 @@ public class Function extends Expression implements FunctionCall {
COALESCE = 204, NULLIF = 205, 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,
LINK_SCHEMA = 218, TABLE = 219;
LINK_SCHEMA = 218, TABLE = 219, LEAST = 220, GREATEST = 221;
private static final int VAR_ARGS = -1;
......@@ -286,6 +286,8 @@ public class Function extends Expression implements FunctionCall {
addFunction("ARRAY_LENGTH", ARRAY_LENGTH, 1, Value.INT);
addFunction("LINK_SCHEMA", LINK_SCHEMA, 6, Value.RESULT_SET);
addFunctionWithNull("TABLE", TABLE, VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("LEAST", LEAST, VAR_ARGS, Value.NULL);
addFunctionWithNull("GREATEST", GREATEST, VAR_ARGS, Value.NULL);
}
private static void addFunction(String name, int type, int parameterCount,
......@@ -394,6 +396,27 @@ public class Function extends Expression implements FunctionCall {
}
return v0;
}
case GREATEST:
case LEAST: {
Value result = ValueNull.INSTANCE;
for (int i = 0; i < args.length; i++) {
Value v = i==0 ? v0 : args[i].getValue(session);
if (!(v == ValueNull.INSTANCE)) {
v = v.convertTo(dataType);
if(result == ValueNull.INSTANCE) {
result = v;
} else {
int comp = database.compareTypeSave(result, v);
if(info.type == GREATEST && comp < 0 ) {
result = v;
} else if(info.type == LEAST && comp > 0 ) {
result = v;
}
}
}
}
return result;
}
case CASE: {
// TODO function CASE: document & implement functionality
int i = 0;
......@@ -1220,6 +1243,8 @@ public class Function extends Expression implements FunctionCall {
case COALESCE:
case CSVREAD:
case TABLE:
case LEAST:
case GREATEST:
min = 1;
break;
case NOW:
......@@ -1303,7 +1328,9 @@ public class Function extends Expression implements FunctionCall {
switch (info.type) {
case IFNULL:
case NULLIF:
case COALESCE: {
case COALESCE:
case LEAST:
case GREATEST: {
dataType = Value.STRING;
scale = 0;
precision = 0;
......
......@@ -78,39 +78,6 @@ public class LinkedIndex extends Index {
}
}
public void remove(Session session, Row row) throws SQLException {
StringBuffer buff = new StringBuffer("DELETE FROM ");
buff.append(originalTable);
buff.append(" WHERE ");
for(int i=0; i<row.getColumnCount(); i++) {
if(i>0) {
buff.append("AND ");
}
buff.append(table.getColumn(i).getSQL());
Value v = row.getValue(i);
if(isNull(v)) {
buff.append(" IS NULL ");
} else {
buff.append("=? ");
}
}
String sql = buff.toString();
try {
PreparedStatement prep = link.getPreparedStatement(sql);
for(int i=0, j=0; i<row.getColumnCount(); i++) {
Value v = row.getValue(i);
if(!isNull(v)) {
v.set(prep, j+1);
j++;
}
}
int count = prep.executeUpdate();
rowCount -= count;
} catch(SQLException e) {
throw Message.getSQLException(Message.ERROR_ACCESSING_LINKED_TABLE_1, new String[]{sql}, e);
}
}
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
StringBuffer buff = new StringBuffer();
for(int i=0; first != null && i<first.getColumnCount(); i++) {
......@@ -196,6 +163,82 @@ public class LinkedIndex extends Index {
public Value findFirstOrLast(Session session, boolean first) throws SQLException {
// TODO optimization: could get the first or last value (in any case; maybe not optimized)
throw Message.getUnsupportedException();
}
}
public void remove(Session session, Row row) throws SQLException {
StringBuffer buff = new StringBuffer("DELETE FROM ");
buff.append(originalTable);
buff.append(" WHERE ");
for(int i=0; i<row.getColumnCount(); i++) {
if(i>0) {
buff.append("AND ");
}
buff.append(table.getColumn(i).getSQL());
Value v = row.getValue(i);
if(isNull(v)) {
buff.append(" IS NULL ");
} else {
buff.append("=? ");
}
}
String sql = buff.toString();
try {
PreparedStatement prep = link.getPreparedStatement(sql);
for(int i=0, j=0; i<row.getColumnCount(); i++) {
Value v = row.getValue(i);
if(!isNull(v)) {
v.set(prep, j+1);
j++;
}
}
int count = prep.executeUpdate();
rowCount -= count;
} catch(SQLException e) {
throw Message.getSQLException(Message.ERROR_ACCESSING_LINKED_TABLE_1, new String[]{sql}, e);
}
}
public void update(Session session, Row oldRow, Row newRow) throws SQLException {
StringBuffer buff = new StringBuffer("UPDATE ");
buff.append(originalTable).append(" SET ");
for (int i = 0; i < newRow.getColumnCount(); i++) {
if (i > 0) {
buff.append(", ");
}
buff.append(table.getColumn(i).getSQL()).append("=?");
}
buff.append(" WHERE ");
for(int i=0; i<oldRow.getColumnCount(); i++) {
if(i>0) {
buff.append("AND ");
}
buff.append(table.getColumn(i).getSQL());
Value v = oldRow.getValue(i);
if(isNull(v)) {
buff.append(" IS NULL ");
} else {
buff.append("=? ");
}
}
String sql = buff.toString();
try {
int j = 1;
PreparedStatement prep = link.getPreparedStatement(sql);
for (int i=0; i<newRow.getColumnCount(); i++) {
newRow.getValue(i).set(prep, j);
j++;
}
for(int i=0; i<oldRow.getColumnCount(); i++) {
Value v = oldRow.getValue(i);
if(!isNull(v)) {
v.set(prep, j);
j++;
}
}
prep.executeUpdate();
} catch(SQLException e) {
throw Message.getSQLException(Message.ERROR_ACCESSING_LINKED_TABLE_1, new String[]{sql}, e);
}
}
}
......@@ -362,9 +362,14 @@ CREATE INDEX IDXNAME ON TEST(NAME)
CREATE LINKED TABLE [IF NOT EXISTS]
name(driverString, urlString,
userString, passwordString, originalTableString)
[EMIT UPDATES]
","
Creates a table link to an external table.
The driver name may be empty if the driver is already loaded.
Usually, for update statements, the old rows are deleted first
and then the new rows inserted. It is possible to emit update
statements (however this is not possible on rollback), however
in this case multi-row unique key updates may not always work.
The current user owner must have admin rights.
","
CREATE LINKED TABLE LINK('org.h2.Driver', 'jdbc:h2:test', 'sa', '', 'TEST')
......@@ -2460,6 +2465,14 @@ Returns NULL otherwise.
DATABASE_PATH()
"
"Functions (System)","GREATEST","
GREATEST(aValue, bValue [,...]): value
","
Returns the largest value that is not NULL, or NULL if all values are NULL.
","
GREATEST(A, B, C)
"
"Functions (System)","IDENTITY","
IDENTITY(): int
","
......@@ -2476,6 +2489,14 @@ Returns the value of 'a' if it is not null, otherwise 'b'.
IFNULL(A, B)
"
"Functions (System)","LEAST","
LEAST(aValue, bValue [,...]): value
","
Returns the smallest value that is not NULL, or NULL if all values are NULL.
","
LEAST(A, B, C)
"
"Functions (System)","LOCK_MODE","
LOCK_MODE(): int
","
......
......@@ -9,7 +9,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
......@@ -211,8 +210,6 @@ public class AppServer {
user = user.trim();
password = password.trim();
org.h2.Driver.load();
JdbcUtils.getConnection(driver, url, user, password);
Class.forName(driver);
// try {
// Driver dr = (Driver) urlClassLoader.loadClass(driver).newInstance();
// Properties p = new Properties();
......@@ -222,7 +219,7 @@ public class AppServer {
// } catch(ClassNotFoundException e2) {
// throw e2;
// }
return DriverManager.getConnection(url, user, password);
return JdbcUtils.getConnection(driver, url, user, password);
}
}
......@@ -7,6 +7,7 @@ package org.h2.table;
import java.sql.SQLException;
import java.util.HashMap;
import org.h2.command.Prepared;
import org.h2.constraint.Constraint;
import org.h2.engine.Constants;
import org.h2.engine.DbObject;
......@@ -24,6 +25,7 @@ import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
import org.h2.schema.Sequence;
import org.h2.schema.TriggerObject;
import org.h2.store.UndoLogRecord;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -125,6 +127,23 @@ public abstract class Table extends SchemaObject {
public abstract ObjectArray getIndexes();
public abstract boolean isLockedExclusively();
public abstract long getMaxDataModificationId();
public void updateRows(Prepared prepared, Session session, ObjectArray oldRows, ObjectArray newRows) throws SQLException {
// remove the old rows
for (int i = 0; i < oldRows.size(); i++) {
prepared.checkCancelled();
Row o = (Row) oldRows.get(i);
removeRow(session, o);
session.log(this, UndoLogRecord.DELETE, o);
}
// add the new rows
for (int i=0; i < newRows.size(); i++) {
prepared.checkCancelled();
Row n = (Row) newRows.get(i);
addRow(session, n);
session.log(this, UndoLogRecord.INSERT, n);
}
}
public void removeChildrenAndResources(Session session) throws SQLException {
while(views != null && views.size() > 0) {
......
......@@ -13,6 +13,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import org.h2.command.Prepared;
import org.h2.engine.Session;
import org.h2.index.Index;
import org.h2.index.IndexType;
......@@ -20,6 +21,7 @@ import org.h2.index.LinkedIndex;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.store.UndoLogRecord;
import org.h2.util.JdbcUtils;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
......@@ -35,15 +37,18 @@ public class TableLink extends Table {
private Connection conn;
private HashMap prepared = new HashMap();
private ObjectArray indexes = new ObjectArray();
private boolean emitUpdates;
private LinkedIndex linkedIndex;
public TableLink(Schema schema, int id, String name, String driver, String url,
String user, String password, String originalTable) throws SQLException {
String user, String password, String originalTable, boolean emitUpdates) throws SQLException {
super(schema, id, name, false);
this.driver = driver;
this.url = url;
this.user = user;
this.password = password;
this.originalTable = originalTable;
this.emitUpdates = emitUpdates;
conn = JdbcUtils.getConnection(driver, url, user, password);
DatabaseMetaData meta = conn.getMetaData();
boolean storesLowerCase = meta.storesLowerCaseIdentifiers();
......@@ -94,8 +99,8 @@ public class TableLink extends Table {
Column[] cols = new Column[columnList.size()];
columnList.toArray(cols);
setColumns(cols);
Index index = new LinkedIndex(this, id, cols, IndexType.createNonUnique(false));
indexes.add(index);
linkedIndex = new LinkedIndex(this, id, cols, IndexType.createNonUnique(false));
indexes.add(linkedIndex);
rs = meta.getPrimaryKeys(null, null, originalTable);
String pkName = "";
ObjectArray list;
......@@ -179,6 +184,9 @@ public class TableLink extends Table {
buff.append(", ");
buff.append(StringUtils.quoteStringSQL(originalTable));
buff.append(")");
if(emitUpdates) {
buff.append(" EMIT UPDATES");
}
return buff.toString();
}
......@@ -195,7 +203,7 @@ public class TableLink extends Table {
}
public Index getScanIndex(Session session) {
return (Index) indexes.get(0);
return linkedIndex;
}
public void removeRow(Session session, Row row) throws SQLException {
......@@ -294,4 +302,24 @@ public class TableLink extends Table {
return null;
}
public void updateRows(Prepared prepared, Session session, ObjectArray oldRows, ObjectArray newRows) throws SQLException {
boolean deleteInsert;
if(emitUpdates) {
for (int i = 0; i < oldRows.size(); i++) {
session.checkCancelled();
Row oldRow = (Row) oldRows.get(i);
Row newRow = (Row) newRows.get(i);
linkedIndex.update(session, oldRow, newRow);
session.log(this, UndoLogRecord.DELETE, oldRow);
session.log(this, UndoLogRecord.INSERT, newRow);
}
deleteInsert = false;
} else {
deleteInsert = true;
}
if(deleteInsert) {
super.updateRows(prepared, session, oldRows, newRows);
}
}
}
......@@ -13,9 +13,49 @@ public class TestLinkedTable extends TestBase {
public void test() throws Exception {
testLinkSchema();
testLinkEmitUpdates();
testLinkTable();
}
private void testLinkEmitUpdates() throws Exception {
deleteDb("linked1");
deleteDb("linked2");
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:"+BASE_DIR+"/linked1", "sa1", "abc");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
Connection conn2 = DriverManager.getConnection("jdbc:h2:"+BASE_DIR+"/linked2", "sa2", "def");
Statement stat2 = conn2.createStatement();
String link = "CREATE LINKED TABLE TEST_LINK_U('', 'jdbc:h2:"+BASE_DIR+"/linked1', 'sa1', 'abc', 'TEST') EMIT UPDATES";
stat2.execute(link);
link = "CREATE LINKED TABLE TEST_LINK_DI('', 'jdbc:h2:"+BASE_DIR+"/linked1', 'sa1', 'abc', 'TEST')";
stat2.execute(link);
stat2.executeUpdate("INSERT INTO TEST_LINK_U VALUES(1, 'Hello')");
stat2.executeUpdate("INSERT INTO TEST_LINK_DI VALUES(2, 'World')");
try {
stat2.executeUpdate("UPDATE TEST_LINK_U SET ID=ID+1");
error("unexpected success");
} catch(SQLException e) {
checkNotGeneralException(e);
}
stat2.executeUpdate("UPDATE TEST_LINK_DI SET ID=ID+1");
stat2.executeUpdate("UPDATE TEST_LINK_U SET NAME=NAME || ID");
ResultSet rs;
rs = stat.executeQuery("SELECT * FROM TEST ORDER BY ID");
rs.next();
check(rs.getInt(1), 2);
check(rs.getString(2), "Hello2");
rs.next();
check(rs.getInt(1), 3);
check(rs.getString(2), "World3");
checkFalse(rs.next());
conn.close();
conn2.close();
}
private void testLinkSchema() throws Exception {
deleteDb("linked1");
deleteDb("linked2");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论