22 July 2010

ColdFusion Convert A Query Row to a Struct in 1 line?

I was writing a webservice today and part of what I wanted to return was a query.

However the query also returns things I don't want like the sql, which shows database table names etc.

Since this is a webservice for a 3rd party developer, I didn't want them to have this. I tried using convertQueryForGrid which dropped the sql but seems to break access to the query data also, or at least the rules are different.


So I looked around asked a few people and got the answer not possible. I asked Steve Onnis on Skype and he also said not possible. But a few hours later came back with this.



Now I couldn't write this, but understand (kinda) what its doing, its generating a long complex variable using regEx and evaluating it. Very scary, but very cool indeed.

8 comments:

Michiel said...

I would call this a nice proof-of-concept, but would never use it in any serious production environment. I avoid usage of the Evaluate() function at all times because it just means trouble.

I would go with a UDF. Shouldn't be that hard.

Dale Fraser said...

I agree, its very cool, and shows the possibilities but I do use a small function and loop over in real use.

Steve Onnis said...

And why exactly is Evaluate() bad? Its performence has improved heaps since CF6 and i honestly don't see why it is so bad...unless someone can prove its evilness?

Michiel said...

@Steve Onnis: It has everything to do with the principle behind the function: allowing unknown code to be executed on the server. It's just a potential security risk and should therefore be avoided at all times.

At least, that's what I think. :)

Steve Onnis said...

So its isn't a performence issue then, a security issue? If you are in control over what is being evaluated then the argument is void as as far as i am concerned. Like anything..it's bad if you use it poorly

Steve Glachan said...

@Steve Onnis
Yeah I was wondering the same thing. I generally don't use Evaluate() if there is another efficient way to get around it (there often is).
Performance from what I hear has improved greatly and as for security in this case, the inputs look pretty controlled to me. Looks secure. A speed comparison to a looped solution might be cool to see.

Nice post Dale, bookmarked :)

Steve Glachan said...

Oh and well done Steve, I didn't see that you had written it :)

Peter Boughton said...

Hmmm, you don't need those parentheses, and since a column name must be at least one character, you should use + instead of *, so the expression can be: '\b[^,]+\b'
With the replace being: '"\0" : "##q.\0[1]##"'

Actually, no need for the word boundaries either, it can be '[^,]+' and '##q['\0'][1]##'

And case-sensitivity doesn't come into it, so overall it could be changed to:
<cfset qRow = deserializeJson("{#evaluate(DE("#REReplace(q.ColumnList, '[^,]+', '"\0" : "##q["\0"][1]##"', 'ALL')#"))#}") />



However, I can't think of any benefit to that over doing a simple loop:

<cfset qRow = StructNew() />
<cfloop index="CurCol" list="#q.ColumnList#">
<cfset qRow[CurCol] = q[CurCol][1] />
</cfloop>

If you want that to be one line, make it a function and do:
<cfset qRow = QueryRowToStruct(q,1) />