protected void serializeForQuery(QueryMetadata metadata, boolean forCountRow) {
        boolean oldInSubquery = inSubquery;
        inSubquery = inSubquery || getLength() > 0;
        boolean oldSkipParent = skipParent;
        skipParent = false;
        final Expression<?> select = metadata.getProjection();
        final List<JoinExpression> joins = metadata.getJoins();
        final Predicate where = metadata.getWhere();
        final List<? extends Expression<?>> groupBy = metadata.getGroupBy();
        final Predicate having = metadata.getHaving();
        final List<OrderSpecifier<?>> orderBy = metadata.getOrderBy();
        final Set<QueryFlag> flags = metadata.getFlags();
        final boolean hasFlags = !flags.isEmpty();
        String suffix = null;

        List<? extends Expression<?>> sqlSelect;
        if (select instanceof FactoryExpression) {
            sqlSelect = ((FactoryExpression<?>) select).getArgs();
        } else if (select != null) {
            sqlSelect = Collections.singletonList(select);
        } else {
            sqlSelect = Collections.emptyList();
        }

        // with
        if (hasFlags) {
            List<Expression<?>> withFlags = new ArrayList<>();
            boolean recursive = false;
            for (QueryFlag flag : flags) {
                if (flag.getPosition() == Position.WITH) {
                    if (flag.getFlag() == SQLTemplates.RECURSIVE) {
                        recursive = true;
                        continue;
                    }
                    withFlags.add(flag.getFlag());
                }
            }
            if (!withFlags.isEmpty()) {
                if (recursive) {
                    append(templates.getWithRecursive());
                } else {
                    append(templates.getWith());
                }
                handle(", ", withFlags);
                append("\n");
            }
        }

        // start
        if (hasFlags) {
            serialize(Position.START, flags);
        }

        // select
        Stage oldStage = stage;
        stage = Stage.SELECT;
        if (forCountRow) {
            append(templates.getSelect());
            if (hasFlags) {
                serialize(Position.AFTER_SELECT, flags);
            }

            if (!metadata.isDistinct()) {
                append(templates.getCountStar());
                if (!groupBy.isEmpty()) {
                    append(templates.getFrom());
                    append("(");
                    append(templates.getSelect());
                    append("1 as one ");
                    suffix = ") internal";
                }

            } else {
                List<? extends Expression<?>> columns;
                if (sqlSelect.isEmpty()) {
                    columns = getIdentifierColumns(joins, !templates.isCountDistinctMultipleColumns());
                } else {
                    columns = sqlSelect;
                }
                if (!groupBy.isEmpty()) {
                    // select count(*) from (select distinct ...)
                    append(templates.getCountStar());
                    append(templates.getFrom());
                    append("(");
                    append(templates.getSelectDistinct());
                    handleSelect(COMMA, columns);
                    suffix = ") internal";
                } else if (columns.size() == 1) {
                    append(templates.getDistinctCountStart());
                    handle(columns.get(0));
                    append(templates.getDistinctCountEnd());
                } else if (templates.isCountDistinctMultipleColumns()) {
                    append(templates.getDistinctCountStart());
                    append("(").handleSelect(COMMA, columns).append(")");
                    append(templates.getDistinctCountEnd());
                } else {
                    // select count(*) from (select distinct ...)
                    append(templates.getCountStar());
                    append(templates.getFrom());
                    append("(");
                    append(templates.getSelectDistinct());
                    handleSelect(COMMA, columns);
                    suffix = ") internal";
                }
            }

        } else if (!sqlSelect.isEmpty()) {
            if (!metadata.isDistinct()) {
                append(templates.getSelect());
            } else {
                append(templates.getSelectDistinct());
            }
            if (hasFlags) {
                serialize(Position.AFTER_SELECT, flags);
            }

            handleSelect(COMMA, sqlSelect);
        }
        if (hasFlags) {
            serialize(Position.AFTER_PROJECTION, flags);
        }

        // from
        stage = Stage.FROM;
        serializeSources(joins);

        // where
        if (hasFlags) {
            serialize(Position.BEFORE_FILTERS, flags);
        }
        if (where != null) {
            stage = Stage.WHERE;
            append(templates.getWhere()).handle(where);
        }
        if (hasFlags) {
            serialize(Position.AFTER_FILTERS, flags);
        }

        // group by
        if (hasFlags) {
            serialize(Position.BEFORE_GROUP_BY, flags);
        }
        if (!groupBy.isEmpty()) {
            stage = Stage.GROUP_BY;
            append(templates.getGroupBy()).handle(COMMA, groupBy);
        }
        if (hasFlags) {
            serialize(Position.AFTER_GROUP_BY, flags);
        }

        // having
        if (hasFlags) {
            serialize(Position.BEFORE_HAVING, flags);
        }
        if (having != null) {
            stage = Stage.HAVING;
            append(templates.getHaving()).handle(having);
        }
        if (hasFlags) {
            serialize(Position.AFTER_HAVING, flags);
        }

        // order by
        if (hasFlags) {
            serialize(Position.BEFORE_ORDER, flags);
        }
        if (!orderBy.isEmpty() && !forCountRow) {
            stage = Stage.ORDER_BY;
            append(templates.getOrderBy());
            handleOrderBy(orderBy);
        }
        if (hasFlags) {
            serialize(Position.AFTER_ORDER, flags);
        }

        // modifiers
        if (!forCountRow && metadata.getModifiers().isRestricting() && !joins.isEmpty()) {
            stage = Stage.MODIFIERS;
            templates.serializeModifiers(metadata, this);
        }

        if (suffix != null) {
            append(suffix);
        }

        // reset stage
        stage = oldStage;
        skipParent = oldSkipParent;
        inSubquery = oldInSubquery;
    }
Return a list of expressions that can be used to uniquely define the query sources

@param joins
@return identifier columns
querydsl-sql/src/main/java/com/querydsl/sql/SQLSerializer.java