--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.SqlXmlExecutor\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.execute;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.sql.Activation;\r
+\r
+import org.apache.derby.iapi.types.BooleanDataValue;\r
+import org.apache.derby.iapi.types.StringDataValue;\r
+import org.apache.derby.iapi.types.XML;\r
+import org.apache.derby.iapi.types.XMLDataValue;\r
+import org.apache.derby.iapi.types.SqlXmlUtil;\r
+\r
+/**\r
+ * This class is really just an execution time "utility" that\r
+ * makes calls to methods on the XMLDataValue interface. Instances\r
+ * of this class are generated at execution time by the various\r
+ * Derby XML operators--one instance for each row in the target\r
+ * result set--and then the appropriate operator call is made on\r
+ * that instance (see, for example, the generateExpression() methods\r
+ * in UnaryOperatorNode and BinaryOperatorNode). When an instance\r
+ * of this class is instantiated, one of the arguments that can be\r
+ * provided is an id that is used to retrieve an already-constructed\r
+ * (from compilation time) instance of SqlXmlUtil from the current\r
+ * Activation. When it comes time to execute the operator, this class\r
+ * just makes the appropriate call on the received XMLDataValue object\r
+ * and passes in the SqlXmlUtil, from which the XMLDataValue can\r
+ * retrieve compile-time objects. The XMLDataValue can also make\r
+ * calls to various XML-specific utilities on the SqlXmlUtil\r
+ * object.\r
+ *\r
+ * Let's take an example. Assume the statement that the user\r
+ * wants to execute is:\r
+ *\r
+ * select id from xtable\r
+ * where XMLEXISTS('/simple' PASSING BY REF xcol)\r
+ *\r
+ * At compilation time we will compile the expression "/simple"\r
+ * and store the compiled version of the query into an instance\r
+ * of SqlXmlUtil. Then we will save that instance of SqlXmlUtil\r
+ * as an object in the statement activation, from which we will\r
+ * receive an id that can be used later to retrieve the object\r
+ * (i.e. to retrieve the SqlXmlUtil). Then, for *each* row\r
+ * in xtable, we'll generate the following:\r
+ *\r
+ * boolean result =\r
+ * (new SqlXmlExecutor(activation, compileTimeObjectId)).\r
+ * XMLExists("/simple", xcol);\r
+ *\r
+ * In other words, for each row we create a new instance of\r
+ * this class and call "XMLExists" on that instance. Then,\r
+ * as seen below, we retrieve the SqlXmlUtil from the activation\r
+ * and pass that into a call to "XMLExists" on the XML value\r
+ * itself (i.e. xcol). XMLDataValue.XMLExists() then uses the\r
+ * methods and objects (which include the compiled query\r
+ * expression for "/simple") defined on SqlXmlUtil to complete\r
+ * the operation.\r
+ * \r
+ * Okay, so why do we use this execution-time SqlXmlExecutor class\r
+ * instead of just generating a call to XMLDataValue.XMLExists()\r
+ * directly? The reason is that we only want to compile the XML\r
+ * query expression once per statement--and where possible we'd\r
+ * also like to only generate re-usable XML-specific objects\r
+ * once per statement, as well. If instead we generated a call to\r
+ * XMLDataValue.XMLExists() directly for each row, then we would\r
+ * have to either pass in the expression string and have XMLDataValue\r
+ * compile it, or we would have to compile the expression string\r
+ * and then pass the compiled object into XMLDataValue--in either\r
+ * case, we'd end up compiling the XML query expression (and creating\r
+ * the corresponding XML-specific objects) once for each row in\r
+ * the target result set. By using the "saveObject" functionality\r
+ * in Activation along with this SqlXmlExecutor class, we make\r
+ * it so that we only have to compile the XML query expression and\r
+ * create XML-specific objects once (at compile time), and then\r
+ * we can re-use those objects for every row in the target\r
+ * result set. Yes, we're still creating an instance of this\r
+ * class (SqlXmlExecutor) once per row, and yes we have to fetch\r
+ * the appropriate SqlXmlUtil object once per row, but this is\r
+ * still going to be cheaper than having to re-compile the query\r
+ * expression and re-create XML objects for every row.\r
+ * \r
+ * So in short, this class allows us to improve the execution-time\r
+ * performance of XML operators by allowing us to create XML-\r
+ * specific objects and compile XML query expressions once per\r
+ * statement, instead of once per row.\r
+ *\r
+ * One final note: the reason this class is in this package\r
+ * instead of the types package is that, in order to retrieve\r
+ * the compile-time objects, we have to use the "getSavedObject()"\r
+ * method on the Activation. But the Activation class is part\r
+ * of the SQL layer (org.apache.derby.iapi.sql.Activation) and\r
+ * we want to keep the types layer independent of the SQL layer\r
+ * because the types can be used during recovery before the SQL\r
+ * system has booted. So the next logical choices were the compile\r
+ * package (impl.sql.compile) or the execution package; of those,\r
+ * the execution package seems more appropriate since this\r
+ * class is only instantiated and used during execution, not\r
+ * during compilation.\r
+ */\r
+\r
+public class SqlXmlExecutor {\r
+\r
+ // The activation from which we load the compile-time XML\r
+ // objects (including the compiled XML query expression in\r
+ // case of XMLEXISTS and XMLQUERY).\r
+ private Activation activation;\r
+ private int sqlXUtilId;\r
+\r
+ // Target type, target width and target collation type that \r
+ // were specified for an XMLSERIALIZE operator.\r
+ private int targetTypeId;\r
+ private int targetMaxWidth;\r
+ private int targetCollationType;\r
+\r
+ // Whether or not to preserve whitespace for XMLPARSE\r
+ // operator.\r
+ private boolean preserveWS;\r
+\r
+ /**\r
+ * Constructor 1: Used for XMLPARSE op.\r
+ * @param activation Activation from which to retrieve saved objects\r
+ * @param utilId Id by which we find saved objects in activation\r
+ * @param preserveWS Whether or not to preserve whitespace\r
+ */\r
+ public SqlXmlExecutor(Activation activation, int utilId,\r
+ boolean preserveWS)\r
+ {\r
+ this.activation = activation;\r
+ this.sqlXUtilId = utilId;\r
+ this.preserveWS = preserveWS;\r
+ }\r
+\r
+ /**\r
+ * Constructor 2: Used for XMLSERIALIZE op.\r
+ * @param targetTypeId The string type to which we want to serialize.\r
+ * @param targetMaxWidth The max width of the target type.\r
+ * @param targetCollationType The collation type of the target type.\r
+ */\r
+ public SqlXmlExecutor(int targetTypeId, int targetMaxWidth, \r
+ int targetCollationType)\r
+ {\r
+ this.targetTypeId = targetTypeId;\r
+ this.targetMaxWidth = targetMaxWidth;\r
+ this.targetCollationType = targetCollationType;\r
+ }\r
+\r
+ /**\r
+ * Constructor 3: Used for XMLEXISTS/XMLQUERY ops.\r
+ * @param activation Activation from which to retrieve saved objects\r
+ * @param utilId Id by which we find saved objects in activation\r
+ */\r
+ public SqlXmlExecutor(Activation activation, int utilId)\r
+ {\r
+ this.activation = activation;\r
+ this.sqlXUtilId = utilId;\r
+ }\r
+\r
+ /**\r
+ * Make the call to perform an XMLPARSE operation on the\r
+ * received XML string and store the result in the received\r
+ * XMLDataValue (or if it's null, create a new one).\r
+ *\r
+ * @param xmlText String to parse\r
+ * @param result XMLDataValue in which to store the result\r
+ * @return The received XMLDataValue with its content set to\r
+ * correspond to the received xmlText, if the text constitutes\r
+ * a valid XML document. If the received XMLDataValue is\r
+ * null, then create a new one and set its content to\r
+ * correspond to the received xmlText.\r
+ */\r
+ public XMLDataValue XMLParse(StringDataValue xmlText, XMLDataValue result)\r
+ throws StandardException\r
+ {\r
+ if (result == null)\r
+ result = new XML();\r
+\r
+ if (xmlText.isNull())\r
+ {\r
+ result.setToNull();\r
+ return result;\r
+ }\r
+\r
+ return result.XMLParse(\r
+ xmlText.getString(), preserveWS, getSqlXmlUtil());\r
+ }\r
+\r
+ /**\r
+ * Make the call to perform an XMLSERIALIZE operation on the\r
+ * received XML data value and store the result in the received\r
+ * StringDataValue (or if it's null, create a new one).\r
+ *\r
+ * @param xmlVal XML value to serialize\r
+ * @param result StringDataValue in which to store the result\r
+ * @return A serialized (to string) version of this XML object,\r
+ * in the form of a StringDataValue object. \r
+ */\r
+ public StringDataValue XMLSerialize(XMLDataValue xmlVal,\r
+ StringDataValue result) throws StandardException\r
+ {\r
+ return xmlVal.XMLSerialize(result, targetTypeId, targetMaxWidth, \r
+ targetCollationType);\r
+ }\r
+\r
+ /**\r
+ * Make the call to perform an XMLEXISTS operation on the\r
+ * received XML data value.\r
+ *\r
+ * @param xExpr Query expression to be evaluated\r
+ * @param xmlContext Context node against which to evaluate\r
+ * the expression.\r
+ * @return True if evaluation of the query expression\r
+ * against xmlContext returns at least one item; unknown if\r
+ * either the xml value is NULL; false otherwise. \r
+ */\r
+ public BooleanDataValue XMLExists(StringDataValue xExpr,\r
+ XMLDataValue xmlContext) throws StandardException\r
+ {\r
+ return xmlContext.XMLExists(getSqlXmlUtil());\r
+ }\r
+\r
+ /**\r
+ * Make the call to perform an XMLQUERY operation on the\r
+ * received XML data value and store the result in the\r
+ * received result holder (or, if it's null, create a\r
+ * new one).\r
+ *\r
+ * @param xExpr Query expression to be evaluated\r
+ * @param xmlContext Context node against which to evaluate\r
+ * the expression.\r
+ * @param result XMLDataValue in which to store the result\r
+ * @return The received XMLDataValue with its content set to\r
+ * result of evaulating the query expression against xmlContext.\r
+ * If the received XMLDataValue is null, then create a new one\r
+ * and set its content to correspond to the received xmlText.\r
+ */\r
+ public XMLDataValue XMLQuery(StringDataValue xExpr,\r
+ XMLDataValue xmlContext, XMLDataValue result)\r
+ throws StandardException\r
+ {\r
+ return xmlContext.XMLQuery(result, getSqlXmlUtil());\r
+ }\r
+\r
+ /**\r
+ * Return the saved object in this.activation that corresponds to\r
+ * this.sqlxUtilId. Assumption is that those fields have been\r
+ * set by the time we get here.\r
+ */\r
+ private SqlXmlUtil getSqlXmlUtil()\r
+ throws StandardException\r
+ {\r
+ return (SqlXmlUtil)\r
+ activation.getPreparedStatement().getSavedObject(sqlXUtilId);\r
+ }\r
+\r
+}\r