package org.cdlib.xtf.saxonExt.sql;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.SimpleExpression;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.instruct.Executable;
import net.sf.saxon.om.Axis;
import net.sf.saxon.om.AxisIterator;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.style.ExtensionInstruction;
import net.sf.saxon.trans.SaxonErrorCode;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.AtomicValue;
import net.sf.saxon.value.ObjectValue;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* An sql:update element in the stylesheet.
*/
public class SQLUpdate extends ExtensionInstruction
{
Expression connection;
Expression table;
Expression where;
public void prepareAttributes()
throws XPathException
{
String connectAtt = getAttributeList().getValue("", "connection");
if (connectAtt == null)
reportAbsence("connection");
connection = makeExpression(connectAtt);
String tableAtt = attributeList.getValue("", "table");
if (tableAtt == null)
reportAbsence("table");
table = makeAttributeValueTemplate(tableAtt);
String whereAtt = attributeList.getValue("", "where");
if (whereAtt == null)
reportAbsence("where");
where = makeAttributeValueTemplate(whereAtt);
}
public void validate()
throws XPathException
{
super.validate();
connection = typeCheck("connection", connection);
table = typeCheck("table", table);
where = typeCheck("where", where);
}
public Expression compile(Executable exec)
throws XPathException
{
return new UpdateInstruction(connection,
table,
where,
getColumnInstructions(exec));
}
public List getColumnInstructions(Executable exec)
throws XPathException
{
List list = new ArrayList(10);
AxisIterator kids = iterateAxis(Axis.CHILD);
NodeInfo child;
while (true)
{
child = (NodeInfo)kids.next();
if (child == null) {
break;
}
if (child instanceof SQLColumn) {
list.add(((SQLColumn)child).compile(exec));
}
}
return list;
}
private static class UpdateInstruction extends SimpleExpression
{
public static final int CONNECTION = 0;
public static final int TABLE = 1;
public static final int WHERE = 2;
public static final int FIRST_COLUMN = 3;
public UpdateInstruction(Expression connection, Expression table,
Expression where, List columnInstructions)
{
Expression[] sub = new Expression[columnInstructions.size() + 3];
sub[CONNECTION] = connection;
sub[TABLE] = table;
sub[WHERE] = where;
for (int i = 0; i < columnInstructions.size(); i++) {
sub[i + FIRST_COLUMN] = (Expression)columnInstructions.get(i);
}
setArguments(sub);
}
/**
* A subclass must provide one of the methods evaluateItem(), iterate(), or process().
* This method indicates which of the three is provided.
*/
public int getImplementationMethod() {
return Expression.EVALUATE_METHOD;
}
public String getExpressionType() {
return "sql:update";
}
public Item evaluateItem(XPathContext context)
throws XPathException
{
// Construct the SQL statement, with question marks for the data.
String dbTab = arguments[TABLE].evaluateAsString(context);
String dbWhere = arguments[WHERE].evaluateAsString(context);
StringBuffer statement = new StringBuffer(120);
statement.append("UPDATE " + dbTab + " SET ");
for (int c = FIRST_COLUMN; c < arguments.length; c++)
{
if (c > FIRST_COLUMN)
statement.append(',');
SQLColumn.ColumnInstruction colInst = (SQLColumn.ColumnInstruction)arguments[c];
String colname = colInst.getColumnName();
statement.append(colname);
if (colInst.evalSql())
{
String val = colInst.getSelectValue(context).toString();
// Strip leading/trailing quotes from the expression.
if ((val.startsWith("\"") && val.endsWith("\"")) ||
(val.startsWith("'") && val.endsWith("'")))
{
val = val.substring(1, val.length() - 1);
}
statement.append("=" + val);
}
else
statement.append("=?");
}
statement.append(" WHERE " + dbWhere);
// Prepare the SQL statement (only do this once)
Item conn = arguments[CONNECTION].evaluateItem(context);
if (!(conn instanceof ObjectValue &&
((ObjectValue)conn).getObject() instanceof Connection))
{
dynamicError("Value of connection expression is not a JDBC Connection",
SaxonErrorCode.SXSQ0001, context);
}
Connection connection = (Connection)((ObjectValue)conn).getObject();
PreparedStatement ps = null;
@SuppressWarnings("unused")
int nUpdated = -1;
try
{
ps = connection.prepareStatement(statement.toString());
// Add the actual column values to be inserted
int i = 1;
for (int c = FIRST_COLUMN; c < arguments.length; c++)
{
SQLColumn.ColumnInstruction colInst = (SQLColumn.ColumnInstruction)arguments[c];
if (colInst.evalSql())
continue;
// SAXONTODO: the values are all strings. There is no way of adding to a numeric column
String val = ((AtomicValue)colInst.getSelectValue(context)).getStringValue();
// another hack: setString() doesn't seem to like single-character string values
if (val.length() == 1)
val += " ";
ps.setObject(i++, val);
}
nUpdated = ps.executeUpdate();
if (!connection.getAutoCommit()) {
connection.commit();
}
}
catch (SQLException ex) {
dynamicError("(SQL UPDATE) " + ex.getMessage(), SaxonErrorCode.SXSQ0004, context);
}
finally {
if (ps != null)
{
try {
ps.close();
}
catch (SQLException ignore) {
}
}
}
// Return nothing, so that it's unnecessary to re-route the result.
return null;
}
}
}
//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is Michael H. Kay.
//
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
// Additional Contributor(s): Martin Haye
//
// Acknowledgements:
//
// A significant amount of new and/or modified code in this module
// was made possible by a grant from the Andrew W. Mellon Foundation,
// as part of the Melvyl Recommender Project.
//