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:insert element in the stylesheet.
*/
public class SQLInsert extends ExtensionInstruction
{
Expression connection;
String table;
boolean ignoreDuplicate = false;
public void prepareAttributes()
throws XPathException
{
table = getAttributeList().getValue("", "table");
if (table == null) {
reportAbsence("table");
}
String connectAtt = getAttributeList().getValue("", "connection");
if (connectAtt == null) {
reportAbsence("connection");
}
else {
connection = makeExpression(connectAtt);
}
String ignoreAtt = getAttributeList().getValue("", "ignoreDuplicate");
if (ignoreAtt != null && ignoreAtt.matches("^true$|^yes$|^1$")) {
ignoreDuplicate = true;
}
}
public void validate()
throws XPathException
{
super.validate();
connection = typeCheck("connection", connection);
}
public Expression compile(Executable exec)
throws XPathException
{
return new InsertInstruction(connection,
table,
getColumnInstructions(exec),
ignoreDuplicate);
}
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 InsertInstruction extends SimpleExpression
{
public static final int CONNECTION = 0;
public static final int FIRST_COLUMN = 1;
String table;
boolean ignoreDuplicate;
public InsertInstruction(Expression connection, String table,
List columnInstructions, boolean ignoreDuplicate)
{
Expression[] sub = new Expression[columnInstructions.size() + 1];
sub[CONNECTION] = connection;
for (int i = 0; i < columnInstructions.size(); i++) {
sub[i + FIRST_COLUMN] = (Expression)columnInstructions.get(i);
}
this.table = table;
setArguments(sub);
this.ignoreDuplicate = ignoreDuplicate;
}
/**
* 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:insert";
}
public Item evaluateItem(XPathContext context)
throws XPathException
{
// Collect names of columns to be added
StringBuffer statement = new StringBuffer(120);
statement.append("INSERT ");
if (ignoreDuplicate)
statement.append("IGNORE ");
statement.append("INTO " + table + " (");
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);
}
statement.append(") VALUES (");
// Add "?" marks for the variable parameters
for (int c = FIRST_COLUMN; c < arguments.length; c++)
{
if (c > FIRST_COLUMN)
statement.append(',');
SQLColumn.ColumnInstruction colInst = (SQLColumn.ColumnInstruction)arguments[c];
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(')');
// 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;
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++)
{
AtomicValue v = (AtomicValue)((SQLColumn.ColumnInstruction)arguments[c]).getSelectValue(
context);
// SAXONTODO: the values are all strings. There is no way of adding to a numeric column
String val = v.getStringValue();
// another hack: setString() doesn't seem to like single-character string values
if (val.length() == 1)
val += " ";
//System.err.println("Set statement parameter " + i + " to " + val);
ps.setObject(i++, val);
}
ps.executeUpdate();
if (!connection.getAutoCommit()) {
connection.commit();
}
}
catch (SQLException ex) {
dynamicError("(SQL INSERT) " + ex.getMessage(), SaxonErrorCode.SXSQ0004, context);
}
finally {
if (ps != null)
{
try {
ps.close();
}
catch (SQLException ignore) {
}
}
}
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.
//
// Contributor(s): none.
//