Getting complex object error when trying to output query values - coldfusion

My goal is to just output the column data specified in the "fieldList".
Getting the following error:
Complex object types cannot be converted to simple values. The expression has requested a variable or an intermediate expression result as a simple value, however, the result cannot be converted to a simple value. Simple values are strings, numbers, boolean values, and date/time values. Queries, arrays, and COM objects are examples of complex values. The most likely cause of the error is that you are trying to use a complex value as a simple one. For example, you might be trying to use a query variable in a cfif tag. The error occurred on line 20.
When trying to do the following:
<cfquery datasource="retailers" name="myQuery">
Select * FROM retailer
WHERE retailer_id = '#url.id#'
</cfquery>
<cfset fieldList = "company,phone,phone_secondary,fax,email,website">
<cfloop list="#fieldList#" index="i">
#myQuery[i]#
</cfloop>
Shouldn't this work without giving me an error? I feel like I'm just overlooking something simple I just can't find the answer anywhere.

Ok, I see you ammended your initial code, so here's my response:
You are looping over a list of columns, and trying to evaluate each column as though it is a single element in a struct.
A query, however, is slightly different: it is accessed as a series of structs, which in turn, are arrays--of each row of data as they return from the query.
If you want to output all of the rows of data, without knowing the columns up front (or passing them in dynamically as you are implying in your code above), try this:
<cfoutput query="myQuery">
<cfloop list="#myQuery.ColumnList#" index="thisColumn">
#myQuery[thisColumn][myQuery.CurrentRow]#
</cfloop>
</cfoutput>
This will provide you with the output you need. The outer cfoutput with the query attribute will loop over all the rows of data you received. Then, for each row, you loop over the list of columns the query produces, and for each column, output the data, specifying the row via myQuery.CurrentRow, which iterates automatically for you via the outer output.
I'd also take a moment now to advocate that you try to stay away from loops within loops, and just output your values explicitly:
<cfoutput query="myQuery">
#company# #phone#
</cfoutput>
Using this syntax is slightly less expensive than the aforementioned loop within a loop.

Related

How can I stop using evaluate() function?

I have a table with values under a column named:str_condition
values in this column can be : variables.bit_male / application.bit_male / isdefined('session.int_user_id')
The value can be complex as it can be.
I need to use the value of the values in the column.
Currently, what I am doing is
<cfif evaluate(query.str_condition) eq true>
.....code....
</cfif>
Now, I need to omit the evaluate.
TBH, I'd stick with evaluate() for this: you're leveraging one of the few situations it makes sense. Provided what you have in the DB field is just an expression (no tags), then evaluate() will work fine.
As others have suggested... storing expressions in the DB is not ideal, but I can see how sometimes it might be the best approach, However do reconsider it though, in case you can come up with a different approach entirely (this is situation-specific, so we can't really give you guidance on that).
The only other real option for you would be to write the code from the DB to file then include it, but that would be a worse solution than just using evaluate(), I think.
A lot of people get hung up on the dogma that is evaluate() is bad, without really stopping to think about why that's the case... it's unnecessary for most situations people use it, but it's completely fine in situations in which it is needed (such as yours).
This is an edited answer, since I originally misread the question.
In many cases, array notation is your freind
<cfif queryname['fieldname'][rownumber] is true>
code for true
Note that the queryname is not quoted but the fieldname is. If you don't quote the fieldname, ColdFusion will assume it's a variable.
Also pertinent is that if you are storing things in a database, such as code, that you want to select and then execute, you have to select those things, write them to another .cfm file, and then cfinclude that file. That's somewhat inefficient.
In your case, you are storing variable names in your database. If using evaluate is giving you the correct results, anything you change would likely be a change for the worse.
How many unique combinations exist in the database? And do new values show up without developer interaction?
If it's a reasonable number of possible values that don't change then use a switch statement and write the line of code that handles each possible value.
<cfswitch expression="#query.str_condition#">
<cfcase value="variables.bit_male">
<cfset passed = variables.bit_male>
</cfcase>
<cfcase value="application.bit_male">
<cfset passed = application.bit_male>
</cfcase>
<cfcase value="isdefined('session.int_user_id')">
<cfset passed = isdefined('session.int_user_id')>
</cfcase>
<cfdefaultcase>
<cfset passed = false>
</cfdefaultcase>
</cfswitch>
<cfif passed>
.....code....
</cfif>
You don't have to hand write all of them, you can use a sql query to generate the repetitive part of the coldfusion code.
SELECT DISTINCT '<cfcase value="' + replace(table.str_condition,'"','""') + '"><cfset passed = ' + table.str_condition + '></cfcase>' as cfml
FROM table
ORDER BY len(str_condition), str_condition
If I am reading this correctly you are not just storing variable names in the database but actual snippets of code such as [ isDefined(session.it_user_id) ].
If this is what you are doing then you need to stop and rethink what you are trying to achieve. Storing code in your database and using evaluate to execute it is an incredibly bad idea.
It sounds to me like you are trying to create a generic code block that you can copy paste in multiple places and just set your conditional logic in the db.
The short answer is not to find a way around using evaluate but to stop storing code in your database full stop.

Is there a way to get each row's value from a database into an array?

Say I have a query like the one below. What would be the best way to put each value into an array if I don't know how many results there will be? Normally I would do this with a loop, but I have no idea how many results there are. Would I need run another query to count the results first?
<CFQUERY name="alllocations" DATASOURCE="#DS#">
SELECT locationID
FROM tblProjectLocations
WHERE projectID = '#ProjectName#'
</CFQUERY>
Depending on what you want to do with the array, you can just refer to the column directly for most array operations, eg:
i = arrayLen(alllocations["locationID"]);
Using that notation will work for most array operations.
Note that this doesn't "create an array", it's simply a matter that a query columns - a coldfusion.sql.QueryColumn object is close enough to a CFML array for CF to be able to convert it to one when an array is needed. Hence the column can be passed to an array function.
What one cannot do is this:
myArray = q["locationID"];
This is because by default CF will treat q["locationID"] as a string if it can, and the string value is what's in the first row of the locationID column in the q query. It's only when an array is actually required will CF convert it to an array instead. This is basically how loose-typing works.
So if you just need to pass your query column to some function that expects an array, you can use the syntax above. If you want to actually put the column into a variable, then you will need to do something like this:
myArray = listToArray(valueList(q.localtionID));
NB: make sure you use <cfqueryparam> on your filter values instead of hard-coding them into your SQL statement.
myquery.column.toArray() is also a good undocumented choice.
Since you're only retrieving 1 field value from the query, you could use ValueList() to convert the query results into a comma-delimited list of locationIds, then use listToArray() to change that list into an array.
If you were retrieving multiple field values from the query, then you'd want to loop through the query, copy all the field values from the given row into a struct, and then add that struct to an array using arrayAppend().
(If you're not familiar with these functions, you can look them up in the Adobe docs or on cfquickdocs.com).

cfloop vs cfoutput on queries

I develop using ColdFusion and wanted to know what is the best strategy to loop over large query result set. Is there any performance difference between using cfloop and cfoutput? If not, is there any reason to prefer one over the other?
I believe that there used to be. I think this difference has been tackled, the best bet is to do a test for each to test in you specific use case.
<cfset t = GetTickCount()/>
<cf... query="qry">
<!--- Do something --->
</cf...>
<cfset dt = GetTickCount() - t/>
<cfdump var="#dt#"/>
<!---
If the differences are small you can use java.lang.System.nanoTime() instead
--->
There are some notable differences though. cfoutput can do grouped loops, which cfloop cannot.
<cfoutput query="qry" group="col">
<!--- Loops once for each group --->
<cfoutput>
<!--- Loops once for each record within the group --->
</cfoutput>
</cfoutput>
For cfoutput you can specify the startrow and the maxrows (or the count) to paginate your result. For cfloop you have to specify the endrow index instead of the count.
Also you cannot use cfoutput for a query nested within an existing cfoutput tag, you will need to end the containing cfoutput first.
One good reason to use cfloop instead of cfoutput is if you need to loop a query output within another query output cfoutput does not support nested query outputting. You can however get away with it using cfloops. So:
<cfoutput query="test1">
#test1ID#
<cfoutput query="test2">
#test2ID#
</cfoutput>
</cfoutput>
does not work, but if you replace the cfoutputs with cfloops, it will.
As of CF10, with the ability to group cfloops, that's the only remaining functional difference. They both perform the same.
I believe it's all the same as performance, Ben Forta
And the rest is pretty much personal preference as far as how you "like" to work with your loop. Keep in mind you should always scope your variables, but inside a cfoutput loop that would be especially important since the query fields "could" be referenced without referring to their scope.
one reason you may prefer the cfloop approach would be if you needed to "escape" cfoutput during your loop for any reason. I have run into that several times, so I generally prefer cfloop.
There wouldn't be a performance difference using either method, it depends on your coding style really. If you put a <cfoutput> at the top and bottom of every page then using <cfloop> will work great. If you use multiple <cfoutput> and only place where they are needed that works as well.
I personally put <cfoutput> only where they are necessary, but I wouldn't say that's more correct than placing them at the top and bottom of the page.

QuerySetCell on a column with a numeric name in ColdFusion

I'm attempting the use QuerySetCell to change value of a specific column in a query object, and have been receiving this error:
Column names must be valid variable names. They must start with a letter and can only include letters, numbers, and underscores.
The reason for this error, and the complication here, is that the columns I am trying to update have some integers as names, taken from a separate record's key/ID. For example, the query may contain three columns with names: "6638, 6639, 6640".
Now, I understand why this error is occurring (though not necessarily why CF has this limitation), however cannot come up with a work around. The further complications are that I cannot make any changes as to how the initial query set's column names, and need to preserve the column names and their order for when I convert the query to a JSON string and update my results table using the JSONified query.
Has anyone encountered this issue before, and if so how were you able to work around it, or were you forced to change how the columns were named in your initial query?
Using CF8 and have the ability to edit the JSONified query after it is returned from my Ajax handler if that makes a difference.
You can use bracket notation to set the values in a query (at least you can in CF9 - I do not have CF8 installed to test).
The sytax is pretty simple:
<cfset queryName[columnName][row] = "some new value" />
From your example, you could use this:
<cfset myQuery["6638"][1] = "moo" />
This will set the value of the '6638' column in the first row to 'moo'. If you have multiple rows being returned, you would need to set each row.

How can I use query-of-query UNION on n-recordsets when var scoping is needed?

I would like to be able to do a query of a query to UNION an unknown number of recordset. However when doing a query-of-query dots or brackets are not allowed in record set names.
For example this fails:
<cfquery name="allRecs" dbtype="query">
SELECT * FROM recordset[1]
UNION
SELECT * FROM recordset[2]
</cfquery>
Using dynamic variable names such as "recordset1" work but this is in a function and needs to be var-scoped so I can't build up the variable names dynamically without producing memory leaks in a persisted object.
Any other ideas?
After posting the question I came up with a couple solutions but there might be a better one out there
I could write dynamically named variables to the arguments scope and then reference them without their scope in query
Create a function that accepts 2 recordsets as arguments and returns one combined recordset. This could be looped over to progressively add a recordset at a time. I'm sure this is very inefficient compared to doing all UNIONs in one query though.
Difficult task. I could imagine a solution with a nested loop based on GetColumnNames(), using QueryAddRow() and QuerySetCell(). It won't be the most efficient one, but it is not really slow. Depends on the size of the task, of course.
Your "create a function that combines two recordsets" could be made much more efficient when you create it to accept, say, ten arguments. Modify the SQL on the fly:
<cfset var local = StructNew()>
<cfquery name="local.union" dbtype="query">
SELECT * FROM argument1
<cfloop from="2" to="#ArrayLen(arguments)#" index="local.i">
<cfif IsQuery(arguments[local.i])>
UNION
SELECT * FROM argument#local.i#
</cfif>
</cfloop>
</cfquery>
<cfreturn local.union>
After a quick bit of poking around, I found this:
queryConcat at CFLib.org. It uses queryaddrow/querysetcell to concatenate two queries.
I added a quick function (with no error checking, or data validation, so I wouldn't use it as-is):
<cffunction name="concatenate">
<cfset var result = arguments[1]>
<cfloop from="2" to="#arraylen(arguments)#" index="i">
<cfset result=queryconcat(result, arguments[i])>
</cfloop>
<cfreturn result>
</cffunction>
As a test, I threw this together:
Which does, in fact, give you fred/sammy/fred.
It's probably not the most efficient implementation, but you can always alter the insert/union code to make it faster if you wanted. Mostly, I was aiming to write as little code as possible by myself. :-)
all of the solutions added here should work for you, but I would also mention that depending on how much data you are working with and the database you are using, you might be better off trying to find a way to do this on the database side. With very large record sets, it might be beneficial to write the records to a temporary table and select them out again, but either way, if you can in any way rewrite the queries to let the database handle this in the first place you will be better off.