001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.SqlXmlExecutor
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.execute;
023:
024: import org.apache.derby.iapi.error.StandardException;
025: import org.apache.derby.iapi.reference.SQLState;
026: import org.apache.derby.iapi.sql.Activation;
027:
028: import org.apache.derby.iapi.types.BooleanDataValue;
029: import org.apache.derby.iapi.types.StringDataValue;
030: import org.apache.derby.iapi.types.XML;
031: import org.apache.derby.iapi.types.XMLDataValue;
032: import org.apache.derby.iapi.types.SqlXmlUtil;
033:
034: /**
035: * This class is really just an execution time "utility" that
036: * makes calls to methods on the XMLDataValue interface. Instances
037: * of this class are generated at execution time by the various
038: * Derby XML operators--one instance for each row in the target
039: * result set--and then the appropriate operator call is made on
040: * that instance (see, for example, the generateExpression() methods
041: * in UnaryOperatorNode and BinaryOperatorNode). When an instance
042: * of this class is instantiated, one of the arguments that can be
043: * provided is an id that is used to retrieve an already-constructed
044: * (from compilation time) instance of SqlXmlUtil from the current
045: * Activation. When it comes time to execute the operator, this class
046: * just makes the appropriate call on the received XMLDataValue object
047: * and passes in the SqlXmlUtil, from which the XMLDataValue can
048: * retrieve compile-time objects. The XMLDataValue can also make
049: * calls to various XML-specific utilities on the SqlXmlUtil
050: * object.
051: *
052: * Let's take an example. Assume the statement that the user
053: * wants to execute is:
054: *
055: * select id from xtable
056: * where XMLEXISTS('/simple' PASSING BY REF xcol)
057: *
058: * At compilation time we will compile the expression "/simple"
059: * and store the compiled version of the query into an instance
060: * of SqlXmlUtil. Then we will save that instance of SqlXmlUtil
061: * as an object in the statement activation, from which we will
062: * receive an id that can be used later to retrieve the object
063: * (i.e. to retrieve the SqlXmlUtil). Then, for *each* row
064: * in xtable, we'll generate the following:
065: *
066: * boolean result =
067: * (new SqlXmlExecutor(activation, compileTimeObjectId)).
068: * XMLExists("/simple", xcol);
069: *
070: * In other words, for each row we create a new instance of
071: * this class and call "XMLExists" on that instance. Then,
072: * as seen below, we retrieve the SqlXmlUtil from the activation
073: * and pass that into a call to "XMLExists" on the XML value
074: * itself (i.e. xcol). XMLDataValue.XMLExists() then uses the
075: * methods and objects (which include the compiled query
076: * expression for "/simple") defined on SqlXmlUtil to complete
077: * the operation.
078: *
079: * Okay, so why do we use this execution-time SqlXmlExecutor class
080: * instead of just generating a call to XMLDataValue.XMLExists()
081: * directly? The reason is that we only want to compile the XML
082: * query expression once per statement--and where possible we'd
083: * also like to only generate re-usable XML-specific objects
084: * once per statement, as well. If instead we generated a call to
085: * XMLDataValue.XMLExists() directly for each row, then we would
086: * have to either pass in the expression string and have XMLDataValue
087: * compile it, or we would have to compile the expression string
088: * and then pass the compiled object into XMLDataValue--in either
089: * case, we'd end up compiling the XML query expression (and creating
090: * the corresponding XML-specific objects) once for each row in
091: * the target result set. By using the "saveObject" functionality
092: * in Activation along with this SqlXmlExecutor class, we make
093: * it so that we only have to compile the XML query expression and
094: * create XML-specific objects once (at compile time), and then
095: * we can re-use those objects for every row in the target
096: * result set. Yes, we're still creating an instance of this
097: * class (SqlXmlExecutor) once per row, and yes we have to fetch
098: * the appropriate SqlXmlUtil object once per row, but this is
099: * still going to be cheaper than having to re-compile the query
100: * expression and re-create XML objects for every row.
101: *
102: * So in short, this class allows us to improve the execution-time
103: * performance of XML operators by allowing us to create XML-
104: * specific objects and compile XML query expressions once per
105: * statement, instead of once per row.
106: *
107: * One final note: the reason this class is in this package
108: * instead of the types package is that, in order to retrieve
109: * the compile-time objects, we have to use the "getSavedObject()"
110: * method on the Activation. But the Activation class is part
111: * of the SQL layer (org.apache.derby.iapi.sql.Activation) and
112: * we want to keep the types layer independent of the SQL layer
113: * because the types can be used during recovery before the SQL
114: * system has booted. So the next logical choices were the compile
115: * package (impl.sql.compile) or the execution package; of those,
116: * the execution package seems more appropriate since this
117: * class is only instantiated and used during execution, not
118: * during compilation.
119: */
120:
121: public class SqlXmlExecutor {
122:
123: // The activation from which we load the compile-time XML
124: // objects (including the compiled XML query expression in
125: // case of XMLEXISTS and XMLQUERY).
126: private Activation activation;
127: private int sqlXUtilId;
128:
129: // Target type and target width that were specified
130: // for an XMLSERIALIZE operator.
131: private int targetTypeId;
132: private int targetMaxWidth;
133:
134: // Whether or not to preserve whitespace for XMLPARSE
135: // operator.
136: private boolean preserveWS;
137:
138: /**
139: * Constructor 1: Used for XMLPARSE op.
140: * @param activation Activation from which to retrieve saved objects
141: * @param utilId Id by which we find saved objects in activation
142: * @param preserveWS Whether or not to preserve whitespace
143: */
144: public SqlXmlExecutor(Activation activation, int utilId,
145: boolean preserveWS) {
146: this .activation = activation;
147: this .sqlXUtilId = utilId;
148: this .preserveWS = preserveWS;
149: }
150:
151: /**
152: * Constructor 2: Used for XMLSERIALIZE op.
153: * @param targetTypeId The string type to which we want to serialize.
154: * @param targetMaxWidth The max width of the target type.
155: */
156: public SqlXmlExecutor(int targetTypeId, int targetMaxWidth) {
157: this .targetTypeId = targetTypeId;
158: this .targetMaxWidth = targetMaxWidth;
159: }
160:
161: /**
162: * Constructor 3: Used for XMLEXISTS/XMLQUERY ops.
163: * @param activation Activation from which to retrieve saved objects
164: * @param utilId Id by which we find saved objects in activation
165: */
166: public SqlXmlExecutor(Activation activation, int utilId) {
167: this .activation = activation;
168: this .sqlXUtilId = utilId;
169: }
170:
171: /**
172: * Make the call to perform an XMLPARSE operation on the
173: * received XML string and store the result in the received
174: * XMLDataValue (or if it's null, create a new one).
175: *
176: * @param xmlText String to parse
177: * @param result XMLDataValue in which to store the result
178: * @return The received XMLDataValue with its content set to
179: * correspond to the received xmlText, if the text constitutes
180: * a valid XML document. If the received XMLDataValue is
181: * null, then create a new one and set its content to
182: * correspond to the received xmlText.
183: */
184: public XMLDataValue XMLParse(StringDataValue xmlText,
185: XMLDataValue result) throws StandardException {
186: if (result == null)
187: result = new XML();
188:
189: if (xmlText.isNull()) {
190: result.setToNull();
191: return result;
192: }
193:
194: return result.XMLParse(xmlText.getString(), preserveWS,
195: getSqlXmlUtil());
196: }
197:
198: /**
199: * Make the call to perform an XMLSERIALIZE operation on the
200: * received XML data value and store the result in the received
201: * StringDataValue (or if it's null, create a new one).
202: *
203: * @param xmlVal XML value to serialize
204: * @param result StringDataValue in which to store the result
205: * @return A serialized (to string) version of this XML object,
206: * in the form of a StringDataValue object.
207: */
208: public StringDataValue XMLSerialize(XMLDataValue xmlVal,
209: StringDataValue result) throws StandardException {
210: return xmlVal
211: .XMLSerialize(result, targetTypeId, targetMaxWidth);
212: }
213:
214: /**
215: * Make the call to perform an XMLEXISTS operation on the
216: * received XML data value.
217: *
218: * @param xExpr Query expression to be evaluated
219: * @param xmlContext Context node against which to evaluate
220: * the expression.
221: * @return True if evaluation of the query expression
222: * against xmlContext returns at least one item; unknown if
223: * either the xml value is NULL; false otherwise.
224: */
225: public BooleanDataValue XMLExists(StringDataValue xExpr,
226: XMLDataValue xmlContext) throws StandardException {
227: return xmlContext.XMLExists(getSqlXmlUtil());
228: }
229:
230: /**
231: * Make the call to perform an XMLQUERY operation on the
232: * received XML data value and store the result in the
233: * received result holder (or, if it's null, create a
234: * new one).
235: *
236: * @param xExpr Query expression to be evaluated
237: * @param xmlContext Context node against which to evaluate
238: * the expression.
239: * @param result XMLDataValue in which to store the result
240: * @return The received XMLDataValue with its content set to
241: * result of evaulating the query expression against xmlContext.
242: * If the received XMLDataValue is null, then create a new one
243: * and set its content to correspond to the received xmlText.
244: */
245: public XMLDataValue XMLQuery(StringDataValue xExpr,
246: XMLDataValue xmlContext, XMLDataValue result)
247: throws StandardException {
248: return xmlContext.XMLQuery(result, getSqlXmlUtil());
249: }
250:
251: /**
252: * Return the saved object in this.activation that corresponds to
253: * this.sqlxUtilId. Assumption is that those fields have been
254: * set by the time we get here.
255: */
256: private SqlXmlUtil getSqlXmlUtil() throws StandardException {
257: return (SqlXmlUtil) activation.getPreparedStatement()
258: .getSavedObject(sqlXUtilId);
259: }
260:
261: }
|