org.hsqldb.StatementCompound Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sqltool Show documentation
Show all versions of sqltool Show documentation
HSQLDB - Lightweight 100% Java SQL Database Engine
/* Copyright (c) 2001-2020, 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;
import org.hsqldb.types.Type;
/**
* Implementation of Statement for PSM compound statements.
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 2.5.1
* @since 1.9.0
*/
public class StatementCompound extends Statement implements RangeGroup {
final boolean isLoop;
HsqlName label;
StatementHandler[] handlers = StatementHandler.emptyExceptionHandlerArray;
boolean hasUndoHandler;
StatementQuery loopCursor;
Statement[] statements;
StatementExpression condition;
boolean isAtomic;
//
ColumnSchema[] variables = ColumnSchema.emptyArray;
StatementCursor[] cursors = StatementCursor.emptyArray;
HashMappedList scopeVariables = new HashMappedList();
RangeVariable[] rangeVariables = RangeVariable.emptyArray;
Table[] tables = Table.emptyArray;
HashMappedList scopeTables;
//
int variablesOffset;
//
public static final StatementCompound[] emptyStatementArray =
new StatementCompound[]{};
StatementCompound(int type, HsqlName label, StatementCompound parent) {
super(type, StatementTypes.X_SQL_CONTROL);
this.label = label;
isTransactionStatement = false;
switch (type) {
case StatementTypes.FOR :
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");
}
this.parent = parent;
}
public String getSQL() {
/*
StringBuilder sb = new StringBuilder();
if (label != null) {
sb.append(label.getStatementName()).append(':').append(' ');
}
switch (type) {
case StatementTypes.FOR :
// todo
break;
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;
}
String describe(Session session, int blanks) {
StringBuilder sb = new StringBuilder();
sb.append('\n');
for (int i = 0; i < blanks; i++) {
sb.append(' ');
}
sb.append(Tokens.T_STATEMENT);
return sb.toString();
}
boolean isLoop() {
return isLoop;
}
void setLocalDeclarations(Object[] declarations) {
int varCount = 0;
int handlerCount = 0;
int cursorCount = 0;
int tableCount = 0;
for (int i = 0; i < declarations.length; i++) {
if (declarations[i] instanceof ColumnSchema) {
varCount++;
} else if (declarations[i] instanceof StatementHandler) {
handlerCount++;
} else if (declarations[i] instanceof Table) {
tableCount++;
} else {
cursorCount++;
}
}
if (varCount > 0) {
variables = new ColumnSchema[varCount];
}
if (handlerCount > 0) {
handlers = new StatementHandler[handlerCount];
}
if (tableCount > 0) {
tables = new Table[tableCount];
}
if (cursorCount > 0) {
cursors = new StatementCursor[cursorCount];
}
varCount = 0;
handlerCount = 0;
tableCount = 0;
cursorCount = 0;
for (int i = 0; i < declarations.length; i++) {
if (declarations[i] instanceof StatementCursor) {
StatementCursor cursor = (StatementCursor) declarations[i];
cursors[cursorCount++] = cursor;
} else if (declarations[i] instanceof ColumnSchema) {
variables[varCount++] = (ColumnSchema) declarations[i];
} else if (declarations[i] instanceof StatementHandler) {
StatementHandler handler = (StatementHandler) declarations[i];
handler.setParent(this);
handlers[handlerCount++] = handler;
if (handler.handlerType == StatementHandler.UNDO) {
hasUndoHandler = true;
}
} else if (declarations[i] instanceof Table) {
Table table = (Table) declarations[i];
tables[tableCount++] = table;
}
}
setVariables();
setHandlers();
setTables();
setCursors();
}
void setLoopStatement(HsqlName name, StatementQuery cursorStatement) {
loopCursor = cursorStatement;
HsqlName[] colNames =
cursorStatement.queryExpression.getResultColumnNames();
Type[] colTypes = cursorStatement.queryExpression.getColumnTypes();
ColumnSchema[] columns = new ColumnSchema[colNames.length];
for (int i = 0; i < colNames.length; i++) {
columns[i] = new ColumnSchema(colNames[i], colTypes[i], false,
false, null);
columns[i].setParameterMode(SchemaObject.ParameterModes.PARAM_IN);
}
setLocalDeclarations(columns);
}
void setStatements(Statement[] statements) {
for (int i = 0; i < statements.length; i++) {
statements[i].setParent(this);
}
this.statements = statements;
}
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.FOR :
result = executeForLoop(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 = executeProtected(session, statements[i]);
result = handleCondition(session, result);
if (result.isError()) {
break;
}
if (result.getType() == ResultConstants.VALUE) {
break;
}
if (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(sqlState)) {
String labelString = label == null ? null
: label.name;
switch (handler.handlerType) {
case StatementHandler.CONTINUE :
result = Result.updateZeroResult;
break;
case StatementHandler.UNDO :
if (session.sessionContext.savepoints.size() > 0) {
session.rollbackToSavepoint();
}
result = Result.newPSMResult(StatementTypes.LEAVE,
labelString, null);
break;
case StatementHandler.EXIT :
result = Result.newPSMResult(StatementTypes.LEAVE,
labelString, null);
break;
}
Result actionResult = executeProtected(session, handler);
if (actionResult.isError()) {
result = actionResult;
// parent should handle this
} else if (actionResult.getType()
== ResultConstants.VALUE) {
result = actionResult;
}
}
}
if (result.isError() && parent != null) {
// unhandled exception condition
return parent.handleCondition(session, result);
}
}
return result;
}
private Result executeForLoop(Session session) {
Result queryResult = loopCursor.execute(session);
if (queryResult.isError()) {
return queryResult;
}
Result result = Result.updateZeroResult;
while (queryResult.navigator.next()) {
Object[] data = queryResult.navigator.getCurrent();
initialiseVariables(session, data,
queryResult.metaData.getColumnCount());
for (int i = 0; i < statements.length; i++) {
result = executeProtected(session, statements[i]);
result = handleCondition(session, result);
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) {
break;
}
// return
break;
}
if (result.getType() == ResultConstants.DATA) {
break;
}
}
queryResult.navigator.release();
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 = executeProtected(session, statements[i]);
result = handleCondition(session, result);
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 = executeProtected(session, statements[i]);
if (result.isError()) {
break;
}
Object value = result.getValueObject();
execute = Boolean.TRUE.equals(value);
i++;
}
result = Result.updateZeroResult;
if (!execute) {
continue;
}
result = executeProtected(session, statements[i]);
result = handleCondition(session, result);
if (result.isError()) {
break;
}
if (result.getType() == ResultConstants.VALUE) {
break;
}
}
return result;
}
private Result executeProtected(Session session, Statement statement) {
int actionIndex = session.rowActionList.size();
long actionTimestamp =
session.database.txManager.getNextGlobalChangeTimestamp();
session.actionTimestamp = actionTimestamp;
Result result = statement.execute(session);
if (result.isError()) {
session.rollbackAction(actionIndex, actionTimestamp);
}
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++) {
OrderedHashSet refs = variables[i].getReferences();
if (refs != null) {
set.addAll(refs);
}
}
if (loopCursor != null) {
set.addAll(loopCursor.getReferences());
readTableNamesSet.addAll(loopCursor.getTableNamesForRead());
}
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() {
HashMappedList list = new HashMappedList();
if (parent != null && parent.scopeVariables != null) {
for (int i = 0; i < parent.scopeVariables.size(); i++) {
list.add(parent.scopeVariables.getKey(i),
parent.scopeVariables.get(i));
}
}
variablesOffset = list.size();
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);
}
}
scopeVariables = list;
RangeVariable[] parameterRangeVariables = root.getRangeVariables();
RangeVariable range = new RangeVariable(list, null, true,
RangeVariable.VARIALBE_RANGE);
rangeVariables = new RangeVariable[parameterRangeVariables.length + 1];
for (int i = 0; i < parameterRangeVariables.length; i++) {
rangeVariables[i] = parameterRangeVariables[i];
}
rangeVariables[parameterRangeVariables.length] = range;
if (list.size() > root.variableCount) {
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 void setTables() {
if (tables.length == 0) {
return;
}
HashMappedList list = new HashMappedList();
if (parent != null && parent.scopeTables != null) {
for (int i = 0; i < parent.scopeTables.size(); i++) {
list.add(parent.scopeTables.getKey(i),
parent.scopeTables.get(i));
}
}
for (int i = 0; i < tables.length; i++) {
String name = tables[i].getName().name;
boolean added = list.add(name, tables[i]);
if (!added) {
throw Error.error(ErrorCode.X_42606, name);
}
}
scopeTables = list;
}
private void setCursors() {
if (cursors.length == 0) {
return;
}
HashSet list = new HashSet();
for (int i = 0; i < cursors.length; i++) {
StatementCursor cursor = cursors[i];
boolean added = list.add(cursor.getCursorName().name);
if (!added) {
throw Error.error(ErrorCode.X_42606,
cursor.getCursorName().name);
}
}
}
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) {}
}
}
private void initialiseVariables(Session session, Object[] data,
int count) {
Object[] vars = session.sessionContext.routineVariables;
for (int i = 0; i < count; i++) {
vars[variablesOffset + i] = data[i];
}
}
public RangeVariable[] getRangeVariables() {
return rangeVariables;
}
public void setCorrelated() {
//
}
public boolean isVariable() {
return true;
}
}