001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdbc.query;
012:
013: import com.versant.core.jdo.query.*;
014: import com.versant.core.jdbc.sql.exp.*;
015: import com.versant.core.jdbc.sql.exp.AggregateCountStarExp;
016: import com.versant.core.jdbc.sql.conv.DummyPreparedStmt;
017: import com.versant.core.jdbc.metadata.*;
018: import com.versant.core.common.BindingSupportImpl;
019: import com.versant.core.metadata.MDStatics;
020: import com.versant.core.metadata.MDStaticUtils;
021: import com.versant.core.metadata.ClassMetaData;
022:
023: import java.sql.SQLException;
024:
025: /**
026: * Walks a JDOQL Node trees to produce SqlExp trees.
027: */
028: public class JDOQLNodeToSqlExp extends NodeVisitorAdapter {
029:
030: private final JdbcJDOQLCompiler comp;
031:
032: /**
033: * This instance can convert Node's that do not need to access a
034: * JdbcQueryCompiler to SqlExp.
035: */
036: public static final JDOQLNodeToSqlExp INSTANCE = new JDOQLNodeToSqlExp();
037:
038: /**
039: * Information passed down from a Node to its children via arrive
040: * callback mechanism.
041: */
042: private static class Context {
043:
044: public SelectExp root;
045: public SqlExp leftSibling;
046: public int method;
047: public Node args;
048:
049: public Context(SelectExp root) {
050: this .root = root;
051: }
052:
053: public Context(SelectExp root, SqlExp leftSibling, int method,
054: Node args) {
055: this .root = root;
056: this .leftSibling = leftSibling;
057: this .method = method;
058: this .args = args;
059: }
060: }
061:
062: public JDOQLNodeToSqlExp(JdbcJDOQLCompiler comp) {
063: this .comp = comp;
064: }
065:
066: private JDOQLNodeToSqlExp() {
067: this (null);
068: }
069:
070: /**
071: * Convert a Node tree to an SqlExp tree.
072: */
073: public SqlExp toSqlExp(Node node, SelectExp root,
074: SqlExp leftSibling, int method, Node args) {
075: return (SqlExp) node.arrive(this , new Context(root,
076: leftSibling, method, args));
077: }
078:
079: protected Object defaultArrive(Node node, Object msg) {
080: throw BindingSupportImpl.getInstance().internal(
081: "Not implemented: " + node.getClass());
082: }
083:
084: public Object arriveFieldNavNode(FieldNavNode node, Object msg) {
085: Context ctx = (Context) msg;
086: SelectExp root = ctx.root;
087: if (node.var == null) {
088: JdbcField jdbcField = (JdbcField) node.fmd.storeField;
089: JdbcClass targetClass = (JdbcClass) node.targetClass.storeClass;
090: SelectExp se;
091: Join j = root.findJoin(jdbcField);
092: if (j == null) {
093: SelectExp leftTableSE = root
094: .findTable(jdbcField.mainTable);
095: if (leftTableSE == null) {
096: throw BindingSupportImpl.getInstance().runtime(
097: "No join to '" + jdbcField.mainTable
098: + "' for nav field '"
099: + jdbcField.fmd.name);
100: }
101:
102: se = new SelectExp();
103: se.table = targetClass.table;
104: se.jdbcField = jdbcField;
105: if (jdbcField instanceof JdbcPolyRefField) {
106: JdbcPolyRefField pf = (JdbcPolyRefField) jdbcField;
107: j = leftTableSE
108: .addJoin(pf.refCols, se.table.pk, se);
109: // add a condition to check that the class-id column of
110: // the polyref matches the type used in the cast
111: j.appendJoinExp(pf.createClassIdMatchExp(root,
112: targetClass.cmd));
113: } else {
114: j = leftTableSE.addJoin(jdbcField.mainTableCols,
115: se.table.pk, se);
116: if (node.cast != null
117: && targetClass.cmd.isInHeirachy()) {
118: //join to basetable for classIdJoin
119: if (targetClass.classIdCol != null
120: && targetClass.classIdCol.table != se.table) {
121: SelectExp bSe = (SelectExp) root
122: .findTable(targetClass.classIdCol.table);
123: if (bSe == null) {
124: bSe = new SelectExp();
125: bSe.outer = se.outer;
126: bSe.table = targetClass.classIdCol.table;
127: j = se.addJoin(se.table.pk,
128: bSe.table.pk, bSe);
129: }
130: }
131:
132: // downcast so join must include code to check the
133: // class-id column of the target table
134: j.appendJoinExp(targetClass
135: .getCheckClassIdExp(se));
136: }
137: }
138: } else {
139: se = j.selectExp;
140: }
141: if (node.childList == null) {
142: return null;
143: } else {
144: return toSqlExp(node.childList, se, ctx.leftSibling,
145: ctx.method, ctx.args);
146: }
147: } else {
148: return toSqlExp(node.childList, (SelectExp) node.var
149: .getStoreExtent(), ctx.leftSibling, ctx.method,
150: ctx.args);
151: }
152: }
153:
154: public Object arriveLiteralNode(LiteralNode node, Object msg) {
155: Context ctx = (Context) msg;
156: SqlExp leftSibling = ctx.leftSibling;
157: String value = node.value;
158: int t;
159: switch (node.type) {
160: case LiteralNode.TYPE_OTHER:
161: t = LiteralExp.TYPE_OTHER;
162: break;
163: case LiteralNode.TYPE_STRING:
164: t = LiteralExp.TYPE_STRING;
165: break;
166: case LiteralNode.TYPE_NULL:
167: t = LiteralExp.TYPE_NULL;
168: break;
169: case LiteralNode.TYPE_BOOLEAN:
170: t = LiteralExp.TYPE_OTHER;
171:
172: /**
173: * The idea here is to convert the boolean literal 'true' or
174: * 'false' via the converter on the field that it is being compared to.
175: * If there is no converter on the field then we expect a 'true'
176: * or 'false' constant to work.
177: */
178: if (ctx.leftSibling instanceof ColumnExp) {
179: ColumnExp cExp = (ColumnExp) leftSibling;
180: if (cExp.col.converter == null)
181: break;
182: DummyPreparedStmt pstmt = new DummyPreparedStmt();
183: try {
184: cExp.col.converter.set(pstmt, 0, cExp.col,
185: new Boolean(value));
186: } catch (SQLException e) {
187: //ignore
188: }
189: value = pstmt.value;
190: if (pstmt.toQuote) {
191: t = LiteralExp.TYPE_STRING;
192: } else {
193: t = LiteralExp.TYPE_OTHER;
194: }
195: }
196: break;
197: case LiteralNode.TYPE_CHAR:
198: case LiteralNode.TYPE_DOUBLE:
199: case LiteralNode.TYPE_LONG:
200: t = LiteralExp.TYPE_OTHER;
201: break;
202: default:
203: throw BindingSupportImpl.getInstance().internal(
204: "Unknown literal type: " + node.type);
205: }
206:
207: LiteralExp ans = new LiteralExp(t, value);
208: if (node.type == LiteralNode.TYPE_NULL) {
209: // Create a list of LiteralExp's with one entry for each expression
210: // on the left hand side. This is to handle references to composite
211: // pk classes.
212: for (SqlExp pos = ans; (leftSibling = leftSibling.next) != null;) {
213: pos = pos.next = new LiteralExp(t, value);
214: }
215: }
216: return ans;
217: }
218:
219: public Object arriveMethodNode(MethodNode node, Object msg) {
220: Context ctx = (Context) msg;
221: SelectExp root = ctx.root;
222: int method = node.getMethod();
223: Node childList = node.childList;
224: switch (method) {
225: case MethodNode.STARTS_WITH:
226: return toSqlLike(childList, method, root, false);
227: case MethodNode.ENDS_WITH:
228: return toSqlLike(childList, method, root, true);
229: case MethodNode.CONTAINS:
230: case MethodNode.CONTAINS_KEY:
231: return toSqlContains(childList, method, root);
232: case MethodNode.IS_EMPTY:
233: return toSqlIsEmpty(childList, root);
234: case MethodNode.TO_LOWER_CASE:
235: return toSqlToLowerCase(childList, method, root);
236: case MethodNode.SQL:
237: return toSqlInline(childList, method, root);
238: }
239: throw BindingSupportImpl.getInstance().internal(
240: "Unknown method: " + method);
241: }
242:
243: private SqlExp toSqlInline(Node childList, int method,
244: SelectExp root) {
245: SqlExp left = toSqlExp(childList, root, null, method, null);
246: SqlExp right;
247: if (childList.next != null) {
248: right = toSqlExp(childList.next, root, left, method, null);
249: } else {
250: right = null;
251: }
252: String template;
253: if (right == null) {
254: template = "$1";
255: } else if (right instanceof LiteralExp) {
256: template = ((LiteralExp) right).value;
257: } else {
258: throw BindingSupportImpl.getInstance().invalidOperation(
259: "Expected literal expression: " + right);
260: }
261: return new InlineSqlExp(template, left);
262: }
263:
264: private SqlExp toSqlLike(Node childList, int method,
265: SelectExp root, boolean endsWith) {
266: SqlExp left = toSqlExp(childList, root, null, method, null);
267: SqlExp right = toSqlExp(childList.next, root, left, method,
268: null);
269: if (right instanceof LiteralExp
270: && ((LiteralExp) right).type == LiteralExp.TYPE_STRING) {
271: // prepend or append a % to the literal string
272: LiteralExp le = (LiteralExp) right;
273: if (endsWith)
274: le.value = "%" + le.value;
275: else
276: le.value = le.value + "%";
277: } else {
278: // create a BinaryExp to prepend or append %
279: LiteralExp pe = new LiteralExp(LiteralExp.TYPE_STRING, "%");
280: if (endsWith)
281: right = new BinaryOpExp(pe, BinaryOpExp.CONCAT, right);
282: else
283: right = new BinaryOpExp(right, BinaryOpExp.CONCAT, pe);
284: }
285: return new BinaryOpExp(left, BinaryOpExp.LIKE, right);
286: }
287:
288: private SqlExp toSqlIsEmpty(Node childList, SelectExp root) {
289: if (childList.next != null) {
290: throw BindingSupportImpl.getInstance().runtime(
291: "isEmpty() does not accept arguments");
292: }
293: return toSqlExp(childList, root, null, MethodNode.IS_EMPTY,
294: null);
295: }
296:
297: private SqlExp toSqlContains(Node childList, int method,
298: SelectExp root) {
299: return toSqlExp(childList, root, null, method, childList.next);
300: }
301:
302: private SqlExp toSqlToLowerCase(Node childList, int method,
303: SelectExp root) {
304: if (childList.next != null) {
305: throw BindingSupportImpl.getInstance().runtime(
306: "toLowerCase() does not accept arguments");
307: }
308: SqlExp left = toSqlExp(childList, root, null, method, null);
309: return new UnaryFunctionExp(left,
310: UnaryFunctionExp.FUNC_TO_LOWER_CASE);
311: }
312:
313: public Object arriveFieldNode(FieldNode node, Object msg) {
314: Context ctx = (Context) msg;
315: int method = ctx.method;
316: SelectExp root = ctx.root;
317: Node args = ctx.args;
318: JdbcField jdbcField = (JdbcField) node.fmd.storeField;
319:
320: switch (method) {
321: case MethodNode.IS_EMPTY:
322: return jdbcField.toIsEmptySqlExp(comp, root);
323: case MethodNode.CONTAINS:
324: return jdbcField.toContainsSqlExp(comp, root, args);
325: case MethodNode.CONTAINS_KEY:
326: return jdbcField.toContainsKeySqlExp(comp, root, args);
327: case MethodNode.CONTAINS_PARAM:
328: if (jdbcField.mainTableCols.length > 1) {
329: throw BindingSupportImpl
330: .getInstance()
331: .invalidOperation(
332: "This is only supported for single column instances");
333: }
334: return new CollectionParamExp(jdbcField.toColumnExp(
335: SelectExp.createJoinToSuperTable(root, jdbcField),
336: true));
337: }
338: ;
339:
340: // This is to detect the scenario where a boolean field is in the jdoql
341: // expression on its own. eg 'booleanField && name == param'
342: if (isUnaryBoolExp(node, jdbcField)) {
343: ColumnExp cExp = jdbcField.toColumnExp(root, true);
344:
345: LiteralExp lExp = new LiteralExp();
346: if (cExp.col.converter != null) {
347: DummyPreparedStmt pstmt = new DummyPreparedStmt();
348: try {
349: cExp.col.converter.set(pstmt, 0, cExp.col,
350: new Boolean(true));
351: } catch (SQLException e) {
352: //ignore
353: }
354:
355: lExp.value = pstmt.value;
356: if (pstmt.toQuote) {
357: lExp.type = LiteralExp.TYPE_STRING;
358: } else {
359: lExp.type = LiteralExp.TYPE_OTHER;
360: }
361: return new BinaryOpExp(cExp, BinaryOpExp.EQUAL, lExp);
362: } else {
363: lExp.type = LiteralExp.TYPE_OTHER;
364: lExp.value = "true";
365: return new BinaryOpExp(cExp, BinaryOpExp.EQUAL, lExp);
366: }
367: }
368:
369: SelectExp fSe = node.useCandidateExtent ? comp
370: .getCandidateSelectExp() : root;
371: ColumnExp columnExp = jdbcField.toColumnExp(SelectExp
372: .createJoinToSuperTable(fSe, jdbcField), true);
373: if (!isPartOfAggregate(node.parent))
374: columnExp.setColAlias(node.asValue);
375: return columnExp;
376: }
377:
378: private static boolean isUnaryBoolExp(Node node, JdbcField jdbcField) {
379: if (isBooleanType(jdbcField)
380: && !(node.parent instanceof OrderNode)) {
381: if (node.parent instanceof FieldNavNode) {
382: return isUnaryBoolExp(node.parent, jdbcField);
383: } else if ((node.parent instanceof AndNode)
384: || (node.parent instanceof OrNode)
385: || (node.parent != null && node.parent.parent == null)) {
386: return true;
387: }
388: }
389: return false;
390: }
391:
392: private static boolean isBooleanType(JdbcField jdbcField) {
393: if (jdbcField == null)
394: return false;
395: return ((jdbcField.fmd.typeCode == MDStatics.BOOLEAN) || (jdbcField.fmd.typeCode == MDStatics.BOOLEANW));
396: }
397:
398: private static boolean isPartOfAggregate(Node n) {
399: if (n == null)
400: return false;
401: if (n instanceof AggregateNode)
402: return true;
403: return isPartOfAggregate(n.parent);
404: }
405:
406: public Object arriveEqualNode(EqualNode node, Object msg) {
407: return arriveEqualOrNotEqual(node, msg, BinaryOpExp.EQUAL);
408: }
409:
410: public Object arriveLikeNode(LikeNode node, Object msg) {
411: return arriveEqualOrNotEqual(node, msg, BinaryOpExp.LIKE);
412: }
413:
414: private Object arriveEqualOrNotEqual(EqualNode node, Object msg,
415: int op) {
416: Context ctx = (Context) msg;
417: SelectExp root = ctx.root;
418:
419: // If a field is being compared to a null literal then only put
420: // columns that are not shared in the left expression unless all of
421: // the columns are shared.
422: SqlExp left;
423: Node childList = node.childList;
424: if (isNullLiteral(childList.next)
425: && childList instanceof FieldNode) {
426: JdbcField f = (JdbcField) ((FieldNode) childList).fmd.storeField;
427: left = f.toColumnExpForNullLiteralCompare(root);
428: } else {
429: left = toSqlExp(childList, root, null, ctx.method, ctx.args);
430: }
431:
432: SqlExp right = toSqlExp(childList.next, root, left, ctx.method,
433: ctx.args);
434: return SqlExp.createBinaryOpExp(left, op, right);
435: }
436:
437: private static boolean isNullLiteral(Node n) {
438: return n instanceof LiteralNode
439: && ((LiteralNode) n).type == LiteralNode.TYPE_NULL;
440: }
441:
442: public Object arriveNotEqualNode(NotEqualNode node, Object msg) {
443: return arriveEqualOrNotEqual(node, msg, BinaryOpExp.NOT_EQUAL);
444: }
445:
446: public Object arriveAndNode(AndNode node, Object msg) {
447: Context ctx = (Context) msg;
448: Node childList = node.childList;
449:
450: // Build the list of expressions to be anded together.
451: SqlExp prev = null, first = null;
452: for (Node cn = childList; cn != null;) {
453: SqlExp e = toSqlExp(cn, ctx.root, prev, ctx.method,
454: ctx.args);
455: if (first == null) {
456: first = e;
457: } else if (prev != null) {
458: prev.next = e;
459: }
460: prev = e;
461:
462: // See if this expression involves a VarNode. If so process the
463: // nodes involving the variable. They become the where clause of
464: // the sub select for the variable.
465: VarNode vn = extractVar(e);
466: if (vn != null) {
467: cn = processVarNode(ctx.root, ctx.method, ctx.args, cn,
468: vn, e);
469: } else {
470: cn = cn.next;
471: }
472: }
473:
474: // if there is now only one entry in the list then return it otherwise
475: // create a new AndExp for the list
476: if (first.next == null) {
477: return first;
478: } else {
479: return new AndExp(first);
480: }
481: }
482:
483: private static VarNode extractVar(SqlExp e) {
484: if (e instanceof ExistsExp && e.childList instanceof SelectExp) {
485: return ((SelectExp) e.childList).var;
486: } else {
487: return null;
488: }
489: }
490:
491: /**
492: * Process the nodes involving the variable. They become the where clause of
493: * of the sub select for the variable. This process stops as soon as a node
494: * is encountered that does not involve the variable i.e. all the variable
495: * related expressions must be together (as per spec).
496: *
497: * @return Next Node for the main loop to process
498: */
499: private Node processVarNode(SelectExp root, int method, Node args,
500: Node cn, VarNode var, SqlExp vare) {
501:
502: SelectExp varSelectExp = (SelectExp) var.getStoreExtent();
503:
504: // Do not use columns from the enclosing query (root) in subquery ON
505: // join conditions if the database does not allow this. If exclude is
506: // null then expressions referencing tables in the subquery and
507: // then enclosing query (root) will be left in the where clause.
508: // This is required for DB2.
509: SelectExp exclude;
510: if (root.table.sqlDriver.isSubQueryJoinMayUseOuterQueryCols()) {
511: exclude = root;
512: } else {
513: exclude = null;
514: }
515:
516: // build a list of all expressions constraining the variable on first
517: SqlExp prev = null, first = null;
518: SqlExp leftSibling = vare;
519: Node ans = cn.next;
520: for (; ans != null;) {
521: SqlExp e = toSqlExp(ans, root, leftSibling, method, args);
522: if (e == null) {
523: break;
524: }
525:
526: SelectExp single = e.getSingleSelectExp(exclude);
527: if (single != null) {
528: Join join;
529: if (single.jdbcField != null) {
530: join = varSelectExp.findJoinRec(single.jdbcField);
531: } else if (vare instanceof ExistsExp) {
532: join = ((SelectExp) vare.childList)
533: .findJoin(single);
534: } else {
535: join = null;
536: }
537: ans = ans.next;
538: if (join != null) {
539: join.appendJoinExp(e);
540: continue;
541: } else if (var.getCmd() == null) {
542: ((SelectExp) ((ExistsExp) vare).childList)
543: .appendToWhereExp(e);
544: }
545: } else {
546: // recursively process nested variables
547: VarNode nested = extractVar(e);
548: if (nested != null) {
549: ans = processVarNode(root, method, args, ans,
550: nested, e);
551: } else {
552: ans = ans.next;
553: }
554: }
555: if (first == null) {
556: first = e;
557: } else {
558: prev.next = e;
559: }
560: leftSibling = e;
561: prev = e;
562: }
563:
564: // attach the expression list at first to the where of the variable
565: if (first != null)
566: varSelectExp.appendToWhereExp(first);
567:
568: return ans;
569: }
570:
571: public Object arriveOrNode(OrNode node, Object msg) {
572: Context ctx = (Context) msg;
573: Node childList = node.childList;
574:
575: SqlExp e = processOrNodeChild(ctx.root, childList, ctx.args);
576: if (e == null) {
577: return null;
578: }
579:
580: SqlExp base = e;
581: for (Node c = childList.next; c != null; c = c.next) {
582: e = e.next = processOrNodeChild(ctx.root, c, ctx.args);
583: }
584: mergeOrNodeRedundantExistsSelects(base);
585: if (base.next == null)
586: return base;
587: return new OrExp(base);
588: }
589:
590: private SqlExp processOrNodeChild(SelectExp root, Node c, Node args) {
591: Join rootJoinList = root.joinList;
592: root.joinList = null;
593: SqlExp e = toSqlExp(c, root, null, 0, args);
594: // if the child expression includes a join (e.g. field navigation)
595: // then convert it into an ExistsSelectExp
596: Join j = root.joinList;
597: if (j != null) {
598: // convert the first join into a ExistsSelectExp
599: SelectExp se = j.selectExp;
600: se.subSelectJoinExp = j.exp;
601: se.whereExp = e;
602: // make the other joins (if any) joins on this select
603: j = se.joinList;
604: if (j == null) {
605: se.joinList = root.joinList.next;
606: } else {
607: for (; j.next != null; j = j.next)
608: ;
609: j.next = root.joinList.next;
610: }
611: // wrap in an exists exp
612: e = new ExistsExp(se, false);
613: }
614: root.joinList = rootJoinList;
615: return e;
616: }
617:
618: /**
619: * Try to merge the list of expressions starting at base. This gets rid
620: * of redundant 'exists (select ...)' sub queries.
621: */
622: private void mergeOrNodeRedundantExistsSelects(SqlExp base) {
623: for (; base != null;) {
624: if (base instanceof ExistsExp) {
625: SqlExp prev = base;
626: for (SqlExp target = base.next; target != null;) {
627: if (mergeOrNodeRedundantExistsSelects(base, target)) {
628: target = prev.next = target.next;
629: } else {
630: prev = target;
631: target = target.next;
632: }
633: }
634: }
635: base = base.next;
636: }
637: }
638:
639: /**
640: * Merge base and e if possible or return false. The expressions can
641: * be merged if:<p>
642: * <ol>
643: * <li>e is an ExistsExp.
644: * <li>The selectExp's have the same jdbcField.
645: * <li>The selectExp's do not have any other joins.
646: * </ol>
647: */
648: private boolean mergeOrNodeRedundantExistsSelects(SqlExp base,
649: SqlExp e) {
650: if (!(e instanceof ExistsExp))
651: return false;
652: SelectExp se = (SelectExp) base.childList;
653: SelectExp ese = (SelectExp) e.childList;
654: if (se.jdbcField != ese.jdbcField)
655: return false;
656: // if (se.joinList != null || ese.joinList != null) return false;
657: if (!Join.isEqaul(se.joinList, ese.joinList))
658: return false;
659: SqlExp we = se.whereExp;
660: if (we instanceof OrExp) { // extend the existing OrExp
661: SqlExp pos;
662: for (pos = we.childList; pos.next != null; pos = pos.next)
663: ;
664: pos.next = ese.whereExp;
665: } else if (se.whereExp != null) { // create an OrExp
666: se.whereExp.next = ese.whereExp;
667: se.whereExp = new OrExp(se.whereExp);
668: }
669: // convert any references to ese into se references
670: if (ese.whereExp != null)
671: ese.whereExp.replaceSelectExpRef(ese, se);
672: return true;
673: }
674:
675: public Object arriveMultiplyNode(MultiplyNode node, Object msg) {
676: Context ctx = (Context) msg;
677: Node childList = node.childList;
678:
679: SqlExp e = processMultiplyNodeChild(ctx.root, childList, null,
680: ctx.args);
681: MultiplyExp ans = new MultiplyExp(e, node.ops);
682: for (Node c = childList.next; c != null; c = c.next) {
683: e = e.next = processMultiplyNodeChild(ctx.root, c, e,
684: ctx.args);
685: }
686: return ans;
687: }
688:
689: private SqlExp processMultiplyNodeChild(SelectExp root, Node c,
690: SqlExp leftSibling, Node args) {
691: SqlExp e = toSqlExp(c, root, leftSibling, 0, args);
692: if (e.next != null) {
693: throw BindingSupportImpl.getInstance().runtime(
694: "Expressions consisting of multiple columns may not be used"
695: + "with * or /");
696: }
697: return e;
698: }
699:
700: public Object arriveAddNode(AddNode node, Object msg) {
701: Context ctx = (Context) msg;
702: Node childList = node.childList;
703: SqlExp e = processAddNodeChild(ctx.root, childList, null,
704: ctx.args);
705: AddExp ans = new AddExp(e, node.ops);
706: if (node.asValue != null)
707: ans.setExpAlias(node.asValue);
708: for (Node c = childList.next; c != null; c = c.next) {
709: e = e.next = processAddNodeChild(ctx.root, c, e, ctx.args);
710: }
711: return ans;
712: }
713:
714: private SqlExp processAddNodeChild(SelectExp root, Node c,
715: SqlExp leftSibling, Node args) {
716: SqlExp e = toSqlExp(c, root, leftSibling, 0, args);
717: if (e.next != null) {
718: throw BindingSupportImpl.getInstance().runtime(
719: "Expressions consisting of multiple columns may not be used"
720: + "with + or -");
721: }
722: return e;
723: }
724:
725: public Object arriveUnaryOpNode(UnaryOpNode node, Object msg) {
726: Context ctx = (Context) msg;
727: return new UnaryOpExp(toSqlExp(node.childList, ctx.root,
728: ctx.leftSibling, ctx.method, ctx.args), node.op);
729: }
730:
731: public Object arriveCompareOpNode(CompareOpNode node, Object msg) {
732: Context ctx = (Context) msg;
733: Node childList = node.childList;
734: SqlExp left = toSqlExp(childList, ctx.root, null, ctx.method,
735: ctx.args);
736: SqlExp right = toSqlExp(childList.next, ctx.root, left,
737: ctx.method, ctx.args);
738: if (left.next == null && right.next == null) {
739: int op;
740: switch (node.op) {
741: case CompareOpNode.GT:
742: op = BinaryOpExp.GT;
743: break;
744: case CompareOpNode.LT:
745: op = BinaryOpExp.LT;
746: break;
747: case CompareOpNode.GE:
748: op = BinaryOpExp.GE;
749: break;
750: case CompareOpNode.LE:
751: op = BinaryOpExp.LE;
752: break;
753: default:
754: throw BindingSupportImpl.getInstance().internal(
755: "Unknown op: " + node.op);
756: }
757: return new BinaryOpExp(left, op, right);
758: }
759: throw BindingSupportImpl.getInstance().runtime(
760: "Expressions consisting of multiple columns may not be compared "
761: + "with >, <, >= or <=\n");
762: }
763:
764: public Object arriveUnaryNode(UnaryNode node, Object msg) {
765: Context ctx = (Context) msg;
766: return toSqlExp(node.childList, ctx.root, ctx.leftSibling,
767: ctx.method, ctx.args);
768: }
769:
770: public Object arriveCastNode(CastNode node, Object msg) {
771: Context ctx = (Context) msg;
772: return toSqlExp(node.childList, ctx.root, ctx.leftSibling,
773: ctx.method, ctx.args);
774: }
775:
776: /**
777: * Add u to the end of the usage list for n.
778: */
779: private void addToParamNode(SqlParamUsage u, ParamNode n) {
780: if (n.usageList == null) {
781: n.usageList = u;
782: } else {
783: SqlParamUsage p = (SqlParamUsage) n.usageList;
784: for (; p.next != null; p = p.next)
785: ;
786: p.next = u;
787: }
788: }
789:
790: public Object arriveParamNode(ParamNode node, Object msg) {
791: Context ctx = (Context) msg;
792: SqlExp leftSibling = ctx.leftSibling;
793: SelectExp root = ctx.root;
794: Node args = ctx.args;
795:
796: SqlParamUsage u = new SqlParamUsage();
797: addToParamNode(u, node);
798: if (leftSibling instanceof ColumnExp) {
799: ColumnExp left = (ColumnExp) leftSibling;
800: u.jdbcField = left.jdbcField;
801: u.col = left.col;
802: SqlExp pos = u.expList = new ParamExp(u.jdbcType, u);
803: for (;;) {
804: if ((left = (ColumnExp) left.next) == null)
805: break;
806: pos = pos.next = new ParamExp(left.getJdbcType(), u);
807: }
808: } else {
809: if (leftSibling == null) {
810: /**
811: * Expect this to be a param collection exp
812: * The args must be either a FieldNode of a FieldNavNode.
813: */
814: if (java.util.Collection.class
815: .isAssignableFrom(MDStaticUtils
816: .toSimpleClass(node.getType()))
817: && (args instanceof FieldNode || args instanceof FieldNavNode)) {
818: CollectionParamExp collectionParamExp = (CollectionParamExp) toSqlExp(
819: args, root, leftSibling,
820: MethodNode.CONTAINS_PARAM, null);
821: u.expList = collectionParamExp;
822: u.jdbcField = collectionParamExp.field.jdbcField;
823: u.col = collectionParamExp.field.col;
824: u.jdbcType = collectionParamExp.field.getJdbcType();
825: u.javaTypeCode = collectionParamExp.field
826: .getJavaTypeCode();
827: u.classIndex = collectionParamExp.field
828: .getClassIndex();
829: return collectionParamExp;
830: }
831: throw BindingSupportImpl.getInstance().internal(
832: "not implemented (leftSibling == null)");
833: }
834: if (leftSibling.next != null) {
835: throw BindingSupportImpl.getInstance().internal(
836: "not implemented (leftSibling.next != null)");
837: }
838: u.expList = new ParamExp(leftSibling.getJdbcType(), u);
839: }
840: if (u.jdbcField == null) {
841: u.jdbcType = leftSibling.getJdbcType();
842: u.javaTypeCode = leftSibling.getJavaTypeCode();
843: u.classIndex = leftSibling.getClassIndex();
844: }
845: return u.expList;
846: }
847:
848: public Object arriveParamNodeProxy(ParamNodeProxy node, Object msg) {
849: Context ctx = (Context) msg;
850: return toSqlExp(node.getParamNode(), ctx.root, ctx.leftSibling,
851: ctx.method, ctx.args);
852: }
853:
854: public Object arriveVarNode(VarNode node, Object msg) {
855: SelectExp jdbcSelectExp = (SelectExp) node.getStoreExtent();
856: // return ColumnExp's for the primary key of our table
857: if (node.getCmd() != null) {
858: JdbcColumn[] cols = jdbcSelectExp.table.pk;
859: ColumnExp ans = new ColumnExp(cols[0], jdbcSelectExp, null);
860: SqlExp e = ans;
861: int nc = cols.length;
862: for (int i = 1; i < nc; i++) {
863: e = e.next = new ColumnExp(cols[i], jdbcSelectExp, null);
864: }
865: return ans;
866: } else {
867: JdbcLinkCollectionField jdbcField = (JdbcLinkCollectionField) node
868: .getFmd().storeField;
869: return new ColumnExp(jdbcField.valueColumns[0],
870: (SelectExp) node.getFieldExtent(), null);
871: }
872: }
873:
874: public Object arriveVarNodeProxy(VarNodeProxy node, Object msg) {
875: Context ctx = (Context) msg;
876: return toSqlExp(node.getVarNode(), ctx.root, ctx.leftSibling,
877: ctx.method, ctx.args);
878: }
879:
880: public Object arriveReservedFieldNode(ReservedFieldNode node,
881: Object msg) {
882: ClassMetaData target = node.getTarget();
883: SelectExp candidateSelectExp = comp.getCandidateSelectExp();
884: JdbcColumn[] pk = ((JdbcClass) target.storeClass).table.pk;
885: ColumnExp list = new ColumnExp(pk[0], candidateSelectExp, null);
886: list.cmd = target;
887: SqlExp e = list;
888: int nc = pk.length;
889: for (int i = 1; i < nc; i++) {
890: e = e.next = new ColumnExp(pk[i], candidateSelectExp, null);
891: }
892: return list;
893: }
894:
895: public Object arriveAggregateCountStarNode(
896: AggregateCountStarNode node, Object msg) {
897: return new AggregateCountStarExp(node.asValue);
898: }
899:
900: public Object arriveAggregateNode(AggregateNode node, Object msg) {
901: Context ctx = (Context) msg;
902: SqlExp se = toSqlExp(node.childList, ctx.root, ctx.leftSibling,
903: ctx.method, ctx.args);
904: String op;
905: switch (node.getType()) {
906: case AggregateNode.TYPE_AVG:
907: op = "AVG";
908: break;
909: case AggregateNode.TYPE_COUNT:
910: op = "COUNT";
911: break;
912: case AggregateNode.TYPE_MAX:
913: op = "MAX";
914: break;
915: case AggregateNode.TYPE_MIN:
916: op = "MIN";
917: break;
918: case AggregateNode.TYPE_SUM:
919: op = "SUM";
920: break;
921: default:
922: throw BindingSupportImpl.getInstance().internal(
923: "Uknown AggregateNode type " + node.getType());
924: }
925: ;
926: return new AggregateExp(se, op, node.asValue);
927: }
928:
929: public Object arriveAsValueNode(AsValueNode node, Object msg) {
930: return new ColumnExp(node.value);
931: }
932:
933: public Object arriveGroupingNode(GroupingNode node, Object msg) {
934: Context ctx = (Context) msg;
935: SqlExp head = new SqlExp();
936: SqlExp current = head;
937: for (Node n = node.childList; n != null; n = n.next) {
938: if (n instanceof ReservedFieldNode) {
939: current.next = new GroupByThisExp();
940: current = current.next;
941: } else {
942: current.next = toSqlExp(n, ctx.root, ctx.leftSibling,
943: ctx.method, ctx.args);
944: current = current.next;
945: }
946: }
947: return head.next;
948: }
949:
950: public Object arriveResultNode(ResultNode node, Object msg) {
951: Context ctx = (Context) msg;
952: SqlExp head = new SqlExp();
953: SqlExp current = head;
954: for (Node n = node.childList; n != null; n = n.next) {
955: if (n instanceof ReservedFieldNode)
956: continue;
957: current.next = toSqlExp(n, ctx.root, ctx.leftSibling,
958: ctx.method, ctx.args);
959: current = current.next;
960: }
961: return head.next;
962: }
963:
964: public Object arriveVarBindingNode(VarBindingNode node, Object msg) {
965: SelectExp se = (SelectExp) node.getVar().getStoreExtent();
966: return new ExistsExp(se, true);
967: }
968:
969: }
|