001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.UnaryOperatorNode
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.compile;
023:
024: import org.apache.derby.iapi.store.access.Qualifier;
025:
026: import org.apache.derby.iapi.sql.compile.Visitable;
027: import org.apache.derby.iapi.sql.compile.Visitor;
028:
029: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
030:
031: import org.apache.derby.iapi.reference.SQLState;
032: import org.apache.derby.iapi.reference.ClassName;
033: import org.apache.derby.iapi.error.StandardException;
034: import org.apache.derby.iapi.services.sanity.SanityManager;
035: import org.apache.derby.iapi.services.compiler.MethodBuilder;
036: import org.apache.derby.iapi.services.compiler.LocalField;
037: import org.apache.derby.iapi.services.io.StoredFormatIds;
038:
039: import org.apache.derby.iapi.types.TypeId;
040: import org.apache.derby.iapi.types.DataTypeDescriptor;
041: import org.apache.derby.iapi.types.SqlXmlUtil;
042:
043: import java.lang.reflect.Modifier;
044: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
045:
046: import org.apache.derby.iapi.util.JBitSet;
047: import org.apache.derby.iapi.services.classfile.VMOpcode;
048:
049: import java.sql.Types;
050: import java.util.Vector;
051:
052: /**
053: * A UnaryOperatorNode represents a built-in unary operator as defined by
054: * the ANSI/ISO SQL standard. This covers operators like +, -, NOT, and IS NULL.
055: * Java operators are not represented here: the JSQL language allows Java
056: * methods to be called from expressions, but not Java operators.
057: *
058: * @author Jeff Lichtman
059: */
060:
061: public class UnaryOperatorNode extends ValueNode {
062: String operator;
063: String methodName;
064: int operatorType;
065:
066: String resultInterfaceType;
067: String receiverInterfaceType;
068:
069: /**
070: * WARNING: operand may be NULL for COUNT(*).
071: */
072: ValueNode operand;
073:
074: public final static int UNARY_PLUS = 1;
075: public final static int UNARY_MINUS = 2;
076: public final static int NOT = 3;
077: public final static int IS_NULL = 4;
078:
079: // At the time of adding XML support, it was decided that
080: // we should avoid creating new OperatorNodes where possible.
081: // So for the XML-related unary operators we just add the
082: // necessary code to _this_ class, similar to what is done in
083: // TernarnyOperatorNode. Subsequent unary operators (whether
084: // XML-related or not) should follow this example when
085: // possible.
086:
087: public final static int XMLPARSE_OP = 0;
088: public final static int XMLSERIALIZE_OP = 1;
089:
090: // NOTE: in the following 4 arrays, order
091: // IS important.
092:
093: static final String[] UnaryOperators = { "xmlparse", "xmlserialize" };
094:
095: static final String[] UnaryMethodNames = { "XMLParse",
096: "XMLSerialize" };
097:
098: static final String[] UnaryResultTypes = { ClassName.XMLDataValue, // XMLParse
099: ClassName.StringDataValue // XMLSerialize
100: };
101:
102: static final String[] UnaryArgTypes = { ClassName.StringDataValue, // XMLParse
103: ClassName.XMLDataValue // XMLSerialize
104: };
105:
106: // Array to hold Objects that contain primitive
107: // args required by the operator method call.
108: private Object[] additionalArgs;
109:
110: // Class used to hold XML-specific objects required for
111: // parsing/serializing XML data.
112: private SqlXmlUtil sqlxUtil;
113:
114: /**
115: * Initializer for a UnaryOperatorNode.
116: *
117: * <ul>
118: * @param operand The operand of the node
119: * @param operatorOrOpType Either 1) the name of the operator,
120: * OR 2) an Integer holding the operatorType for this operator.
121: * @param methodNameOrAddedArgs Either 1) name of the method
122: * to call for this operator, or 2) an array of Objects
123: * from which primitive method parameters can be
124: * retrieved.
125: */
126:
127: public void init(Object operand, Object operatorOrOpType,
128: Object methodNameOrAddedArgs) {
129: this .operand = (ValueNode) operand;
130: if (operatorOrOpType instanceof String) {
131: // then 2nd and 3rd params are operator and methodName,
132: // respectively.
133: this .operator = (String) operatorOrOpType;
134: this .methodName = (String) methodNameOrAddedArgs;
135: this .operatorType = -1;
136: } else {
137: // 2nd and 3rd params are operatorType and additional args,
138: // respectively.
139: if (SanityManager.DEBUG) {
140: SanityManager
141: .ASSERT(
142: ((operatorOrOpType instanceof Integer) && ((methodNameOrAddedArgs == null) || (methodNameOrAddedArgs instanceof Object[]))),
143: "Init params in UnaryOperator node have the "
144: + "wrong type.");
145: }
146: this .operatorType = ((Integer) operatorOrOpType).intValue();
147: this .operator = UnaryOperators[this .operatorType];
148: this .methodName = UnaryMethodNames[this .operatorType];
149: this .resultInterfaceType = UnaryResultTypes[this .operatorType];
150: this .receiverInterfaceType = UnaryArgTypes[this .operatorType];
151: this .additionalArgs = (Object[]) methodNameOrAddedArgs;
152: }
153: }
154:
155: /**
156: * Initializer for a UnaryOperatorNode
157: *
158: * @param operand The operand of the node
159: */
160: public void init(Object operand) {
161: this .operand = (ValueNode) operand;
162: this .operatorType = -1;
163: }
164:
165: /**
166: * Set the operator.
167: *
168: * @param operator The operator.
169: */
170: void setOperator(String operator) {
171: this .operator = operator;
172: this .operatorType = -1;
173: }
174:
175: /**
176: * Get the operator of this unary operator.
177: *
178: * @return The operator of this unary operator.
179: */
180: String getOperatorString() {
181: return operator;
182: }
183:
184: /**
185: * Set the methodName.
186: *
187: * @param methodName The methodName.
188: */
189: void setMethodName(String methodName) {
190: this .methodName = methodName;
191: this .operatorType = -1;
192: }
193:
194: /**
195: * Convert this object to a String. See comments in QueryTreeNode.java
196: * for how this should be done for tree printing.
197: *
198: * @return This object as a String
199: */
200:
201: public String toString() {
202: if (SanityManager.DEBUG) {
203: return "operator: " + operator + "\n" + "methodName: "
204: + methodName + "\n" + super .toString();
205: } else {
206: return "";
207: }
208: }
209:
210: /**
211: * Prints the sub-nodes of this object. See QueryTreeNode.java for
212: * how tree printing is supposed to work.
213: *
214: * @param depth The depth of this node in the tree
215: */
216:
217: public void printSubNodes(int depth) {
218: if (SanityManager.DEBUG) {
219: super .printSubNodes(depth);
220:
221: if (operand != null) {
222: printLabel(depth, "operand: ");
223: operand.treePrint(depth + 1);
224: }
225: }
226: }
227:
228: /**
229: * Get the operand of this unary operator.
230: *
231: * @return The operand of this unary operator.
232: */
233: public ValueNode getOperand() {
234: return operand;
235: }
236:
237: /**
238: * Get the parameter operand of this unary operator.
239: * For the example below, for abs unary operator node, we want to get ?
240: * select * from t1 where -? = max_cni(abs(-?), sqrt(+?))
241: *
242: * This gets called when ParameterNode is needed to get parameter
243: * specific information like getDefaultValue(), getParameterNumber() etc
244: *
245: * @return The parameter operand of this unary operator else null.
246: */
247: public ParameterNode getParameterOperand() {
248: if (requiresTypeFromContext() == false)
249: return null;
250: else {
251: UnaryOperatorNode tempUON = this ;
252: while (!(tempUON.getOperand() instanceof ParameterNode))
253: tempUON = (UnaryOperatorNode) tempUON.getOperand();
254: return (ParameterNode) (tempUON.getOperand());
255: }
256: }
257:
258: /**
259: * Set the clause that this node appears in.
260: *
261: * @param clause The clause that this node appears in.
262: */
263: public void setClause(int clause) {
264: super .setClause(clause);
265:
266: /*
267: ** Operator may be null for COUNT(*)
268: */
269: if (operand != null) {
270: operand.setClause(clause);
271: }
272: }
273:
274: /**
275: * Bind this expression. This means binding the sub-expressions,
276: * as well as figuring out what the return type is for this expression.
277: *
278: * @param fromList The FROM list for the query this
279: * expression is in, for binding columns.
280: * @param subqueryList The subquery list being built as we find SubqueryNodes
281: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
282: *
283: * @return The new top of the expression tree.
284: *
285: * @exception StandardException Thrown on error
286: */
287:
288: public ValueNode bindExpression(FromList fromList,
289: SubqueryList subqueryList, Vector aggregateVector)
290: throws StandardException {
291: return bindUnaryOperator(fromList, subqueryList,
292: aggregateVector);
293: }
294:
295: /**
296: * Workhorse for bindExpression. This exists so it can be called
297: * by child classes.
298: */
299: protected ValueNode bindUnaryOperator(FromList fromList,
300: SubqueryList subqueryList, Vector aggregateVector)
301: throws StandardException {
302: /*
303: ** Operand can be null for COUNT(*) which
304: ** is treated like a normal aggregate.
305: */
306: if (operand == null) {
307: return this ;
308: }
309:
310: //Return with no binding, if the type of unary minus/plus parameter is not set yet.
311: if (operand.requiresTypeFromContext()
312: && ((operator.equals("-") || operator.equals("+")))
313: && operand.getTypeServices() == null)
314: return this ;
315:
316: operand = operand.bindExpression(fromList, subqueryList,
317: aggregateVector);
318:
319: if (operand.requiresTypeFromContext())
320: bindParameter();
321:
322: /* If the operand is not a built-in type, then generate a bound conversion
323: * tree to a built-in type.
324: */
325: if (!(operand instanceof UntypedNullConstantNode)
326: && operand.getTypeId().userType()
327: && !(this instanceof IsNullNode)) {
328: operand = operand.genSQLJavaSQLTree();
329: }
330:
331: if (operatorType == XMLPARSE_OP)
332: bindXMLParse();
333: else if (operatorType == XMLSERIALIZE_OP)
334: bindXMLSerialize();
335:
336: return this ;
337: }
338:
339: /**
340: * Bind an XMLPARSE operator. Makes sure the operand type
341: * is correct, and sets the result type.
342: *
343: * @exception StandardException Thrown on error
344: */
345: public void bindXMLParse() throws StandardException {
346: // Check the type of the operand - this function is allowed only on
347: // string value (char) types.
348: TypeId operandType = operand.getTypeId();
349: if (operandType != null) {
350: switch (operandType.getJDBCTypeId()) {
351: case Types.CHAR:
352: case Types.VARCHAR:
353: case Types.LONGVARCHAR:
354: case Types.CLOB:
355: break;
356: default: {
357: throw StandardException.newException(
358: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE,
359: methodName, operandType.getSQLTypeName());
360: }
361: }
362: }
363:
364: // Create a new XML compiler object; the constructor
365: // here automatically creates the XML-specific objects
366: // required for parsing/serializing XML, so all we
367: // have to do is create an instance.
368: sqlxUtil = new SqlXmlUtil();
369:
370: // The result type of XMLParse() is always an XML type.
371: setType(DataTypeDescriptor
372: .getBuiltInDataTypeDescriptor(StoredFormatIds.XML_TYPE_ID));
373: }
374:
375: /**
376: * Bind an XMLSERIALIZE operator. Makes sure the operand type
377: * and target type are both correct, and sets the result type.
378: *
379: * @exception StandardException Thrown on error
380: */
381: public void bindXMLSerialize() throws StandardException {
382: TypeId operandType;
383:
384: // Check the type of the operand - this function is allowed only on
385: // the XML type.
386: operandType = operand.getTypeId();
387: if ((operandType != null) && !operandType.isXMLTypeId()) {
388: throw StandardException.newException(
389: SQLState.LANG_UNARY_FUNCTION_BAD_TYPE, methodName,
390: operandType.getSQLTypeName());
391: }
392:
393: // Check the target type. We only allow string types to be used as
394: // the target type. The targetType is stored as the first Object
395: // in our list of additional parameters, so we have to retrieve
396: // it from there.
397: if (SanityManager.DEBUG) {
398: SanityManager
399: .ASSERT(
400: ((additionalArgs != null) && (additionalArgs.length > 0)),
401: "Failed to locate target type for XMLSERIALIZE operator");
402: }
403:
404: DataTypeDescriptor targetType = (DataTypeDescriptor) additionalArgs[0];
405:
406: TypeId targetTypeId = targetType.getTypeId();
407: switch (targetTypeId.getJDBCTypeId()) {
408: case Types.CHAR:
409: case Types.VARCHAR:
410: case Types.LONGVARCHAR:
411: case Types.CLOB:
412: break;
413: default: {
414: throw StandardException.newException(
415: SQLState.LANG_INVALID_XMLSERIALIZE_TYPE,
416: targetTypeId.getSQLTypeName());
417: }
418: }
419:
420: // The result type of XMLSerialize() is always a string; which
421: // kind of string is determined by the targetType field.
422: setType(targetType);
423: }
424:
425: /**
426: * Preprocess an expression tree. We do a number of transformations
427: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
428: * subquery flattening.
429: * NOTE: This is done before the outer ResultSetNode is preprocessed.
430: *
431: * @param numTables Number of tables in the DML Statement
432: * @param outerFromList FromList from outer query block
433: * @param outerSubqueryList SubqueryList from outer query block
434: * @param outerPredicateList PredicateList from outer query block
435: *
436: * @return The modified expression
437: *
438: * @exception StandardException Thrown on error
439: */
440: public ValueNode preprocess(int numTables, FromList outerFromList,
441: SubqueryList outerSubqueryList,
442: PredicateList outerPredicateList) throws StandardException {
443: if (operand != null) {
444: operand = operand.preprocess(numTables, outerFromList,
445: outerSubqueryList, outerPredicateList);
446: }
447: return this ;
448: }
449:
450: /**
451: * Categorize this predicate. Initially, this means
452: * building a bit map of the referenced tables for each predicate.
453: * If the source of this ColumnReference (at the next underlying level)
454: * is not a ColumnReference or a VirtualColumnNode then this predicate
455: * will not be pushed down.
456: *
457: * For example, in:
458: * select * from (select 1 from s) a (x) where x = 1
459: * we will not push down x = 1.
460: * NOTE: It would be easy to handle the case of a constant, but if the
461: * inner SELECT returns an arbitrary expression, then we would have to copy
462: * that tree into the pushed predicate, and that tree could contain
463: * subqueries and method calls.
464: * RESOLVE - revisit this issue once we have views.
465: *
466: * @param referencedTabs JBitSet with bit map of referenced FromTables
467: * @param simplePredsOnly Whether or not to consider method
468: * calls, field references and conditional nodes
469: * when building bit map
470: *
471: * @return boolean Whether or not source.expression is a ColumnReference
472: * or a VirtualColumnNode.
473: *
474: * @exception StandardException Thrown on error
475: */
476: public boolean categorize(JBitSet referencedTabs,
477: boolean simplePredsOnly) throws StandardException {
478: return (operand == null) ? false : operand.categorize(
479: referencedTabs, simplePredsOnly);
480: }
481:
482: /**
483: * Remap all ColumnReferences in this tree to be clones of the
484: * underlying expression.
485: *
486: * @return ValueNode The remapped expression tree.
487: *
488: * @exception StandardException Thrown on error
489: */
490: public ValueNode remapColumnReferencesToExpressions()
491: throws StandardException {
492: if (operand != null) {
493: operand = operand.remapColumnReferencesToExpressions();
494: }
495: return this ;
496: }
497:
498: /**
499: * Return whether or not this expression tree represents a constant expression.
500: *
501: * @return Whether or not this expression tree represents a constant expression.
502: */
503: public boolean isConstantExpression() {
504: return (operand == null) ? true : operand
505: .isConstantExpression();
506: }
507:
508: /** @see ValueNode#constantExpression */
509: public boolean constantExpression(PredicateList whereClause) {
510: return (operand == null) ? true : operand
511: .constantExpression(whereClause);
512: }
513:
514: /**
515: * @see ValueNode#requiresTypeFromContext
516: */
517: public boolean requiresTypeFromContext() {
518: if (operand == null)
519: return false;
520: else
521: return (operand.requiresTypeFromContext());
522: }
523:
524: /**
525: * Returns true if this UnaryOperatorNode is for -?/+?.
526: * This is required to check -?/+? say in the following sql
527: * select * from t1 where -? and c11=c11 or +?
528: *
529: * @return True if this +?/-? node
530: */
531: public boolean isUnaryMinusOrPlusWithParameter() {
532: if (operand != null
533: && operand instanceof ParameterNode
534: && operand.requiresTypeFromContext()
535: && (operator != null && (operator.equals("-") || operator
536: .equals("+"))))
537: return true;
538: else
539: return false;
540: }
541:
542: /**
543: * By default unary operators don't accept ? parameters as operands.
544: * This can be over-ridden for particular unary operators.
545: *
546: * We throw an exception if the parameter doesn't have a datatype
547: * assigned to it yet.
548: *
549: * @exception StandardException Thrown if ? parameter doesn't
550: * have a type bound to it yet.
551: * ? parameter where it isn't allowed.
552: */
553:
554: void bindParameter() throws StandardException {
555: if (operatorType == XMLPARSE_OP) {
556: /* SQL/XML[2006] allows both binary and character strings for
557: * the XMLParse parameter (section 10.16:Function). The spec
558: * also goes on to say, in section 6.15:Conformance Rules:4,
559: * that:
560: *
561: * "Without Feature X066, XMLParse: BLOB input and DOCUMENT
562: * option, in conforming SQL language, the declared type of
563: * the <string value expression> immediately contained in
564: * <XML parse> shall not be a binary string type."
565: *
566: * Thus since Derby doesn't currently support BLOB input,
567: * we have to ensure that the "declared type" of the parameter
568: * is not a binary string type; i.e. it must be a character
569: * string type. Since there's no way to determine what the
570: * declared type is from the XMLPARSE syntax, the user must
571: * explicitly declare the type of the parameter, and it must
572: * be a character string. They way s/he does that is by
573: * specifying an explicit CAST on the parameter, such as:
574: *
575: * insert into myXmlTable (xcol) values
576: * XMLPARSE(DOCUMENT cast (? as CLOB) PRESERVE WHITESPACE);
577: *
578: * If that was done then we wouldn't be here; we only get
579: * here if the parameter was specified without a cast. That
580: * means we don't know what the "declared type" is and so
581: * we throw an error.
582: */
583: throw StandardException
584: .newException(SQLState.LANG_XMLPARSE_UNKNOWN_PARAM_TYPE);
585: } else if (operatorType == XMLSERIALIZE_OP) {
586: // For now, since JDBC has no type defined for XML, we
587: // don't allow binding to an XML parameter.
588: throw StandardException
589: .newException(SQLState.LANG_ATTEMPT_TO_BIND_XML);
590: } else if (operand.getTypeServices() == null) {
591: throw StandardException.newException(
592: SQLState.LANG_UNARY_OPERAND_PARM, operator);
593: }
594: }
595:
596: /**
597: * Do code generation for this unary operator.
598: *
599: * @param acb The ExpressionClassBuilder for the class we're generating
600: * @param mb The method the expression will go into
601: *
602: *
603: * @exception StandardException Thrown on error
604: */
605:
606: public void generateExpression(ExpressionClassBuilder acb,
607: MethodBuilder mb) throws StandardException {
608: if (operand == null)
609: return;
610:
611: // For XML operator we do some extra work.
612: boolean xmlGen = (operatorType == XMLPARSE_OP)
613: || (operatorType == XMLSERIALIZE_OP);
614:
615: if (xmlGen) {
616: // We create an execution-time object from which we call
617: // the necessary methods. We do this for two reasons: 1) this
618: // level of indirection allows us to separate the XML data type
619: // from the required XML implementation classes (esp. JAXP and
620: // Xalan classes)--for more on how this works, see the comments
621: // in SqlXmlUtil.java; and 2) this allows us to create the
622: // required XML objects a single time (which we did at bind time
623: // when we created a new SqlXmlUtil) and then reuse those objects
624: // for each row in the target result set, instead of creating
625: // new objects every time; see SqlXmlUtil.java for more.
626: mb
627: .pushNewStart("org.apache.derby.impl.sql.execute.SqlXmlExecutor");
628: mb.pushNewComplete(addXmlOpMethodParams(acb, mb));
629: }
630:
631: String resultTypeName = (operatorType == -1) ? getTypeCompiler()
632: .interfaceName()
633: : resultInterfaceType;
634:
635: // System.out.println("resultTypeName " + resultTypeName + " method " + methodName);
636: // System.out.println("isBooleanTypeId() " + getTypeId().isBooleanTypeId());
637:
638: boolean needField = !getTypeId().isBooleanTypeId();
639:
640: String receiverType = getReceiverInterfaceName();
641: operand.generateExpression(acb, mb);
642: mb.cast(receiverType);
643:
644: if (needField) {
645:
646: /* Allocate an object for re-use to hold the result of the operator */
647: LocalField field = acb.newFieldDeclaration(
648: Modifier.PRIVATE, resultTypeName);
649: mb.getField(field);
650:
651: /* If we're calling a method on a class (SqlXmlExecutor) instead
652: * of calling a method on the operand interface, then we invoke
653: * VIRTUAL; we then have 2 args (the operand and the local field)
654: * instead of one, i.e:
655: *
656: * SqlXmlExecutor.method(operand, field)
657: *
658: * instead of
659: *
660: * <operand>.method(field).
661: */
662: if (xmlGen) {
663: mb.callMethod(VMOpcode.INVOKEVIRTUAL, null, methodName,
664: resultTypeName, 2);
665: } else {
666: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
667: methodName, resultTypeName, 1);
668: }
669:
670: /*
671: ** Store the result of the method call in the field, so we can re-use
672: ** the object.
673: */
674: mb.putField(field);
675: } else {
676: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
677: methodName, resultTypeName, 0);
678: }
679: }
680:
681: /**
682: * Determine the type the binary method is called on.
683: * By default, based on the receiver.
684: *
685: * Override in nodes that use methods on super-interfaces of
686: * the receiver's interface, such as comparisons.
687: *
688: * @exception StandardException Thrown on error
689: */
690: public String getReceiverInterfaceName() throws StandardException {
691: if (SanityManager.DEBUG) {
692: SanityManager.ASSERT(operand != null,
693: "cannot get interface without operand");
694: }
695:
696: if (operatorType != -1)
697: return receiverInterfaceType;
698:
699: return operand.getTypeCompiler().interfaceName();
700: }
701:
702: /**
703: * Return the variant type for the underlying expression.
704: * The variant type can be:
705: * VARIANT - variant within a scan
706: * (method calls and non-static field access)
707: * SCAN_INVARIANT - invariant within a scan
708: * (column references from outer tables)
709: * QUERY_INVARIANT - invariant within the life of a query
710: * (constant expressions)
711: * CONSTANT - immutable
712: *
713: * @return The variant type for the underlying expression.
714: * @exception StandardException thrown on error
715: */
716: protected int getOrderableVariantType() throws StandardException {
717: /*
718: ** If we have nothing in the operator, then
719: ** it must be constant.
720: */
721: return (operand != null) ? operand.getOrderableVariantType()
722: : Qualifier.CONSTANT;
723: }
724:
725: /**
726: * Accept a visitor, and call v.visit()
727: * on child nodes as necessary.
728: *
729: * @param v the visitor
730: *
731: * @exception StandardException on error
732: */
733: public Visitable accept(Visitor v) throws StandardException {
734: Visitable returnNode = v.visit(this );
735:
736: if (v.skipChildren(this )) {
737: return returnNode;
738: }
739:
740: if (operand != null && !v.stopTraversal()) {
741: operand = (ValueNode) operand.accept(v);
742: }
743:
744: return returnNode;
745: }
746:
747: /**
748: * Add some additional arguments to our method call for
749: * XML related operations like XMLPARSE and XMLSERIALIZE.
750: * @param mb The MethodBuilder that will make the call.
751: * @return Number of parameters added.
752: */
753: protected int addXmlOpMethodParams(ExpressionClassBuilder acb,
754: MethodBuilder mb) throws StandardException {
755: if ((operatorType != XMLPARSE_OP)
756: && (operatorType != XMLSERIALIZE_OP))
757: // nothing to do.
758: return 0;
759:
760: if (operatorType == XMLSERIALIZE_OP) {
761: // We push the target type's JDBC type id as well as
762: // the maximum width, since both are required when
763: // we actually perform the operation, and both are
764: // primitive types. Note: we don't have to save
765: // any objects for XMLSERIALIZE because it doesn't
766: // require any XML-specific objects: it just returns
767: // the serialized version of the XML value, which we
768: // already found when the XML value was created (ex.
769: // as part of the XMLPARSE work).
770: DataTypeDescriptor targetType = (DataTypeDescriptor) additionalArgs[0];
771: mb.push(targetType.getJDBCTypeId());
772: mb.push(targetType.getMaximumWidth());
773: return 2;
774: }
775:
776: /* Else we're here for XMLPARSE. */
777:
778: // Push activation, which we use at execution time to
779: // get our saved object (which will hold objects used
780: // for parsing/serializing) back.
781: acb.pushThisAsActivation(mb);
782:
783: // Push our XML object (used for parsing/serializing) as
784: // a saved object, so that we can retrieve it at execution
785: // time. This allows us to avoid having to re-create the
786: // objects for every row in a given result set.
787: mb.push(getCompilerContext().addSavedObject(sqlxUtil));
788:
789: // Push whether or not we want to preserve whitespace.
790: mb.push(((Boolean) additionalArgs[0]).booleanValue());
791: return 3;
792: }
793:
794: /**
795: * @throws StandardException
796: * {@inheritDoc}
797: */
798: protected boolean isEquivalent(ValueNode o)
799: throws StandardException {
800: if (isSameNodeType(o)) {
801: // the first condition in the || covers the case when
802: // both operands are null.
803: UnaryOperatorNode other = (UnaryOperatorNode) o;
804: return (operator.equals(other.operator) && ((operand == other.operand) || ((operand != null) && operand
805: .isEquivalent(other.operand))));
806: }
807: return false;
808: }
809: }
|