001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.LikeEscapeOperatorNode
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.services.compiler.MethodBuilder;
025: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
026:
027: import org.apache.derby.iapi.services.sanity.SanityManager;
028:
029: import org.apache.derby.iapi.error.StandardException;
030:
031: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
032: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
033: import org.apache.derby.iapi.types.TypeId;
034: import org.apache.derby.iapi.types.StringDataValue;
035: import org.apache.derby.iapi.types.DataTypeDescriptor;
036:
037: import org.apache.derby.iapi.sql.compile.TypeCompiler;
038:
039: import org.apache.derby.iapi.reference.SQLState;
040:
041: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
042: import org.apache.derby.iapi.services.compiler.LocalField;
043:
044: import org.apache.derby.iapi.util.ReuseFactory;
045:
046: import java.lang.reflect.Modifier;
047: import org.apache.derby.iapi.services.classfile.VMOpcode;
048:
049: import org.apache.derby.iapi.types.Like;
050:
051: import java.sql.Types;
052: import org.apache.derby.iapi.reference.ClassName;
053:
054: import java.util.Vector;
055:
056: /**
057: * This node represents a like comparison operator (no escape)
058:
059:
060: If the like pattern is a constant or a parameter then if possible
061: the like is modified to include a >= and < operator. In some cases
062: the like can be eliminated.
063:
064:
065: constant or parameter LIKE pattern with prefix followed by optional wild card
066: e.g. Cloudscape%
067:
068: CHAR(n), VARCHAR(n) where n < 255
069:
070: >= prefix padded with '\u0000' to length n -- e.g. Cloudscape\u0000\u0000
071: <= prefix appended with '\uffff' -- e.g. Cloudscape\uffff
072:
073: [ can eliminate LIKE if constant. ]
074:
075:
076: CHAR(n), VARCHAR(n), LONG VARCHAR where n >= 255
077:
078: >= prefix backed up one characer
079: <= prefix appended with '\uffff'
080:
081: no elimination of like
082:
083:
084: parameter like pattern starts with wild card
085:
086: CHAR(n), VARCHAR(n) where n <= 256
087:
088: >= '\u0000' padded with '\u0000' to length n
089: <= '\uffff'
090:
091: no elimination of like
092:
093: CHAR(n), VARCHAR(n), LONG VARCHAR where n > 256
094:
095:
096: >= NULL
097:
098: <= '\uffff'
099:
100:
101: Note that the Unicode value is '\uffff' is defined as not a character value
102: and can be used by a program for any purpose. We use it to set an upper
103: bound on a character range with a less than predicate. We only need a single
104: '\uffff' appended because the string 'Cloudscape\uffff\uffff' is not a valid
105: String because '\uffff' is not a valid character.
106:
107:
108:
109:
110:
111:
112:
113:
114:
115: *
116: * @author ames
117: * converted to TernaryOperatorNode by been
118: */
119:
120: public final class LikeEscapeOperatorNode extends TernaryOperatorNode {
121: boolean addedEquals;
122: String escape;
123:
124: /**
125: * Initializer for a LikeEscapeOperatorNode
126: *
127: * receiver like pattern [ escape escapeValue ]
128: *
129: * @param receiver The left operand of the like, column, CharConstant or Parameter
130: * @param leftOperand The right operand of the like, the pattern
131: * @param rightOperand The optional escape clause, null if not present
132: */
133:
134: public void init(Object receiver, Object leftOperand,
135: Object rightOperand) {
136: /* By convention, the method name for the like operator is "like" */
137: // super.init(leftOperand, rightOperand, new Integer(LIKE), null, null);
138: super .init(receiver, leftOperand, rightOperand, ReuseFactory
139: .getInteger(TernaryOperatorNode.LIKE), null);
140: }
141:
142: /**
143: * overrides BindOperatorNode.bindExpression because like has special
144: * requirements for parameter binding.
145: *
146: * @return The new top of the expression tree.
147: *
148: * @exception StandardException thrown on failure
149: */
150: public ValueNode bindExpression(FromList fromList,
151: SubqueryList subqueryList, Vector aggregateVector)
152: throws StandardException {
153: super .bindExpression(fromList, subqueryList, aggregateVector);
154:
155: String pattern = null;
156:
157: // pattern cannot be a column
158: if (leftOperand instanceof ColumnReference)
159: throw StandardException
160: .newException(SQLState.LANG_DB2_LIKE_SYNTAX_ERROR);
161:
162: // pattern must be a string or a parameter
163:
164: if (!(leftOperand.requiresTypeFromContext())
165: && !(leftOperand.getTypeId().isStringTypeId()))
166: throw StandardException.newException(
167: SQLState.LANG_DB2_FUNCTION_INCOMPATIBLE, "LIKE",
168: "FUNCTION");
169:
170: // escape cannot be a column
171: if (rightOperand != null
172: && rightOperand instanceof ColumnReference) {
173: throw StandardException.newException(
174: SQLState.LANG_INVALID_ESCAPE_CHARACTER,
175: ((ColumnReference) rightOperand).getColumnName());
176: }
177:
178: // escape must be a string or a parameter
179: if ((rightOperand != null)
180: && !(rightOperand.requiresTypeFromContext())
181: && !(rightOperand.getTypeId().isStringTypeId())) {
182: throw StandardException.newException(
183: SQLState.LANG_DB2_FUNCTION_INCOMPATIBLE, "LIKE",
184: "FUNCTION");
185: }
186:
187: // deal with operand parameters
188:
189: /*
190: * Is there a ? parameter on the left?
191: * Do left first because its length is always maximum;
192: * a parameter on the right copies its length from
193: * the left, since it won't match if it is any longer than it.
194: */
195:
196: if (receiver.requiresTypeFromContext()) {
197: receiver.setType(new DataTypeDescriptor(TypeId
198: .getBuiltInTypeId(Types.VARCHAR), true));
199: }
200:
201: /*
202: * Is there a ? parameter for the PATTERN of LIKE?
203: * Copy from the receiver -- legal if both are parameters,
204: * both will be max length.
205: * REMIND: should nullability be copied, or set to true?
206: */
207:
208: if (leftOperand.requiresTypeFromContext()) {
209: /*
210: * Set the pattern to the type of the left parameter, if
211: * the left is a string, otherwise set it to be VARCHAR.
212: */
213: if (receiver.getTypeId().isStringTypeId()) {
214: leftOperand.setType(receiver.getTypeServices());
215: } else {
216: leftOperand.setType(new DataTypeDescriptor(TypeId
217: .getBuiltInTypeId(Types.VARCHAR), true));
218: }
219: }
220:
221: /*
222: * Is there a ? parameter for the ESCAPE of LIKE?
223: * Copy from the receiver -- legal if both are parameters,
224: * both will be max length. nullability is set to true.
225: */
226:
227: if (rightOperand != null
228: && rightOperand.requiresTypeFromContext()) {
229: /*
230: * Set the pattern to the type of the left parameter, if
231: * the left is a string, otherwise set it to be VARCHAR.
232: */
233: if (receiver.getTypeId().isStringTypeId()) {
234: rightOperand.setType(receiver.getTypeServices());
235: } else {
236: rightOperand.setType(new DataTypeDescriptor(TypeId
237: .getBuiltInTypeId(Types.VARCHAR), true));
238: }
239: }
240:
241: bindToBuiltIn();
242:
243: TypeCompiler receiverTC = receiver.getTypeCompiler();
244: TypeCompiler leftTC = leftOperand.getTypeCompiler();
245:
246: /* The receiver must be a string type
247: */
248: if (!receiver.getTypeId().isStringTypeId()) {
249: throw StandardException.newException(
250: SQLState.LANG_DB2_FUNCTION_INCOMPATIBLE, "LIKE",
251: "FUNCTION");
252:
253: }
254:
255: /* If either the left or right operands are non-string types,
256: * then we generate an implicit cast to VARCHAR.
257: */
258: if (!leftOperand.getTypeId().isStringTypeId()) {
259: leftOperand = castArgToString(leftOperand);
260: leftTC = leftOperand.getTypeCompiler();
261: }
262:
263: if (rightOperand != null) {
264: rightOperand = castArgToString(rightOperand);
265: }
266:
267: /* Remember whether or not right side is a string constant.
268: * We need to remember here so that we can transform
269: * LIKE 'constant' into = 'constant' for national char
270: * columns.
271: */
272: boolean leftConstant = (leftOperand instanceof CharConstantNode);
273: if (leftConstant) {
274: pattern = ((CharConstantNode) leftOperand).getString();
275: }
276:
277: boolean rightConstant = (rightOperand instanceof CharConstantNode);
278:
279: if (rightConstant) {
280: escape = ((CharConstantNode) rightOperand).getString();
281: if (escape.length() != 1) {
282: throw StandardException.newException(
283: SQLState.LANG_INVALID_ESCAPE_CHARACTER, escape);
284: }
285: } else if (rightOperand == null) {
286: // No Escape clause: Let optimization continue for the = case below
287: rightConstant = true;
288: }
289:
290: /* If the left side of LIKE is a ColumnReference and
291: * the right side is a string constant without
292: * a wildcard then we transform the LIKE into the
293: * equivalent LIKE AND =.
294: * If we have an escape clause it also must be a constant.
295: * These types of transformations are normally done
296: * at preprocess time, but we make an exception
297: * and do this one at bind time because we transform
298: * a NOT LIKE 'a' into (a LIKE 'a') = false prior to
299: * preprocessing.
300: * The transformed tree will become:
301: * AND
302: * / \
303: * LIKE =
304: */
305:
306: if (receiver instanceof ColumnReference && leftConstant
307: && rightConstant) {
308: if (Like.isOptimizable(pattern)) {
309: String newPattern = null;
310:
311: /*
312: * If our pattern has no pattern chars (after stripping them out
313: * for the ESCAPE case), we are good to apply = to this match
314: */
315:
316: if (escape != null) {
317: /* we return a new pattern stripped of ESCAPE chars */
318: newPattern = Like.stripEscapesNoPatternChars(
319: pattern, escape.charAt(0));
320: } else if (pattern.indexOf('_') == -1
321: && pattern.indexOf('%') == -1) {
322: newPattern = pattern;
323: }
324:
325: if (newPattern != null) {
326: AndNode newAnd = null;
327: BinaryComparisonOperatorNode equals = null;
328: ValueNode leftClone = receiver.getClone();
329:
330: // Remember that we did xform, see preprocess()
331: addedEquals = true;
332:
333: equals = (BinaryComparisonOperatorNode) getNodeFactory()
334: .getNode(
335: C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,
336: leftClone,
337: (ValueNode) getNodeFactory()
338: .getNode(
339: C_NodeTypes.CHAR_CONSTANT_NODE,
340: newPattern,
341: getContextManager()),
342: getContextManager());
343:
344: // Set forQueryRewrite to bypass comparability checks
345: equals.setForQueryRewrite(true);
346: equals = (BinaryComparisonOperatorNode) equals
347: .bindExpression(fromList, subqueryList,
348: aggregateVector);
349:
350: newAnd = (AndNode) getNodeFactory().getNode(
351: C_NodeTypes.AND_NODE, this , equals,
352: getContextManager());
353: finishBindExpr();
354: newAnd.postBindFixup();
355: return newAnd;
356: }
357: }
358: }
359:
360: /* If we are comparing a char with a national char then
361: * we generate a cast above the reciever to force preprocess to
362: * not attempt any of the > <= optimizations since there is no
363: * way to determine the 'next' character for the <= operand.
364: */
365:
366: TypeId leftTypeId = leftOperand.getTypeId();
367: TypeId receiverTypeId = receiver.getTypeId();
368:
369: if (receiverTypeId.isNationalStringTypeId()
370: && !leftTypeId.isNationalStringTypeId()) {
371: receiver = castArgToNationalString(receiver, receiverTC,
372: receiverTypeId);
373: } else if (leftTypeId.isNationalStringTypeId()
374: && !receiverTypeId.isNationalStringTypeId()) {
375: leftOperand = castArgToNationalString(leftOperand, leftTC,
376: leftTypeId);
377: }
378:
379: finishBindExpr();
380:
381: return this ;
382: }
383:
384: private ValueNode castArgToNationalString(ValueNode vn,
385: TypeCompiler vnTC, TypeId vnTypeId)
386: throws StandardException {
387: ValueNode newNode = (ValueNode) getNodeFactory().getNode(
388: C_NodeTypes.CAST_NODE,
389: vn,
390: new DataTypeDescriptor(vnTypeId, true, vnTC
391: .getCastToCharWidth(vn.getTypeServices())),
392: getContextManager());
393: ((CastNode) newNode).bindCastNodeOnly();
394: return newNode;
395: }
396:
397: private void finishBindExpr() throws StandardException {
398: // deal with compatability of operands and result type
399: bindComparisonOperator();
400:
401: /*
402: ** The result type of LIKE is Boolean
403: */
404:
405: boolean nullableResult = receiver.getTypeServices()
406: .isNullable()
407: || leftOperand.getTypeServices().isNullable();
408:
409: if (rightOperand != null) {
410: nullableResult |= rightOperand.getTypeServices()
411: .isNullable();
412: }
413:
414: setType(new DataTypeDescriptor(TypeId.BOOLEAN_ID,
415: nullableResult));
416: }
417:
418: /**
419: * Bind this operator
420: *
421: * @exception StandardException Thrown on error
422: */
423:
424: public void bindComparisonOperator() throws StandardException {
425: TypeId receiverType = receiver.getTypeId();
426: TypeId leftType = leftOperand.getTypeId();
427:
428: /*
429: ** Check the type of the operands - this function is allowed only on
430: ** string types.
431: */
432:
433: if (!receiverType.isStringTypeId()) {
434: throw StandardException.newException(
435: SQLState.LANG_LIKE_BAD_TYPE, receiverType
436: .getSQLTypeName());
437: }
438:
439: if (!leftType.isStringTypeId()) {
440: throw StandardException.newException(
441: SQLState.LANG_LIKE_BAD_TYPE, leftType
442: .getSQLTypeName());
443: }
444:
445: if (rightOperand != null
446: && !rightOperand.getTypeId().isStringTypeId()) {
447: throw StandardException.newException(
448: SQLState.LANG_LIKE_BAD_TYPE, rightOperand
449: .getTypeId().getSQLTypeName());
450: }
451:
452: }
453:
454: /**
455: * Preprocess an expression tree. We do a number of transformations
456: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
457: * subquery flattening.
458: * NOTE: This is done before the outer ResultSetNode is preprocessed.
459: *
460: * @param numTables Number of tables in the DML Statement
461: * @param outerFromList FromList from outer query block
462: * @param outerSubqueryList SubqueryList from outer query block
463: * @param outerPredicateList PredicateList from outer query block
464: *
465: * @return The modified expression
466: *
467: * @exception StandardException Thrown on error
468: */
469: public ValueNode preprocess(int numTables, FromList outerFromList,
470: SubqueryList outerSubqueryList,
471: PredicateList outerPredicateList) throws StandardException {
472: boolean eliminateLikeComparison = false;
473: String greaterEqualString = null;
474: String lessThanString = null;
475: String pattern;
476:
477: /* We must 1st preprocess the component parts */
478: super .preprocess(numTables, outerFromList, outerSubqueryList,
479: outerPredicateList);
480:
481: /* Don't try to optimize for (C)LOB type since it
482: * doesn't allow comparison.
483: */
484: if (receiver.getTypeId().getSQLTypeName().equals("CLOB")) {
485: return this ;
486: }
487: /* No need to consider transformation if we
488: * already did transformation that added =
489: * at bind time.
490: */
491: if (addedEquals) {
492: return this ;
493: }
494:
495: /* This is where we do the transformation for LIKE to make it optimizable.
496: * c1 LIKE 'asdf%' -> c1 LIKE 'asdf%' AND c1 >= 'asdf' AND c1 < 'asdg'
497: * c1 LIKE ? -> c1 LIKE ? and c1 >= ?', where ?' gets calculated at the
498: * beginning of execution.
499: */
500: if (!(leftOperand instanceof CharConstantNode)
501: && !(leftOperand.requiresTypeFromContext())) {
502: return this ;
503: }
504:
505: /* This transformation is only worth doing if it is pushable, ie, if
506: * the receiver is a ColumnReference.
507: */
508: if (!(receiver instanceof ColumnReference)) {
509: //
510: // We also do an early return here if in bindExpression we found we had
511: // a National Char and put a CAST above the receiver.
512: //
513: return this ;
514: }
515:
516: // Build String constants if right side is a constant
517: if (leftOperand instanceof CharConstantNode) {
518: pattern = ((CharConstantNode) leftOperand).getString();
519:
520: if (!Like.isOptimizable(pattern)) {
521: return this ;
522: }
523:
524: int maxWidth = receiver.getTypeServices().getMaximumWidth();
525: greaterEqualString = Like.greaterEqualString(pattern,
526: escape, maxWidth);
527:
528: /* We do not generate the < and we cannot drop the LIKE
529: * when doing LIKE on a national char column.
530: */
531: if (!receiver.getTypeId().isNationalStringTypeId()) {
532: lessThanString = Like.lessThanString(pattern, escape,
533: maxWidth);
534: eliminateLikeComparison = !Like
535: .isLikeComparisonNeeded(pattern);
536: }
537: }
538:
539: //System.out.println(receiver.getTypeServices());
540: //System.out.println("MAX WIDTH " + receiver.getTypeServices().getMaximumWidth());
541:
542: /* For some unknown reason we need to clone the receiver if it is
543: * a ColumnReference because reusing them in Qualifiers for a scan
544: * does not work.
545: */
546:
547: /* The transformed tree has to be normalized. Either:
548: * AND AND
549: * / \ / \
550: * LIKE AND OR: LIKE AND
551: * / \ / \
552: * >= AND >= TRUE
553: * / \
554: * < TRUE
555: * unless the like string is of the form CONSTANT%, in which
556: * case we can do away with the LIKE altogether:
557: * AND AND
558: * / \ / \
559: * >= AND OR: >= TRUE
560: * / \
561: * < TRUE
562: */
563:
564: AndNode newAnd = null;
565: ValueNode trueNode = (ValueNode) getNodeFactory().getNode(
566: C_NodeTypes.BOOLEAN_CONSTANT_NODE, Boolean.TRUE,
567: getContextManager());
568:
569: /* Create the AND <, if lessThanString is non-null or
570: * leftOperand is a parameter.
571: Currently for a national string we do not add a < than operator
572: since we don't know (?) how to calculate such a string.
573: */
574: if (lessThanString != null
575: || (leftOperand.requiresTypeFromContext() && !receiver
576: .getTypeId().isNationalStringTypeId())) {
577: QueryTreeNode likeLTopt;
578: if (leftOperand.requiresTypeFromContext()) {
579: int maxWidth = receiver.getTypeServices()
580: .getMaximumWidth();
581: likeLTopt = setupOptimizeStringFromParameter(
582: leftOperand, rightOperand,
583: "lessThanStringFromParameter", maxWidth);
584: } else {
585: likeLTopt = getNodeFactory().getNode(
586: C_NodeTypes.CHAR_CONSTANT_NODE, lessThanString,
587: getContextManager());
588: }
589:
590: BinaryComparisonOperatorNode lessThan = (BinaryComparisonOperatorNode) getNodeFactory()
591: .getNode(
592: C_NodeTypes.BINARY_LESS_THAN_OPERATOR_NODE,
593: receiver.getClone(), likeLTopt,
594: getContextManager());
595:
596: // Disable comparability checks
597: lessThan.setForQueryRewrite(true);
598: /* Set type info for the operator node */
599: lessThan.bindComparisonOperator();
600:
601: // Use between selectivity for the <
602: lessThan.setBetweenSelectivity();
603:
604: /* Create the AND */
605: newAnd = (AndNode) getNodeFactory().getNode(
606: C_NodeTypes.AND_NODE, lessThan, trueNode,
607: getContextManager());
608: newAnd.postBindFixup();
609: }
610:
611: /* Create the AND >=. Right side could
612: * be a CharConstantNode or a ParameterNode.
613: */
614:
615: ValueNode likeGEopt;
616: if (leftOperand.requiresTypeFromContext()) {
617:
618: // Create an expression off the parameter
619: // new SQLChar(Like.greaterEqualString(?));
620:
621: int maxWidth = receiver.getTypeServices().getMaximumWidth();
622: likeGEopt = setupOptimizeStringFromParameter(leftOperand,
623: rightOperand, "greaterEqualStringFromParameter",
624: maxWidth);
625:
626: } else {
627:
628: likeGEopt = (ValueNode) getNodeFactory().getNode(
629: C_NodeTypes.CHAR_CONSTANT_NODE, greaterEqualString,
630: getContextManager());
631: }
632:
633: BinaryComparisonOperatorNode greaterEqual = (BinaryComparisonOperatorNode) getNodeFactory()
634: .getNode(
635: C_NodeTypes.BINARY_GREATER_EQUALS_OPERATOR_NODE,
636: receiver.getClone(), likeGEopt,
637: getContextManager());
638:
639: // Disable comparability checks
640: greaterEqual.setForQueryRewrite(true);
641: /* Set type info for the operator node */
642: greaterEqual.bindComparisonOperator();
643:
644: // Use between selectivity for the >=
645: greaterEqual.setBetweenSelectivity();
646:
647: /* Create the AND */
648: if (newAnd == null) {
649: newAnd = (AndNode) getNodeFactory().getNode(
650: C_NodeTypes.AND_NODE, greaterEqual, trueNode,
651: getContextManager());
652: } else {
653: newAnd = (AndNode) getNodeFactory().getNode(
654: C_NodeTypes.AND_NODE, greaterEqual, newAnd,
655: getContextManager());
656: }
657: newAnd.postBindFixup();
658:
659: /* Finally, we put a AND LIKE on top of the left deep tree, but
660: * only if it is still necessary.
661: */
662: if (!eliminateLikeComparison) {
663: newAnd = (AndNode) getNodeFactory().getNode(
664: C_NodeTypes.AND_NODE, this , newAnd,
665: getContextManager());
666: newAnd.postBindFixup();
667: }
668:
669: /* Mark this node as transformed so that we don't get
670: * calculated into the selectivity mulitple times.
671: */
672: setTransformed();
673:
674: return newAnd;
675: }
676:
677: /**
678: * Do code generation for this binary operator.
679: *
680: * This code was copied from BinaryOperatorNode and stripped down
681: *
682: * @param acb The ExpressionClassBuilder for the class we're generating
683: * @param mb The method the code to place the code
684: *
685: *
686: * @exception StandardException Thrown on error
687: */
688:
689: public void generateExpression(ExpressionClassBuilder acb,
690: MethodBuilder mb) throws StandardException {
691:
692: /*
693: ** if i have a operator.getOrderableType() == constant, then just cache
694: ** it in a field. if i have QUERY_INVARIANT, then it would be good to
695: ** cache it in something that is initialized each execution,
696: ** but how?
697: */
698:
699: /*
700: ** let the receiver type be determined by an
701: ** overridable method so that if methods are
702: ** not implemented on the lowest interface of
703: ** a class, they can note that in the implementation
704: ** of the node that uses the method.
705: */
706: // receiverType = getReceiverInterfaceName();
707: /*
708: ** Generate LHS (field = <receiver operand>). This assignment is
709: ** used as the receiver of the method call for this operator.
710: **
711: ** (<receiver operand>).method(<left operand>,
712: ** <right operand>, [<escaperightOp>,]
713: ** <result field>)
714: */
715:
716: receiver.generateExpression(acb, mb); // first arg
717:
718: receiverInterfaceType = receiver.getTypeCompiler()
719: .interfaceName();
720:
721: mb.upCast(receiverInterfaceType); // cast the method instance
722:
723: leftOperand.generateExpression(acb, mb);
724: mb.upCast(leftInterfaceType); // first arg with cast
725:
726: if (rightOperand != null) {
727: rightOperand.generateExpression(acb, mb);
728: mb.upCast(rightInterfaceType); // second arg with cast
729: }
730:
731: /* Figure out the result type name */
732: // resultTypeName = getTypeCompiler().interfaceName();
733: mb.callMethod(VMOpcode.INVOKEINTERFACE, null, methodName,
734: resultInterfaceType, rightOperand == null ? 1 : 2);
735: }
736:
737: private ValueNode setupOptimizeStringFromParameter(
738: ValueNode parameterNode, ValueNode escapeNode,
739: String methodName, int maxWidth) throws StandardException {
740:
741: Vector param;
742:
743: if (escapeNode != null) {
744: param = new Vector(2);
745: methodName += "WithEsc";
746: } else
747: param = new Vector(1);
748:
749: StaticMethodCallNode methodCall = (StaticMethodCallNode) getNodeFactory()
750: .getNode(C_NodeTypes.STATIC_METHOD_CALL_NODE,
751: methodName, "org.apache.derby.iapi.types.Like",
752: getContextManager());
753:
754: // using a method call directly, thus need internal sql capability
755: methodCall.internalCall = true;
756:
757: param.addElement(parameterNode);
758: if (escapeNode != null)
759: param.addElement(escapeNode);
760:
761: QueryTreeNode maxWidthNode = getNodeFactory().getNode(
762: C_NodeTypes.INT_CONSTANT_NODE, new Integer(maxWidth),
763: getContextManager());
764: param.addElement(maxWidthNode);
765:
766: methodCall.addParms(param);
767:
768: ValueNode java2SQL = (ValueNode) getNodeFactory().getNode(
769: C_NodeTypes.JAVA_TO_SQL_VALUE_NODE, methodCall,
770: getContextManager());
771:
772: java2SQL = (ValueNode) java2SQL
773: .bindExpression(null, null, null);
774:
775: CastNode likeOpt = (CastNode) getNodeFactory().getNode(
776: C_NodeTypes.CAST_NODE, java2SQL,
777: parameterNode.getTypeServices(), getContextManager());
778:
779: likeOpt.bindCastNodeOnly();
780:
781: return likeOpt;
782: }
783: }
|