/**
   * Ensures we don't have references to EOs before using this in the background thread.
   *
   * @param fs
   * @return a clone of the fetchSpecification with the EOQualifier converted to a schema-based
   *     qualifier or the same {@link EOFetchSpecification} if there is no qualifier.
   */
  private EOFetchSpecification schemaBasedFetchSpecification(
      EOFetchSpecification fetchSpecification) {

    EOQualifier q = fetchSpecification.qualifier();
    if (q != null) {

      // Clone the fetchSpec
      fetchSpecification = (EOFetchSpecification) fetchSpecification.clone();

      EOEditingContext ec = ERXEC.newEditingContext();
      ec.lock();
      try {
        EOEntity entity =
            ERXEOAccessUtilities.entityMatchingString(ec, fetchSpecification.entityName());
        // Convert the qualifier to a schema-based qualifier
        q = entity.schemaBasedQualifier(q);
        fetchSpecification.setQualifier(q);
      } finally {
        ec.unlock();
      }
    } // ~ if (q != null)
    return fetchSpecification;
  }
  /**
   * 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();
  }