提交 5a86eaff authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 faebdbbf
/*
* 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.command;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.result.LocalResult;
import org.h2.result.ResultInterface;
import org.h2.util.ObjectArray;
/**
* @author Thomas
*/
public abstract class Command implements CommandInterface {
protected Session session;
protected long startTime;
protected Trace trace;
private volatile boolean cancel;
public abstract boolean isTransactional();
public abstract boolean isQuery();
public abstract ObjectArray getParameters();
public Command(Parser parser) {
this.session = parser.getSession();
trace = session.getDatabase().getTrace(Trace.COMMAND);
}
public int update() throws SQLException {
throw Message.getSQLException(Message.METHOD_NOT_ALLOWED_FOR_QUERY);
}
public LocalResult query(int maxrows) throws SQLException {
throw Message.getSQLException(Message.METHOD_ONLY_ALLOWED_FOR_QUERY);
}
// TODO insert parameters into the original query, or allow this syntax
// if(parameters != null && parameters.size() > 0) {
// buff.append(" /* ");
// for(int i=0; i<parameters.size(); i++) {
// if(i>0) {
// buff.append(", ");
// }
// Parameter param = (Parameter) parameters.get(i);
// buff.append(i+1);
// buff.append(" = ");
// buff.append(param.getSQL());
// }
// buff.append(" */");
// }
public ResultInterface executeQuery(int maxrows, boolean scrollable) throws SQLException {
return executeQueryLocal(maxrows);
}
public LocalResult executeQueryLocal(int maxrows) throws SQLException {
startTime = System.currentTimeMillis();
Database database = session.getDatabase();
Object sync = Constants.MULTI_THREADED_KERNEL ? (Object)session : (Object)database;
synchronized (sync) {
try {
database.checkPowerOff();
session.setCurrentCommand(this);
LocalResult result = query(maxrows);
return result;
} catch(Throwable e) {
SQLException s = Message.convert(e);
database.exceptionThrown(s);
throw s;
} finally {
stop();
}
}
}
protected void start() {
startTime = System.currentTimeMillis();
}
public void checkCancelled() throws SQLException {
if (cancel) {
throw Message.getSQLException(Message.STATEMENT_WAS_CANCELLED);
}
session.throttle();
}
private void stop() throws SQLException {
session.setCurrentCommand(null);
if (!isTransactional()) {
// meta data changes need to commit in any case
session.commit();
} else if (session.getAutoCommit()) {
session.commit();
}
if (trace.info()) {
long time = System.currentTimeMillis() - startTime;
if (time > Constants.LONG_QUERY_LIMIT_MS) {
trace.info("long query: " + time);
}
}
}
public int executeUpdate() throws SQLException {
startTime = System.currentTimeMillis();
Database database = session.getDatabase();
Object sync = Constants.MULTI_THREADED_KERNEL ? (Object)session : (Object)database;
synchronized (sync) {
int rollback = session.getLogId();
session.setCurrentCommand(this);
try {
database.checkPowerOff();
int result = update();
return result;
} catch (SQLException e) {
database.exceptionThrown(e);
database.checkPowerOff();
session.rollbackTo(rollback);
throw e;
} finally {
stop();
}
}
}
public void close() {
// nothing to do
}
public void cancel() {
this.cancel = true;
}
}
/*
* 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.command;
import java.sql.SQLException;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.result.LocalResult;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
public class CommandContainer extends Command {
private Prepared prepared;
CommandContainer(Parser parser, Prepared prepared) {
super(parser);
prepared.setCommand(this);
this.prepared = prepared;
}
public ObjectArray getParameters() {
return prepared.getParameters();
}
public boolean isTransactional() {
return prepared.isTransactional();
}
public boolean isQuery() {
return prepared.isQuery();
}
private void recompileIfRequired() throws SQLException {
if(prepared == null || prepared.needRecompile()) {
// TODO test with 'always recompile'
prepared.setModificationId(0);
String sql = prepared.getSQL();
ObjectArray oldValues = prepared.getParameters();
Parser parser = new Parser(session);
prepared = parser.parseOnly(sql);
long mod = prepared.getModificationId();
prepared.setModificationId(0);
ObjectArray newParams = prepared.getParameters();
for(int i=0; i<newParams.size(); i++) {
Value v = ((Expression)oldValues.get(i)).getValue(session);
Parameter p = (Parameter) newParams.get(i);
p.setValue(v);
}
prepared.prepare();
prepared.setModificationId(mod);
}
}
public int update() throws SQLException {
recompileIfRequired();
// TODO query time: should keep lock time separate from running time
start();
prepared.checkParameters();
prepared.trace();
return prepared.update();
}
public LocalResult query(int maxrows) throws SQLException {
recompileIfRequired();
// TODO query time: should keep lock time separate from running time
start();
prepared.checkParameters();
prepared.trace();
return prepared.query(maxrows);
}
}
/*
* 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.command;
import java.sql.SQLException;
import org.h2.result.ResultInterface;
import org.h2.util.ObjectArray;
public interface CommandInterface {
boolean isQuery();
ObjectArray getParameters();
ResultInterface executeQuery(int maxRows, boolean scrollable) throws SQLException;
int executeUpdate() throws SQLException;
void close();
void cancel();
}
/*
* 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.command;
import java.sql.SQLException;
import org.h2.result.LocalResult;
import org.h2.util.ObjectArray;
public class CommandList extends Command {
private Command command;
private String remaining;
// TODO lock if possible!
public CommandList(Parser parser, Command c, String remaining) {
super(parser);
this.command = c;
this.remaining = remaining;
}
public ObjectArray getParameters() {
return command.getParameters();
}
private void executeRemaining() throws SQLException {
Command command = session.prepareLocal(remaining);
if(command.isQuery()) {
command.query(0);
} else {
command.update();
}
}
public int update() throws SQLException {
int updateCount = command.executeUpdate();
executeRemaining();
return updateCount;
}
public LocalResult query(int maxrows) throws SQLException {
LocalResult result = command.query(maxrows);
executeRemaining();
return result;
}
public boolean isQuery() {
return command.isQuery();
}
public boolean isTransactional() {
return true;
}
}
/*
* 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.command;
import java.io.IOException;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.engine.SessionRemote;
import org.h2.expression.ParameterInterface;
import org.h2.expression.ParameterRemote;
import org.h2.message.Trace;
import org.h2.result.ResultInterface;
import org.h2.result.ResultRemote;
import org.h2.util.ObjectArray;
import org.h2.value.Transfer;
public class CommandRemote implements CommandInterface {
private SessionRemote session;
private ObjectArray transferList;
private int id;
private boolean isQuery;
private ObjectArray parameters;
private Trace trace;
private String sql;
private int paramCount;
private void prepare(SessionRemote session) throws SQLException {
id = session.getNextId();
paramCount = 0;
for(int i=0; i<transferList.size(); i++) {
try {
Transfer transfer = (Transfer) transferList.get(i);
session.traceOperation("SESSION_PREPARE", id);
transfer.writeInt(SessionRemote.SESSION_PREPARE).writeInt(id).writeString(sql);
session.done(transfer);
isQuery = transfer.readBoolean();
paramCount = transfer.readInt();
} catch(IOException e) {
session.removeServer(i);
}
}
}
public CommandRemote(SessionRemote session, ObjectArray transferList, String sql) throws SQLException {
this.transferList = transferList;
trace = session.getTrace();
this.sql = sql;
parameters = new ObjectArray();
prepare(session);
for(int i=0; i<paramCount; i++) {
parameters.add(new ParameterRemote(i));
}
// set session late because prepare might fail - in this case we don't need to close the object
this.session = session;
}
public boolean isQuery() {
return isQuery;
}
public ObjectArray getParameters() {
return parameters;
}
public ResultInterface executeQuery(int maxRows, boolean scrollable) throws SQLException {
checkParameters();
synchronized(session) {
session.checkClosed();
if(id <= session.getCurrentId() - Constants.SERVER_CACHED_OBJECTS) {
// object is too old - we need to prepare again
prepare(session);
}
int objectId = session.getNextId();
ResultRemote result = null;
// TODO cluster: test sequences and so on
for(int i=0; i<transferList.size(); i++) {
Transfer transfer = (Transfer) transferList.get(i);
try {
// TODO cluster: support load balance with values for each server / auto detect
session.traceOperation("COMMAND_EXECUTE_QUERY", id);
transfer.writeInt(SessionRemote.COMMAND_EXECUTE_QUERY).writeInt(id).writeInt(objectId).writeInt(maxRows);
int readRows;
if(session.isClustered() || scrollable) {
readRows = Integer.MAX_VALUE;
} else {
readRows = Constants.SERVER_SMALL_RESULTSET_SIZE;
}
transfer.writeInt(readRows);
sendParameters(transfer);
session.done(transfer);
int columnCount = transfer.readInt();
if(result != null) {
result.close();
result = null;
}
result = new ResultRemote(session, transfer, objectId, columnCount, readRows);
} catch(IOException e) {
session.removeServer(i);
}
}
session.autoCommitIfCluster();
return result;
}
}
public int executeUpdate() throws SQLException {
checkParameters();
synchronized(session) {
session.checkClosed();
if(id <= session.getCurrentId() - Constants.SERVER_CACHED_OBJECTS) {
// object is too old - we need to prepare again
prepare(session);
}
int updateCount = 0;
boolean autoCommit = false;
for(int i=0; i<transferList.size(); i++) {
try {
Transfer transfer = (Transfer) transferList.get(i);
session.traceOperation("COMMAND_EXECUTE_UPDATE", id);
transfer.writeInt(SessionRemote.COMMAND_EXECUTE_UPDATE).writeInt(id);
sendParameters(transfer);
session.done(transfer);
updateCount = transfer.readInt();
autoCommit = transfer.readBoolean();
} catch(IOException e) {
session.removeServer(i);
}
}
session.setAutoCommit(autoCommit);
session.autoCommitIfCluster();
return updateCount;
}
}
private void checkParameters() throws SQLException {
int len = parameters.size();
for(int i=0; i<len; i++) {
ParameterInterface p = (ParameterInterface)parameters.get(i);
p.checkSet();
}
}
private void sendParameters(Transfer transfer) throws IOException, SQLException {
int len = parameters.size();
transfer.writeInt(len);
for(int i=0; i<len; i++) {
ParameterInterface p = (ParameterInterface)parameters.get(i);
transfer.writeValue(p.getParamValue());
}
}
public SessionInterface getSession() {
return session;
}
public void close() {
if(session == null || session.isClosed()) {
return;
}
synchronized(session) {
for(int i=0; i<transferList.size(); i++) {
try {
Transfer transfer = (Transfer) transferList.get(i);
session.traceOperation("COMMAND_CLOSE", id);
transfer.writeInt(SessionRemote.COMMAND_CLOSE).writeInt(id);
} catch (IOException e) {
// TODO cluster: do we need to to handle ioexception on close?
trace.error("close", e);
}
}
session = null;
}
}
// public void finalize() {
// if(!Database.RUN_FINALIZERS) {
// return;
// }
// close();
// }
public void cancel() {
// TODO server: support cancel
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* 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.command;
import org.h2.engine.Session;
public class ParserInt extends Parser {
public ParserInt(Session session) {
super(session);
}
}
/*
* 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.command;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.util.ObjectArray;
public abstract class Prepared {
protected String sql;
protected int headPos = -1;
protected Session session;
protected ObjectArray parameters;
private long modificationId;
private Command command;
private int objectId;
private boolean prepareAlways;
private int currentRowNumber;
public boolean needRecompile() throws SQLException {
Database db = session.getDatabase();
if(db == null) {
throw Message.getSQLException(Message.CONNECTION_BROKEN);
}
// TODO parser: currently, compiling every create/drop/... twice! because needRecompile return true even for the first execution
return Constants.RECOMPILE_ALWAYS || prepareAlways || modificationId < db.getModificationMetaId();
}
public abstract boolean isTransactional();
public Prepared(Session session) {
this.session = session;
modificationId = session.getDatabase().getModificationMetaId();
}
long getModificationId() {
return modificationId;
}
void setModificationId(long id) {
this.modificationId = id;
}
public void setParameterList(ObjectArray parameters) {
this.parameters = parameters;
}
public ObjectArray getParameters() {
return parameters;
}
protected void checkParameters() throws SQLException {
for (int i = 0; parameters != null && i < parameters.size(); i++) {
Parameter param = (Parameter) parameters.get(i);
param.checkSet();
}
}
public void setCommand(Command command) {
this.command = command;
}
public boolean isQuery() {
return false;
}
public void prepare() throws SQLException {
// nothing to do
}
public int update() throws SQLException {
throw Message.getSQLException(Message.METHOD_NOT_ALLOWED_FOR_QUERY);
}
public LocalResult query(int maxrows) throws SQLException {
throw Message.getSQLException(Message.METHOD_ONLY_ALLOWED_FOR_QUERY);
}
public void setSQL(String sql) {
this.sql = sql;
}
public String getSQL() {
return sql;
}
protected int getObjectId(boolean needFresh, boolean dataFile) {
Database db = session.getDatabase();
int id = objectId;
if(id == 0) {
id = db.allocateObjectId(needFresh, dataFile);
}
objectId = 0;
return id;
}
public String getPlan() {
return null;
}
protected void checkCancelled() throws SQLException {
// TODO strange code: probably checkCancelled should always be called on the session. fix & test after release 1.0
if(command != null) {
command.checkCancelled();
} else {
session.checkCancelled();
}
}
public void setObjectId(int i) {
this.objectId = i;
}
public void setHeadPos(int headPos) {
this.headPos = headPos;
}
public void setSession(Session currentSession) {
this.session = currentSession;
}
void trace() throws SQLException {
if(session.getTrace().info()) {
StringBuffer buff = new StringBuffer();
buff.append(sql);
if(parameters.size()>0) {
buff.append(" {");
for(int i=0; i<parameters.size(); i++) {
if(i>0) {
buff.append(", ");
}
buff.append(i+1);
buff.append(": ");
Expression e = (Expression) parameters.get(i);
buff.append(e.getValue(session).getSQL());
}
buff.append("};");
} else {
buff.append(';');
}
session.getTrace().infoSQL(buff.toString());
}
}
public void setPrepareAlways(boolean prepareAlways) {
this.prepareAlways = prepareAlways;
}
protected void setCurrentRowNumber(int rowNumber) {
this.currentRowNumber = rowNumber;
}
public int getCurrentRowNumber() {
return currentRowNumber;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.schema.Schema;
public class AlterIndexRename extends SchemaCommand {
private Index oldIndex;
private String newIndexName;
public AlterIndexRename(Session session, Schema schema) {
super(session, schema);
}
public void setOldIndex(Index index) {
oldIndex = index;
}
public void setNewName(String name) {
newIndexName = name;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
if(getSchema().findIndex(newIndexName) != null || newIndexName.equals(oldIndex.getName())) {
throw Message.getSQLException(Message.INDEX_ALREADY_EXISTS_1, newIndexName);
}
session.getUser().checkRight(oldIndex.getTable(), Right.ALL);
db.renameSchemaObject(session, oldIndex, newIndexName);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Sequence;
public class AlterSequence extends DefineCommand {
private Sequence sequence;
private boolean newStart;
private long start;
private boolean newIncrement;
private long increment;
public AlterSequence(Session session) {
super(session);
}
public void setSequence(Sequence sequence) {
this.sequence = sequence;
}
public void setStartWith(long start) {
newStart = true;
this.start = start;
}
public void setIncrement(long increment) throws SQLException {
newIncrement = true;
if(increment == 0) {
throw Message.getSQLException(Message.INVALID_VALUE_2, new String[]{"0", "INCREMENT"}, null);
}
this.increment = increment;
}
public int update() throws SQLException {
// TODO rights: what are the rights required for a sequence?
session.commit();
Database db = session.getDatabase();
if(newStart) {
sequence.setStartValue(start);
}
if(newIncrement) {
sequence.setIncrement(increment);
}
db.update(session, sequence);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintCheck;
import org.h2.constraint.ConstraintReferential;
import org.h2.constraint.ConstraintUnique;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
/**
* @author Thomas
*/
public class AlterTableAddConstraint extends SchemaCommand {
public static final int CHECK = 0, UNIQUE = 1, REFERENTIAL = 2, PRIMARY_KEY = 3;
public static final int REFERENTIAL_INTEGRITY_TRUE = 4;
public static final int REFERENTIAL_INTEGRITY_FALSE = 5;
private int type;
private String constraintName;
private String tableName;
private String[] columnNames;
private int deleteAction;
private int updateAction;
private Schema refSchema;
private String refTableName;
private String[] refColumnNames;
private Expression checkExpression;
private Index index, refIndex;
private String comment;
public AlterTableAddConstraint(Session session, Schema schema) {
super(session, schema);
}
private String generateConstraintName(int id) throws SQLException {
if(constraintName == null) {
constraintName = getSchema().getUniqueConstraintName();
}
return constraintName;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
if(getSchema().findConstraint(constraintName)!=null) {
throw Message.getSQLException(Message.CONSTRAINT_ALREADY_EXISTS_1,
constraintName);
}
Constraint constraint;
Table table = getSchema().getTableOrView(session, tableName);
session.getUser().checkRight(table, Right.ALL);
table.lock(session, true);
switch(type) {
case CHECK: {
int id = getObjectId(true, true);
String name = generateConstraintName(id);
ConstraintCheck check = new ConstraintCheck(getSchema(), id, name, table);
TableFilter filter = new TableFilter(session, table, null, false);
checkExpression.mapColumns(filter, 0);
checkExpression = checkExpression.optimize(session);
check.setExpression(checkExpression);
check.setTableFilter(filter);
constraint = check;
break;
}
case UNIQUE: {
Column[] columns = table.getColumns(columnNames);
boolean isOwner = false;
if(index != null && canUseUniqueIndex(index, table, columns)) {
isOwner = true;
index.getIndexType().setBelongsToConstraint(true);
} else {
index = getUniqueIndex(table, columns);
if(index == null) {
index = createIndex(table, columns, true);
isOwner = true;
}
}
int id = getObjectId(true, true);
String name = generateConstraintName(id);
ConstraintUnique unique = new ConstraintUnique(getSchema(), id, name, table);
unique.setColumns(columns);
unique.setIndex(index, isOwner);
constraint = unique;
break;
}
case REFERENTIAL: {
Table refTable = refSchema.getTableOrView(session, refTableName);
session.getUser().checkRight(refTable, Right.ALL);
boolean isOwner = false;
Column[] columns = table.getColumns(columnNames);
if(index != null && canUseIndex(index, table, columns)) {
isOwner = true;
index.getIndexType().setBelongsToConstraint(true);
} else {
index = getIndex(table, columns);
if(index == null) {
index = createIndex(table, columns, false);
isOwner = true;
}
}
Column[] refColumns;
if(refColumnNames == null) {
Index refIdx = refTable.getPrimaryKey();
refColumns = refIdx.getColumns();
} else {
refColumns = refTable.getColumns(refColumnNames);
}
if(refColumns.length != columns.length) {
throw Message.getSQLException(Message.COLUMN_COUNT_DOES_NOT_MATCH);
}
boolean isRefOwner = false;
if(refIndex != null && refIndex.getTable() == refTable) {
isRefOwner = true;
refIndex.getIndexType().setBelongsToConstraint(true);
} else {
refIndex = null;
}
if(refIndex == null) {
refIndex = getUniqueIndex(refTable, refColumns);
if(refIndex == null) {
refIndex = createIndex(refTable, refColumns, true);
isRefOwner = true;
}
}
int id = getObjectId(true, true);
String name = generateConstraintName(id);
ConstraintReferential ref = new ConstraintReferential(getSchema(), id, name, table);
ref.setColumns(columns);
ref.setIndex(index, isOwner);
ref.setRefTable(refTable);
ref.setRefColumns(refColumns);
ref.setRefIndex(refIndex, isRefOwner);
constraint = ref;
refTable.addConstraint(constraint);
ref.setDeleteAction(session, deleteAction);
ref.setUpdateAction(session, updateAction);
break;
}
case REFERENTIAL_INTEGRITY_TRUE:
table.setCheckForeignKeyConstraints(true);
return 0;
case REFERENTIAL_INTEGRITY_FALSE:
table.setCheckForeignKeyConstraints(false);
return 0;
default:
throw Message.getInternalError("type="+type);
}
// parent relationship is already set with addConstraint
constraint.setComment(comment);
db.addSchemaObject(session, constraint);
table.addConstraint(constraint);
return 0;
}
private Index createIndex(Table t, Column[] cols, boolean unique) throws SQLException {
int indexId = getObjectId(true, false);
IndexType indexType;
if(unique) {
// TODO default index (hash or not; memory or not or same as table) for unique constraints
indexType = IndexType.createUnique(t.isPersistent(), false);
} else {
// TODO default index (memory or not or same as table) for unique constraints
indexType = IndexType.createNonUnique(t.isPersistent());
}
indexType.setBelongsToConstraint(true);
String prefix = constraintName == null ? "CONSTRAINT" : constraintName;
String indexName = getSchema().getUniqueIndexName(prefix + "_INDEX_");
return t.addIndex(session, indexName, indexId, cols, indexType, Index.EMPTY_HEAD, null);
}
public void setDeleteAction(int action) {
this.deleteAction = action;
}
public void setUpdateAction(int action) {
this.updateAction = action;
}
private Index getUniqueIndex(Table t, Column[] cols) {
ObjectArray list = t.getIndexes();
for(int i=0; i<list.size(); i++) {
Index index = (Index) list.get(i);
if(canUseUniqueIndex(index, t, cols)) {
return index;
}
}
return null;
}
private boolean canUseUniqueIndex(Index index, Table table, Column[] cols) {
if(index.getTable() != table || !index.getIndexType().isUnique()) {
return false;
}
if(index.getIndexType().belongsToConstraint()) {
// the constraint might be dropped (also in an alter table statement)
return false;
}
Column[] indexCols = index.getColumns();
if(indexCols.length > cols.length) {
return false;
}
HashSet set = new HashSet(Arrays.asList(cols));
for(int j=0; j<indexCols.length; j++) {
// all columns of the index must be part of the list,
// but not all columns of the list need to be part of the index
if(!set.contains(indexCols[j])) {
return false;
}
}
return true;
}
private Index getIndex(Table t, Column[] cols) {
ObjectArray list = t.getIndexes();
for(int i=0; i<list.size(); i++) {
Index index = (Index) list.get(i);
if(canUseIndex(index, t, cols)) {
return index;
}
}
return null;
}
private boolean canUseIndex(Index index, Table table, Column[] cols) {
if(index.getTable() != table || index.getCreateSQL() == null) {
// can't use the scan index or index of another table
return false;
}
Column[] indexCols = index.getColumns();
if(indexCols.length < cols.length) {
return false;
}
for(int j=0; j<cols.length; j++) {
// all columns of the list must be part of the index,
// but not all columns of the index need to be part of the list
// holes are not allowed (index=a,b,c & list=a,b is ok; but list=a,c is not)
int idx = index.getColumnIndex(cols[j]);
if(idx < 0 || idx >= cols.length) {
return false;
}
}
return true;
}
public void setConstraintName(String constraintName) {
this.constraintName = constraintName;
}
public void setType(int type) {
this.type = type;
}
public void setCheckExpression(Expression expression) {
this.checkExpression = expression;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void setColumnNames(String[] columnNames) {
this.columnNames = columnNames;
}
public void setRefTableName(Schema refSchema, String ref) {
this.refSchema = refSchema;
this.refTableName = ref;
}
public void setRefColumnNames(String[] cols) {
this.refColumnNames = cols;
}
public void setIndex(Index index) {
this.index = index;
}
public void setRefIndex(Index refIndex) {
this.refIndex = refIndex;
}
public void setComment(String comment) {
this.comment = comment;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.command.Parser;
import org.h2.command.Prepared;
import org.h2.constraint.ConstraintReferential;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
import org.h2.schema.Sequence;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.util.ObjectArray;
public class AlterTableAlterColumn extends SchemaCommand {
public static final int NOT_NULL = 0, NULL = 1, DEFAULT = 2, RESTART = 3, CHANGE_TYPE = 4;
public static final int ADD = 5, DROP = 6, SELECTIVITY = 7;
private Table table;
private Column oldColumn;
private Column newColumn;
private int type;
private Expression defaultExpression;
private long newStart;
private String addBefore;
public AlterTableAlterColumn(Session session, Schema schema) {
super(session, schema);
}
public void setTable(Table table) {
this.table = table;
}
public void setOldColumn(Column oldColumn) {
this.oldColumn = oldColumn;
}
public void setAddBefore(String before) {
this.addBefore = before;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
session.getUser().checkRight(table, Right.ALL);
table.checkSupportAlter();
table.lock(session, true);
Sequence sequence = oldColumn == null ? null : oldColumn.getSequence();
switch(type) {
case NOT_NULL: {
if(!oldColumn.getNullable()) {
// no change
break;
}
checkNoNullValues();
oldColumn.setNullable(false);
db.update(session, table);
break;
}
case NULL: {
if(oldColumn.getNullable()) {
// no change
break;
}
checkNullable();
oldColumn.setNullable(true);
db.update(session, table);
break;
}
case DEFAULT: {
oldColumn.setSequence(null);
oldColumn.setDefaultExpression(session, defaultExpression);
removeSequence(session, sequence);
db.update(session, table);
break;
}
case RESTART: {
if(sequence == null) {
throw Message.getSQLException(Message.SEQUENCE_NOT_FOUND_1, oldColumn.getSQL());
}
sequence.setStartValue(newStart);
db.update(session, sequence);
break;
}
case CHANGE_TYPE: {
// TODO document data type change problems when used with autoincrement columns.
// sequence will be unlinked
checkNoViews();
oldColumn.setSequence(null);
oldColumn.setDefaultExpression(session, null);
oldColumn.setConvertNullToDefault(false);
if(oldColumn.getNullable() && !newColumn.getNullable()) {
checkNoNullValues();
} else if(!oldColumn.getNullable() && newColumn.getNullable()) {
checkNullable();
}
convertToIdentityIfRequired(newColumn);
copyData();
break;
}
case ADD: {
checkNoViews();
convertToIdentityIfRequired(newColumn);
copyData();
break;
}
case DROP: {
checkNoViews();
if(table.getColumns().length == 1) {
// TODO test each sql exception
throw Message.getSQLException(Message.CANT_DROP_LAST_COLUMN, oldColumn.getSQL());
}
table.checkColumnIsNotReferenced(oldColumn);
dropSingleColumnIndexes();
copyData();
break;
}
case SELECTIVITY: {
oldColumn.setSelectivity((int)newStart);
db.update(session, table);
break;
}
default:
throw Message.getInternalError("type="+type);
}
return 0;
}
private void convertToIdentityIfRequired(Column c) throws SQLException {
if(c.getAutoIncrement()) {
c.setOriginalSQL("IDENTITY");
}
}
private void removeSequence(Session session, Sequence sequence) throws SQLException {
if(sequence != null) {
table.removeSequence(session, sequence);
sequence.setBelongsToTable(false);
Database db = session.getDatabase();
db.removeSchemaObject(session, sequence);
}
}
private void checkNoViews() throws SQLException {
ObjectArray children = table.getChildren();
for (int i=0; i<children.size(); i++) {
DbObject child = (DbObject) children.get(i);
if(child.getType() == DbObject.TABLE_OR_VIEW) {
throw Message.getSQLException(Message.OPERATION_NOT_SUPPORTED_WITH_VIEWS_2, new String[]{table.getName(), child.getName()}, null);
}
}
}
private void copyData() throws SQLException {
Database db = session.getDatabase();
String tempName = db.getTempTableName(session.getId());
Column[] columns = table.getColumns();
ObjectArray newColumns = new ObjectArray();
for(int i=0; i<columns.length; i++) {
Column col = columns[i].getClone();
newColumns.add(col);
}
if(type == DROP) {
int position = oldColumn.getColumnId();
newColumns.remove(position);
} else if(type == ADD) {
int position;
if (addBefore == null) {
position = columns.length;
} else {
position = table.getColumn(addBefore).getColumnId();
}
newColumns.add(position, newColumn);
} else if(type == CHANGE_TYPE) {
int position = oldColumn.getColumnId();
newColumns.remove(position);
newColumns.add(position, newColumn);
}
boolean persistent = table.isPersistent();
// create a table object in order to get the SQL statement
// can't just use this table, because most column objects are 'shared' with the old table
// still need a new id because using 0 would mean: the new table tries to use the rows of the table 0 (the script table)
int id = -1;
TableData newTable = new TableData(getSchema(), tempName, id, newColumns, persistent);
newTable.setComment(table.getComment());
execute(newTable.getCreateSQL());
newTable = (TableData) newTable.getSchema().getTableOrView(session, newTable.getName());
ObjectArray children = table.getChildren();
for (int i=0; i<children.size(); i++) {
DbObject child = (DbObject) children.get(i);
if(child instanceof Sequence) {
continue;
}
String createSQL = child.getCreateSQL();
if (createSQL == null) {
continue;
}
if(child.getType() == DbObject.TABLE_OR_VIEW) {
throw Message.getInternalError();
}
String quotedName = Parser.quoteIdentifier(tempName + "_" + child.getName());
String sql = null;
if(child instanceof ConstraintReferential) {
ConstraintReferential r = (ConstraintReferential) child;
if(r.getTable() != table) {
sql = r.getCreateSQLForCopy(r.getTable(), newTable, quotedName, false);
}
}
if(sql == null) {
sql = child.getCreateSQLForCopy(newTable, quotedName);
}
if(sql != null) {
execute(sql);
}
}
StringBuffer columnList = new StringBuffer();
for(int i=0; i<newColumns.size(); i++) {
Column nc = (Column) newColumns.get(i);
if(type == ADD && nc == newColumn) {
continue;
}
if(columnList.length() > 0) {
columnList.append(", ");
}
columnList.append(nc.getSQL());
}
// TODO loop instead of use insert (saves memory)
/*
Index scan = table.getBestPlanItem(null).getIndex();
Cursor cursor = scan.find(null, null);
while (cursor.next()) {
Row row = cursor.get();
Row newRow = newTable.getTemplateRow();
for (int i=0, j=0; i<columns.length; i++) {
if(i == position) {
continue;
}
newRow.setValue(j++, row.getValue(i));
}
newTable.validateAndConvert(newRow);
newTable.addRow(newRow);
}
*/
String sql = "INSERT INTO " + newTable.getSQL() + "(" + columnList+") "
+ "SELECT " + columnList + " FROM " + table.getSQL();
newTable.setCheckForeignKeyConstraints(false);
try {
execute(sql);
} catch(SQLException e) {
unlinkSequences(newTable);
execute("DROP TABLE " + newTable.getSQL());
throw e;
}
newTable.setCheckForeignKeyConstraints(true);
String tableName = table.getName();
table.setModified();
for(int i=0; i<columns.length; i++) {
// if we don't do that, the sequence is dropped when the table is dropped
Sequence seq = columns[i].getSequence();
if(seq != null) {
table.removeSequence(session, seq);
columns[i].setSequence(null);
}
}
execute("DROP TABLE " + table.getSQL());
db.renameSchemaObject(session, newTable, tableName);
children = newTable.getChildren();
for (int i=0; i<children.size(); i++) {
DbObject child = (DbObject) children.get(i);
if(child instanceof Sequence) {
continue;
}
String name = child.getName();
if (name == null || child.getCreateSQL() == null) {
continue;
}
if(name.startsWith(tempName + "_")) {
name = name.substring(tempName.length() + 1);
db.renameSchemaObject(session, (SchemaObject)child, name);
}
}
}
private void unlinkSequences(Table table) throws SQLException {
Column[] columns = table.getColumns();
for(int i=0; i<columns.length; i++) {
// if we don't do that, the sequence is dropped when the table is dropped
Sequence seq = columns[i].getSequence();
if(seq != null) {
table.removeSequence(session, seq);
columns[i].setSequence(null);
}
}
}
private void execute(String sql) throws SQLException {
Prepared command = session.prepare(sql);
command.update();
}
private void dropSingleColumnIndexes() throws SQLException {
Database db = session.getDatabase();
ObjectArray indexes = table.getIndexes();
for (int i=0; i<indexes.size(); i++) {
Index index = (Index) indexes.get(i);
if(index.getCreateSQL() == null) {
continue;
}
boolean dropIndex = false;
Column[] cols = index.getColumns();
for(int j=0; j<cols.length; j++) {
if(cols[j] == oldColumn) {
if(cols.length == 1) {
dropIndex = true;
} else {
throw Message.getSQLException(Message.COLUMN_IS_PART_OF_INDEX_1, index.getSQL());
}
}
}
if(dropIndex) {
db.removeSchemaObject(session, index);
indexes = table.getIndexes();
i = -1;
}
}
}
private void checkNullable() throws SQLException {
ObjectArray indexes = table.getIndexes();
for(int i=0; i<indexes.size(); i++) {
Index index = (Index)indexes.get(i);
if(index.getColumnIndex(oldColumn) < 0) {
continue;
}
IndexType indexType = index.getIndexType();
if(indexType.isPrimaryKey() || indexType.isHash()) {
throw Message.getSQLException(Message.COLUMN_IS_PART_OF_INDEX_1, index.getSQL());
}
}
}
private void checkNoNullValues() throws SQLException {
String sql = "SELECT COUNT(*) FROM " + table.getSQL() + " WHERE " + oldColumn.getSQL() + " IS NULL";
Prepared command = session.prepare(sql);
LocalResult result = command.query(0);
result.next();
if(result.currentRow()[0].getInt() > 0) {
throw Message.getSQLException(Message.COLUMN_CONTAINS_NULL_VALUES_1, oldColumn.getSQL());
}
}
public void setType(int type) {
this.type = type;
}
public void setStartWith(long start) {
newStart = start;
}
public void setDefaultExpression(Expression defaultExpression) {
this.defaultExpression = defaultExpression;
}
public void setNewColumn(Column newColumn) {
this.newColumn = newColumn;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.constraint.Constraint;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.schema.Schema;
/**
* @author Thomas
*/
public class AlterTableDropConstraint extends SchemaCommand {
private String constraintName;
public AlterTableDropConstraint(Session session, Schema schema) {
super(session, schema);
}
public void setConstraintName(String string) {
constraintName = string;
}
public int update() throws SQLException {
session.commit();
Constraint constraint = getSchema().getConstraint(constraintName);
session.getUser().checkRight(constraint.getTable(), Right.ALL);
session.getUser().checkRight(constraint.getRefTable(), Right.ALL);
session.getDatabase().removeSchemaObject(session, constraint);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.Table;
/**
* @author Thomas
*/
public class AlterTableRename extends SchemaCommand {
private Table oldTable;
private String newTableName;
public AlterTableRename(Session session, Schema schema) {
super(session, schema);
}
public void setOldTable(Table table) {
oldTable = table;
}
public void setNewTableName(String name) {
newTableName = name;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
if(getSchema().findTableOrView(session, newTableName) != null || newTableName.equals(oldTable.getName())) {
throw Message.getSQLException(Message.TABLE_OR_VIEW_ALREADY_EXISTS_1, newTableName);
}
session.getUser().checkRight(oldTable, Right.ALL);
if(oldTable.getTemporary()) {
// TODO renaming a temporary table is not supported
throw Message.getUnsupportedException();
}
db.renameSchemaObject(session, oldTable, newTableName);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
public class AlterTableRenameColumn extends DefineCommand {
private Table table;
private Column column;
private String newName;
public AlterTableRenameColumn(Session session) {
super(session);
}
public void setTable(Table table) {
this.table = table;
}
public void setColumn(Column column) {
this.column = column;
}
public void setNewColumnName(String newName) {
this.newName = newName;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
session.getUser().checkRight(table, Right.ALL);
table.checkSupportAlter();
table.renameColumn(column, newName);
table.setModified();
db.update(session, table);
ObjectArray children = table.getChildren();
for(int i=0; i<children.size(); i++) {
DbObject child = (DbObject) children.get(i);
if(child.getCreateSQL() != null) {
db.update(session, child);
}
}
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.message.Message;
import org.h2.security.SHA256;
import org.h2.util.ByteUtils;
/**
* @author Thomas
*/
public class AlterUser extends DefineCommand {
public static final int SET_PASSWORD = 0, RENAME = 1, ADMIN = 2;
private int type;
private User user;
private String newName;
private byte[] userPasswordHash;
private byte[] salt;
private byte[] hash;
private boolean admin;
public AlterUser(Session session) {
super(session);
}
public void setType(int type) {
this.type = type;
}
public void setNewName(String newName) {
this.newName = newName;
}
public void setUser(User user) {
this.user = user;
}
public void setAdmin(boolean admin) {
this.admin = admin;
}
public void setSalt(String s) throws SQLException {
salt = ByteUtils.convertStringToBytes(s);
}
public void setHash(String s) throws SQLException {
hash = ByteUtils.convertStringToBytes(s);
}
public void setPassword(String password) {
SHA256 sha = new SHA256();
String name = newName == null ? user.getName() : newName;
this.userPasswordHash = sha.getKeyPasswordHash(name, password.toCharArray());
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
switch(type) {
case SET_PASSWORD:
if(user != session.getUser()) {
session.getUser().checkAdmin();
}
if(hash!=null && salt !=null) {
user.setSaltAndHash(salt, hash);
} else {
user.setUserPasswordHash(userPasswordHash);
}
break;
case RENAME:
session.getUser().checkAdmin();
if(db.findUser(newName) != null || newName.equals(user.getName())) {
throw Message.getSQLException(Message.USER_ALREADY_EXISTS_1, newName);
}
db.renameDatabaseObject(session, user, newName);
break;
case ADMIN:
session.getUser().checkAdmin();
user.setAdmin(admin);
break;
default:
throw Message.getInternalError("type="+type);
}
db.update(session, user);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.table.TableView;
public class AlterView extends DefineCommand {
private TableView view;
public AlterView(Session session) {
super(session);
}
public void setView(TableView view) {
this.view= view;
}
public int update() throws SQLException {
session.commit();
session.getUser().checkRight(view, Right.ALL);
view.recompile(session);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Session;
import org.h2.result.LocalResult;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.util.ObjectArray;
public class Analyze extends DefineCommand {
private int sampleRows = Constants.SELECTIVITY_ANALYZE_SAMPLE_ROWS;
public Analyze(Session session) {
super(session);
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
session.getUser().checkAdmin();
ObjectArray tables = db.getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
// TODO do we need to lock the table?
for(int i=0; i<tables.size(); i++) {
Table table = (Table) tables.get(i);
if(!(table instanceof TableData)) {
continue;
}
Column[] columns = table.getColumns();
StringBuffer buff = new StringBuffer();
buff.append("SELECT ");
for(int j=0; j<columns.length; j++) {
if(j>0) {
buff.append(", ");
}
buff.append("SELECTIVITY(");
buff.append(columns[j].getSQL());
buff.append(")");
}
buff.append(" FROM ");
buff.append(table.getSQL());
if(sampleRows > 0) {
buff.append(" LIMIT 1 SAMPLE_SIZE ");
buff.append(sampleRows);
}
String sql = buff.toString();
Prepared command = session.prepare(sql);
LocalResult result = command.query(0);
result.next();
for(int j=0; j<columns.length; j++) {
int selectivity = result.currentRow()[j].getInt();
columns[j].setSelectivity(selectivity);
}
db.update(session, table);
}
return 0;
}
public void setTop(int top) {
this.sampleRows = top;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.message.Message;
import org.h2.schema.Constant;
import org.h2.schema.Schema;
import org.h2.value.Value;
public class CreateConstant extends SchemaCommand {
private String constantName;
private Expression expression;
private boolean ifNotExists;
public CreateConstant(Session session, Schema schema) {
super(session, schema);
}
public void setIfNotExists(boolean ifNotExists) {
// TODO constant: if exists - probably better use 'or replace'
this.ifNotExists = ifNotExists;
}
public int update() throws SQLException {
session.commit();
session.getUser().checkAdmin();
Database db = session.getDatabase();
if(getSchema().findConstant(constantName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.CONSTANT_ALREADY_EXISTS_1,
constantName);
}
int id = getObjectId(false, true);
Constant constant = new Constant(getSchema(), id, constantName);
expression = expression.optimize(session);
Value value = expression.getValue(session);
constant.setValue(value);
db.addSchemaObject(session, constant);
return 0;
}
public void setConstantName(String constantName) {
this.constantName = constantName;
}
public void setExpression(Expression expr) {
this.expression = expr;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Session;
import org.h2.message.Message;
public class CreateFunctionAlias extends DefineCommand {
private String aliasName;
private String javaClassMethod;
private boolean ifNotExists;
public CreateFunctionAlias(Session session) {
super(session);
}
public int update() throws SQLException {
session.commit();
session.getUser().checkAdmin();
Database db = session.getDatabase();
if(db.findFunctionAlias(aliasName) != null) {
if(!ifNotExists) {
throw Message.getSQLException(Message.FUNCTION_ALIAS_ALREADY_EXISTS_1, aliasName);
}
} else {
int id = getObjectId(false, true);
FunctionAlias functionAlias = new FunctionAlias(db, id, aliasName, javaClassMethod);
db.addDatabaseObject(session, functionAlias);
}
return 0;
}
public void setAliasName(String name) {
this.aliasName = name;
}
public void setJavaClassMethod(String string) {
this.javaClassMethod = string;
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.index.IndexType;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.Table;
/**
* @author Thomas
*/
public class CreateIndex extends SchemaCommand {
private String tableName;
private String indexName;
private String[] columnNames;
private boolean primaryKey, unique, hash;
private boolean ifNotExists;
private String comment;
public CreateIndex(Session session, Schema schema) {
super(session, schema);
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void setIndexName(String indexName) {
this.indexName = indexName;
}
public void setColumnNames(String[] columnNames) {
this.columnNames = columnNames;
}
public boolean getPrimaryKey() {
return primaryKey;
}
public String[] getColumnNames() {
return columnNames;
}
public int update() throws SQLException {
// TODO cancel: may support for index creation
session.commit();
Database db = session.getDatabase();
boolean persistent = db.isPersistent();
Table table = getSchema().getTableOrView(session, tableName);
session.getUser().checkRight(table, Right.ALL);
table.lock(session, true);
if(!table.isPersistent()) {
persistent = false;
}
int id = getObjectId(true, false);
if(indexName == null) {
indexName = getSchema().getUniqueIndexName("INDEX_");
}
if(getSchema().findIndex(indexName) != null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.INDEX_ALREADY_EXISTS_1, indexName);
}
IndexType indexType;
if(primaryKey) {
if(table.findPrimaryKey() != null) {
throw Message.getSQLException(Message.SECOND_PRIMARY_KEY);
}
indexType = IndexType.createPrimaryKey(persistent, hash);
} else if(unique) {
indexType = IndexType.createUnique(persistent, hash);
} else {
indexType = IndexType.createNonUnique(persistent);
}
Column[] columns = table.getColumns(columnNames);
table.addIndex(session, indexName, id, columns, indexType, headPos, comment);
return 0;
}
public void setPrimaryKey(boolean b) {
this.primaryKey = b;
}
public void setUnique(boolean b) {
this.unique = b;
}
public void setHash(boolean b) {
this.hash = b;
}
public boolean getHash() {
return hash;
}
public void setComment(String comment) {
this.comment = comment;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.TableLink;
/**
* @author Thomas
*/
public class CreateLinkedTable extends SchemaCommand {
private String tableName;
private String driver, url, user, password, originalTable;
private boolean ifNotExists;
private String comment;
public CreateLinkedTable(Session session, Schema schema) {
super(session, schema);
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void setDriver(String driver) {
this.driver = driver;
}
public void setOriginalTable(String originalTable) {
this.originalTable = originalTable;
}
public void setPassword(String password) {
this.password = password;
}
public void setUrl(String url) {
this.url = url;
}
public void setUser(String user) {
this.user = user;
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
session.getUser().checkAdmin();
if(getSchema().findTableOrView(session, tableName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.TABLE_OR_VIEW_ALREADY_EXISTS_1,
tableName);
}
int id = getObjectId(false, true);
TableLink table = new TableLink(getSchema(), id, tableName, driver, url, user, password, originalTable);
table.setComment(comment);
db.addSchemaObject(session, table);
return 0;
}
public void setComment(String comment) {
this.comment = comment;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Role;
import org.h2.engine.Session;
import org.h2.message.Message;
public class CreateRole extends DefineCommand {
private String roleName;
private boolean ifNotExists;
public CreateRole(Session session) {
super(session);
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public void setRoleName(String name) {
this.roleName = name;
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
if(db.findUser(roleName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.USER_ALREADY_EXISTS_1, roleName);
}
if(db.findRole(roleName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.ROLE_ALREADY_EXISTS_1, roleName);
}
int id = getObjectId(false, true);
Role role = new Role(db, id, roleName, false);
db.addDatabaseObject(session, role);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.message.Message;
import org.h2.schema.Schema;
public class CreateSchema extends DefineCommand {
private String schemaName;
private String authorization;
private boolean ifNotExists;
public CreateSchema(Session session) {
super(session);
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
User user = db.getUser(authorization);
user.checkAdmin();
if(db.findSchema(schemaName) != null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.SCHEMA_ALREADY_EXISTS_1, schemaName);
}
int id = getObjectId(true, true);
Schema schema = new Schema(db, id, schemaName, user, false);
db.addDatabaseObject(session, schema);
return 0;
}
public void setSchemaName(String name) {
this.schemaName = name;
}
public void setAuthorization(String userName) {
this.authorization = userName;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
public class CreateSequence extends SchemaCommand {
private String sequenceName;
private boolean ifNotExists;
private long start = 1;
private long increment = 1;
private boolean belongsToTable;
public CreateSequence(Session session, Schema schema) {
super(session, schema);
}
public void setSequenceName(String sequenceName) {
this.sequenceName = sequenceName;
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
if(getSchema().findSequence(sequenceName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.SEQUENCE_ALREADY_EXISTS_1, sequenceName);
}
int id = getObjectId(false, true);
Sequence sequence = new Sequence(getSchema(), id, sequenceName, belongsToTable);
sequence.setStartValue(start);
sequence.setIncrement(increment);
db.addSchemaObject(session, sequence);
return 0;
}
public void setStartWith(long start) {
this.start = start;
}
public void setIncrement(long increment) {
this.increment = increment;
}
public boolean getBelongsToTable() {
return belongsToTable;
}
public void setBelongsToTable(boolean belongsToTable) {
this.belongsToTable = belongsToTable;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.command.dml.Insert;
import org.h2.command.dml.Query;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
import org.h2.table.Column;
import org.h2.table.TableData;
import org.h2.util.ObjectArray;
/**
* @author Thomas
*/
public class CreateTable extends SchemaCommand {
private String tableName;
private ObjectArray constraintCommands = new ObjectArray();
private ObjectArray columns = new ObjectArray();
private String[] pkColumnNames;
private boolean ifNotExists;
private boolean persistent = true;
private boolean hashPrimaryKey;
private ObjectArray sequences;
private boolean temporary;
private boolean globalTemporary;
private boolean onCommitDrop;
private boolean onCommitTruncate;
private Query asQuery;
private String comment;
public CreateTable(Session session, Schema schema) {
super(session, schema);
}
public void setQuery(Query query) {
this.asQuery = query;
}
public void setTemporary(boolean temporary) {
this.temporary = temporary;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void addColumn(Column column) {
columns.add(column);
}
public void addConstraintCommand(Prepared command) throws SQLException {
if(command instanceof CreateIndex) {
CreateIndex create = (CreateIndex) command;
if(create.getPrimaryKey()) {
setPrimaryKeyColumnNames(create.getColumnNames());
setHashPrimaryKey(create.getHash());
} else {
constraintCommands.add(command);
}
} else {
constraintCommands.add(command);
}
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public int update() throws SQLException {
// TODO rights: what rights are required to create a table?
session.commit();
Database db = session.getDatabase();
if(!db.isPersistent()) {
persistent = false;
}
if(getSchema().findTableOrView(session, tableName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.TABLE_OR_VIEW_ALREADY_EXISTS_1, tableName);
}
if(asQuery != null) {
generateColumnFromQuery();
}
if (pkColumnNames != null) {
int len = pkColumnNames.length;
for(int i=0; i<columns.size(); i++) {
Column c = (Column) columns.get(i);
for(int j=0; j<len; j++) {
if(c.getName().equals(pkColumnNames[j])) {
c.setNullable(false);
}
}
}
}
sequences = new ObjectArray();
for(int i=0; i<columns.size(); i++) {
Column c = (Column) columns.get(i);
if(c.getAutoIncrement()) {
int objId = getObjectId(true, true);
c.convertAutoIncrementToSequence(session, getSchema(), objId);
}
Sequence seq = c.getSequence();
if(seq != null) {
sequences.add(seq);
}
}
int id = getObjectId(true, true);
TableData table = new TableData(getSchema(), tableName, id, columns, persistent);
table.setComment(comment);
table.setTemporary(temporary);
table.setGlobalTemporary(globalTemporary);
if(temporary && !globalTemporary) {
if(onCommitDrop) {
table.setOnCommitDrop(true);
}
if(onCommitTruncate) {
table.setOnCommitTruncate(true);
}
session.addLocalTempTable(table);
} else {
db.addSchemaObject(session, table);
}
try {
for(int i=0; i<columns.size(); i++) {
Column c = (Column) columns.get(i);
c.prepareExpression(session);
}
if (pkColumnNames != null) {
Column[] pk =table.getColumns(pkColumnNames);
int indexId = getObjectId(true, false);
table.addIndex(session, null, indexId, pk, IndexType.createPrimaryKey(persistent, hashPrimaryKey), Index.EMPTY_HEAD, null);
}
for(int i=0; i<sequences.size(); i++) {
Sequence sequence = (Sequence) sequences.get(i);
table.addSequence(sequence);
}
for(int i=0; i<constraintCommands.size(); i++) {
Prepared command = (Prepared) constraintCommands.get(i);
command.update();
}
if(asQuery != null) {
Insert insert = new Insert(session);
insert.setTable(table);
insert.setQuery(asQuery);
insert.prepare();
insert.update();
}
} catch(SQLException e) {
db.checkPowerOff();
db.removeSchemaObject(session, table);
throw e;
}
return 0;
}
private void generateColumnFromQuery() throws SQLException {
asQuery.prepare();
int columnCount = asQuery.getColumnCount();
ObjectArray expressions = asQuery.getExpressions();
for(int i=0; i<columnCount; i++) {
Expression expr = (Expression) expressions.get(i);
int type = expr.getType();
String name = expr.getColumnName();
long precision = expr.getPrecision();
int scale = expr.getScale();
Column col = new Column(name, type, precision, scale);
addColumn(col);
}
}
public void setPrimaryKeyColumnNames(String[] colNames) throws SQLException {
if(pkColumnNames != null) {
if(colNames.length != pkColumnNames.length) {
throw Message.getSQLException(Message.SECOND_PRIMARY_KEY);
}
for(int i=0; i<colNames.length; i++) {
if(!colNames[i].equals(pkColumnNames[i])) {
throw Message.getSQLException(Message.SECOND_PRIMARY_KEY);
}
}
}
this.pkColumnNames = colNames;
}
public void setPersistent(boolean persistent) {
this.persistent = persistent;
}
public void setHashPrimaryKey(boolean b) {
this.hashPrimaryKey = b;
}
public void setGlobalTemporary(boolean globalTemporary) {
this.globalTemporary = globalTemporary;
}
public void setOnCommitDrop() {
this.onCommitDrop = true;
}
public void setOnCommitTruncate() {
this.onCommitTruncate = true;
}
public void setComment(String comment) {
this.comment = comment;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.schema.TriggerObject;
import org.h2.table.Table;
/**
* @author Thomas
*/
public class CreateTrigger extends SchemaCommand {
// TODO implement drop trigger
private String triggerName;
private boolean ifNotExists;
private boolean before;
private int typeMask;
private boolean rowBased;
private int queueSize = TriggerObject.DEFAULT_QUEUE_SIZE;
private boolean noWait;
private String tableName;
private String triggerClassName;
public CreateTrigger(Session session, Schema schema) {
super(session, schema);
}
public void setBefore(boolean before) {
this.before = before;
}
public void setTriggerClassName(String triggerClassName) {
this.triggerClassName = triggerClassName;
}
public void setTypeMask(int typeMask) {
this.typeMask = typeMask;
}
public void setRowBased(boolean rowBased) {
this.rowBased = rowBased;
}
public void setQueueSize(int size) {
this.queueSize = size;
}
public void setNoWait(boolean noWait) {
this.noWait = noWait;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public void setTriggerName(String name) {
this.triggerName = name;
}
public void setIfNotExists(boolean ifNotExists) {
// TODO trigger: if exists - probably better use 'or replace'
this.ifNotExists = ifNotExists;
}
public int update() throws SQLException {
// TODO rights: what rights are required to create a trigger?
session.commit();
Database db = session.getDatabase();
if(getSchema().findTrigger(triggerName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.TRIGGER_ALREADY_EXISTS_1,
triggerName);
}
int id = getObjectId(false, true);
Table table = getSchema().getTableOrView(session, tableName);
TriggerObject trigger = new TriggerObject(getSchema(), id, triggerName, table);
trigger.setBefore(before);
trigger.setNoWait(noWait);
trigger.setQueueSize(queueSize);
trigger.setRowBased(rowBased);
trigger.setTriggerClassName(session, triggerClassName);
trigger.setTypeMask(typeMask);
db.addSchemaObject(session, trigger);
table.addTrigger(trigger);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.message.Message;
import org.h2.security.SHA256;
import org.h2.util.ByteUtils;
public class CreateUser extends DefineCommand {
private String userName;
private boolean admin;
private byte[] userPasswordHash;
private byte[] salt;
private byte[] hash;
private boolean ifNotExists;
private String comment;
public CreateUser(Session session) {
super(session);
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassword(String password) {
SHA256 sha = new SHA256();
this.userPasswordHash = sha.getKeyPasswordHash(userName, password.toCharArray());
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
if(db.findUser(userName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.USER_ALREADY_EXISTS_1, userName);
}
int id = getObjectId(false, true);
User user = new User(db, id, userName, false);
user.setAdmin(admin);
user.setComment(comment);
if(hash!=null && salt !=null) {
user.setSaltAndHash(salt, hash);
} else {
user.setUserPasswordHash(userPasswordHash);
}
db.addDatabaseObject(session, user);
return 0;
}
public void setSalt(String s) throws SQLException {
salt = ByteUtils.convertStringToBytes(s);
}
public void setHash(String s) throws SQLException {
hash = ByteUtils.convertStringToBytes(s);
}
public void setAdmin(boolean b) {
admin = b;
}
public void setComment(String comment) {
this.comment = comment;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.UserDataType;
import org.h2.message.Message;
import org.h2.table.Column;
public class CreateUserDataType extends DefineCommand {
private String typeName;
private Column column;
private boolean ifNotExists;
public CreateUserDataType(Session session) {
super(session);
}
public void setTypeName(String name) {
this.typeName = name;
}
public void setColumn(Column column) {
this.column = column;
}
public void setIfNotExists(boolean ifNotExists) {
// TODO user data type: if exists - probably better use 'or replace'
this.ifNotExists = ifNotExists;
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
session.getUser().checkAdmin();
if(db.findUserDataType(typeName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.USER_DATA_TYPE_ALREADY_EXISTS_1,
typeName);
}
int id = getObjectId(false, true);
UserDataType type = new UserDataType(db, id, typeName);
type.setColumn(column);
db.addDatabaseObject(session, type);
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.command.dml.Query;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.TableView;
public class CreateView extends SchemaCommand {
private Query select;
private String viewName;
private boolean ifNotExists;
private boolean force;
private String selectSQL;
private String[] columnNames;
private String comment;
public CreateView(Session session, Schema schema) {
super(session, schema);
}
public void setViewName(String name) {
viewName = name;
}
public void setSelect(Query select) {
this.select = select;
}
public int update() throws SQLException {
// TODO rights: what rights are required to create a view?
session.commit();
Database db = session.getDatabase();
if(getSchema().findTableOrView(session, viewName)!=null) {
if (ifNotExists) {
return 0;
}
throw Message.getSQLException(Message.VIEW_ALREADY_EXISTS_1,
viewName);
}
int id = getObjectId(true, true);
String querySQL;
if(select == null && force) {
querySQL = selectSQL;
} else {
querySQL = select.getSQL();
}
TableView view = new TableView(getSchema(), id, viewName, querySQL, null, columnNames, session);
view.setComment(comment);
db.addSchemaObject(session, view);
return 0;
}
public void setIfNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
}
public void setForce(boolean force) {
this.force = force;
}
public void setSelectSQL(String selectSQL) {
this.selectSQL = selectSQL;
}
public void setColumnNames(String[] cols) {
this.columnNames = cols;
}
public void setComment(String comment) {
this.comment = comment;
}
}
/*
* 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.command.ddl;
import org.h2.command.Prepared;
import org.h2.engine.Session;
public abstract class DefineCommand extends Prepared {
public DefineCommand(Session session) {
super(session);
}
public boolean isTransactional() {
return false;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Constant;
import org.h2.schema.Schema;
public class DropConstant extends SchemaCommand {
private String constantName;
private boolean ifExists;
public DropConstant(Session session, Schema schema) {
super(session, schema);
}
public void setIfExists(boolean b) {
ifExists = b;
}
public void setConstantName(String constantName) {
this.constantName = constantName;
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
Constant constant = getSchema().findConstant(constantName);
if(constant == null) {
if(!ifExists) {
throw Message.getSQLException(Message.CONSTANT_NOT_FOUND_1, constantName);
}
} else {
db.removeSchemaObject(session, constant);
}
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
public class DropDatabase extends DefineCommand {
private boolean dropAllObjects;
private boolean deleteFiles;
public DropDatabase(Session session) {
super(session);
}
public int update() throws SQLException {
if(dropAllObjects) {
dropAllObjects();
}
if(deleteFiles) {
session.getDatabase().setDeleteFilesOnDisconnect(true);
}
return 0;
}
private void dropAllObjects() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
ObjectArray list;
// TODO local temp tables are not removed
list = db.getAllSchemas();
for(int i=0; i<list.size(); i++) {
Schema schema = (Schema) list.get(i);
if(schema.canDrop()) {
db.removeDatabaseObject(session, schema);
}
}
list = db.getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
for(int i=0; i<list.size(); i++) {
Table t = (Table) list.get(i);
if(t.getName() != null && Table.VIEW.equals(t.getTableType())) {
db.removeSchemaObject(session, t);
}
}
for(int i=0; i<list.size(); i++) {
Table t = (Table) list.get(i);
if(t.getName() != null && Table.TABLE_LINK.equals(t.getTableType())) {
db.removeSchemaObject(session, t);
}
}
for(int i=0; i<list.size(); i++) {
Table t = (Table) list.get(i);
if(t.getName() != null && Table.TABLE.equals(t.getTableType())) {
db.removeSchemaObject(session, t);
}
}
list = db.getAllSchemaObjects(DbObject.SEQUENCE);
// maybe constraints and triggers on system tables will be allowed in the future
list.addAll(db.getAllSchemaObjects(DbObject.CONSTRAINT));
list.addAll(db.getAllSchemaObjects(DbObject.TRIGGER));
for(int i=0; i<list.size(); i++) {
SchemaObject obj = (SchemaObject) list.get(i);
db.removeSchemaObject(session, obj);
}
list = db.getAllUsers();
for(int i=0; i<list.size(); i++) {
User user = (User) list.get(i);
if(user != session.getUser()) {
db.removeDatabaseObject(session, user);
}
}
list = db.getAllRoles();
list.addAll(db.getAllRights());
list.addAll(db.getAllFunctionAliases());
for(int i=0; i<list.size(); i++) {
DbObject obj = (DbObject) list.get(i);
db.removeDatabaseObject(session, obj);
}
}
public void setDropAllObjects(boolean b) {
this.dropAllObjects = b;
}
public void setDeleteFiles(boolean b) {
this.deleteFiles = b;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Session;
import org.h2.message.Message;
public class DropFunctionAlias extends DefineCommand {
private String aliasName;
private boolean ifExists;
public DropFunctionAlias(Session session) {
super(session);
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
FunctionAlias functionAlias = db.findFunctionAlias(aliasName);
if(functionAlias == null) {
if(!ifExists) {
throw Message.getSQLException(Message.FUNCTION_ALIAS_NOT_FOUND_1, aliasName);
}
} else {
db.removeDatabaseObject(session, functionAlias);
}
return 0;
}
public void setAliasName(String name) {
this.aliasName = name;
}
public void setIfExists(boolean ifExists) {
this.ifExists = 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.command.ddl;
import java.sql.SQLException;
import org.h2.constraint.Constraint;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
/**
* @author Thomas
*/
public class DropIndex extends SchemaCommand {
private String indexName;
private boolean ifExists;
public DropIndex(Session session, Schema schema) {
super(session, schema);
}
public void setIfExists(boolean b) {
ifExists = b;
}
public void setIndexName(String indexName) {
this.indexName = indexName;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
Index index = getSchema().findIndex(indexName);
if(index == null) {
if(!ifExists) {
throw Message.getSQLException(Message.INDEX_NOT_FOUND_1, indexName);
}
} else {
Table table = index.getTable();
ObjectArray constraints = table.getConstraints();
for(int i=0; constraints != null && i<constraints.size(); i++) {
Constraint cons = (Constraint) constraints.get(i);
if(cons.usesIndex(index)) {
throw Message.getSQLException(Message.INDEX_BELONGS_TO_CONSTRAINT_1, indexName);
}
}
session.getUser().checkRight(index.getTable(), Right.ALL);
index.getTable().setModified();
db.removeSchemaObject(session, index);
}
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Role;
import org.h2.engine.Session;
import org.h2.message.Message;
public class DropRole extends DefineCommand {
private String roleName;
private boolean ifExists;
public DropRole(Session session) {
super(session);
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
if(roleName.equals(Constants.PUBLIC_ROLE_NAME)) {
throw Message.getSQLException(Message.ROLE_CAN_NOT_BE_DROPPED_1, roleName);
}
Role role = db.findRole(roleName);
if(role == null) {
if(!ifExists) {
throw Message.getSQLException(Message.ROLE_NOT_FOUND_1, roleName);
}
} else {
db.removeDatabaseObject(session, role);
}
return 0;
}
public void setIfExists(boolean ifExists) {
this.ifExists = 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
public class DropSchema extends DefineCommand {
private String schemaName;
private boolean ifExists;
public DropSchema(Session session) {
super(session);
}
public void setSchemaName(String name) {
this.schemaName = name;
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
Schema schema = db.findSchema(schemaName);
if(schema == null) {
if(!ifExists) {
throw Message.getSQLException(Message.SCHEMA_NOT_FOUND_1, schemaName);
}
} else {
if(!schema.canDrop()) {
throw Message.getSQLException(Message.SCHEMA_CAN_NOT_BE_DROPPED_1, schemaName);
}
db.removeDatabaseObject(session, schema);
}
return 0;
}
public void setIfExists(boolean ifExists) {
this.ifExists = 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
public class DropSequence extends SchemaCommand {
private String sequenceName;
private boolean ifExists;
public DropSequence(Session session, Schema schema) {
super(session, schema);
}
public void setIfExists(boolean b) {
ifExists = b;
}
public void setSequenceName(String sequenceName) {
this.sequenceName = sequenceName;
}
public int update() throws SQLException {
// TODO rights: what are the rights required for a sequence?
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
Sequence sequence = getSchema().findSequence(sequenceName);
if(sequence == null) {
if(!ifExists) {
throw Message.getSQLException(Message.SEQUENCE_NOT_FOUND_1, sequenceName);
}
} else {
if(sequence.getBelongsToTable()) {
throw Message.getSQLException(Message.SEQUENCE_BELONGS_TO_A_TABLE_1, sequenceName);
}
db.removeSchemaObject(session, sequence);
}
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.Table;
/**
* @author Thomas
*/
public class DropTable extends SchemaCommand {
private boolean ifExists;
private String tableName;
private Table table;
private DropTable next;
public DropTable(Session session, Schema schema) {
super(session, schema);
}
public void addNextDropTable(DropTable next) {
if(this.next == null) {
this.next = next;
} else {
this.next.addNextDropTable(next);
}
}
public void setIfExists(boolean b) {
ifExists = b;
if(next != null) {
next.setIfExists(b);
}
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
private void prepareDrop() throws SQLException {
table = getSchema().findTableOrView(session, tableName);
// TODO drop table: drops views as well (is this ok?)
if(table == null) {
if(!ifExists) {
throw Message.getSQLException(Message.TABLE_OR_VIEW_NOT_FOUND_1, tableName);
}
} else {
session.getUser().checkRight(table, Right.ALL);
if(!table.canDrop()) {
throw Message.getSQLException(Message.CANT_DROP_TABLE_1, tableName);
}
table.lock(session, true);
}
if(next != null) {
next.prepareDrop();
}
}
private void executeDrop() throws SQLException {
// need to get the table again, because it may be dropped already meanwhile (dependent object, or same object)
table = getSchema().findTableOrView(session, tableName);
if(table != null) {
table.setModified();
Database db = session.getDatabase();
db.removeSchemaObject(session, table);
}
if(next != null) {
next.executeDrop();
}
}
public int update() throws SQLException {
session.commit();
prepareDrop();
executeDrop();
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.schema.TriggerObject;
import org.h2.table.Table;
public class DropTrigger extends SchemaCommand {
private String triggerName;
private boolean ifExists;
public DropTrigger(Session session, Schema schema) {
super(session, schema);
}
public void setIfExists(boolean b) {
ifExists = b;
}
public void setTriggerName(String triggerName) {
this.triggerName = triggerName;
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
TriggerObject trigger = getSchema().findTrigger(triggerName);
if(trigger == null) {
if(!ifExists) {
throw Message.getSQLException(Message.TRIGGER_NOT_FOUND_1, triggerName);
}
} else {
Table table = trigger.getTable();
session.getUser().checkRight(table, Right.ALL);
db.removeSchemaObject(session, trigger);
}
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.message.Message;
public class DropUser extends DefineCommand {
private boolean ifExists;
private String userName;
public DropUser(Session session) {
super(session);
}
public void setIfExists(boolean b) {
ifExists = b;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
User user = db.findUser(userName);
if(user == null) {
if(!ifExists) {
throw Message.getSQLException(Message.USER_NOT_FOUND_1, userName);
}
} else {
if(user == session.getUser()) {
throw Message.getSQLException(Message.CANT_DROP_CURRENT_USER);
}
db.removeDatabaseObject(session, user);
}
return 0;
}
public boolean isTransactional() {
return false;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.UserDataType;
import org.h2.message.Message;
public class DropUserDataType extends DefineCommand {
private String typeName;
private boolean ifExists;
public DropUserDataType(Session session) {
super(session);
}
public void setIfExists(boolean ifExists) {
this.ifExists = ifExists;
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
UserDataType type = db.findUserDataType(typeName);
if(type == null) {
if(!ifExists) {
throw Message.getSQLException(Message.USER_DATA_TYPE_NOT_FOUND_1, typeName);
}
} else {
db.removeDatabaseObject(session, type);
}
return 0;
}
public void setTypeName(String name) {
this.typeName = name;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.Table;
public class DropView extends SchemaCommand {
private String viewName;
private boolean ifExists;
public DropView(Session session, Schema schema) {
super(session, schema);
}
public void setIfExists(boolean b) {
ifExists = b;
}
public void setViewName(String viewName) {
this.viewName = viewName;
}
public int update() throws SQLException {
// TODO rights: what rights are required to drop a view?
session.commit();
Table view = getSchema().findTableOrView(session, viewName);
if(view == null) {
if(!ifExists) {
throw Message.getSQLException(Message.VIEW_NOT_FOUND_1, viewName);
}
} else {
if(!view.getTableType().equals(Table.VIEW)) {
throw Message.getSQLException(Message.VIEW_NOT_FOUND_1, viewName);
}
session.getUser().checkRight(view, Right.ALL);
view.lock(session, true);
session.getDatabase().removeSchemaObject(session, view);
}
return 0;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Database;
import org.h2.engine.Right;
import org.h2.engine.RightOwner;
import org.h2.engine.Role;
import org.h2.engine.Session;
import org.h2.jdbc.JdbcSQLException;
import org.h2.message.Message;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
public class GrantRevoke extends DefineCommand {
public static final int GRANT = 0, REVOKE = 1;
private ObjectArray roleNames;
private int operationType;
private int rightMask;
private ObjectArray tables = new ObjectArray();
private RightOwner grantee;
public GrantRevoke(Session session) {
super(session);
}
public void setOperationType(int operationType) {
this.operationType = operationType;
}
public void addRight(int right) {
this.rightMask |= right;
}
public void addRoleName(String roleName) {
if(roleNames == null) {
roleNames = new ObjectArray();
}
roleNames.add(roleName);
}
public void setGranteeName(String granteeName) throws JdbcSQLException {
Database db = session.getDatabase();
grantee = db.findUser(granteeName);
if(grantee == null) {
grantee = db.findRole(granteeName);
if(grantee == null) {
throw Message.getSQLException(Message.USER_OR_ROLE_NOT_FOUND_1, granteeName);
}
}
}
public int update() throws SQLException {
session.getUser().checkAdmin();
session.commit();
Database db = session.getDatabase();
if(roleNames != null) {
for(int i=0; i<roleNames.size(); i++) {
String name = (String) roleNames.get(i);
Role grantedRole = db.findRole(name);
if (grantedRole == null) {
throw Message.getSQLException(Message.ROLE_NOT_FOUND_1, name);
}
if(operationType == GRANT) {
grantRole(grantedRole);
} else if (operationType == REVOKE) {
revokeRole(grantedRole);
} else {
throw Message.getInternalError("type="+operationType);
}
}
} else {
if(operationType == GRANT) {
grantRight();
} else if (operationType == REVOKE) {
revokeRight();
} else {
throw Message.getInternalError("type="+operationType);
}
}
return 0;
}
private void grantRight() throws SQLException {
Database db = session.getDatabase();
for(int i=0; i<tables.size(); i++) {
Table table = (Table) tables.get(i);
Right right = grantee.getRightForTable(table);
if(right == null) {
int id = getObjectId(true, true);
right = new Right(db, id, grantee, rightMask, table);
grantee.grantRight(table, right);
db.addDatabaseObject(session, right);
} else {
right.setRightMask(right.getRightMask() | rightMask);
}
}
}
private void grantRole(Role grantedRole) throws SQLException {
if(grantee.isRoleGranted(grantedRole)) {
throw Message.getSQLException(Message.ROLE_ALREADY_GRANTED_1, grantedRole.getSQL());
}
if(grantee instanceof Role) {
Role granteeRole = (Role) grantee;
if(grantedRole.isRoleGranted(granteeRole)) {
// TODO role: should be 'cyclic role grants are not allowed'
throw Message.getSQLException(Message.ROLE_ALREADY_GRANTED_1, grantedRole.getSQL());
}
}
Database db = session.getDatabase();
int id = getObjectId(true, true);
Right right = new Right(db, id, grantee, grantedRole);
db.addDatabaseObject(session, right);
grantee.grantRole(session, grantedRole, right);
}
private void revokeRight() throws SQLException {
for(int i=0; i<tables.size(); i++) {
Table table = (Table) tables.get(i);
Right right = grantee.getRightForTable(table);
if(right == null) {
throw Message.getSQLException(Message.RIGHT_NOT_FOUND);
}
int mask = right.getRightMask();
if((mask & rightMask) != rightMask) {
throw Message.getSQLException(Message.RIGHT_NOT_FOUND);
}
int newRight = mask ^ rightMask;
Database db = session.getDatabase();
if(newRight == 0) {
db.removeDatabaseObject(session, right);
} else {
right.setRightMask(newRight);
db.update(session, right);
}
}
}
private void revokeRole(Role grantedRole) throws SQLException {
Right right = grantee.getRightForRole(grantedRole);
if(right == null) {
throw Message.getSQLException(Message.RIGHT_NOT_FOUND);
}
Database db = session.getDatabase();
db.removeDatabaseObject(session, right);
}
public boolean isTransactional() {
return false;
}
public void addTable(Table table) {
tables.add(table);
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.schema.Schema;
public abstract class SchemaCommand extends DefineCommand {
private Schema schema;
public SchemaCommand(Session session, Schema schema) {
super(session);
this.schema = schema;
}
protected Schema getSchema() throws SQLException {
return schema;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Comment;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.message.Message;
import org.h2.table.Table;
public class SetComment extends DefineCommand {
private String schemaName;
private String objectName;
private boolean column;
private String columnName;
private int objectType;
private Expression expr;
public SetComment(Session session) {
super(session);
}
public int update() throws SQLException {
session.commit();
Database db = session.getDatabase();
session.getUser().checkAdmin();
DbObject object = null;
int errorCode = Message.GENERAL_ERROR_1;
if(schemaName == null) {
schemaName = session.getCurrentSchemaName();
}
switch(objectType) {
case DbObject.CONSTANT:
object = db.getSchema(schemaName).getConstant(session, objectName);
break;
case DbObject.CONSTRAINT:
object = db.getSchema(schemaName).getConstraint(objectName);
break;
case DbObject.FUNCTION_ALIAS:
schemaName = null;
object = db.findFunctionAlias(objectName);
errorCode = Message.FUNCTION_ALIAS_NOT_FOUND_1;
break;
case DbObject.INDEX:
object = db.getSchema(schemaName).getIndex(objectName);
break;
case DbObject.ROLE:
schemaName = null;
object = db.findRole(objectName);
errorCode = Message.ROLE_NOT_FOUND_1;
break;
case DbObject.SCHEMA:
schemaName = null;
object = db.findSchema(objectName);
errorCode = Message.SCHEMA_NOT_FOUND_1;
break;
case DbObject.SEQUENCE:
object = db.getSchema(schemaName).getSequence(objectName);
break;
case DbObject.TABLE_OR_VIEW:
object = db.getSchema(schemaName).getTableOrView(session, objectName);
break;
case DbObject.TRIGGER:
object = db.getSchema(schemaName).findTrigger(objectName);
errorCode = Message.TRIGGER_NOT_FOUND_1;
break;
case DbObject.USER:
schemaName = null;
object = db.getUser(objectName);
break;
case DbObject.USER_DATATYPE:
schemaName = null;
object = db.findUserDataType(objectName);
errorCode = Message.USER_DATA_TYPE_ALREADY_EXISTS_1;
break;
}
if(object == null) {
throw Message.getSQLException(errorCode, objectName);
}
String text = expr.getValue(session).getString();
if(column) {
Table table = (Table) object;
table.getColumn(columnName).setComment(text);
} else {
object.setComment(text);
}
if(column || objectType == DbObject.TABLE_OR_VIEW || objectType == DbObject.USER || objectType == DbObject.INDEX || objectType == DbObject.CONSTRAINT) {
db.update(session, object);
} else {
Comment comment = db.findComment(object);
if(comment == null) {
if(text == null) {
// reset a non-existing comment - nothing to do
} else {
int id = getObjectId(false, false);
comment = new Comment(db, id, object);
comment.setCommentText(text);
db.addDatabaseObject(session, comment);
}
} else {
if(text == null) {
db.removeDatabaseObject(session, comment);
} else {
comment.setCommentText(text);
db.update(session, comment);
}
}
}
return 0;
}
public void setCommentExpression(Expression expr) {
this.expr = expr;
}
public void setObjectName(String objectName) {
this.objectName = objectName;
}
public void setObjectType(int objectType) {
this.objectType = objectType;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
public void setColumn(boolean column) {
this.column = column;
}
}
/*
* 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.command.ddl;
import java.sql.SQLException;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.Table;
public class TruncateTable extends SchemaCommand {
private String tableName;
public TruncateTable(Session session, Schema schema) {
super(session, schema);
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public int update() throws SQLException {
session.commit();
Table table = getSchema().getTableOrView(session, tableName);
if(!table.canTruncate()) {
throw Message.getSQLException(Message.CANT_TRUNCATE_1, tableName);
} else {
session.getUser().checkRight(table, Right.DELETE);
table.lock(session, true);
table.truncate(session);
}
return 0;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.result.LocalResult;
import org.h2.table.Column;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueResultSet;
/**
* @author Thomas
*/
public class Call extends Prepared {
private Expression value;
private ObjectArray expressions;
public Call(Session session) {
super(session);
}
public LocalResult query(int maxrows) throws SQLException {
setCurrentRowNumber(1);
Value v = value.getValue(session);
if(v.getType() == Value.RESULT_SET) {
return LocalResult.read(session, ((ValueResultSet)v).getResultSet());
} else if(v.getType() == Value.ARRAY) {
Value[] list = ((ValueArray)v).getList();
ObjectArray expr = new ObjectArray();
for(int i = 0; i<list.length; i++) {
Value e = list[i];
Column col = new Column("C" + (i+1), e.getType(), e.getPrecision(), e.getScale());
expr.add(new ExpressionColumn(session.getDatabase(), null, col));
}
LocalResult result = new LocalResult(session, expr, list.length);
result.addRow(list);
result.done();
return result;
}
LocalResult result = new LocalResult(session, expressions, 1);
Value[] row = new Value[1];
row[0] = v;
result.addRow(row);
result.done();
return result;
}
public void prepare() throws SQLException {
value = value.optimize(session);
expressions = new ObjectArray();
expressions.add(value);
}
public void setValue(Expression expression) {
value = expression;
}
public boolean isQuery() {
return true;
}
public boolean isTransactional() {
return true;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.result.Row;
import org.h2.store.UndoLogRecord;
import org.h2.table.PlanItem;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
/**
* @author Thomas
*/
public class Delete extends Prepared {
private Expression condition;
private TableFilter tableFilter;
public Delete(Session session) {
super(session);
}
public void setTableFilter(TableFilter tableFilter) {
this.tableFilter = tableFilter;
}
public void setCondition(Expression condition) {
this.condition = condition;
}
public int update() throws SQLException {
tableFilter.startQuery();
tableFilter.reset();
Table table = tableFilter.getTable();
session.getUser().checkRight(table, Right.DELETE);
table.fireBefore(session);
table.lock(session, true);
ObjectArray rows = new ObjectArray();
setCurrentRowNumber(0);
while (tableFilter.next()) {
checkCancelled();
setCurrentRowNumber(rows.size()+1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row row = tableFilter.get();
rows.add(row);
}
}
if(table.fireRow()) {
for (int i = 0; i < rows.size(); i++) {
checkCancelled();
Row row = (Row) rows.get(i);
table.fireBeforeRow(session, row, null);
}
}
for (int i = 0; i < rows.size(); i++) {
checkCancelled();
Row row = (Row) rows.get(i);
table.removeRow(session, row);
session.log(new UndoLogRecord(table, UndoLogRecord.DELETE, row));
}
if(table.fireRow()) {
for (int i = 0; i < rows.size(); i++) {
checkCancelled();
Row row = (Row) rows.get(i);
table.fireAfterRow(session, row, null);
}
}
table.fireAfter(session);
return rows.size();
}
public String getPlan() {
StringBuffer buff = new StringBuffer();
buff.append("DELETE FROM ");
buff.append(tableFilter.getPlanSQL(false));
if(condition != null) {
buff.append("\nWHERE " + StringUtils.unEnclose(condition.getSQL()));
}
return buff.toString();
}
public void prepare() throws SQLException {
if (condition != null) {
condition.mapColumns(tableFilter, 0);
condition = condition.optimize(session);
condition.createIndexConditions(tableFilter);
}
PlanItem item = tableFilter.getBestPlanItem(session);
tableFilter.setPlanItem(item);
tableFilter.prepare();
}
public boolean isTransactional() {
return true;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.engine.Session;
import org.h2.expression.ExpressionColumn;
import org.h2.result.LocalResult;
import org.h2.table.Column;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
import org.h2.value.ValueString;
public class ExplainPlan extends Prepared {
private Prepared command;
private LocalResult result;
public ExplainPlan(Session session) {
super(session);
}
public void setCommand(Prepared command) {
this.command = command;
}
public void prepare() throws SQLException {
command.prepare();
}
public LocalResult query(int maxrows) throws SQLException {
// TODO rights: are rights required for explain?
ObjectArray expressions = new ObjectArray();
Column column = new Column("PLAN", Value.STRING, 0, 0);
ExpressionColumn expr = new ExpressionColumn(session.getDatabase(), null, column);
expressions.add(expr);
result = new LocalResult(session, expressions, 1);
String plan = command.getPlan();
add(plan);
result.done();
return result;
}
private void add(String text) throws SQLException {
Value[] row = new Value[1];
Value value = ValueString.get(text);
row[0] = value;
result.addRow(row);
}
public boolean isQuery() {
return true;
}
public boolean isTransactional() {
return true;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import org.h2.command.Command;
import org.h2.command.Prepared;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.store.UndoLogRecord;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
/**
* @author Thomas
*/
public class Insert extends Prepared {
private Table table;
private Column[] columns;
private ObjectArray list = new ObjectArray();
private Query query;
public Insert(Session session) {
super(session);
}
public void setCommand(Command command) {
super.setCommand(command);
if(query != null) {
query.setCommand(command);
}
}
public void setTable(Table table) {
this.table = table;
}
public void setColumns(Column[] columns) {
this.columns = columns;
}
public void setQuery(Query query) {
this.query = query;
}
public void addRow(Expression[] expr) {
list.add(expr);
}
public int update() throws SQLException {
int count;
session.getUser().checkRight(table, Right.INSERT);
setCurrentRowNumber(0);
if(list.size() > 0) {
count = 0;
for(int x=0; x<list.size(); x++) {
Expression[] expr = (Expression[])list.get(x);
Row newRow = table.getTemplateRow();
setCurrentRowNumber(x+1);
for (int i = 0; i < columns.length; i++) {
Column c = columns[i];
int index = c.getColumnId();
Expression e = expr[i];
if(e != null) {
// e can be null (DEFAULT)
e = e.optimize(session);
Value v = e.getValue(session).convertTo(c.getType());
newRow.setValue(index, v);
}
}
// TODO insert: set default values before calling triggers?
checkCancelled();
table.fireBefore(session);
table.validateConvertUpdateSequence(session, newRow);
table.fireBeforeRow(session, null, newRow);
table.lock(session, true);
table.addRow(session, newRow);
session.log(new UndoLogRecord(table, UndoLogRecord.INSERT, newRow));
table.fireAfter(session);
table.fireAfterRow(session, null, newRow);
count++;
}
} else {
LocalResult rows = query.query(0);
count = 0;
table.fireBefore(session);
table.lock(session, true);
while(rows.next()) {
checkCancelled();
count++;
Value[] r = rows.currentRow();
Row newRow = table.getTemplateRow();
setCurrentRowNumber(count);
for (int j = 0; j < columns.length; j++) {
Column c = columns[j];
int index = c.getColumnId();
Value v = r[j].convertTo(c.getType());
newRow.setValue(index, v);
}
table.validateConvertUpdateSequence(session, newRow);
table.fireBeforeRow(session, null, newRow);
table.addRow(session, newRow);
session.log(new UndoLogRecord(table, UndoLogRecord.INSERT, newRow));
table.fireAfterRow(session, null, newRow);
}
rows.close();
table.fireAfter(session);
}
return count;
}
public String getPlan() {
StringBuffer buff = new StringBuffer();
buff.append("INSERT INTO ");
buff.append(table.getSQL());
buff.append('(');
for(int i=0; i<columns.length; i++) {
if(i>0) {
buff.append(", ");
}
buff.append(columns[i].getSQL());
}
buff.append(")\n");
if(list.size() > 0) {
buff.append("VALUES ");
for(int x=0; x<list.size(); x++) {
Expression[] expr = (Expression[])list.get(x);
if(x > 0) {
buff.append(", ");
}
buff.append("(");
for (int i = 0; i < columns.length; i++) {
if(i>0) {
buff.append(", ");
}
Expression e = expr[i];
if(e == null) {
buff.append("DEFAULT");
} else {
buff.append(StringUtils.unEnclose(e.getSQL()));
}
}
buff.append(')');
}
} else {
buff.append(query.getPlan());
}
return buff.toString();
}
public void prepare() throws SQLException {
if (columns == null) {
if(list.size() > 0 && ((Expression[])list.get(0)).length == 0) {
// special case where table is used as a sequence
columns = new Column[0];
} else {
columns = table.getColumns();
}
}
if(list.size() > 0) {
for(int x=0; x<list.size(); x++) {
Expression[] expr = (Expression[])list.get(x);
if(expr.length != columns.length) {
throw Message.getSQLException(Message.COLUMN_COUNT_DOES_NOT_MATCH);
}
for(int i=0; i<expr.length; i++) {
Expression e = expr[i];
if(e != null) {
expr[i] = e.optimize(session);
}
}
}
} else {
query.prepare();
if(query.getColumnCount() != columns.length) {
throw Message.getSQLException(Message.COLUMN_COUNT_DOES_NOT_MATCH);
}
}
}
public boolean isTransactional() {
return true;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import org.h2.command.Command;
import org.h2.command.Prepared;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.Parameter;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.store.UndoLogRecord;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
/**
* @author Thomas
*/
public class Merge extends Prepared {
private Table table;
private Column[] columns;
private Column[] keys;
private ObjectArray list = new ObjectArray();
private Query query;
private Prepared update;
public Merge(Session session) {
super(session);
}
public void setCommand(Command command) {
super.setCommand(command);
if(query != null) {
query.setCommand(command);
}
}
public void setTable(Table table) {
this.table = table;
}
public void setColumns(Column[] columns) {
this.columns = columns;
}
public void setKeys(Column[] keys) {
this.keys = keys;
}
public void setQuery(Query query) {
this.query = query;
}
public void addRow(Expression[] expr) {
list.add(expr);
}
public int update() throws SQLException {
int count;
session.getUser().checkRight(table, Right.INSERT);
session.getUser().checkRight(table, Right.UPDATE);
if(keys == null) {
Index idx = table.getPrimaryKey();
if(idx == null) {
throw Message.getSQLException(Message.CONSTRAINT_NOT_FOUND_1, "PRIMARY KEY");
}
keys = idx.getColumns();
}
StringBuffer buff = new StringBuffer("UPDATE ");
buff.append(table.getSQL());
buff.append(" SET ");
for(int i=0; i<columns.length; i++) {
if(i>0) {
buff.append(", ");
}
buff.append(columns[i].getSQL());
buff.append("=?");
}
buff.append(" WHERE ");
for(int i=0; i<keys.length; i++) {
if(i>0) {
buff.append(" AND ");
}
buff.append(keys[i].getSQL());
buff.append("=?");
}
String sql = buff.toString();
update = session.prepare(sql);
setCurrentRowNumber(0);
if(list.size() > 0) {
count = 0;
for(int x=0; x<list.size(); x++) {
setCurrentRowNumber(x+1);
Expression[] expr = (Expression[])list.get(x);
Row newRow = table.getTemplateRow();
for (int i = 0; i < columns.length; i++) {
Column c = columns[i];
int index = c.getColumnId();
Expression e = expr[i];
if(e != null) {
// e can be null (DEFAULT)
Value v = expr[i].getValue(session).convertTo(c.getType());
newRow.setValue(index, v);
}
}
merge(newRow);
count++;
}
} else {
LocalResult rows = query.query(0);
count = 0;
table.fireBefore(session);
table.lock(session, true);
while(rows.next()) {
checkCancelled();
count++;
Value[] r = rows.currentRow();
Row newRow = table.getTemplateRow();
setCurrentRowNumber(count);
for (int j = 0; j < columns.length; j++) {
Column c = columns[j];
int index = c.getColumnId();
Value v = r[j].convertTo(c.getType());
newRow.setValue(index, v);
}
merge(newRow);
}
rows.close();
table.fireAfter(session);
}
return count;
}
private void merge(Row row) throws SQLException {
ObjectArray k = update.getParameters();
for(int i=0; i<columns.length; i++) {
Column col = columns[i];
Value v = row.getValue(col.getColumnId());
Parameter p = (Parameter) k.get(i);
p.setValue(v);
}
for(int i=0; i<keys.length; i++) {
Column col = keys[i];
Value v = row.getValue(col.getColumnId());
if(v == null) {
throw Message.getSQLException(Message.COLUMN_CONTAINS_NULL_VALUES_1, col.getSQL());
}
Parameter p = (Parameter) k.get(columns.length + i);
p.setValue(v);
}
int count = update.update();
if(count == 0) {
table.fireBefore(session);
table.validateConvertUpdateSequence(session, row);
table.fireBeforeRow(session, null, row);
table.lock(session, true);
table.addRow(session, row);
session.log(new UndoLogRecord(table, UndoLogRecord.INSERT, row));
table.fireAfter(session);
table.fireAfterRow(session, null, row);
} else if(count != 1) {
throw Message.getSQLException(Message.DUPLICATE_KEY_1, table.getSQL());
}
}
public String getPlan() {
StringBuffer buff = new StringBuffer();
buff.append("MERGE INTO ");
buff.append(table.getSQL());
buff.append('(');
for(int i=0; i<columns.length; i++) {
if(i>0) {
buff.append(", ");
}
buff.append(columns[i].getSQL());
}
buff.append(")");
if(keys != null) {
buff.append(" KEY(");
for(int i=0; i<keys.length; i++) {
if(i>0) {
buff.append(", ");
}
buff.append(keys[i].getSQL());
}
buff.append(")");
}
buff.append('\n');
if(list.size() > 0) {
buff.append("VALUES ");
for(int x=0; x<list.size(); x++) {
Expression[] expr = (Expression[])list.get(x);
if(x > 0) {
buff.append(", ");
}
buff.append("(");
for (int i = 0; i < columns.length; i++) {
if(i>0) {
buff.append(", ");
}
Expression e = expr[i];
if(e == null) {
buff.append("DEFAULT");
} else {
buff.append(StringUtils.unEnclose(e.getSQL()));
}
}
buff.append(')');
}
} else {
buff.append(query.getPlan());
}
return buff.toString();
}
public void prepare() throws SQLException {
if (columns == null) {
if(list.size() > 0 && ((Expression[])list.get(0)).length == 0) {
// special case where table is used as a sequence
columns = new Column[0];
} else {
columns = table.getColumns();
}
}
if(list.size() > 0) {
for(int x=0; x<list.size(); x++) {
Expression[] expr = (Expression[])list.get(x);
if(expr.length != columns.length) {
throw Message.getSQLException(Message.COLUMN_COUNT_DOES_NOT_MATCH);
}
for(int i=0; i<expr.length; i++) {
Expression e = expr[i];
if(e != null) {
expr[i] = e.optimize(session);
}
}
}
} else {
query.prepare();
if(query.getColumnCount() != columns.length) {
throw Message.getSQLException(Message.COLUMN_COUNT_DOES_NOT_MATCH);
}
}
}
public boolean isTransactional() {
return true;
}
}
/*
* 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.command.dml;
import org.h2.command.Prepared;
import org.h2.engine.Session;
public class NoOperation extends Prepared {
public NoOperation(Session session) {
super(session);
}
public int update() {
return 0;
}
public boolean isQuery() {
return false;
}
public boolean isTransactional() {
return true;
}
public boolean needRecompile() {
return false;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import java.util.BitSet;
import java.util.Enumeration;
import java.util.Random;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.table.Plan;
import org.h2.table.PlanItem;
import org.h2.table.TableFilter;
import org.h2.util.Permutations;
public class Optimizer {
private static final int MAX_BRUTE_FORCE_FILTERS=7;
private static final int MAX_BRUTE_FORCE=2000;
private static final int MAX_GENETIC=2000;
private long start;
private BitSet switched;
// possible plans for filters:
// 1 filter 1 plan
// 2 filters 2 plans
// 3 filters 6 plans
// 4 filters 24 plans
// 5 filters 120 plans
// 6 filters 720 plans
// 7 filters 5040 plans
// 8 filters 40320 plan
// 9 filters 362880 plans
// 10 filters 3628800 filters
// 1 of 1, 2, 3, 4, 5, 6 filters: 1, 2, 3, 4, 5, 6
// 2 of 2, 3, 4, 5, 6 filters: 2, 6, 12, 20, 30
// 3 of 3, 4, 5, 6 filters: 6, 24, 75, 120
// 4 of 4, 5, 6 filters: 24, 120, 260
private TableFilter[] filters;
private Expression condition;
private Session session;
private Plan bestPlan;
private TableFilter topFilter;
private double cost;
private Random random;
private int getMaxBruteForceFilters(int filterCount) {
int i = 0, j = filterCount, total = filterCount;
while(j>0 && total < MAX_BRUTE_FORCE) {
j--;
total *= j;
i++;
}
return i;
}
Optimizer(TableFilter[] filters, Expression condition, Session session) {
this.filters = filters;
this.condition = condition;
this.session = session;
}
private void calculateBestPlan() throws SQLException {
start = System.currentTimeMillis();
cost = -1;
if(filters.length==1) {
testPlan(filters);
} else if (filters.length <= MAX_BRUTE_FORCE_FILTERS) {
calculateBruteForceAll();
} else {
calculateBruteForceSome();
random = new Random(0);
calculateGenetic();
// TODO optimizer: how to use rule based optimizer?
}
}
private boolean canStop(int x) {
if((x & 127) == 0) {
long t = System.currentTimeMillis() - start;
// don't calculate for simple queries (no rows or so)
if(cost >= 0 && 10*t > cost) {
return true;
}
}
return false;
}
private void calculateBruteForceAll() throws SQLException {
Enumeration en = new Permutations(filters);
for(int x=0; en.hasMoreElements(); x++) {
if(canStop(x)) {
break;
}
Object[] f = (Object[]) en.nextElement();
TableFilter[] ftry = new TableFilter[filters.length];
System.arraycopy(f, 0, ftry, 0, filters.length);
testPlan(ftry);
}
}
private void calculateBruteForceSome() throws SQLException {
int bruteForce = getMaxBruteForceFilters(filters.length);
TableFilter[] ftry = new TableFilter[filters.length];
Enumeration en = new Permutations(filters, bruteForce);
for(int x=0; en.hasMoreElements(); x++) {
if(canStop(x)) {
break;
}
Object[] f = (Object[]) en.nextElement();
System.arraycopy(f, 0, ftry, 0, bruteForce);
// find out what filters are not used yet
for(int i=0; i<filters.length; i++) {
filters[i].setUsed(false);
}
for(int i=0; i<f.length; i++) {
ftry[i].setUsed(true);
}
// fill the remaining elements with the unused elements (greedy)
for(int i=bruteForce; i<filters.length; i++) {
double costPart = -1.0;
int bestPart = -1;
for(int j=0; j<filters.length; j++) {
if(!filters[j].getUsed()) {
if(i==filters.length-1) {
bestPart = j;
break;
}
ftry[i] = filters[j];
Plan part = new Plan(ftry, i+1, condition);
double costNow = part.calculateCost(session);
if (costPart < 0 || costNow < costPart) {
costPart = costNow;
bestPart = j;
}
}
}
filters[bestPart].setUsed(true);
ftry[i] = filters[bestPart];
}
testPlan(ftry);
}
}
private void calculateGenetic() throws SQLException {
TableFilter[] fbest = new TableFilter[filters.length];
TableFilter[] ftry = new TableFilter[filters.length];
for(int x=0; x<MAX_GENETIC; x++) {
if(canStop(x)) {
break;
}
boolean generateRandom = (x & 127) == 0;
if(!generateRandom) {
System.arraycopy(fbest, 0, ftry, 0, filters.length);
if(!shuffleTwo(ftry)) {
generateRandom = true;
}
}
if(generateRandom) {
switched = new BitSet();
System.arraycopy(filters, 0, fbest, 0, filters.length);
shuffleAll(fbest);
System.arraycopy(fbest, 0, ftry, 0, filters.length);
}
if(testPlan(ftry)) {
switched = new BitSet();
System.arraycopy(ftry, 0, fbest, 0, filters.length);
}
}
}
private boolean testPlan(TableFilter[] ftry) throws SQLException {
Plan p = new Plan(ftry, ftry.length, condition);
double costNow = p.calculateCost(session);
if (cost < 0 || costNow < cost) {
cost = costNow;
bestPlan = p;
return true;
}
return false;
}
private void shuffleAll(TableFilter[] f) {
for(int i=0; i<f.length-1; i++) {
int j = i + random.nextInt(f.length - i);
if(j != i) {
TableFilter temp = f[i];
f[i] = f[j];
f[j] = temp;
}
}
}
private boolean shuffleTwo(TableFilter[] f) {
int a = 0, b = 0, i = 0;
for(; i<20; i++) {
a = random.nextInt(f.length);
b = random.nextInt(f.length);
if(a==b) {
continue;
}
if(a<b) {
int temp = a;
a = b;
b = temp;
}
int s = a * f.length + b;
if(switched.get(s)) {
continue;
}
switched.set(s);
break;
}
if(i==20) {
return false;
}
TableFilter temp = f[a];
f[a] = f[b];
f[b] = temp;
return true;
}
void optimize() throws SQLException {
calculateBestPlan();
bestPlan.removeUnusableIndexConditions();
TableFilter[] f2 = bestPlan.getFilters();
topFilter = f2[0];
for (int i = 0; i < f2.length - 1; i++) {
f2[i].addJoin(f2[i + 1], false, null);
}
for (int i = 0; i < f2.length; i++) {
PlanItem item = bestPlan.getItem(f2[i]);
f2[i].setPlanItem(item);
}
}
public TableFilter getTopFilter() {
return topFilter;
}
double getCost() {
return cost;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import java.util.HashSet;
import org.h2.command.Prepared;
import org.h2.engine.Session;
import org.h2.expression.Alias;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.SortOrder;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
public abstract class Query extends Prepared {
protected Expression limit, offset;
protected int sampleSize;
private int lastLimit;
private long lastEvaluated;
private LocalResult lastResult;
private Value[] lastParameters;
abstract LocalResult queryWithoutCache(int limit) throws SQLException;
public Query(Session session) {
super(session);
}
public boolean isQuery() {
return true;
}
public boolean isTransactional() {
return true;
}
public final boolean sameResultAsLast(Session session, Value[] params, Value[] lastParams, long lastEvaluated) throws SQLException {
for(int i=0; i<params.length; i++) {
if(!session.getDatabase().areEqual(lastParams[i], params[i])) {
return false;
}
}
if(!isEverything(ExpressionVisitor.DETERMINISTIC) || !isEverything(ExpressionVisitor.INDEPENDENT)) {
return false;
}
if(getMaxDataModificationId() > lastEvaluated) {
return false;
}
return true;
}
public final Value[] getParameterValues() throws SQLException {
ObjectArray list = getParameters();
if(list == null) {
list = new ObjectArray();
}
Value[] params = new Value[list.size()];
for(int i=0; i<list.size(); i++) {
Value v = ((Parameter) list.get(i)).getParamValue();
params[i] = v;
}
return params;
}
public final LocalResult query(int limit) throws SQLException {
if(!session.getDatabase().getOptimizeReuseResults()) {
return queryWithoutCache(limit);
}
Value[] params = getParameterValues();
long now = session.getDatabase().getModificationDataId();
if(lastResult != null && limit == lastLimit) {
if(sameResultAsLast(session, params, lastParameters, lastEvaluated)) {
lastResult = lastResult.createShallowCopy(session);
lastResult.reset();
return lastResult;
}
}
lastParameters = params;
lastResult = queryWithoutCache(limit);
this.lastEvaluated = now;
lastLimit = limit;
return lastResult;
}
protected SortOrder initOrder(ObjectArray expressions, ObjectArray orderList, int visible, boolean mustBeInResult) throws SQLException {
int[] index = new int[orderList.size()];
int[] sortType = new int[orderList.size()];
int originalLength = expressions.size();
for(int i=0; i<orderList.size(); i++) {
SelectOrderBy o = (SelectOrderBy) orderList.get(i);
int idx;
if(o.expression != null) {
Expression e = o.expression;
// special case: SELECT 1 AS A FROM DUAL ORDER BY A
// (oracle supports it, but only in order by, not in group by and not in having):
// SELECT 1 AS A FROM DUAL ORDER BY -A
boolean isAlias = false;
idx = expressions.size();
if(e instanceof ExpressionColumn) {
ExpressionColumn ecol = (ExpressionColumn)e;
String alias = ecol.getOriginalAliasName();
String col = ecol.getOriginalColumnName();
for(int j=0; j<visible; j++) {
boolean found = false;
Expression ec = (Expression) expressions.get(j);
if(ec instanceof ExpressionColumn) {
ExpressionColumn c = (ExpressionColumn) ec;
found = col.equals(c.getColumnName());
if(alias != null && found) {
found = alias.equals(c.getOriginalAliasName());
}
} else if(!(ec instanceof Alias)) {
continue;
} else if(col.equals(ec.getAlias())) {
found = true;
} else {
Expression ec2 = ec.getNonAliasExpression();
if(ec2 instanceof ExpressionColumn) {
ExpressionColumn c2 = (ExpressionColumn) ec2;
found = col.equals(c2.getColumnName());
}
}
if(found) {
idx = j;
isAlias = true;
break;
}
}
}
if(!isAlias) {
if(mustBeInResult) {
throw Message.getSQLException(Message.ORDER_BY_NOT_IN_RESULT, e.getSQL());
}
expressions.add(e);
}
} else {
idx = o.column;
if(idx >= originalLength) {
throw Message.getSQLException(Message.ORDER_BY_NOT_IN_RESULT, "index " + idx);
}
}
index[i] = idx;
int type = o.descending ? SortOrder.DESCENDING : SortOrder.ASCENDING;
if(o.nullsFirst) {
type += SortOrder.NULLS_FIRST;
} else if(o.nullsLast) {
type += SortOrder.NULLS_LAST;
}
sortType[i] = type;
}
return new SortOrder(session.getDatabase(), index, sortType);
}
public void setOffset(Expression offset) {
this.offset = offset;
}
public void setLimit(Expression limit) {
this.limit = limit;
}
public abstract void init() throws SQLException;
public abstract ObjectArray getExpressions();
public abstract double getCost();
public abstract HashSet getTables();
public abstract void setOrder(ObjectArray order);
public abstract void setForUpdate(boolean forUpdate);
public abstract int getColumnCount();
public abstract void mapColumns(ColumnResolver resolver, int level) throws SQLException;
public abstract void setEvaluatable(TableFilter tableFilter, boolean b);
public abstract void addGlobalCondition(Expression expr, int columnId, int comparisonType) throws SQLException;
public abstract void setDistinct(boolean b);
public void setSampleSize(int sampleSize) {
this.sampleSize = sampleSize;
}
public final long getMaxDataModificationId() {
ExpressionVisitor visitor = ExpressionVisitor.get(ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID);
isEverything(visitor);
return visitor.getMaxDataModificationId();
}
public abstract boolean isEverything(ExpressionVisitor visitor);
public final boolean isEverything(int expressionVisitorType) {
ExpressionVisitor visitor = ExpressionVisitor.get(expressionVisitorType);
return isEverything(visitor);
}
}
/*
* 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.command.dml;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.util.ScriptReader;
import org.h2.util.StringUtils;
public class RunScript extends ScriptBase {
private String charset = StringUtils.getDefaultCharset();
public RunScript(Session session) {
super(session);
}
public int update() throws SQLException {
session.getUser().checkAdmin();
int count = 0;
try {
openInput();
Reader reader = new InputStreamReader(in, charset);
ScriptReader r = new ScriptReader(reader);
while(true) {
String sql = r.readStatement();
if(sql == null) {
break;
}
execute(sql);
count++;
}
reader.close();
} catch(IOException e) {
throw Message.convert(e);
} finally {
closeIO();
}
return count;
}
private void execute(String sql) throws SQLException {
try {
Prepared command = session.prepare(sql);
if(command.isQuery()) {
command.query(0);
} else {
command.update();
}
if(session.getAutoCommit()) {
session.commit();
}
} catch(SQLException e) {
throw Message.addSQL(e, sql);
}
}
public void setCharset(String charset) {
this.charset = charset;
}
}
/*
* 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.command.dml;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Comparator;
import org.h2.command.Parser;
import org.h2.constraint.Constraint;
import org.h2.engine.Comment;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Right;
import org.h2.engine.Role;
import org.h2.engine.Session;
import org.h2.engine.Setting;
import org.h2.engine.User;
import org.h2.engine.UserDataType;
import org.h2.expression.ExpressionColumn;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.schema.Constant;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
import org.h2.schema.TriggerObject;
import org.h2.table.Column;
import org.h2.table.PlanItem;
import org.h2.table.Table;
import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueLob;
import org.h2.value.ValueString;
public class Script extends ScriptBase {
private boolean passwords;
private boolean data;
private boolean settings;
private boolean drop;
private LocalResult result;
private byte[] lineSeparator;
private byte[] buffer;
private boolean tempLobTableCreated;
private int nextLobId;
private int lobBlockSize = Integer.MAX_VALUE;
private static final String TEMP_LOB_FILENAME = "system_temp_lob.db";
public Script(Session session) {
super(session);
}
public boolean isQuery() {
return true;
}
// TODO lock all tables for 'script' command
public void setData(boolean data) {
this.data = data;
}
public void setPasswords(boolean passwords) {
this.passwords = passwords;
}
public void setSettings(boolean settings) {
this.settings = settings;
}
public void setLobBlockSize(long blockSize) {
this.lobBlockSize = MathUtils.convertLongToInt(blockSize);
}
public void setDrop(boolean drop) {
this.drop = drop;
}
public LocalResult query(int maxrows) throws SQLException {
session.getUser().checkAdmin();
reset();
try {
ObjectArray cols = new ObjectArray();
cols.add(new ExpressionColumn(session.getDatabase(), null, new Column("SCRIPT", Value.STRING, 0, 0)));
result = new LocalResult(session, cols, 1);
deleteStore();
openOutput();
if(out != null) {
buffer = new byte[Constants.IO_BUFFER_SIZE];
}
Database db = session.getDatabase();
if(settings) {
ObjectArray settings = db.getAllSettings();
for(int i=0; i<settings.size(); i++) {
Setting setting = (Setting) settings.get(i);
add(setting.getCreateSQL(), false);
}
}
if(out != null) {
add("", true);
}
ObjectArray users = db.getAllUsers();
for(int i=0; i<users.size(); i++) {
User user = (User) users.get(i);
add(user.getCreateSQL(passwords, true), false);
}
ObjectArray roles = db.getAllRoles();
for(int i=0; i<roles.size(); i++) {
Role role = (Role) roles.get(i);
add(role.getCreateSQL(), false);
}
ObjectArray schemas = db.getAllSchemas();
for(int i=0; i<schemas.size(); i++) {
Schema schema = (Schema) schemas.get(i);
add(schema.getCreateSQL(), false);
}
ObjectArray datatypes = db.getAllUserDataTypes();
for(int i=0; i<datatypes.size(); i++) {
UserDataType datatype = (UserDataType) datatypes.get(i);
add(datatype.getCreateSQL(), false);
}
ObjectArray constants = db.getAllSchemaObjects(DbObject.CONSTANT);
for(int i=0; i<constants.size(); i++) {
Constant constant = (Constant) constants.get(i);
add(constant.getCreateSQL(), false);
}
ObjectArray functionAliases = db.getAllFunctionAliases();
for(int i=0; i<functionAliases.size(); i++) {
FunctionAlias alias = (FunctionAlias) functionAliases.get(i);
add(alias.getCreateSQL(), false);
}
ObjectArray tables = db.getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
// sort by id, so that views are after tables and views on views after the base views
tables.sort(new Comparator() {
public int compare(Object o1, Object o2) {
Table t1 = (Table)o1;
Table t2 = (Table)o2;
return t1.getId() - t2.getId();
}
});
for(int i=0; i<tables.size(); i++) {
Table table = (Table) tables.get(i);
table.lock(session, false);
String sql = table.getCreateSQL();
if(sql == null) {
// null for metadata tables
continue;
}
String tableType = table.getTableType();
if(drop) {
if(Table.VIEW.equals(tableType)) {
add("DROP VIEW IF EXISTS " + table.getSQL(), false);
} else {
add("DROP TABLE IF EXISTS " + table.getSQL(), false);
}
}
}
ObjectArray sequences = db.getAllSchemaObjects(DbObject.SEQUENCE);
for(int i=0; i<sequences.size(); i++) {
Sequence sequence = (Sequence) sequences.get(i);
if(drop && !sequence.getBelongsToTable()) {
add("DROP SEQUENCE IF EXISTS " + sequence.getSQL(), false);
}
add(sequence.getCreateSQL(), false);
}
for(int i=0; i<tables.size(); i++) {
Table table = (Table) tables.get(i);
table.lock(session, false);
String sql = table.getCreateSQL();
if(sql == null) {
// null for metadata tables
continue;
}
String tableType = table.getTableType();
add(sql, false);
if(data && Table.TABLE.equals(tableType)) {
PlanItem plan = table.getBestPlanItem(session, null);
Index index = plan.getIndex();
Cursor cursor = index.find(session, null, null);
Column[] columns = table.getColumns();
String ins = "INSERT INTO " + table.getSQL() + "(";
for(int j=0; j<columns.length; j++) {
if(j>0) {
ins += ", ";
}
ins += Parser.quoteIdentifier(columns[j].getName());
}
ins += ") VALUES(";
while(cursor.next()) {
Row row = cursor.get();
String s = ins;
for(int j=0; j<row.getColumnCount(); j++) {
if(j>0) {
s += ", ";
}
Value v = row.getValue(j);
if(v.getPrecision() > lobBlockSize) {
int id;
if(v.getType() == Value.CLOB) {
id = writeLobStream((ValueLob)v);
s += "SYSTEM_COMBINE_CLOB("+id+")";
} else if(v.getType() == Value.BLOB) {
id = writeLobStream((ValueLob)v);
s += "SYSTEM_COMBINE_BLOB("+id+")";
} else {
s += v.getSQL();
}
} else {
s += v.getSQL();
}
}
s += ")";
add(s, true);
}
}
ObjectArray indexes = table.getIndexes();
for(int j=0; indexes != null && j<indexes.size(); j++) {
Index index = (Index) indexes.get(j);
if(!index.getIndexType().belongsToConstraint()) {
add(index.getCreateSQL(), false);
}
}
}
if(tempLobTableCreated) {
add("DROP TABLE IF EXISTS SYSTEM_LOB_STREAM", true);
add("CALL SYSTEM_COMBINE_BLOB(-1)", true);
add("DROP ALIAS IF EXISTS SYSTEM_COMBINE_CLOB", true);
add("DROP ALIAS IF EXISTS SYSTEM_COMBINE_BLOB", true);
tempLobTableCreated = false;
}
ObjectArray constraints = db.getAllSchemaObjects(DbObject.CONSTRAINT);
for(int i=0; i<constraints.size(); i++) {
Constraint constraint = (Constraint) constraints.get(i);
add(constraint.getCreateSQLWithoutIndexes(), false);
}
ObjectArray triggers = db.getAllSchemaObjects(DbObject.TRIGGER);
for(int i=0; i<triggers.size(); i++) {
TriggerObject trigger = (TriggerObject) triggers.get(i);
add(trigger.getCreateSQL(), false);
}
ObjectArray rights = db.getAllRights();
for(int i=0; i<rights.size(); i++) {
Right right = (Right) rights.get(i);
add(right.getCreateSQL(), false);
}
ObjectArray comments = db.getAllComments();
for(int i=0; i<comments.size(); i++) {
Comment comment = (Comment) comments.get(i);
add(comment.getCreateSQL(), false);
}
closeIO();
} catch(IOException e) {
throw Message.convert(e);
} finally {
closeIO();
}
result.done();
LocalResult r = result;
reset();
return r;
}
private int writeLobStream(ValueLob v) throws IOException, SQLException {
if(!tempLobTableCreated) {
add("CREATE TABLE IF NOT EXISTS SYSTEM_LOB_STREAM(ID INT, PART INT, CDATA VARCHAR, BDATA BINARY, PRIMARY KEY(ID, PART))", true);
add("CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_CLOB FOR \"" + this.getClass().getName() + ".combineClob\"", true);
add("CREATE ALIAS IF NOT EXISTS SYSTEM_COMBINE_BLOB FOR \"" + this.getClass().getName() + ".combineBlob\"", true);
tempLobTableCreated = true;
}
int id = nextLobId++;
switch(v.getType()) {
case Value.BLOB: {
byte[] bytes = new byte[lobBlockSize];
InputStream in = v.getInputStream();
try {
for(int i=0; ; i++) {
StringBuffer buff = new StringBuffer(lobBlockSize * 2);
buff.append("INSERT INTO SYSTEM_LOB_STREAM VALUES(" + id + ", " + i + ", NULL, '");
int len = IOUtils.readFully(in, bytes, lobBlockSize);
if(len < 0) {
break;
}
buff.append(ByteUtils.convertBytesToString(bytes, len));
buff.append("');");
String sql = buff.toString();
add(sql, true);
}
} finally {
IOUtils.closeSilently(in);
}
break;
}
case Value.CLOB: {
char[] chars = new char[lobBlockSize];
Reader in = v.getReader();
try {
for(int i=0; ; i++) {
StringBuffer buff = new StringBuffer(lobBlockSize * 2);
buff.append("INSERT INTO SYSTEM_LOB_STREAM VALUES(" + id + ", " + i + ", ");
int len = IOUtils.readFully(in, chars, lobBlockSize);
if(len < 0) {
break;
}
buff.append(StringUtils.quoteStringSQL(new String(chars)));
buff.append(", NULL);");
String sql = buff.toString();
add(sql, true);
}
} finally {
IOUtils.closeSilently(in);
}
break;
}
default:
throw Message.getInternalError("type:"+v.getType());
}
return id;
}
// called from the script
public static InputStream combineBlob(Connection conn, int id) throws SQLException, IOException {
if(id < 0) {
FileUtils.delete(TEMP_LOB_FILENAME);
return null;
}
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT BDATA FROM SYSTEM_LOB_STREAM WHERE ID=" + id + " ORDER BY PART");
FileOutputStream out = new FileOutputStream(TEMP_LOB_FILENAME);
while(rs.next()) {
InputStream in = rs.getBinaryStream(1);
IOUtils.copyAndCloseInput(in, out);
}
out.close();
stat.execute("DELETE FROM SYSTEM_LOB_STREAM WHERE ID=" + id);
return new FileInputStream(TEMP_LOB_FILENAME);
}
// called from the script
public static Reader combineClob(Connection conn, int id) throws SQLException, IOException {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT CDATA FROM SYSTEM_LOB_STREAM WHERE ID=" + id + " ORDER BY PART");
FileWriter out = new FileWriter(TEMP_LOB_FILENAME);
while(rs.next()) {
Reader in = rs.getCharacterStream(1);
IOUtils.copyAndCloseInput(in, out);
}
out.close();
stat.execute("DELETE FROM SYSTEM_LOB_STREAM WHERE ID=" + id);
return new FileReader(TEMP_LOB_FILENAME);
}
private void reset() throws SQLException {
result = null;
buffer = null;
lineSeparator = StringUtils.utf8Encode(System.getProperty("line.separator"));
}
private void add(String s, boolean insert) throws SQLException, IOException {
if(s==null) {
return;
}
if (out != null) {
byte[] buff = StringUtils.utf8Encode(s + ";");
int len = MathUtils.roundUp(buff.length + lineSeparator.length, Constants.FILE_BLOCK_SIZE);
buffer = ByteUtils.copy(buff, buffer);
if(len > buffer.length) {
buffer = new byte[len];
}
System.arraycopy(buff, 0, buffer, 0, buff.length);
for(int i=buff.length; i<len - lineSeparator.length; i++) {
buffer[i] = ' ';
}
for(int j=0, i=len - lineSeparator.length; i<len; i++, j++) {
buffer[i] = lineSeparator[j];
}
out.write(buffer, 0, len);
if(!insert) {
Value[] row = new Value[1];
row[0] = ValueString.get(s);
result.addRow(row);
}
} else {
Value[] row = new Value[1];
row[0] = ValueString.get(s);
result.addRow(row);
}
}
}
/*
* 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.command.dml;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.security.SHA256;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.store.FileStoreInputStream;
import org.h2.store.FileStoreOutputStream;
import org.h2.tools.CompressTool;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.value.Value;
public class ScriptBase extends Prepared implements DataHandler {
private String cipher;
private byte[] key;
private FileStore store;
private FileOutputStream outStream;
private FileInputStream inStream;
protected OutputStream out;
protected InputStream in;
protected String fileName;
private String compressionAlgorithm;
public ScriptBase(Session session) {
super(session);
}
public void setCipher(String c) {
cipher = c;
}
protected boolean isEncrypted() {
return cipher != null;
}
public void setPassword(char[] password) {
SHA256 sha = new SHA256();
key = sha.getKeyPasswordHash("script", password);
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public boolean isTransactional() {
return false;
}
protected void deleteStore() throws SQLException {
if(fileName != null) {
FileUtils.delete(fileName);
}
}
private void initStore() throws SQLException {
byte[] magic = Database.getMagic(true);
Database db = session.getDatabase();
// script files are always in text format
store = FileStore.open(db, fileName, magic, cipher, key);
store.init();
}
protected void openOutput() throws SQLException {
if(fileName == null) {
return;
}
if(isEncrypted()) {
initStore();
out = new FileStoreOutputStream(store, this, compressionAlgorithm);
// always use a big buffer, otherwise end-of-block is written a lot
out = new BufferedOutputStream(out, Constants.IO_BUFFER_SIZE_COMPRESS);
} else {
try {
outStream = FileUtils.openFileOutputStream(new File(fileName));
} catch (IOException e) {
throw Message.convert(e);
}
out = new BufferedOutputStream(outStream, Constants.IO_BUFFER_SIZE);
out = CompressTool.wrapOutputStream(out, compressionAlgorithm, Constants.SCRIPT_SQL);
}
}
protected void openInput() throws SQLException {
if(fileName == null) {
return;
}
if(isEncrypted()) {
initStore();
in = new FileStoreInputStream(store, this, compressionAlgorithm != null);
} else {
try {
inStream = new FileInputStream(fileName);
} catch (IOException e) {
throw Message.convert(e);
}
in = new BufferedInputStream(inStream, Constants.IO_BUFFER_SIZE);
in = CompressTool.wrapInputStream(in, compressionAlgorithm, Constants.SCRIPT_SQL);
if(in == null) {
throw Message.getSQLException(Message.FILE_NOT_FOUND_1, Constants.SCRIPT_SQL + " in " + fileName);
}
}
}
protected void closeIO() {
IOUtils.closeSilently(out);
out = null;
IOUtils.closeSilently(in);
in = null;
if(store != null) {
store.closeSilently();
store = null;
}
IOUtils.closeSilently(inStream);
inStream = null;
IOUtils.closeSilently(outStream);
outStream = null;
}
public boolean needRecompile() {
return false;
}
public boolean getTextStorage() {
// script files are always in text format
return true;
}
public String getDatabasePath() {
return null;
}
public FileStore openFile(String name, boolean mustExist) throws SQLException {
return null;
}
public int getChecksum(byte[] data, int start, int end) {
return session.getDatabase().getChecksum(data, start, end);
}
public void checkPowerOff() throws SQLException {
session.getDatabase().checkPowerOff();
}
public void checkWritingAllowed() throws SQLException {
session.getDatabase().checkWritingAllowed();
}
public void freeUpDiskSpace() throws SQLException {
session.getDatabase().checkWritingAllowed();
}
public void handleInvalidChecksum() throws SQLException {
session.getDatabase().handleInvalidChecksum();
}
public int compareTypeSave(Value a, Value b) throws SQLException {
throw Message.getInternalError();
}
public int getMaxLengthInplaceLob() {
return session.getDatabase().getMaxLengthInplaceLob();
}
public int allocateObjectId(boolean b, boolean c) {
return session.getDatabase().allocateObjectId(b, c);
}
public String createTempFile() throws SQLException {
return session.getDatabase().createTempFile();
}
public String getLobCompressionAlgorithm(int type) {
return session.getDatabase().getLobCompressionAlgorithm(type);
}
public void setCompressionAlgorithm(String algorithm) {
this.compressionAlgorithm = algorithm;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.ValueExpression;
import org.h2.expression.Wildcard;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.util.ValueHashMap;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
/**
* visibleColumnCount <= distinctColumnCount <= expressionCount
* Sortable count could include ORDER BY expressions that are not in the select list
* Expression count could include GROUP BY expressions
*
* init
* (maybe additional mapColumns if it's a subquery)
* prepare
*/
public class Select extends Query {
private TableFilter topTableFilter;
private ObjectArray filters = new ObjectArray();
private ObjectArray topFilters = new ObjectArray();
private ObjectArray expressions;
private Expression having;
private Expression condition;
private int visibleColumnCount, distinctColumnCount;
private ObjectArray orderList;
private ObjectArray group;
private int[] groupIndex;
private boolean[] groupByExpression;
private boolean distinct;
private HashMap currentGroup;
private int havingIndex;
private boolean isGroupQuery;
private boolean isForUpdate;
private double cost;
private boolean isQuickQuery;
private boolean isPrepared, checkInit;
private SortOrder sort;
public Select(Session session) {
super(session);
}
public void addTableFilter(TableFilter filter, boolean isTop) {
// TODO compatibility: it seems oracle doesn't check on duplicate aliases; do other databases check it?
// String alias = filter.getAlias();
// if(filterNames.contains(alias)) {
// throw Message.getSQLException(Message.DUPLICATE_TABLE_ALIAS, alias);
// }
// filterNames.add(alias);
filters.add(filter);
if(isTop) {
topFilters.add(filter);
}
}
public ObjectArray getTopFilters() {
return topFilters;
}
public void setExpressions(ObjectArray expressions) {
this.expressions = expressions;
}
public void setGroupQuery() {
isGroupQuery = true;
}
public void setGroupBy(ObjectArray group) {
this.group = group;
}
public HashMap getCurrentGroup() {
return currentGroup;
}
public void setOrder(ObjectArray order) {
orderList = order;
}
public void addCondition(Expression cond) {
if(condition == null) {
condition = cond;
} else {
condition = new ConditionAndOr(ConditionAndOr.AND, cond, condition);
}
}
private void queryGroup(int columnCount, LocalResult result) throws SQLException {
ValueHashMap groups = new ValueHashMap(session.getDatabase());
int rowNumber = 0;
setCurrentRowNumber(0);
ValueArray defaultGroup = ValueArray.get(new Value[0]);
while (topTableFilter.next()) {
checkCancelled();
setCurrentRowNumber(rowNumber+1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Value key;
rowNumber++;
if(groupIndex == null) {
key = defaultGroup;
} else {
Value[] keyValues = new Value[groupIndex.length];
// update group
for (int i = 0; i < groupIndex.length; i++) {
int idx = groupIndex[i];
Expression expr = (Expression) expressions.get(idx);
keyValues[i] = expr.getValue(session);
}
key = ValueArray.get(keyValues);
}
HashMap values = (HashMap)groups.get(key);
if(values == null) {
values = new HashMap();
groups.put(key, values);
}
currentGroup = values;
int len = columnCount;
for (int i = 0; i < len; i++) {
if(groupByExpression == null || !groupByExpression[i]) {
Expression expr = (Expression) expressions.get(i);
expr.updateAggregate(session);
}
}
if(sampleSize > 0 && rowNumber >= sampleSize) {
break;
}
}
}
if(groupIndex == null && groups.size()==0) {
groups.put(defaultGroup, new HashMap());
}
ObjectArray keys = groups.keys();
for (int i=0; i<keys.size(); i++) {
ValueArray key = (ValueArray) keys.get(i);
currentGroup = (HashMap) groups.get(key);
Value[] keyValues = key.getList();
Value[] row = new Value[columnCount];
for (int j=0; groupIndex != null && j<groupIndex.length; j++) {
row[groupIndex[j]] = keyValues[j];
}
for (int j = 0; j < columnCount; j++) {
if(groupByExpression != null && groupByExpression[j]) {
continue;
}
Expression expr = (Expression) expressions.get(j);
row[j] = expr.getValue(session);
}
if (havingIndex > 0) {
Value v = row[havingIndex];
if (v == ValueNull.INSTANCE) {
continue;
}
if (!Boolean.TRUE.equals(v.getBoolean())) {
continue;
}
}
if(columnCount != distinctColumnCount) {
// remove columns so that 'distinct' can filter duplicate rows
Value[] r2 = new Value[distinctColumnCount];
System.arraycopy(row, 0, r2, 0, distinctColumnCount);
row = r2;
}
result.addRow(row);
}
}
private Index getSortIndex() throws SQLException {
if(sort == null) {
return null;
}
int[] sortTypes = sort.getSortTypes();
for(int i=0; i<sortTypes.length; i++) {
if((sortTypes[i] & (SortOrder.DESCENDING | SortOrder.NULLS_LAST)) != 0) {
return null;
}
}
int[] indexes = sort.getIndexes();
ObjectArray sortColumns = new ObjectArray();
for(int i=0; i<indexes.length; i++) {
int idx = indexes[i];
if(idx < 0 || idx >= expressions.size()) {
throw Message.getInvalidValueException("order by", ""+idx);
}
Expression expr = (Expression) expressions.get(idx);
expr = expr.getNonAliasExpression();
if(expr.isConstant()) {
continue;
}
if(!(expr instanceof ExpressionColumn)) {
return null;
}
Column col = ((ExpressionColumn) expr).getColumn();
if(col.getTable() != topTableFilter.getTable()) {
return null;
}
sortColumns.add(col);
}
Column[] sortCols = new Column[sortColumns.size()];
sortColumns.toArray(sortCols);
if(sortCols.length == 0) {
// sort just on constants - can use scan index
return topTableFilter.getTable().getScanIndex(session);
}
ObjectArray list = topTableFilter.getTable().getIndexes();
for(int i=0; list != null && i<list.size(); i++) {
Index index = (Index) list.get(i);
if(index.getCreateSQL() == null) {
// can't use the scan index
continue;
}
if(index.indexType.isHash()) {
continue;
}
Column[] indexCols = index.getColumns();
if(indexCols.length < sortCols.length) {
continue;
}
boolean ok = true;
for(int j=0; j<sortCols.length; j++) {
// the index and the sort order must start with the exact same columns
if(indexCols[j] != sortCols[j]) {
ok = false;
break;
}
}
if(ok) {
return index;
}
}
return null;
}
private void queryFlat(int columnCount, LocalResult result) throws SQLException {
int limitRows;
if(limit == null) {
limitRows = 0;
} else {
limitRows = limit.getValue(session).getInt();
if(offset != null) {
limitRows += offset.getValue(session).getInt();
}
}
int rowNumber = 0;
setCurrentRowNumber(0);
while (topTableFilter.next()) {
checkCancelled();
setCurrentRowNumber(rowNumber+1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Value[] row = new Value[columnCount];
for (int i = 0; i < columnCount; i++) {
Expression expr = (Expression) expressions.get(i);
row[i] = expr.getValue(session);
}
result.addRow(row);
rowNumber++;
if(sort == null && limitRows != 0 && result.getRowCount() >= limitRows) {
break;
}
if(sampleSize > 0 && rowNumber >= sampleSize) {
break;
}
}
}
}
private void queryQuick(int columnCount, LocalResult result) throws SQLException {
Value[] row = new Value[columnCount];
for (int i = 0; i < columnCount; i++) {
Expression expr = (Expression) expressions.get(i);
row[i] = expr.getValue(session);
}
result.addRow(row);
}
public LocalResult queryWithoutCache(int maxrows) throws SQLException {
if(maxrows != 0) {
if(limit != null) {
maxrows = Math.min(limit.getValue(session).getInt(), maxrows);
}
limit = ValueExpression.get(ValueInt.get(maxrows));
}
int columnCount = expressions.size();
LocalResult result = new LocalResult(session, expressions, visibleColumnCount);
result.setSortOrder(sort);
if(distinct) {
result.setDistinct();
}
topTableFilter.startQuery();
topTableFilter.reset();
// TODO lock tables of subqueries
topTableFilter.lock(session, isForUpdate);
if(isQuickQuery) {
queryQuick(columnCount, result);
} else if(isGroupQuery) {
queryGroup(columnCount, result);
} else {
queryFlat(columnCount, result);
}
if(offset != null) {
result.setOffset(offset.getValue(session).getInt());
}
if(limit != null) {
result.setLimit(limit.getValue(session).getInt());
}
result.done();
return result;
}
private void expandColumnList() throws SQLException {
// TODO this works: select distinct count(*) from system_columns group by table
for (int i = 0; i < expressions.size(); i++) {
Expression expr = (Expression) expressions.get(i);
if (!expr.isWildcard()) {
continue;
}
String tableAlias = expr.getTableAlias();
if (tableAlias == null) {
int temp = i;
expressions.remove(i);
for (int j = 0; j < filters.size(); j++) {
TableFilter filter = (TableFilter) filters.get(j);
Wildcard c2 = new Wildcard(filter.getTable().getSchema().getName(), filter.getTableAlias());
expressions.add(i++, c2);
}
i = temp - 1;
} else {
TableFilter filter = null;
for (int j = 0; j < filters.size(); j++) {
TableFilter f = (TableFilter) filters.get(j);
if (tableAlias.equals(f.getTableAlias())) {
filter = f;
break;
}
}
if (filter == null) {
throw Message.getSQLException(Message.TABLE_OR_VIEW_NOT_FOUND_1, tableAlias);
}
Table t = filter.getTable();
String alias = filter.getTableAlias();
expressions.remove(i);
Column[] columns = t.getColumns();
for (int j = 0; j < columns.length; j++) {
Column c = columns[j];
ExpressionColumn ec = new ExpressionColumn(session.getDatabase(), this, null, alias, c.getName());
expressions.add(i++, ec);
}
i--;
}
}
}
public void init() throws SQLException {
if(Constants.CHECK && checkInit) {
throw Message.getInternalError();
}
checkInit = true;
expandColumnList();
visibleColumnCount = expressions.size();
if(orderList != null) {
sort = initOrder(expressions, orderList, visibleColumnCount, distinct);
orderList = null;
}
distinctColumnCount = expressions.size();
if(having != null) {
expressions.add(having);
havingIndex = expressions.size()-1;
having = null;
} else {
havingIndex = -1;
}
// first visible columns, then order by, then having, and then group by at the end
if(group != null) {
groupIndex = new int[group.size()];
ObjectArray expressionSQL = new ObjectArray();
for(int i=0; i<expressions.size(); i++) {
Expression expr = (Expression) expressions.get(i);
expr = expr.getNonAliasExpression();
String sql = expr.getSQL();
expressionSQL.add(sql);
}
for(int i=0; i<group.size(); i++) {
Expression expr = (Expression) group.get(i);
String sql = expr.getSQL();
int found = -1;
for(int j=0; j<expressionSQL.size(); j++) {
String s2 = (String) expressionSQL.get(j);
if(s2.equals(sql)) {
found = j;
break;
}
}
if(found < 0) {
int index = expressions.size();
groupIndex[i] = index;
expressions.add(expr);
} else {
groupIndex[i] = found;
}
}
groupByExpression = new boolean[expressions.size()];
for(int i=0; i<groupIndex.length; i++) {
groupByExpression[groupIndex[i]] = true;
}
group = null;
}
// map columns in select list and condition
for (int i = 0; i < filters.size(); i++) {
TableFilter f = (TableFilter) filters.get(i);
for (int j = 0; j < expressions.size(); j++) {
Expression expr = (Expression) expressions.get(j);
expr.mapColumns(f, 0);
}
if (condition != null) {
condition.mapColumns(f, 0);
}
}
}
public void prepare() throws SQLException {
if(isPrepared) {
// TODO optimization: sometimes a subquery is prepared twice. why?
return;
}
if(Constants.CHECK && !checkInit) {
throw Message.getInternalError("already prepared");
}
isPrepared = true;
for(int i=0; i<expressions.size(); i++) {
Expression e = (Expression) expressions.get(i);
expressions.set(i, e.optimize(session));
}
if(condition != null) {
condition = condition.optimize(session);
for (int j = 0; j < filters.size(); j++) {
TableFilter f = (TableFilter) filters.get(j);
condition.createIndexConditions(f);
}
}
if(condition == null && isGroupQuery && groupIndex == null && havingIndex<0 && filters.size()==1) {
ExpressionVisitor optimizable = ExpressionVisitor.get(ExpressionVisitor.OPTIMIZABLE_MIN_MAX_COUNT_ALL);
optimizable.table = ((TableFilter)filters.get(0)).getTable();
isQuickQuery = isEverything(optimizable);
}
cost = preparePlan();
if(sort != null && !isQuickQuery && !isGroupQuery) {
Index index = getSortIndex();
Index current = topTableFilter.getIndex();
if(index != null && (current.indexType.isScan() || current == index)) {
topTableFilter.setIndex(index);
sort = null;
}
}
}
public double getCost() {
return cost;
}
public HashSet getTables() {
HashSet set = new HashSet();
for(int i=0; i<filters.size(); i++) {
TableFilter filter = (TableFilter) filters.get(i);
set.add(filter.getTable());
}
return set;
}
private double preparePlan() throws SQLException {
TableFilter[] topArray = new TableFilter[topFilters.size()];
topFilters.toArray(topArray);
for(int i=0; i<topArray.length; i++) {
topArray[i].setFullCondition(condition);
}
Optimizer optimizer = new Optimizer(topArray, condition, session);
optimizer.optimize();
topTableFilter = optimizer.getTopFilter();
double cost = optimizer.getCost();
TableFilter f = topTableFilter;
while(f != null) {
f.setEvaluatable(f, true);
if(condition != null) {
condition.setEvaluatable(f, true);
}
Expression on = f.getJoinCondition();
if(on != null) {
if(!on.isEverything(ExpressionVisitor.EVALUATABLE)) {
f.removeJoinCondition();
addCondition(on);
}
}
on = f.getFilterCondition();
if(on != null) {
if(!on.isEverything(ExpressionVisitor.EVALUATABLE)) {
f.removeFilterCondition();
addCondition(on);
}
}
f = f.getJoin();
}
topTableFilter.prepare();
return cost;
}
public String getPlan() {
StringBuffer buff = new StringBuffer();
Expression[] exprList = new Expression[expressions.size()];
expressions.toArray(exprList);
buff.append("SELECT ");
if(distinct) {
buff.append("DISTINCT ");
}
for(int i=0; i<visibleColumnCount; i++) {
if(i>0) {
buff.append(", ");
}
Expression expr = exprList[i];
buff.append(StringUtils.unEnclose(expr.getSQL()));
}
buff.append("\nFROM ");
TableFilter filter = topTableFilter;
boolean join = false;
int id=0;
do {
if(id > 0) {
buff.append('\n');
}
id++;
buff.append(filter.getPlanSQL(join));
join = true;
filter = filter.getJoin();
} while(filter != null);
if(condition != null) {
buff.append("\nWHERE " + StringUtils.unEnclose(condition.getSQL()));
}
if(groupIndex != null) {
buff.append("\nGROUP BY ");
for(int i=0; i<groupIndex.length; i++) {
Expression gro = exprList[groupIndex[i]];
if(i>0) {
buff.append(", ");
}
buff.append(StringUtils.unEnclose(gro.getSQL()));
}
}
if(havingIndex >= 0) {
Expression hav = exprList[havingIndex];
buff.append("\nHAVING " + StringUtils.unEnclose(hav.getSQL()));
}
if(sort != null) {
buff.append("\nORDER BY ");
buff.append(sort.getSQL(exprList, visibleColumnCount));
}
if(limit != null) {
buff.append("\nLIMIT ");
buff.append(StringUtils.unEnclose(limit.getSQL()));
if(offset != null) {
buff.append(" OFFSET ");
buff.append(StringUtils.unEnclose(offset.getSQL()));
}
}
if(isForUpdate) {
buff.append("\nFOR UPDATE");
}
return buff.toString();
}
public void setDistinct(boolean b) {
distinct = b;
}
public void setHaving(Expression having) {
this.having = having;
}
public int getColumnCount() {
return visibleColumnCount;
}
public TableFilter getTopTableFilter() {
return topTableFilter;
}
public ObjectArray getExpressions() {
return expressions;
}
public Expression getCondition() {
return condition;
}
public boolean isDistinct() {
return distinct;
}
public ObjectArray getGroupBy() {
return group;
}
public void setForUpdate(boolean b) {
this.isForUpdate = b;
}
public void mapColumns(ColumnResolver resolver, int level) throws SQLException {
for(int i=0; i<expressions.size(); i++) {
Expression e = (Expression) expressions.get(i);
e.mapColumns(resolver, level);
}
if(condition != null) {
condition.mapColumns(resolver, level);
}
}
public void setEvaluatable(TableFilter tableFilter, boolean b) {
for(int i=0; i<expressions.size(); i++) {
Expression e = (Expression) expressions.get(i);
e.setEvaluatable(tableFilter, b);
}
if(condition != null) {
condition.setEvaluatable(tableFilter, b);
}
}
public boolean isQuickQuery() {
return isQuickQuery;
}
public void addGlobalCondition(Expression expr, int columnId, int comparisonType) throws SQLException {
Expression col = (Expression)expressions.get(columnId);
Expression comp = new Comparison(session, comparisonType, col, expr);
comp = comp.optimize(session);
if(isGroupQuery) {
if(having == null) {
having = comp;
} else {
having = new ConditionAndOr(ConditionAndOr.AND, having, comp);
}
} else {
if(condition == null) {
condition = comp;
} else {
condition = new ConditionAndOr(ConditionAndOr.AND, condition, comp);
}
}
}
public boolean isEverything(ExpressionVisitor visitor) {
if(visitor.type == ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID) {
for(int i=0; i<filters.size(); i++) {
TableFilter f = (TableFilter) filters.get(i);
long m = f.getTable().getMaxDataModificationId();
visitor.addDataModificationId(m);
}
}
if(visitor.type == ExpressionVisitor.EVALUATABLE) {
if(!Constants.OPTIMIZE_EVALUATABLE_SUBQUERIES) {
return false;
}
}
if(visitor.type != ExpressionVisitor.EVALUATABLE) {
visitor.queryLevel(1);
}
boolean result = true;
for(int i=0; i<expressions.size(); i++) {
Expression e = (Expression) expressions.get(i);
if(!e.isEverything(visitor)) {
result = false;
break;
}
}
if(result && condition != null && !condition.isEverything(visitor)) {
result = false;
}
if(result && having != null && !having.isEverything(visitor)) {
result = false;
}
if(visitor.type != ExpressionVisitor.EVALUATABLE) {
visitor.queryLevel(-1);
}
return result;
}
}
/*
* 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.command.dml;
import org.h2.expression.Expression;
/**
* @author Thomas
*/
public class SelectOrderBy {
public Expression expression;
public int column;
public boolean descending;
public boolean nullsFirst;
public boolean nullsLast;
}
/*
* 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.command.dml;
import java.sql.SQLException;
import java.util.HashSet;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.ValueExpression;
import org.h2.jdbc.JdbcSQLException;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueInt;
public class SelectUnion extends Query {
public static final int UNION=0, UNION_ALL=1, EXCEPT=2, INTERSECT=3;
private int unionType;
private Query left, right;
private ObjectArray expressions;
private ObjectArray orderList;
private SortOrder sort;
private boolean distinct;
private boolean checkPrepared, checkInit;
private boolean isForUpdate;
public SelectUnion(Session session, Query query) {
super(session);
this.left = query;
}
public void setUnionType(int type) {
this.unionType = type;
}
public void setRight(Query select) throws JdbcSQLException {
right = select;
}
public void setSQL(String sql) {
this.sql = sql;
}
public void setOrder(ObjectArray order) {
orderList = order;
}
private Value[] convert(Value[] values) throws SQLException {
for(int i=0; i<values.length; i++) {
Expression e = (Expression) expressions.get(i);
values[i] = values[i].convertTo(e.getType());
}
return values;
}
public LocalResult queryWithoutCache(int maxrows) throws SQLException {
if(maxrows != 0) {
if(limit != null) {
maxrows = Math.min(limit.getValue(session).getInt(), maxrows);
}
limit = ValueExpression.get(ValueInt.get(maxrows));
}
ObjectArray expressions = left.getExpressions();
int columnCount = left.getColumnCount();
LocalResult result = new LocalResult(session, expressions, columnCount);
result.setSortOrder(sort);
if(distinct) {
left.setDistinct(true);
right.setDistinct(true);
result.setDistinct();
}
switch(unionType) {
case UNION:
left.setDistinct(true);
right.setDistinct(true);
result.setDistinct();
break;
case UNION_ALL:
break;
case EXCEPT:
result.setDistinct();
// fall through
case INTERSECT: {
left.setDistinct(true);
right.setDistinct(true);
break;
}
default:
throw Message.getInternalError("type="+unionType);
}
LocalResult l = left.query(0);
LocalResult r = right.query(0);
l.reset();
r.reset();
switch(unionType) {
case UNION_ALL:
case UNION: {
while(l.next()) {
result.addRow(convert(l.currentRow()));
}
while(r.next()) {
result.addRow(convert(r.currentRow()));
}
break;
}
case EXCEPT: {
while(l.next()) {
result.addRow(convert(l.currentRow()));
}
while(r.next()) {
result.removeDistinct(convert(r.currentRow()));
}
break;
}
case INTERSECT: {
LocalResult temp = new LocalResult(session, expressions, columnCount);
temp.setDistinct();
while(l.next()) {
temp.addRow(convert(l.currentRow()));
}
while(r.next()) {
Value[] values = convert(r.currentRow());
if(temp.containsDistinct(values)) {
result.addRow(values);
}
}
break;
}
default:
throw Message.getInternalError("type="+unionType);
}
if(offset != null) {
result.setOffset(offset.getValue(session).getInt());
}
if(limit != null) {
result.setLimit(limit.getValue(session).getInt());
}
result.done();
return result;
}
public void init() throws SQLException {
if(Constants.CHECK && checkInit) {
throw Message.getInternalError();
}
checkInit = true;
left.init();
right.init();
int len = left.getColumnCount();
if(len != right.getColumnCount()) {
throw Message.getSQLException(Message.COLUMN_COUNT_DOES_NOT_MATCH);
}
}
public void prepare() throws SQLException {
if(Constants.CHECK && (checkPrepared || !checkInit)) {
throw Message.getInternalError("already prepared");
}
checkPrepared = true;
left.prepare();
right.prepare();
ObjectArray le = left.getExpressions();
ObjectArray re = right.getExpressions();
expressions = new ObjectArray();
int len = left.getColumnCount();
for(int i=0; i<len; i++) {
Expression l = (Expression)le.get(i);
Expression r = (Expression)re.get(i);
int type = Value.getHigherOrder(l.getType(), r.getType());
long prec = Math.max(l.getPrecision(), r.getPrecision());
int scale = Math.max(l.getScale(), r.getScale());
Column col = new Column(l.getAlias(), type, prec, scale);
Expression e = new ExpressionColumn(session.getDatabase(), null, col);
expressions.add(e);
}
if(orderList != null) {
sort = initOrder(expressions, orderList, getColumnCount(), true);
orderList = null;
}
}
public double getCost() {
return left.getCost() + right.getCost();
}
public HashSet getTables() {
HashSet set = left.getTables();
set.addAll(right.getTables());
return set;
}
public void setDistinct(boolean b) {
distinct = b;
}
public ObjectArray getExpressions() {
return expressions;
}
public void setForUpdate(boolean forUpdate) {
left.setForUpdate(forUpdate);
right.setForUpdate(forUpdate);
isForUpdate = forUpdate;
}
public int getColumnCount() {
return left.getColumnCount();
}
public void mapColumns(ColumnResolver resolver, int level) throws SQLException {
left.mapColumns(resolver, level);
right.mapColumns(resolver, level);
}
public void setEvaluatable(TableFilter tableFilter, boolean b) {
left.setEvaluatable(tableFilter, b);
right.setEvaluatable(tableFilter, b);
}
public void addGlobalCondition(Expression expr, int columnId, int comparisonType) throws SQLException {
switch(unionType) {
case UNION_ALL:
case UNION:
case INTERSECT: {
left.addGlobalCondition(expr, columnId, comparisonType);
right.addGlobalCondition(expr, columnId, comparisonType);
break;
}
case EXCEPT: {
left.addGlobalCondition(expr, columnId, comparisonType);
break;
}
default:
throw Message.getInternalError("type="+unionType);
}
}
public String getPlan() {
StringBuffer buff = new StringBuffer();
buff.append('(');
buff.append(left.getPlan());
buff.append(") ");
switch(unionType) {
case UNION_ALL:
buff.append("UNION ALL ");
break;
case UNION:
buff.append("UNION ");
break;
case INTERSECT:
buff.append("INTERSECT ");
break;
case EXCEPT:
buff.append("EXCEPT ");
break;
default:
throw Message.getInternalError("type="+unionType);
}
buff.append('(');
buff.append(right.getPlan());
buff.append(')');
Expression[] exprList = new Expression[expressions.size()];
expressions.toArray(exprList);
if(sort != null) {
buff.append(" ORDER BY ");
buff.append(sort.getSQL(exprList, exprList.length));
}
// TODO refactoring: limit and order by could be in Query (now in SelectUnion and in Select)
if(limit != null) {
buff.append(" LIMIT ");
buff.append(StringUtils.unEnclose(limit.getSQL()));
if(offset != null) {
buff.append(" OFFSET ");
buff.append(StringUtils.unEnclose(offset.getSQL()));
}
}
if(isForUpdate) {
buff.append(" FOR UPDATE");
}
return buff.toString();
}
public boolean isEverything(ExpressionVisitor visitor) {
return left.isEverything(visitor) && right.isEverything(visitor);
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import java.text.Collator;
import org.h2.command.Prepared;
import org.h2.compress.Compresser;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.Mode;
import org.h2.engine.Session;
import org.h2.engine.Setting;
import org.h2.expression.Expression;
import org.h2.expression.ValueExpression;
import org.h2.message.Message;
import org.h2.schema.Schema;
import org.h2.table.Table;
import org.h2.tools.CompressTool;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.CompareMode;
import org.h2.value.ValueInt;
/**
* @author Thomas
*/
public class Set extends Prepared {
private int type;
private Expression expression;
private String stringValue;
public Set(Session session, int type) {
super(session);
this.type = type;
}
public void setString(String v) {
this.stringValue = v;
}
public boolean isTransactional() {
return false;
}
public int update() throws SQLException {
// Value v = expr.getValue();
Database database = session.getDatabase();
String name = SetTypes.getTypeName(type);
switch (type) {
case SetTypes.MAX_LOG_SIZE:
session.getUser().checkAdmin();
session.getDatabase().setMaxLogSize((long)getIntValue() * 1024 * 1024);
addOrUpdateSetting(name, null, getIntValue());
break;
case SetTypes.LOCK_TIMEOUT:
session.setLockTimeout(getIntValue());
break;
case SetTypes.LOCK_MODE:
session.getUser().checkAdmin();
database.setLockMode(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
case SetTypes.DEFAULT_LOCK_TIMEOUT:
session.getUser().checkAdmin();
addOrUpdateSetting(name, null, getIntValue());
break;
case SetTypes.DEFAULT_TABLE_TYPE:
session.getUser().checkAdmin();
addOrUpdateSetting(name, null, getIntValue());
break;
case SetTypes.TRACE_LEVEL_SYSTEM_OUT:
session.getUser().checkAdmin();
database.getTraceSystem().setLevelSystemOut(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
case SetTypes.TRACE_LEVEL_FILE:
session.getUser().checkAdmin();
database.getTraceSystem().setLevelFile(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
case SetTypes.TRACE_MAX_FILE_SIZE: {
session.getUser().checkAdmin();
int size = getIntValue() * 1024 * 1024;
database.getTraceSystem().setMaxFileSize(size);
addOrUpdateSetting(name, null, getIntValue());
break;
}
case SetTypes.CACHE_SIZE:
session.getUser().checkAdmin();
database.setCacheSize(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
case SetTypes.MODE:
session.getUser().checkAdmin();
Mode mode = Mode.getMode(stringValue);
if(mode == null) {
throw Message.getSQLException(Message.UNKNOWN_MODE_1, stringValue);
}
Mode.setCurrentMode(mode);
break;
case SetTypes.COLLATION: {
session.getUser().checkAdmin();
ObjectArray array = database.getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
for(int i=0; i<array.size(); i++) {
Table table = (Table) array.get(i);
if(table.getCreateSQL() != null) {
throw Message.getSQLException(Message.COLLATION_CHANGE_WITH_DATA_TABLE_1, table.getSQL());
}
}
CompareMode compareMode;
StringBuffer buff = new StringBuffer(stringValue);
if(stringValue.equals(CompareMode.OFF)) {
compareMode = new CompareMode(null, null);
} else {
Collator coll = CompareMode.getCollator(stringValue);
compareMode = new CompareMode(coll, stringValue);
buff.append(" STRENGTH ");
if(getIntValue() == Collator.IDENTICAL) {
buff.append("IDENTICAL");
} else if(getIntValue() == Collator.PRIMARY) {
buff.append("PRIMARY");
} else if(getIntValue() == Collator.SECONDARY) {
buff.append("SECONDARY");
} else if(getIntValue() == Collator.TERTIARY) {
buff.append("TERTIARY");
}
coll.setStrength(getIntValue());
}
addOrUpdateSetting(name, buff.toString(), 0);
database.setCompareMode(compareMode);
break;
}
case SetTypes.IGNORECASE:
session.getUser().checkAdmin();
session.getDatabase().setIgnoreCase(getIntValue() == 1);
addOrUpdateSetting(name, null, getIntValue());
break;
case SetTypes.CLUSTER: {
session.getUser().checkAdmin();
database.setCluster(StringUtils.quoteStringSQL(stringValue));
addOrUpdateSetting(name, StringUtils.quoteStringSQL(stringValue), 0);
break;
}
case SetTypes.WRITE_DELAY: {
session.getUser().checkAdmin();
database.setWriteDelay(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
}
case SetTypes.DATABASE_EVENT_LISTENER: {
session.getUser().checkAdmin();
database.setEventListener(stringValue);
break;
}
case SetTypes.MAX_MEMORY_ROWS: {
session.getUser().checkAdmin();
database.setMaxMemoryRows(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
}
case SetTypes.ASSERT: {
session.getUser().checkAdmin();
Constants.CHECK = (getIntValue() == 1);
break;
}
case SetTypes.MULTI_THREADED: {
session.getUser().checkAdmin();
Constants.MULTI_THREADED_KERNEL = (getIntValue() == 1);
break;
}
case SetTypes.DB_CLOSE_DELAY: {
session.getUser().checkAdmin();
database.setCloseDelay(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
}
case SetTypes.LOG: {
int value = getIntValue();
if(value<0 || value>2) {
throw Message.getInvalidValueException(""+getIntValue(), "LOG");
}
if(value==0) {
session.getUser().checkAdmin();
}
database.setLog(value);
break;
}
case SetTypes.THROTTLE: {
if(getIntValue() < 0) {
throw Message.getInvalidValueException(""+getIntValue(), "THROTTLE");
}
session.setThrottle(getIntValue());
break;
}
case SetTypes.MAX_MEMORY_UNDO: {
if(getIntValue() < 0) {
throw Message.getInvalidValueException(""+getIntValue(), "MAX_MEMORY_UNDO");
}
session.getUser().checkAdmin();
database.setMaxMemoryUndo(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
}
case SetTypes.MAX_LENGTH_INPLACE_LOB: {
if(getIntValue() < 0) {
throw Message.getInvalidValueException(""+getIntValue(), "MAX_LENGTH_INPLACE_LOB");
}
session.getUser().checkAdmin();
database.setMaxLengthInplaceLob(getIntValue());
addOrUpdateSetting(name, null, getIntValue());
break;
}
case SetTypes.COMPRESS_LOB: {
session.getUser().checkAdmin();
int algo = CompressTool.getInstance().getCompressAlgorithm(stringValue);
database.setLobCompressionAlgorithm(algo == Compresser.NO ? null : stringValue);
addOrUpdateSetting(name, stringValue, 0);
break;
}
case SetTypes.ALLOW_LITERALS: {
session.getUser().checkAdmin();
int value = getIntValue();
if(value < 0 || value > 2) {
throw Message.getInvalidValueException(""+getIntValue(), "ALLOW_LITERALS");
}
database.setAllowLiterals(value);
addOrUpdateSetting(name, null, value);
break;
}
case SetTypes.SCHEMA: {
Schema schema = database.getSchema(stringValue);
session.setCurrentSchema(schema);
break;
}
case SetTypes.OPTIMIZE_REUSE_RESULTS: {
session.getUser().checkAdmin();
database.setOptimizeReuseResults(getIntValue() != 0);
break;
}
default:
throw Message.getInternalError("type="+type);
}
return 0;
}
private int getIntValue() throws SQLException {
expression = expression.optimize(session);
return expression.getValue(session).getInt();
}
public void setInt(int value) {
this.expression = ValueExpression.get(ValueInt.get(value));
}
public void setExpression(Expression expression) {
this.expression = expression;
}
private void addOrUpdateSetting(String name, String s, int v) throws SQLException {
Database database = session.getDatabase();
if(database.getReadOnly()) {
return;
}
Setting setting = database.findSetting(name);
boolean addNew = false;
if(setting == null) {
addNew = true;
int id = getObjectId(false, true);
setting = new Setting(database, id, name);
}
if(s != null) {
if(!addNew && setting.getStringValue().equals(s)) {
return;
}
setting.setStringValue(s);
} else {
if(!addNew && setting.getIntValue() == v) {
return;
}
setting.setIntValue(v);
}
if(addNew) {
database.addDatabaseObject(session, setting);
} else {
database.update(session, setting);
}
}
public boolean needRecompile() {
return false;
}
}
/*
* 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.command.dml;
import org.h2.util.ObjectArray;
public class SetTypes {
public static final int IGNORECASE = 1, MAX_LOG_SIZE = 2, MODE = 3, READONLY = 4, LOCK_TIMEOUT = 5;
public static final int DEFAULT_LOCK_TIMEOUT = 6, DEFAULT_TABLE_TYPE = 7;
public static final int CACHE_SIZE = 8;
public static final int TRACE_LEVEL_SYSTEM_OUT = 9, TRACE_LEVEL_FILE = 10, TRACE_MAX_FILE_SIZE = 11;
public static final int COLLATION = 12, CLUSTER = 13, WRITE_DELAY = 14, DATABASE_EVENT_LISTENER = 15;
public static final int MAX_MEMORY_ROWS = 16, LOCK_MODE = 17, ASSERT = 18, DB_CLOSE_DELAY = 19;
public static final int LOG = 20, THROTTLE = 21, MAX_MEMORY_UNDO = 22, MAX_LENGTH_INPLACE_LOB = 23;
public static final int COMPRESS_LOB = 24, ALLOW_LITERALS = 25, MULTI_THREADED = 26, SCHEMA = 27;
public static final int OPTIMIZE_REUSE_RESULTS = 28;
private static ObjectArray types = new ObjectArray();
static {
setType(IGNORECASE, "IGNORECASE");
setType(MAX_LOG_SIZE, "MAX_LOG_SIZE");
setType(MODE, "MODE");
setType(READONLY, "READONLY");
setType(LOCK_TIMEOUT, "LOCK_TIMEOUT");
setType(DEFAULT_LOCK_TIMEOUT, "DEFAULT_LOCK_TIMEOUT");
setType(DEFAULT_TABLE_TYPE, "DEFAULT_TABLE_TYPE");
setType(CACHE_SIZE, "CACHE_SIZE");
setType(TRACE_LEVEL_SYSTEM_OUT, "TRACE_LEVEL_SYSTEM_OUT");
setType(TRACE_LEVEL_FILE, "TRACE_LEVEL_FILE");
setType(TRACE_MAX_FILE_SIZE, "TRACE_MAX_FILE_SIZE");
setType(COLLATION, "COLLATION");
setType(CLUSTER, "CLUSTER");
setType(WRITE_DELAY, "WRITE_DELAY");
setType(DATABASE_EVENT_LISTENER, "DATABASE_EVENT_LISTENER");
setType(MAX_MEMORY_ROWS, "MAX_MEMORY_ROWS");
setType(LOCK_MODE, "LOCK_MODE");
setType(ASSERT, "ASSERT");
setType(DB_CLOSE_DELAY, "DB_CLOSE_DELAY");
setType(LOG, "LOG");
setType(THROTTLE, "THROTTLE");
setType(MAX_MEMORY_UNDO, "MAX_MEMORY_UNDO");
setType(MAX_LENGTH_INPLACE_LOB, "MAX_LENGTH_INPLACE_LOB");
setType(COMPRESS_LOB, "COMPRESS_LOB");
setType(ALLOW_LITERALS, "ALLOW_LITERALS");
setType(MULTI_THREADED, "MULTI_THREADED");
setType(SCHEMA, "SCHEMA");
setType(OPTIMIZE_REUSE_RESULTS, "OPTIMIZE_REUSE_RESULTS");
}
private static void setType(int type, String name) {
while(types.size()<=type) {
types.add(null);
}
types.set(type, name);
}
public static int getType(String name) {
for(int i=0; i<types.size(); i++) {
if(name.equals(types.get(i))) {
return i;
}
}
return -1;
}
public static ObjectArray getSettings() {
return types;
}
public static String getTypeName(int type) {
return (String)types.get(type);
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
/**
* @author Thomas
*/
public class TransactionCommand extends Prepared {
public static final int AUTOCOMMIT_TRUE = 1;
public static final int AUTOCOMMIT_FALSE = 2;
public static final int COMMIT = 3;
public static final int ROLLBACK = 4;
public static final int CHECKPOINT = 5;
public static final int SAVEPOINT = 6;
public static final int ROLLBACK_TO_SAVEPOINT = 7;
public static final int CHECKPOINT_SYNC = 8;
public static final int PREPARE_COMMIT = 9;
public static final int COMMIT_TRANSACTION = 10;
public static final int ROLLBACK_TRANSACTION = 11;
public static final int SHUTDOWN = 12;
public static final int SHUTDOWN_IMMEDIATELY = 13;
private int type;
private String savepointName;
private String transactionName;
public TransactionCommand(Session session, int type) {
super(session);
this.type = type;
}
public void setSavepointName(String name) {
this.savepointName = name;
}
public int update() throws SQLException {
switch (type) {
case AUTOCOMMIT_TRUE:
session.setAutoCommit(true);
break;
case AUTOCOMMIT_FALSE:
session.setAutoCommit(false);
break;
case COMMIT:
session.commit();
break;
case ROLLBACK:
session.rollback();
break;
case CHECKPOINT:
session.getUser().checkAdmin();
session.getDatabase().getLog().checkpoint();
break;
case SAVEPOINT:
session.addSavepoint(savepointName);
break;
case ROLLBACK_TO_SAVEPOINT:
session.rollbackToSavepoint(savepointName);
break;
case CHECKPOINT_SYNC:
session.getUser().checkAdmin();
session.getDatabase().sync();
break;
case PREPARE_COMMIT:
session.prepareCommit(transactionName);
break;
case COMMIT_TRANSACTION:
session.getUser().checkAdmin();
session.setPreparedTransaction(transactionName, true);
break;
case ROLLBACK_TRANSACTION:
session.getUser().checkAdmin();
session.setPreparedTransaction(transactionName, false);
break;
case SHUTDOWN_IMMEDIATELY:
session.getUser().checkAdmin();
session.getDatabase().setPowerOffCount(1);
try {
session.getDatabase().checkPowerOff();
} catch(SQLException e) {
// ignore
}
break;
case SHUTDOWN: {
session.getUser().checkAdmin();
session.commit();
// close the database, but don't update the persistent setting
session.getDatabase().setCloseDelay(0);
Database db = session.getDatabase();
Session[] sessions = db.getSessions();
for(int i=0; i<sessions.length; i++) {
Session s = sessions[i];
synchronized(s) {
s.rollback();
}
if(s != session) {
s.close();
}
}
db.getLog().checkpoint();
session.close();
break;
}
default:
throw Message.getInternalError("type=" + type);
}
return 0;
}
public boolean isTransactional() {
return true;
}
public boolean needRecompile() {
return false;
}
public void setTransactionName(String string) {
this.transactionName = string;
}
}
/*
* 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.command.dml;
import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.store.UndoLogRecord;
import org.h2.table.Column;
import org.h2.table.PlanItem;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
/**
* @author Thomas
*/
public class Update extends Prepared {
private Expression condition;
private TableFilter tableFilter;
private Expression[] expressions;
public Update(Session session) {
super(session);
}
public void setTableFilter(TableFilter tableFilter) {
this.tableFilter = tableFilter;
Table table = tableFilter.getTable();
expressions = new Expression[table.getColumns().length];
}
public void setCondition(Expression condition) {
this.condition = condition;
}
public void setAssignment(Column column, Expression expression)
throws SQLException {
int id = column.getColumnId();
if (expressions[id] != null) {
throw Message.getSQLException(Message.DUPLICATE_COLUMN_NAME_1, column
.getName());
}
expressions[id] = expression;
}
public int update() throws SQLException {
tableFilter.startQuery();
tableFilter.reset();
// TODO optimization: update old / new list: maybe use a linked list (to avoid array allocation)
ObjectArray oldRows = new ObjectArray();
ObjectArray newRows = new ObjectArray();
Table table = tableFilter.getTable();
session.getUser().checkRight(table, Right.UPDATE);
table.fireBefore(session);
table.lock(session, true);
int columnCount = table.getColumns().length;
// get the old rows, compute the new rows
setCurrentRowNumber(0);
while (tableFilter.next()) {
checkCancelled();
setCurrentRowNumber(oldRows.size()+1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row oldRow = tableFilter.get();
oldRows.add(oldRow);
Row newRow = table.getTemplateRow();
for (int i = 0; i < columnCount; i++) {
Expression newExpr = expressions[i];
Value newValue;
if (newExpr == null) {
newValue = oldRow.getValue(i);
} else {
Column column = table.getColumn(i);
newValue = newExpr.getValue(session).convertTo(column.getType());
}
newRow.setValue(i, newValue);
}
table.validateConvertUpdateSequence(session, newRow);
newRows.add(newRow);
}
}
// TODO performance: loop only if required
// TODO self referencing referential integrity constraints don't work if update is multi-row and 'inversed' the condition! probably need multi-row triggers with 'deleted' and 'inserted' at the same time. anyway good for sql compatibility
// TODO update in-place (but if the position changes, we need to update all indexes)
// before row triggers
if(table.fireRow()) {
for (int i = 0; i < oldRows.size(); i++) {
checkCancelled();
Row o = (Row) oldRows.get(i);
Row n = (Row) newRows.get(i);
table.fireBeforeRow(session, o, n);
}
}
// remove the old rows
for (int i = 0; i < oldRows.size(); i++) {
checkCancelled();
Row o = (Row) oldRows.get(i);
table.removeRow(session, o);
session.log(new UndoLogRecord(table, UndoLogRecord.DELETE, o));
}
// add the new rows
for (int i=0; i < newRows.size(); i++) {
checkCancelled();
Row n = (Row) newRows.get(i);
table.addRow(session, n);
session.log(new UndoLogRecord(table, UndoLogRecord.INSERT, n));
}
if(table.fireRow()) {
for (int i=0; i < newRows.size(); i++) {
checkCancelled();
Row n = (Row) newRows.get(i);
Row o = (Row) oldRows.get(i);
table.fireAfterRow(session, o, n);
}
}
table.fireAfter(session);
return newRows.size();
}
public String getPlan() {
StringBuffer buff = new StringBuffer();
buff.append("UPDATE ");
buff.append(tableFilter.getPlanSQL(false));
buff.append("\nSET ");
Table table = tableFilter.getTable();
int columnCount = table.getColumns().length;
for (int i = 0, j = 0; i < columnCount; i++) {
Expression newExpr = expressions[i];
if (newExpr != null) {
if(j>0) {
buff.append(",\n");
}
j++;
Column column = table.getColumn(i);
buff.append(column.getName());
buff.append(" = ");
buff.append(StringUtils.unEnclose(newExpr.getSQL()));
}
}
if(condition != null) {
buff.append("\nWHERE " + StringUtils.unEnclose(condition.getSQL()));
}
return buff.toString();
}
public void prepare() throws SQLException {
if (condition != null) {
condition.mapColumns(tableFilter, 0);
condition = condition.optimize(session);
condition.createIndexConditions(tableFilter);
}
for (int i = 0; i < expressions.length; i++) {
Expression expr = expressions[i];
if (expr != null) {
expr.mapColumns(tableFilter, 0);
expressions[i] = expr.optimize(session);
}
}
PlanItem item = tableFilter.getBestPlanItem(session);
tableFilter.setPlanItem(item);
tableFilter.prepare();
}
public boolean isTransactional() {
return true;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论