In CF10, why do structure members become undefined? - coldfusion

The code below has some unexpected behavior in CF10 especially when compared to CF8 (I haven't tried in CF9).
Line 6 causes some previously defined structure members to become undefined. Why? Is this a bug or did I miss a memo?
<cfset struct = structNew()>
<cfset struct["x.foo"] = 1> <!--- LINE 2 --->
<cfset struct["x.bar"] = 2> <!--- LINE 3 --->
<cfdump var="#struct#" label="before"> <!--- struct["x.foo"] & struct["x.bar"] have values (they're defined) at this point --->
<cfset struct["x"] = structNew()> <!--- LINE 6: in CF10 struct["x.foo"] & struct["x.bar"] become undefined --->
<cfdump var="#struct#" label="after">
<h3>Coldfusion Version</h3>
#server.coldfusion.productversion#
Thanks for looking.

It sounds like you are experiencing what I document on my blog? "Odd behaviour with struct keys with dots in their names".
It's a bit late to be raising regressions that impact ColdFusion 8, but you could raise a ticket with Adobe if you wanted. They'll probbaly just ignore it though.
Other than that, the only salient reaction is kinda "well now you know". Sorry :-/

Related

Nullif - using in Coldfusion

Looking for a little help with this code. Without posting the entire file which is way to big I just need a little help with using Nullif in Coldfusion.
I could I guess use it in my SQL statment, but for the sake of learning I am wondering if it can be used when setting variables as follows :-
The code doesn't throw any errors but I'd like to know where I would place the 0 after the Nullif.
<cfif AE_C NEQ 0>
<cfset AE_P=AE_T/AE_C>
<cfset AE_A=AE/AE_C*100>
<cfset AE_B = AE-AE_C/H8*H9>
<cfset AE_D=AE/H9*H8>
<cfelse>
<cfset AE_P=ISNULL(AE_T/NULLIF(AE_C))>
<cfset AE_A=ISNULL(AE/NULLIF(AE_C*100))>
<cfset AE_B=ISNULL(AE-AE_C/NULLIF(H8*H9))>
<cfset AE_D=ISNULL(AE/NULLIF(H9*H8))>
</cfif>
Hoping it can be done this way.
IMPORTANT: Your code is not showing any error because ISNULL is masking the error.
Also NULLIF is not a valid ColdFusion function. I believe the reason why there is no error in your page because, ColdFusion ISNULL() function seems to be a very versatile one and showing some undocumented characteristics. ISNULL() does not return an error even if the expression inside it is defined or not if the expression is syntactically valid.
eg.
ISNULL(AE_T/NULLIF(AE_C)) // No error because AE_T/NULLIF(AE_C) is a valid statement.
What you could do as an alternative is the following.
The following is a bit hacky, but you can check out the function val(). It will return 0 for any string that is not a number (check the doc for more details).
NULLIF(AE_C) becomes val(AE_C).
<cfset AE_P=ISNULL(AE_T/val(AE_C))>
Still if the val() return 0, then the output of ISNULL() will be YES, because division by 0 throws error.
This is some code that was written by Ben Nadel that I've found to resolve the error, perhaps someone can assist me in how I would implement it as I just can't get my head around it.
<!---
Do SQL division with divide-by-zero protection. But this,
time, let's provide a default value if the division is
not valid.
--->
<cfquery name="qDivision" datasource="#REQUEST.DSN.Source#">
SELECT
(
ISNULL(
(45 / NULLIF( 0, 0 )),
0
)
) AS value
;
</cfquery>
<!--- Output resulting value. --->
[ #qDivision.value# ]
This is more a comment than an answer. See RRK's answer about NULLIF not being a valid ColdFusion function and ISNULL() hiding an error.
My comment is more about the logic of your operation. You don't need to do some of the steps in your <CFELSE>
Original:
<cfif AE_C NEQ 0>
<cfset AE_P=AE_T/AE_C>
<cfset AE_A=AE/AE_C*100>
<cfset AE_B = AE-AE_C/H8*H9>
<cfset AE_D=AE/H9*H8>
<cfelse>
<cfset AE_P=ISNULL(AE_T/NULLIF(AE_C))>
<cfset AE_A=ISNULL(AE/NULLIF(AE_C*100))>
<cfset AE_B=ISNULL(AE-AE_C/NULLIF(H8*H9))>
<cfset AE_D=ISNULL(AE/NULLIF(H9*H8))>
</cfif>
I'm assuming you're using the NULLIF() to prevent divide by zero error.
First, in the languages I'm familiar with, NULLIF() requires two arguments: NULLIF(a,b) so that if a is not equal to b, it will return a, otherwise it will return NULL.
That said, your cfif says that if AE_C is not 0 then do x, else do y. I'm assuming it should be <cfset AE_P=ISNULL(AE_T/NULLIF(AE_C,0))>. If you extrapolate that out, you'd get AE_T/NULLIF(0,0) == AE_T/NULL == NULL. So you can just short-circuit that block and just set AE_P=NULL. But you also have the ISNULL() function around that, so that will always be true also. And ISNULL() (except in ColdFusion) also takes 2 arguments. In CF, it returns TRUE/FALSE (or YES/NO to be pedantic). So now you've essentially done <cfset AE_P = YES/TRUE>. I'm guessing that wasn't your original intent. So ISNULL() probably isn't appropriate here. In SQL, ISNULL(a,b) would mean that if a is NULL, return b. So (in SQL) you could essentially do AE_P = ISNULL(x,0) and that would mean that if AE_C was 0, rather than getting the divide by zero error, you could just set AE_P = 0.
TLDR; There isn't a NULLIF() function in ColdFusion, so if you're trying to prevent the div0 errors, you'll have to either do it in your SQL or modify your ColdFusion sets:
<cfif AE_C NEQ 0>
<cfset AE_P=AE_T/AE_C>
<cfset AE_A=AE/AE_C*100>
<cfelse>
<cfset AE_P=0>
<cfset AE_A=0>
</cfif>
I don't know the flow of your code, so I don't know how H8 and H9 are affected by AE_C, so you may have to check those for 0 also.
But, I'd still go back to my usual belief that if these calculations are used in SQL and not really in code, then they should be done in SQL rather than being passed out to the app server and then back to the SQL. Again, though, I don't know what your code is doing, so it may be more appropriate to have those calcs in code.

CFSET Invoking Function without variable

New pretty new with this language, started code with it this week.
So my problem is the new company that I started with uses CF11 and they mainly only code with tags. I want to know if is possible to call cfset without a variable declaration.
If not what is the better way to call functions (that don't have return) with tags?
<cfset myFunction()>
I usually call my initiate functions at cfset, but they all have returning.
<cfset something = #initSomething()#>
Yes, invoking a function without capturing the result is perfectly fine. Sadly, there used to be a lot of that syntax in older CF documentation. It gave the erroneous impression you MUST capture the result of a function (and use extra pound signs everywhere). Neither is true. Even if a function does return something, you're not required to capture the result. Only if you wish to use it for something later. You're always free to invoke a function and completely ignore the result. So both of these are valid:
<!--- 1. capture result --->
<cfset result = getTimeNow()>
<!--- 2. ignore result --->
<cfset getTimeNow()>
<!--- sample function --->
<cffunction name="getTimeNow" return="date">
<cfreturn now()>
</cffunction>
Technically, there's nothing stopping you from capturing the result of a function that doesn't return anything. However, the "result" variable will be undefined, so it really serves no purpose.
<cfset result = doNothing()>
<!--- this will error --->
<cfoutput>#result#</cfoutput>
<!--- sample function --->
<cffunction name="doNothing" return="void">
<!--- function that returns nothing --->
</cffunction>

CF9: What is this evaluate statement evaluating?

I'm stuck and need a fresh set of eyes on this, please.
I'm working with someone else's spaghetti code who is no longer around and having a heck of a time figuring out what they were evaluating.
<cfset surveyCount = 0>
<cfloop query="surveys">
<cfif evaluate("defaultReport" & ID)>
<cfset surveyCount = surveyCount + 1>
</cfif>
</cfloop>
In the query dump, I see 9 records which is what I am expecting but because because the evaluate is failing, the surveyCount isn't being incremented. I do not see any columns for defaultReport. In my 15 years of working with CF, I've always avoided evaluate() and now when I need to analyze it, I'm at a complete loss. Can someone offer any guidance?
Added CFDump image (some columns names have been removed for privacy and security):
UPDATE I:
This file has numerous cfinclude statements and very little code formatting. As a result, I overlooked some cfinclude statements. I found the following. I'm still looking but wanted to document this as I dig.
<cfloop query="surveys">
<cfscript>
variables["defaultReport" & ID] = evaluate(thisAssociation & "Price");
</cfscript>
</cfloop>
UPDATE II:
Dumping the variable scope, I did confirm the variable I am looking for (finding the query I posted in UPDATE I helped, too). :)
What they wanted to do is to increase surveyCount but only if this thing: evaluate("defaultReport" & ID) evaluates to true.
From your query dump picture it looks like the IDs are numbers like 144, 145, etc...
In this context, you can think at evaluate("defaultReport" & ID) as something like defaultReport144, defaultReport145, etc... (these are variables set somewhere in the code).
So the code:
<cfif evaluate("defaultReport" & ID)>
<cfset surveyCount = surveyCount + 1>
</cfif>
becomes (for an ID of 144, the first one on your query loop)
<cfif defaultReport144>
<cfset surveyCount = surveyCount + 1>
</cfif>
and so on... for the other IDs
So, search your code for where variables like defaultReport144, defaultReport145, etc... are set to either true or false (0 or 1).
Something like:
<cfset defaultReport144 = true />
or maybe they use some expression that evaluates to true or false, like:
<cfset defaultReport144 = [some expression] />
If you can't find, then maybe the code was changed or removed in the place where these defaultReport... variables were set.
ColdFusion evaluate() documentation:
https://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7f4e.html
You need to look for a variable outside of your query. This variable has a name of default#ID# . It may be called.
variables.default#ID#
form.default#ID#
url.default#ID#
request.default#ID#
attributes.default#ID#
etc.
Basically ColdFusion is going to go through every scope until it finds something. (No this is not a good approach)
If you have to clean this up, I would recommend not using such an ambiguous approach. In short, there is no real way to know what it is evaluating.

Checking the structure is existing or not

Here is my array loop which has structures inside that
I am looping over it and i need to skip those fields where the struct is not defined, howver i am getting everytime
Here is my code:
<cfloop index="apos" from=1 to="#arrayLen(myarray)#">
<cfdump var="#myarray[apos].company#">
<cfdump var="#StructKeyExists(myarray[apos].company,'#myarray[apos].company.size#')#">
<cfdump var="#StructFindKey(myarray[apos].company,'myarray[apos].company.size','ALL')#">
</cfloop>
The error is throwing on line 3, where i am getting error: Element COMPANY.SIZE is undefined in a CFML structure referenced as part of an expression.
Although i had tried the structFindvalue, but that does not work, perhaps that is expecting some simple values, so what could be the best possible alternative here
With structKeyExists you want to give that function the struture to look and the key you're looking for, so in myarray[apos].company you want to see if 'size' exists, not the entire structure.
<cfloop index="apos" from=1 to="#arrayLen(myarray)#">
<cfdump var="#myarray[apos].company#">
<cfdump var="#StructKeyExists(myarray[apos].company,'size')#">
</cfloop>

Find value in a array of Structure

I am trying to find the value of a Struture which is contained inside an Array. The following diagram shows the representation:
I tried the code below, but it is giving me this error, using CF 9.0.1
Element OPTIONTYPE_NAME is undefined in a CFML structure referenced as part of an expression.
Code I am trying:
<cfif !ArrayIsEmpty(result)>
<cfset arrayIndex = 0>
<cfloop from="1" to="#ArrayLen(result)#" index="counter">
<cfif result[counter].OPTIONTYPE_NAME IS "color">
<cfset arrayIndex = counter>
<cfdump var="#arrayIndex#">
</cfif>
</cfloop>
Well the error is pretty much telling you what the problem is. There isn't a key OPTIONTYPE_NAME in result[counter].
result[counter] has keys key, owner and path.
I think you want to be looking at result[counter].owner.OPTIONTYPE_NAME