The amazing adventures of Doug Hughes

A while ago I received an email from a client who is also a ColdFusion developer. He was trying to get an report to display correctly, but kept running into what appeared to be a bug in ColdFusion. The behavior was consistent in all the versions of ColdFusion he tested, namely versions 5, 7, and 8.

The problem was described like this: Suppose that you run a query which returns a set of data that can be grouped in a cfoutput by, for example, a field named monthYear. You want to loop over the query and, each time monthYear value changes, you want to print totals, etc. The problem was that outside of the inner cfoutput the currentRow of the query was not returning what he expected.

Take a look at this example code:

<!--- create some random data --->
<cfset query=QueryNew("monthYear,min,max")/>
<cfset startDate=createDate(year(now()), month(now())-3, day(now()))/><br/>
<cfset endDate=now()/>
<cfloop from="#startDate#" index="date" step="5" to="#endDate#"><br/>
    <cfset QueryAddRow(query)/><br/>
    <cfset QuerySetCell(query, "monthYear", dateFormat(date, "mm/yy"))/><br/>
    <cfset QuerySetCell(query, "min", randRange(1, 100))/><br/>
    <cfset QuerySetCell(query, "max", randRange(101, 200))/><br/>
</cfloop>

<!--- loop over the data and output the data and summarise at the end of the month --->
<br/>
<table><br/>
    <tr><br/>
        <th>Month Year</th><br/>
        <th>Min</th><br/>
        <th>Max</th><br/>
    </tr><br/>
    <cfoutput group="monthYear" query="query"><br/>
        <br/>
        <cfoutput><br/>
            <tr><br/>
                <td>#monthYear#</td><br/>
                <td>#min#</td><br/>
                <td>#max#</td><br/>
            </tr><br/>
        </cfoutput><br/>
        <!--- output the current row --->
        <br/>
        <tr><br/>
            <td colspan="2">Current Row:</td><br/>
            <td>#currentRow#</td><br/>
        </tr><br/>
    </cfoutput><br/>
</table>

If you run this code, which generates some fake data to group on, it results in this output:

Output 1

What seems weird about this? Well, to me, not much. However, my client though it was strange that the current row was reported to be the row before we output the data in the inner cfoutput tag. So, its like this in pseudo code:

  1. Run a query
  2. Start output and group on monthYear.
  3. Start an inner output and show 4 or so rows of data and end the inner output.
  4. Now, output the current row.

Would you expect the current row to show 1? My clients argument was that the current row should be 4, not 1.

My take on this is that it should show 1, not 4. Why do I say this? Because of where we are in our code. If cut out the inner cfoutput and ran it youd end up with this:

Output 2

As you can see, this is the same as what we have above, except that we only showed the current row. To me, this is consistent.

The counter argument is that the inner cfoutput is modifying the state of the query and that a modified value should stay modified. I can see this point too.

Meanwhile, the client decided to end the argument and emailed Ben Forta directly and asked him what he thought. The answer was fairly simple: This was a bug that was introduced early in CF history that was not caught before it was released into the wild and now, to maintain backwards compatibility, it needs to stay as it is.

What do you think?

Comments on: "Apparent Bug in Grouped Output" (5)

  1. Matt Williams said:

    I agree that it should be the lower number. The part of the outer grouped output is the same above the inner loop as it is below the inner loop.

    With that said, I do wish there was some sort of real counter that did as he expects. Whenever I have to do grouped output with alternating row colors on the inner data, I always have to do a manual counter to keep track. Something like “outputRow” or something.

    Like

  2. Giampaolo Bellavite said:

    I agree with Matt. Perhaps an “index” attribute in cfoutput, as is for cfloop. (cfloop should have a “group” attribute too)

    Like

  3. Patrick McElhaney said:

    I agree too. It’s not a bug.

    If the currentRow was incremented by the inner cfoutput, what would happen if you added another inner cfoutput? It would only loop over the last record in the group.

    And yes, I’ve run into situations where having two cfoutputs nested within the same cfoutput group has been quite useful.

    For example, say you have query of sales per person per month, and you want to know what percentage of the months’ sales were contributed by each salesperson.

    First, you group by the month. The first inner cfoutput is used to add up the total sales for the month. The second cfoutput is used to display the salesperson’s name, monthly sales, and percent contribution (by dividing individual sales / by the total you just calculated).

    Patrick

    Like

  4. Scott Krebs said:

    The part that I’d like to add is that currentRow is scoped to the cfquery and not cfoutput. In the inner cfoutput query.currentRow changes as the rows are output then reverts back to the original value after exiting the inner cfoutput. If #query.currentRow# was also displayed in the inner cfoutput above you would see it’s value increment 1, 2, 3, 4, 5, 6, then outside the inner loop #query.currentRow# is 1 again. Thinking of the cfquery as an entity unto itself, for me it is intuitive that the currentRow value of the query “object” should stay where it was at rather than changing back to a lesser value. If after the inner cfoutput you need the value from before it, I would think you would need to store it in a variable first for later use. After all, the rows *have* been output already. Now if it was cfoutput.currentRow, I would agree that after the first inner cfoutput it should be 1. But that’s just me and I’m a strange fellow. Since cfoutput and cfquery are so tightly bound to each other, I can see both sides of the argument; but I’m still going to stick my neck out and agree with Ben and say bug (or at least that this behavior should be documented somewhere obvious, and it’s not).

    Like

  5. Scott Krebs said:

    Oh, forgot to add Patrick does make a very good point. I’ve not run into a use-case where multiple nested cfoutputs were necessary.

    Like

Comments are closed.

Tag Cloud

%d bloggers like this: