如何将print/display cfquery 与cfqueryparam 值替换?

how to print/display cfquery with cfqueryparam values replaced?

您可能知道,当我们转储 cfquery 时,它会将 cfqueryparam 值转储到转储的 SQLPARAMETERS 中。手动替换几个参数就好了。但是,最近我开始从事一个项目,其中有数十个(有时甚至更多)cfqueryparams 用于构建复杂的数据库查询。

有没有办法实际打印替换了 cfqueryparam 值的查询并在屏幕上显示有效的 sql 语句?

提前致谢!

是否有任何我找不到或从未编写过的插件或代码?

我不知道有任何内置函数或代码片段可以执行此操作,但您可以编写适用于大多数 简单参数类型的内容。之所以说“最*”是因为有一些并发症

  1. SQLParameters 数组不包括原始 cfsqltypes。因此无法明确区分字符串(必须用引号引起来)和数值(不能用引号引起来)。

    解决该缺陷的一种方法是使用内部调试服务,该服务在其输出中包含 cfsqltypes。一个明显的缺点是它需要启用调试,并使用未记录的内部 class。但是,由于这种任务通常不是您在生产中 运行 无论如何,这是一个可接受的限制 IMO。

  2. 不幸的是,none 个可用选项包括 cfqueryparam 的 null 属性。由于 Adob​​e 选择在参数列表中将 null 视为空字符串,因此无法确定参数值何时为 null 以及何时实际为空字符串 ""。遗憾的是,您对此无能为力。可能您能做的最好的事情就是选择适用于您的应用程序的任何默认值:null 或空字符串。

  3. 语法和支持的数据类型因供应商而异,因此任何代码都可能需要针对每个 dbms 进行调整。特别是对于不太常见或复杂的类型,如 refcursorblob.

    还有处理字符串参数值中嵌入的单引号的问题。使用 replace() 转义单引号应该可以解决问题,但可能存在边缘情况。


您没有提到您的 dbms,但这是 SQL 服务器的一个非常粗略的入门示例。您必须决定 how/if 来处理不太常见的类型,例如 refcursor and blob`(目前 returns "{{unhandled_type_(typeName)}}".

演示:

queries = unParameterizeQueries();
for (qry in unParameterizeQueries()) {
   writeOutput("<pre>"& encodeForHTML(qry) &"</pre>");
}

原始查询

<!--- deliberately embed single quotes in string values --->
<cfquery ...>
   SELECT 
         <cfqueryparam value="1234567890" cfsqltype="idstamp"> AS idstampCol
        , <cfqueryparam value="100.50" cfsqltype="money"> AS moneyCol
        , <cfqueryparam value="6789.876423" cfsqltype="float"> AS floatCol
        , <cfqueryparam value="abc '1,2,3'" cfsqltype="char"> AS charCol
        , <cfqueryparam value="10.52" cfsqltype="decimal" scale="2"> AS decimalCol
        , <cfqueryparam value="abc '1,2,3'" cfsqltype="nchar"> AS ncharCol
        , <cfqueryparam value="abc '1,2,3'" cfsqltype="nvarchar"> AS nvarcharCol
        , <cfqueryparam value="#now()#" cfsqltype="timestamp"> AS timestampCol
        , <cfqueryparam value="123.964" cfsqltype="double"> AS doubleCol
        , <cfqueryparam value="123" cfsqltype="cf_sql_tinyint"> AS tinyintCol
        , <cfqueryparam value="123" cfsqltype="integer"> AS integerCol
        , <cfqueryparam value="12345.75" cfsqltype="numeric" scale="2"> AS numericCol
        , <cfqueryparam value="abc '1,2,3'" cfsqltype="longvarchar"> AS longvarcharCol
        , <cfqueryparam value="123" cfsqltype="bigint"> AS bigintCol
        , <cfqueryparam value="#now()#" cfsqltype="time"> AS timeCol
        , <cfqueryparam value="123" cfsqltype="bit"> AS bitCol
        , <cfqueryparam value="#now()#" cfsqltype="date"> AS dateCol
        , <cfqueryparam value='<AdventureWorks2012.Person.Person LastName="Achong" />' cfsqltype="sqlxml"> AS sqlxmlCol
        , <cfqueryparam value="123" cfsqltype="smallint"> AS smallintCol
        , <cfqueryparam value="123" cfsqltype="real"> AS realCol
        , <cfqueryparam value="abc '1,2,3'" cfsqltype="varchar"> AS varcharCol
        , <cfqueryparam cfsqltype="varchar" null="true"> AS NullVarcharCol
</cfquery>

输出

SELECT 

         '1234567890' AS idstampCol
        , 100.5 AS moneyCol
        , 6789.876423 AS floatCol
        , 'abc ''1,2,3''' AS charCol
        , 10.52 AS decimalCol
        , N'abc ''1,2,3''' AS ncharCol
        , N'abc ''1,2,3''' AS nvarcharCol
        , '2022-04-06 02:33:16.59' AS timestampCol
        , 123.964 AS doubleCol
        , 123 AS tinyintCol
        , 123 AS integerCol
        , 12345.75 AS numericCol
        , 'abc ''1,2,3''' AS longvarcharCol
        , 123 AS bigintCol
        , '02:33:16' AS timeCol
        , 1 AS bitCol
        , '2022-04-06' AS dateCol
        , '<AdventureWorks2012.Person.Person LastName="Achong" />' AS sqlxmlCol
        , 123 AS smallintCol
        , 123.0 AS realCol
        , 'abc ''1,2,3''' AS varcharCol
        , NULL AS NullVarcharCol

UDF 的

public array function unParameterizeQueries() {
    
    // store results 
    local.results = [];
    
    // get debugging service 
    local.svc = createObject("java", "coldfusion.server.ServiceFactory").getDebuggingService().getDebugger();
    local.qry = local.svc.getData();

    // get all queries for request 
    local.allQueries = queryExecute(
        "   SELECT  Attributes AS SQLParameters, Body AS SQLString 
            FROM   qry
            WHERE  Type = :type 
        "
        ,  { type   : "SqlQuery" }
        ,  { dbtype : "query" }
    );
    
    // for each query, replace parameters and output sql 
    local.allQueries.each( function(row, currentRow, qry) {
    
        local.sql = row.SQLString;
        
        row.SQLParameters.each(function(param, index) {
            sql = sql.replace( "?"
                , formatSQLParam( param.value, param.sqlType, true )
                , "one"
            );
        });
        
        results.append( sql );
    });

    return local.results;
}

public string function formatSQLParam( 
        required string paramValue 
        , required string sqlType
        , boolean emptyStringAsNull = true 
){

  local.handleAsString = "char,varchar,longvarchar,date,idstamp,time,timestamp,sqlxml,clob";
  local.handleAsNumeric = "bigint,decimal,double,float,integer,money,money4,numeric,smallint,real,tinyint";
  local.handleAsUnicodeString = "longnvarchar,nchar,nvarchar,nclob";
  local.handleAsBoolean = "bit";
  
  // remove used in old versions "cf_sql_" prefix used in old versions 
  local.typeName = arguments.sqlType.replaceNoCase("cf_sql_", "");
  
  if ( emptyStringAsNull && isSimpleValue(arguments.paramValue) && arguments.paramValue == "" ) {
      local.result = "NULL";
  }
  else if ( local.handleAsString.listFindNoCase(local.typeName) ) {
     local.result = "'"& replace( arguments.paramValue, "'", "''", "all" ) &"'";
  }
  //For sql server, prefix unicode columns with "N"
  else if ( local.handleAsUnicodeString.listFindNoCase(local.typeName) ) {
     local.result = "N'"& replace( arguments.paramValue, "'", "''", "all" ) &"'";
  }
  else if ( local.handleAsNumeric.listFindNoCase(local.typeName) ) {
     local.result = arguments.paramValue ;
  }
  else if ( local.handleAsBoolean.listFindNoCase(local.typeName) ) {
     local.result = arguments.paramValue ? 1 : 0;
  }
  // Otherwise, indicate the type isn't currently handled 
  else {
     local.result = " {{unhandled_type_"& arguments.sqlType &"}}";
  }
  
  return local.result;
}