Запрос на обновление Coldfusion выдает ошибку при использовании операторов в переменной - PullRequest
0 голосов
/ 01 февраля 2019

Попытка использовать переменную, содержащую разделенный запятыми набор обновлений столбцов.Переменная в конечном итоге содержит действительный SQL (или, точнее, действительный синтаксис Coldfusion), но запрос выдает ошибку: [Macromedia] [Драйвер JDBC SQLServer] [SQLServer] Неверный синтаксис рядом с '<'.</p>

Я начинаю с начального столбца для обновления:

<cfset sqlString = myColumn & ' = <cfqueryparam value="#myValue#" cfsqltype="#myDataType#" null="#NOT len(trim(myValue))#">'>

Затем перебираю некоторые элементы формы, делая это:

<cfset sqlString = sqlString & ', ' & myColumn & ' = <cfqueryparam value="#myValue#" cfsqltype="#myDataType#" null="#NOT len(trim(myValue))#">'>

Я, конечно, получаю снепрерывная строка в sqlString, но для удобства чтения она содержит что-то вроде этого:

id = <cfqueryparam value="123" cfsqltype="cf_sql_integer">,
csp = <cfqueryparam value="5" cfsqltype="cf_sql_integer">,
Q185 = <cfqueryparam value="" cfsqltype="cf_sql_integer" null="YES">,
Q3 = <cfqueryparam value="" cfsqltype="cf_sql_integer" null="YES">,
Q177 = <cfqueryparam value="" cfsqltype="cf_sql_date" null="YES">

Поэтому я пытаюсь использовать это в запросе на обновление, подобном следующему:

<cfquery name="update_answers" datasource="#application.datasource#">
   UPDATE answers
   SET    #PreserveSingleQuotes(sqlString)#                                                     
   WHERE  rec_id = #id#
</cfquery>

У меня естьпробовал это с и без функции PreserveSingleQuotes безрезультатно.

Если я выведу содержимое sqlString и вставлю его непосредственно в запрос, вот так, он прекрасно работает:

<cfquery name="update_answers" datasource="#application.datasource#">
   UPDATE answers
          id = <cfqueryparam value="123" cfsqltype="cf_sql_integer">,
          csp = <cfqueryparam value="5" cfsqltype="cf_sql_integer">,
          Q185 = <cfqueryparam value="" cfsqltype="cf_sql_integer" null="YES">,
          Q3 = <cfqueryparam value="" cfsqltype="cf_sql_integer" null="YES">,
          Q177 = <cfqueryparam value="" cfsqltype="cf_sql_date" null="YES">                                                    
   WHERE  rec_id = #id#
</cfquery>

Опять же,Здесь я показываю разрывы строк для удобства чтения, но не имеет значения, вставляю ли я содержимое sqlString в запрос с или без разрывов строк;это будет работать.

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Вы можете использовать следующий подход.Я написал эту процедуру несколько лет назад, когда мне нужно было динамически создать массив cfquerparams.

Я добавил функцию 'ReadQuery', а также, на всякий случай, вам нужно будет использовать ее в будущем.

Он был тщательно протестирован на Lucee 4.5 (Windows 2008R2) и ACF11 (Windows 10).

Если вам требуется дополнительная инструкция по вызывающему sysntax, пожалуйста, дайте мне знать, но я добавилпример внизу, чтобы вы начали.

Функции фактически являются частью службы CFC, но вы можете использовать их автономно:


<!--- FUNCTION UDF: build SQL params --->

<!--- 

Notes:
Use for building sql with simple read queries: 

Supported:

- INNER JOINS
- value paramaterization
- IN operator with or without paramaterization
- ignore WHERE clause item, if a certain value or a value in a list of values, is matched: {...'ignore'='value'}
- ignore value list custom delimeter: default {...'ignoredelimiter'=','}
- use {...'sqltype'='number|string'} for non value paramaterization 
- positional paramaters 'table column ?'

Unsupported:

- subqueries
- named parameters, 'table column = :table column', due to a bug with the way Railo parses table prefixed columns sql

--->

<cffunction name="BuildSQLParams" access="public" returntype="string" hint="function description: build SQL params">
  <cfargument name="query" required="false" default="#StructNew()#" type="struct" hint="argument description: query">
  <cfargument name="params" required="false" default="#ArrayNew(1)#" type="array" hint="argument description: params">
  <cfset var result =  "">
  <cfset var local = StructNew()>
  <cfset local.keylist = "column,operator,sqltype,value">
  <cfset local.sqlarray = ArrayNew(1)>
  <cfif IsStruct(arguments.query) AND ArrayLen(arguments.params)>
    <cfloop from="1" to="#ArrayLen(arguments.params)#" index="local.i">
      <cfset local.param = arguments.params[local.i]>
      <cfif IsStruct(local.param) AND NOT StructIsEmpty(local.param)>
        <cfset local.isValidParam = false>
        <cfset local.counter = 0>
        <cfloop collection="#local.param#" item="local.key">
          <cfif ListFindNoCase(local.keylist,local.key)>
            <cfset local.counter = local.counter + 1>
          </cfif>
        </cfloop>
        <cfif local.counter EQ ListLen(local.keylist)>
        <cfset local.isValidParam = true>
        </cfif>
        <cfif local.isValidParam>
        <cfset local.ignore = false>
        <cfset local.ignoredelimiter = ",">
          <cfif StructKeyExists(param,'ignoredelimiter')>
            <cfset local.ignoredelimiter = param['ignoredelimiter']>
          </cfif>
          <cfif StructKeyExists(param,'ignore')>
            <cfif ListLen(param['ignore'],local.ignoredelimiter)>
              <cfif ListFindNoCase(param['ignore'],param['value'],local.ignoredelimiter)>
                <cfset local.ignore = true>
              </cfif>
            <cfelse>
              <cfif param['ignore'] EQ param['value']>
                <cfset local.ignore = true>
              </cfif>
            </cfif>
          </cfif>
          <cfif NOT local.ignore>
          <cfif NOT ListFindNoCase("number,string",param['sqltype'])>
              <cfif param['operator'] EQ "IN">
                <cfset arguments.query.addParam(value=param['value'],cfsqltype="cf_sql_#param['sqltype']#",list="yes")>
              <cfelse>
                <cfset arguments.query.addParam(value=param['value'],cfsqltype="cf_sql_#param['sqltype']#")>
              </cfif>
            </cfif>
          </cfif>
          <cfif NOT local.ignore>
            <cfsavecontent variable="local.sql">
              <cfoutput>
                #param['column']# 
                #param['operator']# 
                <cfif ListFindNoCase("number,string",param['sqltype'])>
                  <cfif param['sqltype'] EQ "number">
                    #param['value']# 
                  <cfelse>
                    '#param['value']#' 
                  </cfif>
                <cfelse>
                <cfif param['operator'] EQ "IN">
                    (?) 
                  <cfelse>
                    ?
                  </cfif>
                </cfif>
                <cfif StructKeyExists(param,'andOr')>
                  #param['andOr']# 
                </cfif>
              </cfoutput>
            </cfsavecontent>
            <cfset result = result & local.sql>
          </cfif>
        </cfif>
      </cfif>
    </cfloop>
  </cfif>     
  <cfif Len(Trim(result))>
  <cfset result = REReplaceNoCase(result,"[\s]+"," ","ALL")>
    <cfset result = REReplaceNoCase(result,"[\s]+(AND|OR|,)[\s]*$","","ALL")>
    <cfset result = Trim(result)>
  </cfif>
<cfreturn result /> 
</cffunction>


<!--- FUNCTION UDF: read query --->

<!--- 

Notes:
Use for executing sql with simple read queries: 

Supported:

- query attributes: datasource

Unsupported:

- apart from 'datasource', no other query attributes are supported

--->

<cffunction name="ReadQuery" returntype="struct" output="false" access="public" hint="function description: read query">
  <!--- arguments --->
  <cfargument name="dsn" required="yes" hint="argument description: dsn">
  <cfargument name="columns" type="string" required="no" default="" hint="argument description: columns">
  <cfargument name="tables" type="string" required="no" default="" hint="argument description: tables">
  <cfargument name="params" required="false" default="#ArrayNew(1)#" type="array" hint="argument description: params">
  <cfargument name="groupby" type="string" required="no" default="" hint="argument description: group by">
  <cfargument name="orderby" type="string" required="no" default="" hint="argument description: order by">
  <cfargument name="sortorder" type="string" required="no" default="ASC" hint="argument description: sort order">
  <!--- local variables --->
  <cfset var result = StructNew()>
  <cfset var local = StructNew()>
  <!--- logic --->
  <cfset StructInsert(result,"query",QueryNew(''))>
  <cfset StructInsert(result,"metaInfo",StructNew())>
  <cfif Len(Trim(arguments.tables))>
  <cfset local.wheresql = "">
    <cfset local.groupby = "">
    <cfset local.orderby = "">
    <cfif Len(Trim(arguments.groupby))>
      <cfset local.groupby = " GROUP BY " & arguments.groupby>
    </cfif>
    <cfif Len(Trim(arguments.orderby))>
      <cfset local.orderby = " ORDER BY " & arguments.orderby & " " & arguments.sortorder>
    </cfif>
  <cfset local.query = new Query()> 
    <cfset local.query.setAttributes(datasource=arguments.dsn)> 
    <cfset local.query.setAttributes(name="result")>
    <cfif ArrayLen(arguments.params)>
      <cfset local.wheresql = BuildSQLParams(local.query,arguments.params)>
    </cfif>
    <cfif Len(Trim(local.wheresql))>
      <cfset local.wheresql = " WHERE " & local.wheresql>
    </cfif>
    <cfset local.execute = local.query.execute(sql="SELECT #arguments.columns# FROM #arguments.tables##local.wheresql##local.groupby##local.orderby#")>
    <cfset result.query = local.execute.getResult()> 
    <cfset result.metaInfo = local.execute.getPrefix()>
  </cfif>
<cfreturn result>
</cffunction>


<!--- FUNCTION UDF: update query --->

<!--- 

Notes:
Use for executing sql with simple update queries: 

Supported:

- query attributes: datasource

Unsupported:

- apart from 'datasource', no other query attributes are supported

--->

<cffunction name="UpdateQuery" returntype="struct" output="false" access="public" hint="function description: read query">
  <!--- arguments --->
  <cfargument name="dsn" required="yes" hint="argument description: dsn" />
  <cfargument name="tables" type="string" required="no" default="" hint="argument description: tables">
  <cfargument name="setparams" required="false" default="#ArrayNew(1)#" type="array" hint="argument description: set params">
  <cfargument name="whereparams" required="false" default="#ArrayNew(1)#" type="array" hint="argument description: where params">
  <!--- local variables --->
  <cfset var result = StructNew()>
  <cfset var local = StructNew()>
  <!--- logic --->
  <cfset StructInsert(result,"query",QueryNew(''))>
  <cfset StructInsert(result,"metaInfo",StructNew())>
  <cfif Len(Trim(arguments.tables))>
  <cfset local.setsql = "">
  <cfset local.wheresql = "">
  <cfset local.query = new Query()> 
    <cfset local.query.setAttributes(datasource=arguments.dsn)> 
    <cfset local.query.setAttributes(name="result")>
    <cfif ArrayLen(arguments.setparams)>
      <cfset local.setsql = BuildSQLParams(local.query,arguments.setparams)>
    </cfif>
    <cfif Len(Trim(local.setsql))>
      <cfset local.setsql = " SET " & local.setsql>
    </cfif>
    <cfif ArrayLen(arguments.whereparams)>
      <cfset local.wheresql = BuildSQLParams(local.query,arguments.whereparams)>
    </cfif>
    <cfif Len(Trim(local.wheresql))>
      <cfset local.wheresql = " WHERE " & local.wheresql>
    </cfif>
    <cfset local.execute = local.query.execute(sql="UPDATE #arguments.tables##local.setsql##local.wheresql#")>
    <cfset result.query = local.execute.getResult()> 
    <cfset result.metaInfo = local.execute.getPrefix()>
  </cfif>
<cfreturn result>
</cffunction>

<cfset sqlarray = ArrayNew(1)>
<cfset wherearray = ArrayNew(1)>

<cfset ArrayAppend(sqlarray,{'column'='id','operator'='=','sqltype'='integer','value'='123','andOr'=','})>

<cfset ArrayAppend(sqlarray,{'column'='csp','operator'='=','sqltype'='integer','value'='5','andOr'=','})>

...

<cfset ArrayAppend(wherearray,{'column'='rec_id','operator'='=','sqltype'='integer','value'=id,'andOr'=''}>

<cfset UpdateQuery(dsn=application.datasource,tables="answers",setparams=sqlarray,whereparams=wherearray)>


0 голосов
/ 01 февраля 2019

tldr;

CFQueryparam не может быть вложено в строку.Он должен использоваться непосредственно в тегах <cfquery>.Чтобы создать операторы sql с динамическими параметрами, взгляните на cfscript-эквивалент cfqueryparam .

Проблемы безопасности

Хотя вы, вероятно, только пыталисьPreserveSingleQuotes() от отчаяния, никогда не используйте эту функцию, если вы не понимаете последствий, потому что она в основном создает огромную дыру для инъекций SQL в вашем приложении.

<cfset sqlString = sqlString & ', ' & myColumn

Кроме того, будьте очень осторожны с такого рода динамическим SQL-оператором.Даже если вы защищаете все параметры с помощью cfqueryparam, запрос все равно уязвим для внедрения SQL, поскольку myColumn является предоставленным пользователем значением.К сожалению, cfqueryparam не может защитить имена объектов (имена таблиц, столбцов и т. Д.), Только литералы (строки, даты и т. Д.).Поэтому, если вы абсолютно должны использовать динамические имена столбцов в raw sql, не забудьте проверить их по белому списку и отклонить запрос, если обнаружены недопустимые столбцы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...