/** This combines the sql from named/positioned parameters and expressions. */
  private void prepare(boolean buildSql, boolean parseRaw, DeployParser deployParser) {

    buildBindWhereRawSql(buildSql, parseRaw, deployParser);
    buildBindHavingRawSql(buildSql, parseRaw, deployParser);

    SpiExpressionList<?> whereExp = query.getWhereExpressions();
    if (whereExp != null) {
      DefaultExpressionRequest whereReq = new DefaultExpressionRequest(request, deployParser);
      whereExprBindValues = whereExp.buildBindValues(whereReq);
      if (buildSql) {
        whereExprSql = whereExp.buildSql(whereReq);
      }
    }

    BeanPropertyAssocMany<?> manyProperty = request.getManyProperty();
    if (manyProperty != null) {
      OrmQueryProperties chunk = query.getDetail().getChunk(manyProperty.getName(), false);
      SpiExpressionList<?> filterMany = chunk.getFilterMany();
      if (filterMany != null) {
        DefaultExpressionRequest filterReq = new DefaultExpressionRequest(request, deployParser);
        filterManyExprBindValues = filterMany.buildBindValues(filterReq);
        if (buildSql) {
          filterManyExprSql = filterMany.buildSql(filterReq);
        }
      }
    }

    // having expression
    SpiExpressionList<?> havingExpr = query.getHavingExpressions();
    if (havingExpr != null) {
      DefaultExpressionRequest havingReq = new DefaultExpressionRequest(request, deployParser);
      havingExprBindValues = havingExpr.buildBindValues(havingReq);
      if (buildSql) {
        havingExprSql = havingExpr.buildSql(havingReq);
      }
    }

    if (buildSql) {
      parsePropertiesToDbColumns(deployParser);
    }
  }
  /** There is a many property so we need to make sure the ordering is appropriate. */
  private String deriveOrderByWithMany(BeanPropertyAssocMany<?> manyProp) {

    if (manyProp == null) {
      return parseOrderBy();
    }

    String orderBy = parseOrderBy();

    BeanDescriptor<?> desc = request.getBeanDescriptor();
    String orderById = desc.getDefaultOrderBy();

    if (orderBy == null) {
      orderBy = orderById;
    }

    // check for default ordering on the many property...
    String manyOrderBy = manyProp.getFetchOrderBy();
    if (manyOrderBy != null) {
      orderBy = orderBy + ", " + CQueryBuilder.prefixOrderByFields(manyProp.getName(), manyOrderBy);
    }

    if (request.isFindById()) {
      // only one master bean so should be fine...
      return orderBy;
    }

    if (orderBy.startsWith(orderById)) {
      return orderBy;
    }

    // more than one top level row may be returned so
    // we need to make sure their is an order by on the
    // top level first (to ensure master/detail construction).

    int manyPos = orderBy.indexOf(manyProp.getName());
    int idPos = orderBy.indexOf(" " + orderById);

    if (manyPos == -1) {
      // no ordering of the many
      if (idPos == -1) {
        // append the orderById so that master level objects are ordered
        // even if the orderBy is not unique for the master object
        return orderBy + ", " + orderById;
      }
      // orderById is already in the order by clause
      return orderBy;
    }

    if (idPos <= -1 || idPos >= manyPos) {
      if (idPos > manyPos) {
        // there was an error with the order by...
        String msg =
            "A Query on ["
                + desc
                + "] includes a join to a 'many' association ["
                + manyProp.getName();
        msg += "] with an incorrect orderBy [" + orderBy + "]. The id property [" + orderById + "]";
        msg += " must come before the many property [" + manyProp.getName() + "] in the orderBy.";
        msg += " Ebean has automatically modified the orderBy clause to do this.";

        logger.warn(msg);
      }

      // the id needs to come before the manyPropName
      orderBy = orderBy.substring(0, manyPos) + orderById + ", " + orderBy.substring(manyPos);
    }

    return orderBy;
  }