/** * Gets the sql string for the named attribute using the provided expression. The difference * between this and the standard {@link EOSQLExpression#sqlStringForAttributeNamed} is this one * can handle an "attribute" name that ends in a EORelationship rather than an actual * EOAttribute. This is necessary to support the {@link ERXExistsQualifier#baseKeyPath} syntax * (being the relationship path to the entity to which the subqualifier will be applied) chosen * for this qualifier. * * @param name of the attribute to get, e.g., department.division * @param expression to use when generating the SQL * @return the SQL string for the attribute */ private String sqlStringForAttributeNamedInExpression(String name, EOSQLExpression expression) { NSArray pieces = NSArray.componentsSeparatedByString(name, "."); EOEntity entity = expression.entity(); EORelationship rel; EOAttribute att; NSMutableArray path = new NSMutableArray(); int numPieces = pieces.count(); if (numPieces == 1 && null == entity.anyRelationshipNamed(name)) { att = entity.anyAttributeNamed(name); if (null == att) { return null; } return expression.sqlStringForAttribute(att); } else { for (int i = 0; i < numPieces - 1; i++) { rel = entity.anyRelationshipNamed((String) pieces.objectAtIndex(i)); if (null == rel) { return null; } path.addObject(rel); entity = rel.destinationEntity(); } String key = (String) pieces.lastObject(); if (entity.anyRelationshipNamed(key) != null) { // Test first for a relationship. rel = entity.anyRelationshipNamed(key); if (rel.isFlattened()) { String relPath = rel.relationshipPath(); NSArray relParts = NSArray.componentsSeparatedByString(relPath, "."); for (int i = 0; i < relParts.count(); i++) { rel = entity.anyRelationshipNamed((String) pieces.objectAtIndex(i)); path.addObject(rel); entity = rel.destinationEntity(); } } else { path.addObject(rel); } att = (EOAttribute) rel.destinationAttributes().lastObject(); } else { // The test for an attribute. att = entity.anyAttributeNamed(key); } if (null == att) { return null; } path.addObject(att); } return expression.sqlStringForAttributePath(path); }
/** * Generates the sql string for the given sql expression. Bulk of the logic for generating the * sub-query is in this method. * * @param e a given sql expression * @return sql string for the current sub-query. */ public String sqlStringForSQLExpression(EOSQLExpression e) { StringBuffer sb = new StringBuffer(); if (attributeName != null) sb.append(e.sqlStringForAttributeNamed(attributeName)); else { EOAttribute pk = (EOAttribute) e.entity().primaryKeyAttributes().lastObject(); sb.append(e.sqlStringForAttribute(pk)); } sb.append(" IN ( "); EOEntity entity = entityName == null ? e.entity() : e.entity().model().modelGroup().entityNamed(entityName); EOFetchSpecification fs = new EOFetchSpecification(entity.name(), qualifier, null, false, true, null); if (qualifier != null) { qualifier = EOQualifierSQLGeneration.Support._schemaBasedQualifierWithRootEntity(qualifier, entity); } if (qualifier != fs.qualifier()) { fs.setQualifier(qualifier); } // ASSUME: This makes a few assumptions, if anyone can figure out a fool // proof way that would be nice to get the model // Note you can't use: // EOAdaptor.adaptorWithModel(e.entity().model()).expressionFactory(); // as this creates a // EODatabaseContext context = EODatabaseContext.registeredDatabaseContextForModel( entity.model(), EOObjectStoreCoordinator.defaultCoordinator()); EOSQLExpressionFactory factory = context.database().adaptor().expressionFactory(); NSArray subAttributes = destinationAttName != null ? new NSArray(entity.attributeNamed(destinationAttName)) : entity.primaryKeyAttributes(); EOSQLExpression subExpression = factory.expressionForEntity(entity); // Arroz: Having this table identifier replacement causes serious // problems if you have more than a table being processed in the subquery. Disabling // it will apparently not cause problems, because t0 inside the subquery is not // the same t0 outside it. // subExpression.aliasesByRelationshipPath().setObjectForKey("t1", ""); subExpression.setUseAliases(true); subExpression.prepareSelectExpressionWithAttributes(subAttributes, false, fs); // EOSQLExpression // expression=factory.selectStatementForAttributes(entity.primaryKeyAttributes(), // false, fs, entity); for (Enumeration bindEnumeration = subExpression.bindVariableDictionaries().objectEnumerator(); bindEnumeration.hasMoreElements(); ) { e.addBindVariableDictionary((NSDictionary) bindEnumeration.nextElement()); } // sb.append(ERXStringUtilities.replaceStringByStringInString("t0.", // "t1.", subExpression.statement())); sb.append(subExpression.statement()); sb.append(" ) "); return sb.toString(); }
/** * Generates the EXISTS SQL string for the given SQL expression. The bulk of the logic for * generating the sub-query is in this method. * * @param qualifier for which to generate the SQL * @param expression to use during SQL generation * @return SQL string for the current sub-query */ public String sqlStringForSQLExpression(EOQualifier qualifier, EOSQLExpression expression) { if (null == qualifier || null == expression) { return null; } ERXExistsQualifier existsQualifier = (ERXExistsQualifier) qualifier; EOQualifier subqualifier = existsQualifier.subqualifier(); String baseKeyPath = existsQualifier.baseKeyPath(); EOEntity baseEntity = expression.entity(); EORelationship relationship = null; // Walk the key path to the last entity. if (baseKeyPath != null) { for (Enumeration pathEnum = NSArray.componentsSeparatedByString(baseKeyPath, ".").objectEnumerator(); pathEnum.hasMoreElements(); ) { String path = (String) pathEnum.nextElement(); if (null == relationship) { relationship = baseEntity.anyRelationshipNamed(path); } else { relationship = relationship.destinationEntity().anyRelationshipNamed(path); } } } EOEntity srcEntity = relationship != null ? relationship.entity() : baseEntity; EOEntity destEntity = relationship != null ? relationship.destinationEntity() : baseEntity; // We need to do a bunch of hand-waiving to get the right table aliases for the table used in // the exists // subquery and for the join clause back to the source table. String sourceTableAlias = "t0"; // The alias for the the source table of the baseKeyPath from the main query. String destTableAlias; // The alias for the table used in the subquery. if (!srcEntity.equals(baseEntity)) { // The exists clause is applied to the different table. String sourceKeyPath = ERXStringUtilities.keyPathWithoutLastProperty(baseKeyPath); sqlStringForAttributeNamedInExpression(sourceKeyPath, expression); sqlStringForAttributeNamedInExpression(baseKeyPath, expression); sourceTableAlias = (String) expression.aliasesByRelationshipPath().valueForKey(sourceKeyPath); destTableAlias = (String) expression.aliasesByRelationshipPath().valueForKey(baseKeyPath); if (null == destTableAlias) { destTableAlias = "t" + (expression.aliasesByRelationshipPath().count()); // The first entry = "t0". expression.aliasesByRelationshipPath().takeValueForKey(destTableAlias, baseKeyPath); } } else { // The exists clause is applied to the base table. destTableAlias = "t" + expression.aliasesByRelationshipPath().count(); // Probably "t1" } EOAttribute sourceKeyAttribute = srcEntity.primaryKeyAttributes().lastObject(); String sourceKey = expression.sqlStringForAttribute(sourceKeyAttribute); EOAttribute destKeyAttribute = relationship.destinationAttributes().lastObject(); String destKey = expression.sqlStringForAttribute(destKeyAttribute); EOQualifier qual = EOQualifierSQLGeneration.Support._schemaBasedQualifierWithRootEntity( subqualifier, destEntity); EOFetchSpecification fetchSpecification = new EOFetchSpecification(destEntity.name(), qual, null, false, true, null); EODatabaseContext context = EODatabaseContext.registeredDatabaseContextForModel( destEntity.model(), EOObjectStoreCoordinator.defaultCoordinator()); EOSQLExpressionFactory factory = context.database().adaptor().expressionFactory(); EOSQLExpression subExpression = factory.expressionForEntity(destEntity); subExpression.aliasesByRelationshipPath().setObjectForKey(destTableAlias, ""); subExpression.setUseAliases(true); subExpression.prepareSelectExpressionWithAttributes( destEntity.primaryKeyAttributes(), false, fetchSpecification); for (Enumeration bindEnumeration = subExpression.bindVariableDictionaries().objectEnumerator(); bindEnumeration.hasMoreElements(); ) { expression.addBindVariableDictionary((NSDictionary) bindEnumeration.nextElement()); } StringBuffer sb = new StringBuffer(); sb.append(" EXISTS ( "); sb.append( ERXStringUtilities.replaceStringByStringInString( "t0.", destTableAlias + ".", subExpression.statement())); sb.append(" AND "); sb.append( ERXStringUtilities.replaceStringByStringInString("t0.", destTableAlias + ".", destKey)); sb.append(" = "); sb.append( ERXStringUtilities.replaceStringByStringInString( "t0.", sourceTableAlias + ".", sourceKey)); sb.append(" ) "); return sb.toString(); }
@Override @SuppressWarnings("unchecked") public String sqlStringForSQLExpression(EOQualifier eoqualifier, EOSQLExpression e) { ERXToManyQualifier qualifier = (ERXToManyQualifier) eoqualifier; StringBuilder result = new StringBuilder(); EOEntity targetEntity = e.entity(); NSArray<String> toManyKeys = NSArray.componentsSeparatedByString(qualifier.key(), "."); EORelationship targetRelationship = null; for (int i = 0; i < toManyKeys.count() - 1; i++) { targetRelationship = targetEntity.anyRelationshipNamed(toManyKeys.objectAtIndex(i)); targetEntity = targetRelationship.destinationEntity(); } targetRelationship = targetEntity.relationshipNamed(toManyKeys.lastObject()); targetEntity = targetRelationship.destinationEntity(); if (targetRelationship.joins() == null || targetRelationship.joins().isEmpty()) { // we have a flattened many to many String definitionKeyPath = targetRelationship.definition(); NSArray<String> definitionKeys = NSArray.componentsSeparatedByString(definitionKeyPath, "."); EOEntity lastStopEntity = targetRelationship.entity(); EORelationship firstHopRelationship = lastStopEntity.relationshipNamed(definitionKeys.objectAtIndex(0)); EOEntity endOfFirstHopEntity = firstHopRelationship.destinationEntity(); EOJoin join = firstHopRelationship.joins().objectAtIndex(0); // assumes 1 join EOAttribute sourceAttribute = join.sourceAttribute(); EOAttribute targetAttribute = join.destinationAttribute(); EORelationship secondHopRelationship = endOfFirstHopEntity.relationshipNamed(definitionKeys.objectAtIndex(1)); join = secondHopRelationship.joins().objectAtIndex(0); // assumes 1 join EOAttribute secondHopSourceAttribute = join.sourceAttribute(); NSMutableArray<String> lastStopPKeyPath = toManyKeys.mutableClone(); lastStopPKeyPath.removeLastObject(); lastStopPKeyPath.addObject(firstHopRelationship.name()); lastStopPKeyPath.addObject(targetAttribute.name()); String firstHopRelationshipKeyPath = lastStopPKeyPath.componentsJoinedByString("."); result.append(e.sqlStringForAttributeNamed(firstHopRelationshipKeyPath)); result.append(" IN ( SELECT "); result.append(lastStopEntity.externalName()); result.append('.'); result.append(lastStopEntity.primaryKeyAttributes().objectAtIndex(0).columnName()); result.append(" FROM "); result.append(lastStopEntity.externalName()); result.append(','); lastStopPKeyPath.removeLastObject(); String tableAliasForJoinTable = (String) e.aliasesByRelationshipPath() .objectForKey( lastStopPKeyPath.componentsJoinedByString(".")); // "j"; //+random# result.append(endOfFirstHopEntity.externalName()); result.append(' '); result.append(tableAliasForJoinTable); result.append(" WHERE "); appendColumnForAttributeToStringBuilder(sourceAttribute, result); result.append('='); result.append(e.sqlStringForAttributeNamed(firstHopRelationshipKeyPath)); if (qualifier.elements() != null) { NSArray pKeys = ERXEOAccessUtilities.primaryKeysForObjects(qualifier.elements()); result.append(" AND "); result.append(tableAliasForJoinTable); result.append('.'); result.append(secondHopSourceAttribute.columnName()); result.append(" IN ("); EOAttribute pk = targetEntity.primaryKeyAttributes().lastObject(); for (int i = 0; i < pKeys.count(); i++) { Object key = pKeys.objectAtIndex(i); String keyString = e.formatValueForAttribute(key, pk); // AK: default is is broken if ("NULL".equals(keyString)) { keyString = "" + key; } result.append(keyString); if (i < pKeys.count() - 1) { result.append(','); } } result.append(") "); } result.append(" GROUP BY "); appendColumnForAttributeToStringBuilder(sourceAttribute, result); result.append(" HAVING COUNT(*)"); if (qualifier.minCount() <= 0) { result.append("=" + qualifier.elements().count()); } else { result.append(">=" + qualifier.minCount()); } result.append(" )"); } else { throw new RuntimeException("not implemented!!"); } return result.toString(); }