/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.HashMappedList;
import org.hsqldb.lib.HashSet;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.lib.OrderedIntHashSet;
import org.hsqldb.result.Result;
import org.hsqldb.result.ResultConstants;
/**
* Implementation of Statement for PSM compound statements.
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class StatementCompound extends Statement {
final boolean isLoop;
HsqlName label;
StatementHandler[] handlers = StatementHandler.emptyExceptionHandlerArray;
boolean hasUndoHandler;
Statement loopCursor;
Statement[] statements;
StatementExpression condition;
boolean isAtomic;
//
ColumnSchema[] variables = ColumnSchema.emptyArray;
HashMappedList scopeVariables;
RangeVariable[] rangeVariables = RangeVariable.emptyArray;
//
public static final StatementCompound[] emptyStatementArray =
new StatementCompound[]{};
StatementCompound(int type, HsqlName label) {
super(type, StatementTypes.X_SQL_CONTROL);
this.label = label;
isTransactionStatement = false;
switch (type) {
case StatementTypes.LOOP :
case StatementTypes.WHILE :
case StatementTypes.REPEAT :
isLoop = true;
break;
case StatementTypes.BEGIN_END :
case StatementTypes.IF :
isLoop = false;
break;
default :
throw Error.runtimeError(ErrorCode.U_S0500,
"StatementCompound");
}
}
public String getSQL() {
/*
StringBuffer sb = new StringBuffer();
if (label != null) {
sb.append(label.getStatementName()).append(':').append(' ');
}
switch (type) {
case StatementTypes.LOOP :
sb.append(Tokens.T_LOOP).append(' ');
for (int i = 0; i < statements.length; i++) {
sb.append(statements[i].getSQL()).append(';');
}
sb.append(Tokens.T_END).append(' ').append(Tokens.T_LOOP);
break;
case StatementTypes.WHILE :
sb.append(Tokens.T_WHILE).append(' ');
sb.append(condition.getSQL()).append(' ').append(Tokens.T_DO);
sb.append(' ');
for (int i = 0; i < statements.length; i++) {
sb.append(statements[i].getSQL()).append(';');
}
sb.append(Tokens.T_END).append(' ').append(Tokens.T_WHILE);
break;
case StatementTypes.REPEAT :
sb.append(Tokens.T_REPEAT).append(' ');
for (int i = 0; i < statements.length; i++) {
sb.append(statements[i].getSQL()).append(';');
}
sb.append(Tokens.T_UNTIL).append(' ');
sb.append(condition.getSQL()).append(' ');
sb.append(Tokens.T_END).append(' ').append(Tokens.T_REPEAT);
break;
case StatementTypes.BEGIN_END :
sb.append(Tokens.T_BEGIN).append(' ').append(Tokens.T_ATOMIC);
sb.append(' ');
for (int i = 0; i < handlers.length; i++) {
sb.append(handlers[i].getSQL()).append(';');
}
for (int i = 0; i < variables.length; i++) {
sb.append(Tokens.T_DECLARE).append(' ');
sb.append(variables[i].getSQL());
if (variables[i].hasDefault()) {
sb.append(' ').append(Tokens.T_DEFAULT).append(' ');
sb.append(variables[i].getDefaultSQL());
}
sb.append(';');
}
for (int i = 0; i < statements.length; i++) {
sb.append(statements[i].getSQL()).append(';');
}
sb.append(Tokens.T_END);
break;
case StatementTypes.IF :
for (int i = 0; i < statements.length; i++) {
if (statements[i].type == StatementTypes.CONDITION) {
if (i != 0) {
sb.append(Tokens.T_ELSE).append(' ');
}
sb.append(Tokens.T_IF).append(' ');
sb.append(statements[i].getSQL()).append(' ');
sb.append(Tokens.T_THEN).append(' ');
} else {
sb.append(statements[i].getSQL()).append(';');
}
}
sb.append(Tokens.T_END).append(' ').append(Tokens.T_IF);
break;
}
return sb.toString();
*/
return sql;
}
protected String describe(Session session, int blanks) {
StringBuffer sb = new StringBuffer();
sb.append('\n');
for (int i = 0; i < blanks; i++) {
sb.append(' ');
}
sb.append(Tokens.T_STATEMENT);
return sb.toString();
}
public void setLocalDeclarations(Object[] declarations) {
int varCount = 0;
int handlerCount = 0;
for (int i = 0; i < declarations.length; i++) {
if (declarations[i] instanceof ColumnSchema) {
varCount++;
} else {
handlerCount++;
}
}
variables = new ColumnSchema[varCount];
handlers = new StatementHandler[handlerCount];
varCount = 0;
handlerCount = 0;
for (int i = 0; i < declarations.length; i++) {
if (declarations[i] instanceof ColumnSchema) {
variables[varCount++] = (ColumnSchema) declarations[i];
} else {
StatementHandler handler = (StatementHandler) declarations[i];
handler.setParent(this);
handlers[handlerCount++] = handler;
if (handler.handlerType == StatementHandler.UNDO) {
hasUndoHandler = true;
}
}
}
setVariables();
setHandlers();
}
public void setLoopStatement(Statement cursorStatement) {
loopCursor = cursorStatement;
}
void setStatements(Statement[] statements) {
for (int i = 0; i < statements.length; i++) {
statements[i].setParent(this);
}
this.statements = statements;
}
public void setCondition(StatementExpression condition) {
this.condition = condition;
}
public Result execute(Session session) {
Result result;
switch (type) {
case StatementTypes.BEGIN_END : {
initialiseVariables(session);
result = executeBlock(session);
break;
}
case StatementTypes.LOOP :
case StatementTypes.WHILE :
case StatementTypes.REPEAT : {
result = executeLoop(session);
break;
}
case StatementTypes.IF : {
result = executeIf(session);
break;
}
default :
throw Error.runtimeError(ErrorCode.U_S0500,
"StatementCompound");
}
if (result.isError()) {
result.getException().setStatementType(group, type);
}
return result;
}
private Result executeBlock(Session session) {
Result result = Result.updateZeroResult;
boolean push = !root.isTrigger();
if (push) {
session.sessionContext.push();
if (hasUndoHandler) {
String name = HsqlNameManager.getAutoSavepointNameString(
session.actionTimestamp, session.sessionContext.depth);
session.savepoint(name);
}
}
for (int i = 0; i < statements.length; i++) {
result = statements[i].execute(session);
result = handleCondition(session, result);
if (result.isError()) {
break;
}
if (result.getType() == ResultConstants.VALUE
|| result.getType() == ResultConstants.DATA) {
break;
}
}
if (result.getType() == ResultConstants.VALUE) {
if (result.getErrorCode() == StatementTypes.LEAVE) {
if (result.getMainString() == null) {
result = Result.updateZeroResult;
} else if (label != null
&& label.name.equals(result.getMainString())) {
result = Result.updateZeroResult;
}
}
}
if (push) {
session.sessionContext.pop();
}
return result;
}
private Result handleCondition(Session session, Result result) {
String sqlState = null;
if (result.isError()) {
sqlState = result.getSubString();
} else if (session.getLastWarning() != null) {
sqlState = session.getLastWarning().getSQLState();
} else {
return result;
}
if (sqlState != null) {
for (int i = 0; i < handlers.length; i++) {
StatementHandler handler = handlers[i];
session.clearWarnings();
/**
* @todo - if condition is "transaction rollback" promote to
* top call level without any further action
* if condition is system related promote to top level
* schema manipulation conditions are never handled
*/
if (handler.handlesCondition(result.getSubString())) {
session.resetSchema();
switch (handler.handlerType) {
case StatementHandler.CONTINUE :
result = Result.updateZeroResult;
break;
case StatementHandler.UNDO :
session.rollbackToSavepoint();
result = Result.newPSMResult(StatementTypes.LEAVE,
label.name, null);
break;
case StatementHandler.EXIT :
result = Result.newPSMResult(StatementTypes.LEAVE,
null, null);
break;
}
Result actionResult = handler.statement.execute(session);
if (actionResult.isError()) {
result = actionResult;
handleCondition(session, result);
} else {
return result;
}
}
}
if (parent != null) {
// unhandled exception condition
return parent.handleCondition(session, result);
}
}
return result;
}
private Result executeLoop(Session session) {
Result result = Result.updateZeroResult;
while (true) {
if (type == StatementTypes.WHILE) {
result = condition.execute(session);
if (result.isError()) {
break;
}
if (!Boolean.TRUE.equals(result.getValueObject())) {
result = Result.updateZeroResult;
break;
}
}
for (int i = 0; i < statements.length; i++) {
result = statements[i].execute(session);
if (result.isError()) {
break;
}
if (result.getType() == ResultConstants.VALUE) {
break;
}
if (result.getType() == ResultConstants.DATA) {
break;
}
}
if (result.isError()) {
break;
}
if (result.getType() == ResultConstants.VALUE) {
if (result.getErrorCode() == StatementTypes.ITERATE) {
if (result.getMainString() == null) {
continue;
}
if (label != null
&& label.name.equals(result.getMainString())) {
continue;
}
break;
}
if (result.getErrorCode() == StatementTypes.LEAVE) {
if (result.getMainString() == null) {
result = Result.updateZeroResult;
}
if (label != null
&& label.name.equals(result.getMainString())) {
result = Result.updateZeroResult;
}
break;
}
// return
break;
}
if (result.getType() == ResultConstants.DATA) {
break;
}
if (type == StatementTypes.REPEAT) {
result = condition.execute(session);
if (result.isError()) {
break;
}
if (Boolean.TRUE.equals(result.getValueObject())) {
result = Result.updateZeroResult;
break;
}
}
}
return result;
}
private Result executeIf(Session session) {
Result result = Result.updateZeroResult;
boolean execute = false;
for (int i = 0; i < statements.length; i++) {
if (statements[i].getType() == StatementTypes.CONDITION) {
if (execute) {
break;
}
result = statements[i].execute(session);
if (result.isError()) {
break;
}
Object value = result.getValueObject();
execute = Boolean.TRUE.equals(value);
i++;
}
result = Result.updateZeroResult;
if (!execute) {
continue;
}
result = statements[i].execute(session);
if (result.isError()) {
break;
}
if (result.getType() == ResultConstants.VALUE) {
break;
}
}
return result;
}
public void resolve(Session session) {
for (int i = 0; i < statements.length; i++) {
if (statements[i].getType() == StatementTypes.LEAVE
|| statements[i].getType() == StatementTypes.ITERATE) {
if (!findLabel((StatementSimple) statements[i])) {
throw Error.error(
ErrorCode.X_42508,
((StatementSimple) statements[i]).label.name);
}
continue;
}
if (statements[i].getType() == StatementTypes.RETURN) {
if (!root.isFunction()) {
throw Error.error(ErrorCode.X_42602, Tokens.T_RETURN);
}
}
}
for (int i = 0; i < statements.length; i++) {
statements[i].resolve(session);
}
for (int i = 0; i < handlers.length; i++) {
handlers[i].resolve(session);
}
OrderedHashSet writeTableNamesSet = new OrderedHashSet();
OrderedHashSet readTableNamesSet = new OrderedHashSet();
OrderedHashSet set = new OrderedHashSet();
for (int i = 0; i < variables.length; i++) {
set.addAll(variables[i].getReferences());
}
if (condition != null) {
set.addAll(condition.getReferences());
readTableNamesSet.addAll(condition.getTableNamesForRead());
}
for (int i = 0; i < statements.length; i++) {
set.addAll(statements[i].getReferences());
readTableNamesSet.addAll(statements[i].getTableNamesForRead());
writeTableNamesSet.addAll(statements[i].getTableNamesForWrite());
}
for (int i = 0; i < handlers.length; i++) {
set.addAll(handlers[i].getReferences());
readTableNamesSet.addAll(handlers[i].getTableNamesForRead());
writeTableNamesSet.addAll(handlers[i].getTableNamesForWrite());
}
readTableNamesSet.removeAll(writeTableNamesSet);
readTableNames = new HsqlName[readTableNamesSet.size()];
readTableNamesSet.toArray(readTableNames);
writeTableNames = new HsqlName[writeTableNamesSet.size()];
writeTableNamesSet.toArray(writeTableNames);
references = set;
}
public void setRoot(Routine routine) {
root = routine;
/*
if (condition != null) {
condition.setRoot(routine);
}
for (int i = 0; i < statements.length; i++) {
statements[i].setRoot(routine);
}
*/
}
public String describe(Session session) {
return "";
}
public OrderedHashSet getReferences() {
return references;
}
public void setAtomic(boolean atomic) {
this.isAtomic = atomic;
}
//
private void setVariables() {
if (variables.length == 0) {
if (parent == null) {
rangeVariables = root.getParameterRangeVariables();
} else {
rangeVariables = parent.rangeVariables;
}
return;
}
HashMappedList list = new HashMappedList();
if (parent != null) {
for (int i = 0; i < parent.scopeVariables.size(); i++) {
list.add(parent.scopeVariables.getKey(i),
parent.scopeVariables.get(i));
}
}
for (int i = 0; i < variables.length; i++) {
String name = variables[i].getName().name;
boolean added = list.add(name, variables[i]);
if (!added) {
throw Error.error(ErrorCode.X_42606, name);
}
if (root.getParameterIndex(name) != -1) {
throw Error.error(ErrorCode.X_42606, name);
}
}
RangeVariable range = new RangeVariable(list, true);
rangeVariables = new RangeVariable[] {
root.getParameterRangeVariables()[0], range
};
root.variableCount = list.size();
}
private void setHandlers() {
if (handlers.length == 0) {
return;
}
HashSet statesSet = new HashSet();
OrderedIntHashSet typesSet = new OrderedIntHashSet();
for (int i = 0; i < handlers.length; i++) {
int[] types = handlers[i].getConditionTypes();
for (int j = 0; j < types.length; j++) {
if (!typesSet.add(types[j])) {
throw Error.error(ErrorCode.X_42601);
}
}
String[] states = handlers[i].getConditionStates();
for (int j = 0; j < states.length; j++) {
if (!statesSet.add(states[j])) {
throw Error.error(ErrorCode.X_42601);
}
}
}
}
private boolean findLabel(StatementSimple statement) {
if (label != null && statement.label.name.equals(label.name)) {
if (!isLoop && statement.getType() == StatementTypes.ITERATE) {
return false;
}
return true;
}
if (parent == null) {
return false;
}
return parent.findLabel(statement);
}
private void initialiseVariables(Session session) {
Object[] vars = session.sessionContext.routineVariables;
int offset = parent == null ? 0
: parent.scopeVariables.size();
for (int i = 0; i < variables.length; i++) {
try {
vars[offset + i] = variables[i].getDefaultValue(session);
} catch (HsqlException e) {}
}
}
public RangeVariable[] getRangeVariables() {
return rangeVariables;
}
}