/**
* Copyright (C) 2010 Orbeon, Inc.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version
* 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
*/
package org.orbeon.oxf.processor.sql.interpreters;
import org.orbeon.oxf.common.ValidationException;
import org.orbeon.oxf.processor.sql.SQLProcessor;
import org.orbeon.oxf.processor.sql.SQLProcessorInterpreterContext;
import org.orbeon.oxf.xml.dom4j.LocationData;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import java.sql.*;
/**
*
*/
public class ResultSetInterpreter extends SQLProcessor.InterpreterContentHandler {
private static final String UNBOUNDED = "unbounded";
public ResultSetInterpreter(SQLProcessorInterpreterContext interpreterContext) {
super(interpreterContext, true);
setForward(true);
}
public void start(String uri, String localname, String qName, Attributes attributes) throws SAXException {
addAllDefaultElementHandlers();
// Optional result sets count
final int allowedResultSetCount;
{
final String resultSetsAttribute = attributes.getValue("result-sets");
allowedResultSetCount = (resultSetsAttribute == null) ? 1 : (resultSetsAttribute.equals(UNBOUNDED) ? -1 : Integer.parseInt(resultSetsAttribute));
}
final SQLProcessorInterpreterContext interpreterContext = getInterpreterContext();
try {
final PreparedStatement stmt = interpreterContext.getStatement(0);
if (stmt != null) {
int currentCount = 0;
boolean hasMoreResultSets = interpreterContext.getResultSet() != null;
do {
final boolean hasNext = !interpreterContext.isEmptyResultSet();
if (SQLProcessor.logger.isDebugEnabled()) {
SQLProcessor.logger.debug("Preparing to execute result set: " +
"statement = " + interpreterContext.getStatementSHA() + ", " +
"hasMoreResultSets = " + hasMoreResultSets + ", " +
"hasNext = " + hasNext + ", " +
"currentCount = " + currentCount);
}
if (hasMoreResultSets) {
// Evaluate body
// NOTE: we do not evaluate the body if there is no row in the result set,
// for backward compatibility. This is questionable, as the sql:result-set
// element should intuitively evaluate its content in any case. Maybe an
// option should be provided on the sql:result-set element?
if (hasNext) {
repeatBody();
}
// One more result set has been processed
currentCount++;
}
// Try to go to next result set
hasMoreResultSets = setResultSetInfo(interpreterContext, stmt, stmt.getMoreResults());
// While we have not processed all the result sets we can process
} while (hasMoreResultSets && (allowedResultSetCount == -1 || currentCount < allowedResultSetCount));
}
} catch (SQLException e) {
throw new ValidationException(e, new LocationData(getDocumentLocator()));
}
}
public void end(String uri, String localname, String qName) throws SAXException {
}
public static boolean setResultSetInfo(SQLProcessorInterpreterContext interpreterContext, PreparedStatement stmt, boolean hasResultSet) throws SQLException {
if (!hasResultSet) {
// There is no more result set, we can close everything
final int updateCount = stmt.getUpdateCount();
interpreterContext.setUpdateCount(updateCount);//FIXME: should add?
closeStatement(interpreterContext, stmt);
if (SQLProcessor.logger.isDebugEnabled())
SQLProcessor.logger.debug("ResultSet info: no more result set, " +
"statement = " + interpreterContext.getStatementSHA() + ", " +
"update count = " + updateCount);
return false;
} else {
// There is one more result set
final ResultSet resultSet = stmt.getResultSet();
final boolean hasNext = resultSet.next();
interpreterContext.setEmptyResultSet(!hasNext);
interpreterContext.setResultSet(resultSet);
interpreterContext.setGotResults(hasNext || interpreterContext.isGotResults());
if (SQLProcessor.logger.isDebugEnabled())
SQLProcessor.logger.debug("ResultSet info: more result set, " +
"statement = " + interpreterContext.getStatementSHA() + ", " +
"hasNext = " + hasNext);
return true;
}
}
public static boolean setGeneratedKeysResultSetInfo(SQLProcessorInterpreterContext interpreterContext, PreparedStatement stmt) throws SQLException {
// TODO: temporarily disabled as Oracle doesn't like it as is
/*
final ResultSet resultSet = stmt.getGeneratedKeys();
final boolean hasNext = resultSet.next();
interpreterContext.setEmptyResultSet(!hasNext);
interpreterContext.setResultSet(resultSet);
interpreterContext.setGotResults(hasNext || interpreterContext.isGotResults());
if (SQLProcessor.logger.isDebugEnabled())
SQLProcessor.logger.debug("GeneratedKeysResultSet info: more result set, hasNext = " + hasNext);
*/
return true;
}
public static void closeStatement(SQLProcessorInterpreterContext interpreterContext, PreparedStatement stmt) throws SQLException {
stmt.close();
interpreterContext.setStatement(null);
interpreterContext.setResultSet(null);
interpreterContext.setEmptyResultSet(true);
}
}