0001: /*
0002: * JBoss, Home of Professional Open Source.
0003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
0004: * as indicated by the @author tags. See the copyright.txt file in the
0005: * distribution for a full listing of individual contributors.
0006: *
0007: * This is free software; you can redistribute it and/or modify it
0008: * under the terms of the GNU Lesser General Public License as
0009: * published by the Free Software Foundation; either version 2.1 of
0010: * the License, or (at your option) any later version.
0011: *
0012: * This software is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * You should have received a copy of the GNU Lesser General Public
0018: * License along with this software; if not, write to the Free
0019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
0021: */
0022: package org.jboss.ejb.plugins.cmp.jdbc;
0023:
0024: import java.io.StringReader;
0025: import java.util.ArrayList;
0026: import java.util.List;
0027: import java.util.Set;
0028: import java.util.HashSet;
0029: import java.util.Map;
0030: import java.util.HashMap;
0031: import java.util.Iterator;
0032:
0033: import org.jboss.ejb.plugins.cmp.ejbql.*;
0034: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge;
0035: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractEntityBridge;
0036: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCAbstractCMRFieldBridge;
0037: import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge;
0038: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
0039: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCTypeMappingMetaData;
0040: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData;
0041: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCFunctionMappingMetaData;
0042: import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData;
0043: import org.jboss.ejb.plugins.cmp.bridge.CMPFieldBridge;
0044: import org.jboss.ejb.EntityPersistenceStore;
0045: import org.jboss.logging.Logger;
0046:
0047: /**
0048: * Compiles EJB-QL and JBossQL into SQL using OUTER and INNER joins.
0049: *
0050: * @author <a href="mailto:alex@jboss.org">Alex Loubyansky</a>
0051: * @version $Revision: 63348 $
0052: */
0053: public final class EJBQLToSQL92Compiler implements QLCompiler,
0054: JBossQLParserVisitor {
0055: private static final Logger log = Logger
0056: .getLogger(EJBQLToSQL92Compiler.class);
0057:
0058: // input objects
0059: private final Catalog catalog;
0060: private Class returnType;
0061: private Class[] parameterTypes;
0062: private JDBCReadAheadMetaData readAhead;
0063:
0064: // alias info
0065: private AliasManager aliasManager;
0066: private Map joinPaths = new HashMap();
0067: private Map identifierToTable = new HashMap();
0068: private Set joinedAliases = new HashSet();
0069:
0070: // mapping metadata
0071: private JDBCTypeMappingMetaData typeMapping;
0072: private JDBCTypeFactory typeFactory;
0073:
0074: // output objects
0075: private boolean forceDistinct;
0076: private String sql;
0077: private int offsetParam;
0078: private int offsetValue;
0079: private int limitParam;
0080: private int limitValue;
0081: private JDBCEntityPersistenceStore selectManager;
0082: private Object selectObject;
0083: private List inputParameters = new ArrayList();
0084:
0085: private List leftJoinCMRList = new ArrayList();
0086: private StringBuffer onFindCMRJoin;
0087:
0088: private boolean countCompositePk;
0089: private boolean selectDistinct;
0090:
0091: public EJBQLToSQL92Compiler(Catalog catalog) {
0092: this .catalog = catalog;
0093: }
0094:
0095: public void compileEJBQL(String ejbql, Class returnType,
0096: Class[] parameterTypes, JDBCQueryMetaData metadata)
0097: throws Exception {
0098: // reset all state variables
0099: reset();
0100:
0101: // set input arguemts
0102: this .returnType = returnType;
0103: this .parameterTypes = parameterTypes;
0104: this .readAhead = metadata.getReadAhead();
0105:
0106: // get the parser
0107: EJBQLParser parser = new EJBQLParser(new StringReader(""));
0108:
0109: try {
0110: // parse the ejbql into an abstract sytax tree
0111: ASTEJBQL ejbqlNode = parser.parse(catalog, parameterTypes,
0112: ejbql);
0113:
0114: // translate to sql
0115: sql = ejbqlNode.jjtAccept(this , new StringBuffer())
0116: .toString();
0117: } catch (Exception e) {
0118: // if there is a problem reset the state before exiting
0119: reset();
0120: throw e;
0121: } catch (Error e) {
0122: // lame javacc lexer throws Errors
0123: reset();
0124: throw e;
0125: }
0126: }
0127:
0128: public void compileJBossQL(String ejbql, Class returnType,
0129: Class[] parameterTypes, JDBCQueryMetaData metadata)
0130: throws Exception {
0131: // reset all state variables
0132: reset();
0133:
0134: // set input arguemts
0135: this .returnType = returnType;
0136: this .parameterTypes = parameterTypes;
0137: this .readAhead = metadata.getReadAhead();
0138:
0139: // get the parser
0140: JBossQLParser parser = new JBossQLParser(new StringReader(""));
0141:
0142: try {
0143: // parse the ejbql into an abstract sytax tree
0144: ASTEJBQL ejbqlNode = parser.parse(catalog, parameterTypes,
0145: ejbql);
0146:
0147: // translate to sql
0148: sql = ejbqlNode.jjtAccept(this , new StringBuffer())
0149: .toString();
0150:
0151: if (log.isTraceEnabled()) {
0152: log.trace("ejbql: " + ejbql);
0153: log.trace("sql: " + sql);
0154: }
0155: } catch (Exception e) {
0156: // if there is a problem reset the state before exiting
0157: reset();
0158: throw e;
0159: } catch (Error e) {
0160: // lame javacc lexer throws Errors
0161: reset();
0162: throw e;
0163: }
0164: }
0165:
0166: public String getSQL() {
0167: return sql;
0168: }
0169:
0170: public int getOffsetValue() {
0171: return offsetValue;
0172: }
0173:
0174: public int getOffsetParam() {
0175: return offsetParam;
0176: }
0177:
0178: public int getLimitValue() {
0179: return limitValue;
0180: }
0181:
0182: public int getLimitParam() {
0183: return limitParam;
0184: }
0185:
0186: public boolean isSelectEntity() {
0187: return selectObject instanceof JDBCAbstractEntityBridge;
0188: }
0189:
0190: public JDBCAbstractEntityBridge getSelectEntity() {
0191: return (JDBCAbstractEntityBridge) selectObject;
0192: }
0193:
0194: public boolean isSelectField() {
0195: boolean result;
0196: if (selectObject instanceof JDBCFieldBridge) {
0197: JDBCFieldBridge field = (JDBCFieldBridge) selectObject;
0198: result = field.isCMPField();
0199: } else {
0200: result = false;
0201: }
0202: return result;
0203: }
0204:
0205: public JDBCFieldBridge getSelectField() {
0206: return (JDBCFieldBridge) selectObject;
0207: }
0208:
0209: public SelectFunction getSelectFunction() {
0210: return (SelectFunction) selectObject;
0211: }
0212:
0213: public EntityPersistenceStore getStoreManager() {
0214: return selectManager;
0215: }
0216:
0217: public List getInputParameters() {
0218: return inputParameters;
0219: }
0220:
0221: public List getLeftJoinCMRList() {
0222: return leftJoinCMRList;
0223: }
0224:
0225: public boolean isSelectDistinct() {
0226: return selectDistinct;
0227: }
0228:
0229: public Object visit(SimpleNode node, Object data) {
0230: throw new RuntimeException(
0231: "Internal error: Found unknown node type in "
0232: + "EJB-QL abstract syntax tree: node=" + node);
0233: }
0234:
0235: public Object visit(ASTEJBQL node, Object data) {
0236: Node selectNode = node.jjtGetChild(0);
0237: Node fromNode = node.jjtGetChild(1);
0238:
0239: // compile selectNode
0240: StringBuffer selectClause = new StringBuffer(50);
0241: selectNode.jjtAccept(this , selectClause);
0242:
0243: StringBuffer whereClause = null;
0244: StringBuffer orderByClause = null;
0245: for (int i = 2; i < node.jjtGetNumChildren(); ++i) {
0246: Node childNode = node.jjtGetChild(i);
0247: if (childNode instanceof ASTWhere) {
0248: whereClause = new StringBuffer(20);
0249: childNode.jjtAccept(this , whereClause);
0250: } else if (childNode instanceof ASTOrderBy) {
0251: orderByClause = new StringBuffer();
0252: childNode.jjtAccept(this , orderByClause);
0253: } else if (childNode instanceof ASTLimitOffset) {
0254: childNode.jjtAccept(this , null);
0255: }
0256: }
0257:
0258: // compile fromNode
0259: StringBuffer fromClause = new StringBuffer(30);
0260: fromNode.jjtAccept(this , fromClause);
0261:
0262: // left-join
0263: for (Iterator iter = identifierToTable.entrySet().iterator(); iter
0264: .hasNext();) {
0265: final Map.Entry entry = (Map.Entry) iter.next();
0266: final String identifier = (String) entry.getKey();
0267: final String table = (String) entry.getValue();
0268: final String alias = aliasManager.getAlias(identifier);
0269:
0270: fromClause.append(table).append(' ').append(alias);
0271: join(alias, fromClause);
0272:
0273: if (iter.hasNext()) {
0274: fromClause.append(SQLUtil.COMMA);
0275: }
0276: }
0277:
0278: selectDistinct = ((ASTSelect) selectNode).distinct
0279: || returnType == Set.class || forceDistinct;
0280:
0281: // assemble sql
0282: StringBuffer sql = (StringBuffer) data;
0283: if (selectManager.getMetaData().hasRowLocking()
0284: && !(selectObject instanceof SelectFunction)) {
0285: JDBCFunctionMappingMetaData rowLockingTemplate = typeMapping
0286: .getRowLockingTemplate();
0287: if (rowLockingTemplate == null) {
0288: throw new IllegalStateException(
0289: "Row locking template is not defined for given mapping: "
0290: + typeMapping.getName());
0291: }
0292:
0293: boolean distinct = selectDistinct;
0294:
0295: Object args[] = new Object[] {
0296: distinct ? SQLUtil.DISTINCT + selectClause
0297: : selectClause.toString(),
0298: fromClause,
0299: whereClause == null || whereClause.length() == 0 ? null
0300: : whereClause,
0301: orderByClause == null
0302: || orderByClause.length() == 0 ? null
0303: : orderByClause };
0304: rowLockingTemplate.getFunctionSql(args, sql);
0305: } else {
0306: sql.append(SQLUtil.SELECT);
0307: if (selectDistinct) {
0308: sql.append(SQLUtil.DISTINCT);
0309: }
0310: sql.append(selectClause).append(SQLUtil.FROM).append(
0311: fromClause);
0312:
0313: if (whereClause != null && whereClause.length() > 0) {
0314: sql.append(SQLUtil.WHERE).append(whereClause);
0315: }
0316:
0317: if (orderByClause != null && orderByClause.length() > 0) {
0318: sql.append(SQLUtil.ORDERBY).append(orderByClause);
0319: }
0320: }
0321:
0322: if (countCompositePk) {
0323: sql.insert(0, "SELECT COUNT(*) FROM (").append(") t_count");
0324: }
0325:
0326: return data;
0327: }
0328:
0329: public Object visit(ASTOrderBy node, Object data) {
0330: StringBuffer buf = (StringBuffer) data;
0331: node.jjtGetChild(0).jjtAccept(this , data);
0332: for (int i = 1; i < node.jjtGetNumChildren(); i++) {
0333: buf.append(SQLUtil.COMMA);
0334: node.jjtGetChild(i).jjtAccept(this , data);
0335: }
0336: return data;
0337: }
0338:
0339: public Object visit(ASTOrderByPath node, Object data) {
0340: StringBuffer buf = (StringBuffer) data;
0341: node.jjtGetChild(0).jjtAccept(this , data);
0342: if (node.ascending) {
0343: buf.append(SQLUtil.ASC);
0344: } else {
0345: buf.append(SQLUtil.DESC);
0346: }
0347: return data;
0348: }
0349:
0350: public Object visit(ASTLimitOffset node, Object data) {
0351: int child = 0;
0352: if (node.hasOffset) {
0353: Node offsetNode = node.jjtGetChild(child++);
0354: if (offsetNode instanceof ASTParameter) {
0355: ASTParameter param = (ASTParameter) offsetNode;
0356: Class parameterType = getParameterType(param.number);
0357: if (int.class != parameterType
0358: && Integer.class != parameterType) {
0359: throw new IllegalStateException(
0360: "OFFSET parameter must be an int");
0361: }
0362: offsetParam = param.number;
0363: } else {
0364: ASTExactNumericLiteral param = (ASTExactNumericLiteral) offsetNode;
0365: offsetValue = (int) param.value;
0366: }
0367: }
0368:
0369: if (node.hasLimit) {
0370: Node limitNode = node.jjtGetChild(child);
0371: if (limitNode instanceof ASTParameter) {
0372: ASTParameter param = (ASTParameter) limitNode;
0373: Class parameterType = getParameterType(param.number);
0374: if (int.class != parameterType
0375: && Integer.class != parameterType) {
0376: throw new IllegalStateException(
0377: "LIMIT parameter must be an int");
0378: }
0379: limitParam = param.number;
0380: } else {
0381: ASTExactNumericLiteral param = (ASTExactNumericLiteral) limitNode;
0382: limitValue = (int) param.value;
0383: }
0384: }
0385: return data;
0386: }
0387:
0388: public Object visit(ASTSelect select, Object data) {
0389: StringBuffer sql = (StringBuffer) data;
0390:
0391: final Node child0 = select.jjtGetChild(0);
0392: final ASTPath path;
0393: if (child0 instanceof ASTPath) {
0394: path = (ASTPath) child0;
0395:
0396: if (path.isCMPField()) {
0397: // set the select object
0398: JDBCFieldBridge selectField = (JDBCFieldBridge) path
0399: .getCMPField();
0400: selectManager = selectField.getManager();
0401: selectObject = selectField;
0402: setTypeFactory(selectManager.getJDBCTypeFactory());
0403:
0404: // todo inner or left?
0405: //addLeftJoinPath(path);
0406: addInnerJoinPath(path);
0407:
0408: String alias = aliasManager.getAlias(path.getPath(path
0409: .size() - 2));
0410: SQLUtil.getColumnNamesClause(selectField, alias, sql);
0411: } else {
0412: JDBCAbstractEntityBridge selectEntity = (JDBCAbstractEntityBridge) path
0413: .getEntity();
0414: selectManager = selectEntity.getManager();
0415: selectObject = selectEntity;
0416: setTypeFactory(selectEntity.getManager()
0417: .getJDBCTypeFactory());
0418:
0419: final String alias = aliasManager.getAlias(path
0420: .getPath());
0421: if (select.distinct) {
0422: SQLUtil.getSearchableColumnNamesClause(selectEntity
0423: .getTableFields(), alias, sql);
0424: } else {
0425: SQLUtil.getColumnNamesClause(selectEntity
0426: .getTableFields(), alias, sql);
0427: }
0428:
0429: /*
0430: if(readAhead.isOnFind())
0431: {
0432: String eagerLoadGroupName = readAhead.getEagerLoadGroup();
0433: boolean[] loadGroupMask = selectEntity.getLoadGroupMask(eagerLoadGroupName);
0434: SQLUtil.appendColumnNamesClause(
0435: selectEntity.getTableFields(),
0436: loadGroupMask,
0437: alias,
0438: sql
0439: );
0440: }
0441: */
0442:
0443: addLeftJoinPath(path);
0444: }
0445: } else {
0446: // the function should take a path expresion as a parameter
0447: path = getPathFromChildren(child0);
0448:
0449: if (path == null) {
0450: throw new IllegalStateException(
0451: "The function in SELECT clause does not contain a path expression.");
0452: }
0453:
0454: if (path.isCMPField()) {
0455: JDBCFieldBridge selectField = (JDBCFieldBridge) path
0456: .getCMPField();
0457: selectManager = selectField.getManager();
0458: setTypeFactory(selectManager.getJDBCTypeFactory());
0459: } else if (path.isCMRField()) {
0460: JDBCFieldBridge cmrField = (JDBCFieldBridge) path
0461: .getCMRField();
0462: selectManager = cmrField.getManager();
0463: setTypeFactory(selectManager.getJDBCTypeFactory());
0464: addLeftJoinPath(path);
0465: } else {
0466: final JDBCAbstractEntityBridge entity = (JDBCAbstractEntityBridge) path
0467: .getEntity();
0468: selectManager = entity.getManager();
0469: setTypeFactory(selectManager.getJDBCTypeFactory());
0470: addLeftJoinPath(path);
0471: }
0472:
0473: selectObject = child0;
0474: child0.jjtAccept(this , data);
0475: }
0476:
0477: return data;
0478: }
0479:
0480: public Object visit(ASTWhere node, Object data) {
0481: node.jjtGetChild(0).jjtAccept(this , data);
0482: return data;
0483: }
0484:
0485: public Object visit(ASTOr node, Object data) {
0486: StringBuffer buf = (StringBuffer) data;
0487: node.jjtGetChild(0).jjtAccept(this , data);
0488: for (int i = 1; i < node.jjtGetNumChildren(); ++i) {
0489: buf.append(SQLUtil.OR);
0490: node.jjtGetChild(i).jjtAccept(this , data);
0491: }
0492: return data;
0493: }
0494:
0495: public Object visit(ASTWhereConditionalTerm node, Object data) {
0496: for (int i = 0; i < node.jjtGetNumChildren(); ++i) {
0497: node.jjtGetChild(i).jjtAccept(this , data);
0498: }
0499: return data;
0500: }
0501:
0502: public Object visit(ASTAnd node, Object data) {
0503: StringBuffer buf = (StringBuffer) data;
0504: node.jjtGetChild(0).jjtAccept(this , data);
0505: for (int i = 1; i < node.jjtGetNumChildren(); i++) {
0506: buf.append(SQLUtil.AND);
0507: node.jjtGetChild(i).jjtAccept(this , data);
0508: }
0509: return data;
0510: }
0511:
0512: public Object visit(ASTNot node, Object data) {
0513: StringBuffer buf = (StringBuffer) data;
0514: buf.append(SQLUtil.NOT);
0515: node.jjtGetChild(0).jjtAccept(this , data);
0516: return data;
0517: }
0518:
0519: public Object visit(ASTConditionalParenthetical node, Object data) {
0520: StringBuffer buf = (StringBuffer) data;
0521: buf.append('(');
0522: node.jjtGetChild(0).jjtAccept(this , data);
0523: buf.append(')');
0524: return data;
0525: }
0526:
0527: public Object visit(ASTBetween node, Object data) {
0528: StringBuffer buf = (StringBuffer) data;
0529: node.jjtGetChild(0).jjtAccept(this , data);
0530: if (node.not) {
0531: buf.append(SQLUtil.NOT);
0532: }
0533: buf.append(SQLUtil.BETWEEN);
0534: node.jjtGetChild(1).jjtAccept(this , data);
0535: buf.append(SQLUtil.AND);
0536: node.jjtGetChild(2).jjtAccept(this , data);
0537: return data;
0538: }
0539:
0540: public Object visit(ASTIn node, Object data) {
0541: StringBuffer buf = (StringBuffer) data;
0542: node.jjtGetChild(0).jjtAccept(this , data);
0543: if (node.not) {
0544: buf.append(SQLUtil.NOT);
0545: }
0546: buf.append(SQLUtil.IN).append('(');
0547: node.jjtGetChild(1).jjtAccept(this , data);
0548: for (int i = 2; i < node.jjtGetNumChildren(); i++) {
0549: buf.append(SQLUtil.COMMA);
0550: node.jjtGetChild(i).jjtAccept(this , data);
0551: }
0552: buf.append(')');
0553: return data;
0554: }
0555:
0556: public Object visit(ASTLike node, Object data) {
0557: StringBuffer buf = (StringBuffer) data;
0558: node.jjtGetChild(0).jjtAccept(this , data);
0559: if (node.not) {
0560: buf.append(SQLUtil.NOT);
0561: }
0562: buf.append(SQLUtil.LIKE);
0563: node.jjtGetChild(1).jjtAccept(this , data);
0564: if (node.jjtGetNumChildren() == 3) {
0565: buf.append(" {ESCAPE ");
0566: node.jjtGetChild(2).jjtAccept(this , data);
0567: buf.append('}');
0568: }
0569: return data;
0570: }
0571:
0572: public Object visit(ASTNullComparison node, Object data) {
0573: StringBuffer sql = (StringBuffer) data;
0574:
0575: final Node child0 = node.jjtGetChild(0);
0576: if (child0 instanceof ASTPath) {
0577: ASTPath path = (ASTPath) child0;
0578: addLeftJoinPath(path);
0579:
0580: JDBCFieldBridge field = (JDBCFieldBridge) path.getField();
0581:
0582: if (field instanceof JDBCAbstractCMRFieldBridge) {
0583: JDBCAbstractCMRFieldBridge cmrField = (JDBCAbstractCMRFieldBridge) field;
0584: final String alias;
0585: final JDBCFieldBridge[] keyFields;
0586:
0587: if (cmrField.hasForeignKey()) {
0588: alias = aliasManager.getAlias(path.getPath(path
0589: .size() - 2));
0590: keyFields = cmrField.getForeignKeyFields();
0591: } else {
0592: alias = aliasManager.getAlias(path.getPath());
0593: if (cmrField.getMetaData().getRelationMetaData()
0594: .isTableMappingStyle()) {
0595: keyFields = cmrField.getRelatedCMRField()
0596: .getEntity().getPrimaryKeyFields();
0597: } else {
0598: keyFields = cmrField.getRelatedCMRField()
0599: .getForeignKeyFields();
0600: }
0601: }
0602:
0603: SQLUtil
0604: .getIsNullClause(node.not, keyFields, alias,
0605: sql);
0606: } else {
0607: String alias = aliasManager.getAlias(path.getPath(path
0608: .size() - 2));
0609: SQLUtil.getIsNullClause(node.not, field, alias, sql);
0610: }
0611: } else if (child0 instanceof ASTParameter) {
0612: ASTParameter param = (ASTParameter) child0;
0613: Class type = getParameterType(param.number);
0614:
0615: QueryParameter queryParam = new QueryParameter(
0616: param.number - 1, typeFactory.getJDBCType(type));
0617: inputParameters.add(queryParam);
0618:
0619: sql.append("? IS ");
0620: if (node.not) {
0621: sql.append(SQLUtil.NOT);
0622: }
0623: sql.append(SQLUtil.NULL);
0624: } else {
0625: throw new IllegalStateException(
0626: "Unexpected node in IS NULL clause: " + node);
0627: }
0628:
0629: return data;
0630: }
0631:
0632: public Object visit(ASTIsEmpty node, Object data) {
0633: ASTPath path = (ASTPath) node.jjtGetChild(0);
0634: if (!path.isCMRField()) {
0635: throw new IllegalStateException(
0636: "IS EMPTY can be applied only to collection valued CMR field.");
0637: }
0638:
0639: addLeftJoinPath(path);
0640:
0641: StringBuffer sql = (StringBuffer) data;
0642: JDBCAbstractCMRFieldBridge cmrField = (JDBCAbstractCMRFieldBridge) path
0643: .getCMRField();
0644: JDBCAbstractEntityBridge relatedEntity = (JDBCAbstractEntityBridge) cmrField
0645: .getRelatedEntity();
0646: String alias = aliasManager.getAlias(path.getPath());
0647: SQLUtil.getIsNullClause(node.not, relatedEntity
0648: .getPrimaryKeyFields(), alias, sql);
0649:
0650: return data;
0651: }
0652:
0653: public Object visit(ASTMemberOf node, Object data) {
0654: Node member = node.jjtGetChild(0);
0655: ASTPath colPath = (ASTPath) node.jjtGetChild(1);
0656: JDBCAbstractEntityBridge colEntity = (JDBCAbstractEntityBridge) colPath
0657: .getEntity();
0658:
0659: StringBuffer sql = (StringBuffer) data;
0660:
0661: if (node.not) {
0662: sql.append(SQLUtil.NOT);
0663: }
0664:
0665: sql.append(SQLUtil.EXISTS).append('(').append(SQLUtil.SELECT);
0666:
0667: if (member instanceof ASTParameter) {
0668: ASTParameter toParam = (ASTParameter) member;
0669: verifyParameterEntityType(toParam.number, colEntity);
0670: inputParameters.addAll(QueryParameter.createParameters(
0671: toParam.number - 1, colEntity));
0672:
0673: String parentAlias = aliasManager.getAlias(colPath
0674: .getPath(0));
0675: String localParentAlias = aliasManager.getAlias(colPath
0676: .getPath(0)
0677: + "_local");
0678: JDBCAbstractEntityBridge parentEntity = (JDBCAbstractEntityBridge) colPath
0679: .getEntity(0);
0680: SQLUtil.getColumnNamesClause(parentEntity
0681: .getPrimaryKeyFields(), localParentAlias, sql);
0682: sql.append(SQLUtil.FROM).append(
0683: parentEntity.getQualifiedTableName()).append(' ')
0684: .append(localParentAlias);
0685: innerJoinPath(colPath, sql);
0686:
0687: sql.append(SQLUtil.WHERE);
0688:
0689: JDBCAbstractEntityBridge col0 = (JDBCAbstractEntityBridge) colPath
0690: .getEntity(0);
0691: SQLUtil.getSelfCompareWhereClause(col0
0692: .getPrimaryKeyFields(), parentAlias,
0693: localParentAlias, sql);
0694: sql.append(SQLUtil.AND);
0695:
0696: String localColAlias = aliasManager.getAlias(colPath
0697: .getPath()
0698: + "_local");
0699: SQLUtil.getWhereClause(colEntity.getPrimaryKeyFields(),
0700: localColAlias, sql);
0701: } else {
0702: ASTPath memberPath = (ASTPath) member;
0703: JDBCAbstractEntityBridge memberEntity = (JDBCAbstractEntityBridge) memberPath
0704: .getEntity();
0705:
0706: if (!memberEntity.equals(colEntity)) {
0707: throw new IllegalStateException(
0708: "Member must be if the same type as the collection, got: member="
0709: + memberEntity.getEntityName()
0710: + ", collection="
0711: + colEntity.getEntityName());
0712: }
0713:
0714: String memberAlias = aliasManager.getAlias(memberPath
0715: .getPath());
0716:
0717: if (memberPath.size() > 1) {
0718: String parentAlias = aliasManager.getAlias(memberPath
0719: .getPath(0)
0720: + "_local");
0721: JDBCAbstractEntityBridge parentEntity = (JDBCAbstractEntityBridge) memberPath
0722: .getEntity(0);
0723: SQLUtil.getColumnNamesClause(parentEntity
0724: .getPrimaryKeyFields(), parentAlias, sql);
0725: sql.append(SQLUtil.FROM).append(
0726: parentEntity.getQualifiedTableName()).append(
0727: ' ').append(parentAlias);
0728: innerJoinPath(memberPath, sql);
0729: innerJoinPath(colPath, sql);
0730: } else if (colPath.size() > 1) {
0731: String parentAlias = aliasManager.getAlias(colPath
0732: .getPath(0)
0733: + "_local");
0734: JDBCAbstractEntityBridge parentEntity = (JDBCAbstractEntityBridge) colPath
0735: .getEntity(0);
0736: SQLUtil.getColumnNamesClause(parentEntity
0737: .getPrimaryKeyFields(), parentAlias, sql);
0738: sql.append(SQLUtil.FROM).append(
0739: parentEntity.getQualifiedTableName()).append(
0740: ' ').append(parentAlias);
0741: innerJoinPath(colPath, sql);
0742: } else {
0743: throw new IllegalStateException(
0744: "There should be collection valued path expression, not identification variable.");
0745: }
0746:
0747: sql.append(SQLUtil.WHERE);
0748:
0749: JDBCAbstractEntityBridge member0 = (JDBCAbstractEntityBridge) memberPath
0750: .getEntity(0);
0751: String colAliasLocal = aliasManager.getAlias(colPath
0752: .getPath()
0753: + "_local");
0754: if (memberPath.size() > 1) {
0755: String memberAliasLocal = aliasManager
0756: .getAlias(memberPath.getPath() + "_local");
0757: SQLUtil.getSelfCompareWhereClause(colEntity
0758: .getPrimaryKeyFields(), memberAliasLocal,
0759: colAliasLocal, sql);
0760:
0761: sql.append(SQLUtil.AND);
0762:
0763: String member0Alias = aliasManager.getAlias(memberPath
0764: .getPath(0));
0765: String member0AliasLocal = aliasManager
0766: .getAlias(memberPath.getPath(0) + "_local");
0767: SQLUtil.getSelfCompareWhereClause(member0
0768: .getPrimaryKeyFields(), member0Alias,
0769: member0AliasLocal, sql);
0770: } else {
0771: SQLUtil.getSelfCompareWhereClause(member0
0772: .getPrimaryKeyFields(), memberAlias,
0773: colAliasLocal, sql);
0774: }
0775: }
0776:
0777: sql.append(')');
0778:
0779: return data;
0780: }
0781:
0782: private void innerJoinPath(ASTPath path, StringBuffer sql) {
0783: if (path.size() < 2) {
0784: return;
0785: }
0786:
0787: String parentAlias = aliasManager.getAlias(path.getPath(0)
0788: + "_local");
0789: String leftAlias = parentAlias;
0790: for (int i = 1; i < path.size(); ++i) {
0791: String curPath = path.getPath(i);
0792: final String joinAlias = aliasManager.getAlias(curPath
0793: + "_local");
0794:
0795: final JDBCAbstractCMRFieldBridge cmrField = (JDBCAbstractCMRFieldBridge) path
0796: .getCMRField(i);
0797: final JDBCAbstractEntityBridge joinEntity = (JDBCAbstractEntityBridge) cmrField
0798: .getRelatedEntity();
0799:
0800: JDBCRelationMetaData relation = cmrField.getMetaData()
0801: .getRelationMetaData();
0802:
0803: String join = " INNER JOIN ";
0804:
0805: if (relation.isTableMappingStyle()) {
0806: String relTableAlias = aliasManager
0807: .getRelationTableAlias(curPath + "_local");
0808: sql.append(join).append(
0809: cmrField.getQualifiedTableName()).append(' ')
0810: .append(relTableAlias).append(" ON ");
0811: SQLUtil.getRelationTableJoinClause(cmrField, leftAlias,
0812: relTableAlias, sql);
0813:
0814: sql.append(join).append(
0815: joinEntity.getQualifiedTableName()).append(' ')
0816: .append(joinAlias).append(" ON ");
0817: SQLUtil.getRelationTableJoinClause(cmrField
0818: .getRelatedCMRField(), joinAlias,
0819: relTableAlias, sql);
0820: } else {
0821: sql.append(join).append(
0822: joinEntity.getQualifiedTableName()).append(' ')
0823: .append(joinAlias).append(" ON ");
0824:
0825: SQLUtil.getJoinClause(cmrField, leftAlias, joinAlias,
0826: sql);
0827: }
0828:
0829: leftAlias = joinAlias;
0830: }
0831: }
0832:
0833: public Object visit(ASTStringComparison node, Object data) {
0834: StringBuffer buf = (StringBuffer) data;
0835: node.jjtGetChild(0).jjtAccept(this , data);
0836: buf.append(' ').append(node.opp).append(' ');
0837: node.jjtGetChild(1).jjtAccept(this , data);
0838: return data;
0839: }
0840:
0841: public Object visit(ASTBooleanComparison node, Object data) {
0842: StringBuffer buf = (StringBuffer) data;
0843: node.jjtGetChild(0).jjtAccept(this , data);
0844: if (node.jjtGetNumChildren() == 2) {
0845: buf.append(' ').append(node.opp).append(' ');
0846: node.jjtGetChild(1).jjtAccept(this , data);
0847: }
0848: return data;
0849: }
0850:
0851: public Object visit(ASTDatetimeComparison node, Object data) {
0852: StringBuffer buf = (StringBuffer) data;
0853: node.jjtGetChild(0).jjtAccept(this , data);
0854: buf.append(' ').append(node.opp).append(' ');
0855: node.jjtGetChild(1).jjtAccept(this , data);
0856: return data;
0857: }
0858:
0859: public Object visit(ASTValueClassComparison node, Object data) {
0860: StringBuffer buf = (StringBuffer) data;
0861:
0862: boolean not = (node.opp.equals(SQLUtil.NOT_EQUAL));
0863: String comparison = node.opp;
0864: buf.append('(');
0865: if (not) {
0866: buf.append(SQLUtil.NOT).append('(');
0867: comparison = "=";
0868: }
0869:
0870: // setup the from path
0871: ASTPath fromPath = (ASTPath) node.jjtGetChild(0);
0872: addInnerJoinPath(fromPath);
0873: String fromAlias = aliasManager.getAlias(fromPath
0874: .getPath(fromPath.size() - 2));
0875: CMPFieldBridge fromCMPField = (CMPFieldBridge) fromPath
0876: .getCMPField();
0877:
0878: Node toNode = node.jjtGetChild(1);
0879: if (toNode instanceof ASTParameter) {
0880: ASTParameter toParam = (ASTParameter) toNode;
0881:
0882: // can only compare like kind entities
0883: Class parameterType = getParameterType(toParam.number);
0884: if (!(fromCMPField.getFieldType().equals(parameterType))) {
0885: throw new IllegalStateException(
0886: "Only like types can be "
0887: + "compared: from CMP field="
0888: + fromCMPField.getFieldType()
0889: + " to parameter=" + parameterType);
0890: }
0891:
0892: inputParameters.addAll(QueryParameter.createParameters(
0893: toParam.number - 1, fromCMPField));
0894: SQLUtil.getWhereClause(fromCMPField.getJDBCType(),
0895: fromAlias, comparison, buf);
0896: } else {
0897: ASTPath toPath = (ASTPath) toNode;
0898: addInnerJoinPath(toPath);
0899: String toAlias = aliasManager.getAlias(toPath
0900: .getPath(toPath.size() - 2));
0901: JDBCCMPFieldBridge toCMPField = (JDBCCMPFieldBridge) toPath
0902: .getCMPField();
0903:
0904: // can only compare like kind entities
0905: if (!(fromCMPField.getFieldType().equals(toCMPField
0906: .getFieldType()))) {
0907: throw new IllegalStateException(
0908: "Only like types can be "
0909: + "compared: from CMP field="
0910: + fromCMPField.getFieldType()
0911: + " to CMP field="
0912: + toCMPField.getFieldType());
0913: }
0914:
0915: SQLUtil.getSelfCompareWhereClause(fromCMPField, toCMPField,
0916: fromAlias, toAlias, comparison, buf);
0917: }
0918:
0919: return (not ? buf.append(')') : buf).append(')');
0920: }
0921:
0922: public Object visit(ASTEntityComparison node, Object data) {
0923: StringBuffer buf = (StringBuffer) data;
0924: Node arg0 = node.jjtGetChild(0);
0925: Node arg1 = node.jjtGetChild(1);
0926: if (node.opp.equals(SQLUtil.NOT_EQUAL)) {
0927: compareEntity(true, arg0, arg1, buf);
0928: } else {
0929: compareEntity(false, arg0, arg1, buf);
0930: }
0931: return data;
0932: }
0933:
0934: public Object visit(ASTArithmeticComparison node, Object data) {
0935: StringBuffer buf = (StringBuffer) data;
0936: node.jjtGetChild(0).jjtAccept(this , data);
0937: buf.append(' ').append(node.opp).append(' ');
0938: node.jjtGetChild(1).jjtAccept(this , data);
0939: return data;
0940: }
0941:
0942: public Object visit(ASTPlusMinus node, Object data) {
0943: StringBuffer buf = (StringBuffer) data;
0944: node.jjtGetChild(0).jjtAccept(this , data);
0945: for (int i = 1; i < node.jjtGetNumChildren(); i++) {
0946: buf.append(' ').append(node.opps.get(i - 1)).append(' ');
0947: node.jjtGetChild(i).jjtAccept(this , data);
0948: }
0949: return data;
0950: }
0951:
0952: public Object visit(ASTMultDiv node, Object data) {
0953: StringBuffer buf = (StringBuffer) data;
0954: node.jjtGetChild(0).jjtAccept(this , data);
0955: for (int i = 1; i < node.jjtGetNumChildren(); i++) {
0956: buf.append(' ').append(node.opps.get(i - 1)).append(' ');
0957: node.jjtGetChild(i).jjtAccept(this , data);
0958: }
0959: return data;
0960: }
0961:
0962: public Object visit(ASTNegation node, Object data) {
0963: StringBuffer buf = (StringBuffer) data;
0964: buf.append('-');
0965: node.jjtGetChild(0).jjtAccept(this , data);
0966: return data;
0967: }
0968:
0969: public Object visit(ASTArithmeticParenthetical node, Object data) {
0970: StringBuffer buf = (StringBuffer) data;
0971: buf.append('(');
0972: node.jjtGetChild(0).jjtAccept(this , data);
0973: buf.append(')');
0974: return data;
0975: }
0976:
0977: public Object visit(ASTStringParenthetical node, Object data) {
0978: StringBuffer buf = (StringBuffer) data;
0979: buf.append('(');
0980: node.jjtGetChild(0).jjtAccept(this , data);
0981: buf.append(')');
0982: return data;
0983: }
0984:
0985: public Object visit(ASTConcat node, Object data) {
0986: StringBuffer buf = (StringBuffer) data;
0987: JDBCFunctionMappingMetaData function = typeMapping
0988: .getFunctionMapping(JDBCTypeMappingMetaData.CONCAT);
0989: Object[] args = childrenToStringArr(2, node);
0990: function.getFunctionSql(args, buf);
0991: return data;
0992: }
0993:
0994: public Object visit(ASTSubstring node, Object data) {
0995: StringBuffer buf = (StringBuffer) data;
0996: JDBCFunctionMappingMetaData function = typeMapping
0997: .getFunctionMapping(JDBCTypeMappingMetaData.SUBSTRING);
0998: Object[] args = childrenToStringArr(3, node);
0999: function.getFunctionSql(args, buf);
1000: return data;
1001: }
1002:
1003: public Object visit(ASTUCase node, Object data) {
1004: StringBuffer buf = (StringBuffer) data;
1005: JDBCFunctionMappingMetaData function = typeMapping
1006: .getFunctionMapping(JDBCTypeMappingMetaData.UCASE);
1007: Object[] args = childrenToStringArr(1, node);
1008: function.getFunctionSql(args, buf);
1009: return data;
1010: }
1011:
1012: public Object visit(ASTLCase node, Object data) {
1013: StringBuffer buf = (StringBuffer) data;
1014: JDBCFunctionMappingMetaData function = typeMapping
1015: .getFunctionMapping(JDBCTypeMappingMetaData.LCASE);
1016: Object[] args = childrenToStringArr(1, node);
1017: function.getFunctionSql(args, buf);
1018: return data;
1019: }
1020:
1021: public Object visit(ASTLength node, Object data) {
1022: StringBuffer buf = (StringBuffer) data;
1023: JDBCFunctionMappingMetaData function = typeMapping
1024: .getFunctionMapping(JDBCTypeMappingMetaData.LENGTH);
1025: Object[] args = childrenToStringArr(1, node);
1026: function.getFunctionSql(args, buf);
1027: return data;
1028: }
1029:
1030: public Object visit(ASTLocate node, Object data) {
1031: StringBuffer buf = (StringBuffer) data;
1032: JDBCFunctionMappingMetaData function = typeMapping
1033: .getFunctionMapping(JDBCTypeMappingMetaData.LOCATE);
1034: Object[] args = new Object[3];
1035: args[0] = node.jjtGetChild(0).jjtAccept(this ,
1036: new StringBuffer()).toString();
1037: args[1] = node.jjtGetChild(1).jjtAccept(this ,
1038: new StringBuffer()).toString();
1039: if (node.jjtGetNumChildren() == 3) {
1040: args[2] = node.jjtGetChild(2).jjtAccept(this ,
1041: new StringBuffer()).toString();
1042: } else {
1043: args[2] = "1";
1044: }
1045: function.getFunctionSql(args, buf);
1046: return data;
1047: }
1048:
1049: public Object visit(ASTAbs node, Object data) {
1050: StringBuffer buf = (StringBuffer) data;
1051: JDBCFunctionMappingMetaData function = typeMapping
1052: .getFunctionMapping(JDBCTypeMappingMetaData.ABS);
1053: Object[] args = childrenToStringArr(1, node);
1054: function.getFunctionSql(args, buf);
1055: return data;
1056: }
1057:
1058: public Object visit(ASTSqrt node, Object data) {
1059: StringBuffer buf = (StringBuffer) data;
1060: JDBCFunctionMappingMetaData function = typeMapping
1061: .getFunctionMapping(JDBCTypeMappingMetaData.SQRT);
1062: Object[] args = childrenToStringArr(1, node);
1063: function.getFunctionSql(args, buf);
1064: return data;
1065: }
1066:
1067: public Object visit(ASTMod node, Object data) {
1068: StringBuffer buf = (StringBuffer) data;
1069: JDBCFunctionMappingMetaData function = typeMapping
1070: .getFunctionMapping(JDBCTypeMappingMetaData.MOD);
1071: Object[] args = childrenToStringArr(2, node);
1072: function.getFunctionSql(args, buf);
1073: return data;
1074: }
1075:
1076: public Object visit(ASTAvg node, Object data) {
1077: node.setResultType(returnType);
1078: StringBuffer buf = (StringBuffer) data;
1079: Object[] args = new Object[] {
1080: node.distinct,
1081: node.jjtGetChild(0).jjtAccept(this , new StringBuffer())
1082: .toString(), };
1083: JDBCTypeMappingMetaData.AVG_FUNC.getFunctionSql(args, buf);
1084: return data;
1085: }
1086:
1087: public Object visit(ASTMax node, Object data) {
1088: node.setResultType(returnType);
1089: StringBuffer buf = (StringBuffer) data;
1090: Object[] args = new Object[] {
1091: node.distinct,
1092: node.jjtGetChild(0).jjtAccept(this , new StringBuffer())
1093: .toString(), };
1094: JDBCTypeMappingMetaData.MAX_FUNC.getFunctionSql(args, buf);
1095: return data;
1096: }
1097:
1098: public Object visit(ASTMin node, Object data) {
1099: node.setResultType(returnType);
1100: StringBuffer buf = (StringBuffer) data;
1101: Object[] args = new Object[] {
1102: node.distinct,
1103: node.jjtGetChild(0).jjtAccept(this , new StringBuffer())
1104: .toString(), };
1105: JDBCTypeMappingMetaData.MIN_FUNC.getFunctionSql(args, buf);
1106: return data;
1107: }
1108:
1109: public Object visit(ASTSum node, Object data) {
1110: node.setResultType(returnType);
1111: StringBuffer buf = (StringBuffer) data;
1112: Object[] args = new Object[] {
1113: node.distinct,
1114: node.jjtGetChild(0).jjtAccept(this , new StringBuffer())
1115: .toString(), };
1116: JDBCTypeMappingMetaData.SUM_FUNC.getFunctionSql(args, buf);
1117: return data;
1118: }
1119:
1120: public Object visit(ASTCount node, Object data) {
1121: StringBuffer buf = (StringBuffer) data;
1122: node.setResultType(returnType);
1123:
1124: Object args[];
1125: final ASTPath cntPath = (ASTPath) node.jjtGetChild(0);
1126: if (cntPath.isCMPField()) {
1127: args = new Object[] {
1128: node.distinct,
1129: node.jjtGetChild(0).jjtAccept(this ,
1130: new StringBuffer()).toString() };
1131: } else {
1132: JDBCAbstractEntityBridge entity = (JDBCAbstractEntityBridge) cntPath
1133: .getEntity();
1134: final JDBCFieldBridge[] pkFields = entity
1135: .getPrimaryKeyFields();
1136: if (pkFields.length > 1) {
1137: countCompositePk = true;
1138: forceDistinct = node.distinct.length() > 0;
1139:
1140: addLeftJoinPath(cntPath);
1141:
1142: String alias = aliasManager.getAlias(cntPath.getPath());
1143: SQLUtil.getColumnNamesClause(entity
1144: .getPrimaryKeyFields(), alias, buf);
1145:
1146: return buf;
1147: } else {
1148: final String alias = aliasManager.getAlias(cntPath
1149: .getPath());
1150: StringBuffer keyColumn = new StringBuffer(20);
1151: SQLUtil.getColumnNamesClause(pkFields[0], alias,
1152: keyColumn);
1153: args = new Object[] { node.distinct,
1154: keyColumn.toString() };
1155: }
1156: }
1157:
1158: JDBCTypeMappingMetaData.COUNT_FUNC.getFunctionSql(args, buf);
1159: return data;
1160: }
1161:
1162: public Object visit(ASTPath node, Object data) {
1163: StringBuffer buf = (StringBuffer) data;
1164: if (!node.isCMPField()) {
1165: throw new IllegalStateException(
1166: "Can only visit cmp valued path node. "
1167: + "Should have been handled at a higher level.");
1168: }
1169:
1170: JDBCFieldBridge cmpField = (JDBCFieldBridge) node.getCMPField();
1171:
1172: // make sure this is mapped to a single column
1173: switch (node.type) {
1174: case EJBQLTypes.ENTITY_TYPE:
1175: case EJBQLTypes.VALUE_CLASS_TYPE:
1176: if (cmpField.getJDBCType().hasMapper()
1177: || cmpField.getJDBCType().getParameterSetter() != null) {
1178: break;
1179: }
1180: case EJBQLTypes.UNKNOWN_TYPE:
1181: throw new IllegalStateException(
1182: "Can not visit multi-column path "
1183: + "node. Should have been handled at a higher level.");
1184: }
1185:
1186: addLeftJoinPath(node);
1187: String alias = aliasManager.getAlias(node
1188: .getPath(node.size() - 2));
1189: SQLUtil.getColumnNamesClause(cmpField, alias, buf);
1190: return data;
1191: }
1192:
1193: public Object visit(ASTAbstractSchema node, Object data) {
1194: throw new IllegalStateException(
1195: "Can not visit abstract schema node. "
1196: + " Should have been handled at a higher level.");
1197: }
1198:
1199: public Object visit(ASTIdentifier node, Object data) {
1200: throw new UnsupportedOperationException(
1201: "Must not visit ASTIdentifier noe.");
1202: }
1203:
1204: public Object visit(ASTParameter node, Object data) {
1205: StringBuffer buf = (StringBuffer) data;
1206: Class type = getParameterType(node.number);
1207:
1208: // make sure this is mapped to a single column
1209: int ejbqlType = EJBQLTypes.getEJBQLType(type);
1210: if (ejbqlType == EJBQLTypes.ENTITY_TYPE
1211: || ejbqlType == EJBQLTypes.VALUE_CLASS_TYPE
1212: || ejbqlType == EJBQLTypes.UNKNOWN_TYPE) {
1213: throw new IllegalStateException(
1214: "Can not visit multi-column "
1215: + "parameter node. Should have been handled at a higher level.");
1216: }
1217:
1218: QueryParameter param = new QueryParameter(node.number - 1,
1219: typeFactory.getJDBCType(type));
1220: inputParameters.add(param);
1221: buf.append('?');
1222:
1223: return data;
1224: }
1225:
1226: public Object visit(ASTExactNumericLiteral node, Object data) {
1227: StringBuffer buf = (StringBuffer) data;
1228: buf.append(node.literal);
1229: return data;
1230: }
1231:
1232: public Object visit(ASTApproximateNumericLiteral node, Object data) {
1233: StringBuffer buf = (StringBuffer) data;
1234: buf.append(node.literal);
1235: return data;
1236: }
1237:
1238: public Object visit(ASTStringLiteral node, Object data) {
1239: StringBuffer buf = (StringBuffer) data;
1240: buf.append(node.value);
1241: return data;
1242: }
1243:
1244: public Object visit(ASTBooleanLiteral node, Object data) {
1245: StringBuffer buf = (StringBuffer) data;
1246: if (node.value) {
1247: buf.append(typeMapping.getTrueMapping());
1248: } else {
1249: buf.append(typeMapping.getFalseMapping());
1250: }
1251: return data;
1252: }
1253:
1254: public Object visit(ASTFrom from, Object data) {
1255: StringBuffer sql = (StringBuffer) data;
1256: from.jjtGetChild(0).jjtAccept(this , data);
1257: for (int i = 1; i < from.jjtGetNumChildren(); ++i) {
1258: from.jjtGetChild(i).jjtAccept(this , data);
1259: }
1260:
1261: return data;
1262: }
1263:
1264: public Object visit(ASTCollectionMemberDeclaration node, Object data) {
1265: ASTPath path = (ASTPath) node.jjtGetChild(0);
1266:
1267: // assign the same alias for path and identifier
1268: ASTIdentifier id = (ASTIdentifier) node.jjtGetChild(1);
1269: String alias = aliasManager.getAlias(id.identifier);
1270: aliasManager.addAlias(path.getPath(), alias);
1271:
1272: addInnerJoinPath(path);
1273:
1274: return data;
1275: }
1276:
1277: public Object visit(ASTRangeVariableDeclaration node, Object data) {
1278: ASTAbstractSchema schema = (ASTAbstractSchema) node
1279: .jjtGetChild(0);
1280: JDBCAbstractEntityBridge entity = (JDBCAbstractEntityBridge) schema.entity;
1281: ASTIdentifier id = (ASTIdentifier) node.jjtGetChild(1);
1282: declareTable(id.identifier, entity.getQualifiedTableName());
1283: return data;
1284: }
1285:
1286: // Private
1287:
1288: private void compareEntity(boolean not, Node fromNode, Node toNode,
1289: StringBuffer buf) {
1290: buf.append('(');
1291: if (not) {
1292: buf.append(SQLUtil.NOT).append('(');
1293: }
1294:
1295: ASTPath fromPath = (ASTPath) fromNode;
1296: addLeftJoinPath(fromPath);
1297: String fromAlias = aliasManager.getAlias(fromPath.getPath());
1298: JDBCAbstractEntityBridge fromEntity = (JDBCAbstractEntityBridge) fromPath
1299: .getEntity();
1300:
1301: if (toNode instanceof ASTParameter) {
1302: ASTParameter toParam = (ASTParameter) toNode;
1303:
1304: // can only compare like kind entities
1305: verifyParameterEntityType(toParam.number, fromEntity);
1306:
1307: inputParameters.addAll(QueryParameter.createParameters(
1308: toParam.number - 1, fromEntity));
1309:
1310: SQLUtil.getWhereClause(fromEntity.getPrimaryKeyFields(),
1311: fromAlias, buf);
1312: } else {
1313: ASTPath toPath = (ASTPath) toNode;
1314: addLeftJoinPath(toPath);
1315: String toAlias = aliasManager.getAlias(toPath.getPath());
1316: JDBCAbstractEntityBridge toEntity = (JDBCAbstractEntityBridge) toPath
1317: .getEntity();
1318:
1319: // can only compare like kind entities
1320: if (!fromEntity.equals(toEntity)) {
1321: throw new IllegalStateException(
1322: "Only like types can be "
1323: + "compared: from entity="
1324: + fromEntity.getEntityName()
1325: + " to entity="
1326: + toEntity.getEntityName());
1327: }
1328:
1329: SQLUtil.getSelfCompareWhereClause(fromEntity
1330: .getPrimaryKeyFields(), fromAlias, toAlias, buf);
1331: }
1332:
1333: if (not) {
1334: buf.append(')');
1335: }
1336: buf.append(')');
1337: }
1338:
1339: private void join(String alias, StringBuffer sql) {
1340: Map paths = (Map) joinPaths.get(alias);
1341: if (paths == null || paths.isEmpty()) {
1342: return;
1343: }
1344:
1345: for (Iterator iter = paths.values().iterator(); iter.hasNext();) {
1346: String leftAlias = alias;
1347: ASTPath path = (ASTPath) iter.next();
1348: for (int i = 1; i < path.size(); ++i) {
1349: if (path.isCMRField(i)) {
1350: final String curPath = path.getPath(i);
1351: final String joinAlias = aliasManager
1352: .getAlias(curPath);
1353:
1354: if (joinedAliases.add(joinAlias)) {
1355: final JDBCAbstractCMRFieldBridge cmrField = (JDBCAbstractCMRFieldBridge) path
1356: .getCMRField(i);
1357: final JDBCAbstractEntityBridge joinEntity = (JDBCAbstractEntityBridge) cmrField
1358: .getRelatedEntity();
1359:
1360: JDBCRelationMetaData relation = cmrField
1361: .getMetaData().getRelationMetaData();
1362:
1363: String join = (path.innerJoin ? " INNER JOIN "
1364: : " LEFT OUTER JOIN ");
1365:
1366: if (relation.isTableMappingStyle()) {
1367: String relTableAlias = aliasManager
1368: .getRelationTableAlias(curPath);
1369: sql.append(join).append(
1370: cmrField.getQualifiedTableName())
1371: .append(' ').append(relTableAlias)
1372: .append(" ON ");
1373: SQLUtil.getRelationTableJoinClause(
1374: cmrField, leftAlias, relTableAlias,
1375: sql);
1376:
1377: sql.append(join).append(
1378: joinEntity.getQualifiedTableName())
1379: .append(' ').append(joinAlias)
1380: .append(" ON ");
1381: SQLUtil.getRelationTableJoinClause(cmrField
1382: .getRelatedCMRField(), joinAlias,
1383: relTableAlias, sql);
1384: } else {
1385: sql.append(join).append(
1386: joinEntity.getQualifiedTableName())
1387: .append(' ').append(joinAlias)
1388: .append(" ON ");
1389:
1390: SQLUtil.getJoinClause(cmrField, leftAlias,
1391: joinAlias, sql);
1392: }
1393:
1394: join(joinAlias, sql);
1395: }
1396: leftAlias = joinAlias;
1397: }
1398: }
1399: }
1400: }
1401:
1402: private void declareTable(String alias, String table) {
1403: identifierToTable.put(alias, table);
1404: }
1405:
1406: private void addLeftJoinPath(ASTPath path) {
1407: if (path.size() > 1 && path.isCMRField(1)) {
1408: final String identifier = path.getPath(0);
1409: final String alias = aliasManager.getAlias(identifier);
1410: Map paths = (Map) joinPaths.get(alias);
1411: if (paths == null) {
1412: paths = new HashMap();
1413: joinPaths.put(alias, paths);
1414: }
1415:
1416: ASTPath oldPath = (ASTPath) paths.put(path, path);
1417: if (oldPath != null && oldPath.innerJoin) {
1418: path.innerJoin = true;
1419: }
1420: }
1421: }
1422:
1423: private void addInnerJoinPath(ASTPath path) {
1424: if (path.size() > 1 && path.isCMRField(1)) {
1425: final String identifier = path.getPath(0);
1426: final String alias = aliasManager.getAlias(identifier);
1427: Map paths = (Map) joinPaths.get(alias);
1428: if (paths == null) {
1429: paths = new HashMap();
1430: joinPaths.put(alias, paths);
1431: }
1432:
1433: path.innerJoin = true;
1434: paths.put(path, path);
1435: }
1436: }
1437:
1438: private Object[] childrenToStringArr(int numChildren, Node node) {
1439: Object[] args = new Object[numChildren];
1440: for (int i = 0; i < numChildren; ++i) {
1441: args[i] = node.jjtGetChild(i).jjtAccept(this ,
1442: new StringBuffer()).toString();
1443: }
1444: return args;
1445: }
1446:
1447: /**
1448: * Recursively searches for ASTPath among children.
1449: *
1450: * @param selectFunction a node implements SelectFunction
1451: * @return ASTPath child or null if there was no child of type ASTPath
1452: */
1453: private ASTPath getPathFromChildren(Node selectFunction) {
1454: for (int childInd = 0; childInd < selectFunction
1455: .jjtGetNumChildren(); ++childInd) {
1456: Node child = selectFunction.jjtGetChild(childInd);
1457: if (child instanceof ASTPath) {
1458: return (ASTPath) child;
1459: } else if (child instanceof SelectFunction) {
1460: Node path = getPathFromChildren(child);
1461: if (path != null) {
1462: return (ASTPath) path;
1463: }
1464: }
1465: }
1466: return null;
1467: }
1468:
1469: private void setTypeFactory(JDBCTypeFactory typeFactory) {
1470: this .typeFactory = typeFactory;
1471: this .typeMapping = typeFactory.getTypeMapping();
1472: aliasManager = new AliasManager(typeMapping
1473: .getAliasHeaderPrefix(), typeMapping
1474: .getAliasHeaderSuffix(), typeMapping
1475: .getAliasMaxLength());
1476: }
1477:
1478: private Class getParameterType(int index) {
1479: int zeroBasedIndex = index - 1;
1480: Class[] params = parameterTypes;
1481: if (zeroBasedIndex < params.length) {
1482: return params[zeroBasedIndex];
1483: }
1484: return null;
1485: }
1486:
1487: // verify that parameter is the same type as the entity
1488: private void verifyParameterEntityType(int number,
1489: JDBCAbstractEntityBridge entity) {
1490: Class parameterType = getParameterType(number);
1491: Class remoteClass = entity.getRemoteInterface();
1492: Class localClass = entity.getLocalInterface();
1493: if ((localClass == null || !localClass
1494: .isAssignableFrom(parameterType))
1495: && (remoteClass == null || !remoteClass
1496: .isAssignableFrom(parameterType))) {
1497: throw new IllegalStateException(
1498: "Only like types can be compared: from entity="
1499: + entity.getEntityName()
1500: + " to parameter type=" + parameterType);
1501: }
1502: }
1503:
1504: private void reset() {
1505: returnType = null;
1506: parameterTypes = null;
1507: readAhead = null;
1508: inputParameters.clear();
1509: selectObject = null;
1510: selectManager = null;
1511: typeFactory = null;
1512: typeMapping = null;
1513: aliasManager = null;
1514: forceDistinct = false;
1515: limitParam = 0;
1516: limitValue = 0;
1517: offsetParam = 0;
1518: offsetValue = 0;
1519: leftJoinCMRList.clear();
1520: onFindCMRJoin = null;
1521: countCompositePk = false;
1522: joinPaths.clear();
1523: identifierToTable.clear();
1524: joinedAliases.clear();
1525: selectDistinct = false;
1526: }
1527: }
|