是否可以在我的查询中使用不使用字符串插值的变量?

时间:2016-05-18 13:09:45

标签: sql ruby-on-rails database postgresql

好的,所以我有一个使用字符串插值并完美运行的SQL请求:

  ActiveRecord::Base.connection.execute("UPDATE my_table SET location = ST_SetSRID(ST_MakePoint('#{my_table.longitude}'::numeric, '#{my_table.latitude}'::numeric), 4326)::geography WHERE id=#{id}")

我想要做的是使用SQL变量而不是字符串插值(出于安全原因(SQL注入))但我的查询遇到了一些麻烦:

ActiveRecord::Base.connection.execute("UPDATE my_table SET location = ST_SetSRID(ST_MakePoint(':longitude'::numeric, ':latitude'::numeric), 4326)::geography WHERE id=#{id}", longitude: my_table.longitude, latitude: my_table.latitude)

我得到的错误是:

  

PG :: InvalidTextRepresentation:错误:类型的输入语法无效   数字:":经度" (ActiveRecord的:: StatementInvalid)

有没有办法在上一次查询中正确使用SQL变量?

5 个答案:

答案 0 :(得分:0)

尝试'?'而不是插值

ActiveRecord::Base.connection.execute("UPDATE my_table SET location = ST_SetSRID(ST_MakePoint( ? , ? , 4326)::geography WHERE id= ? ", "#{my_table.longitude}::numeric", "#{my_table.latitude}::numeric)", id  )

答案 1 :(得分:0)

字符串插值这种方式非常危险,可以打开服务器SQL injection attacks。而是使用ActiveRecord's query interface来构建查询,如下所示:

my_table = MyTable.find(id)
my_table.update(location: "ST_SetSRID(ST_MakePoint('#{my_table.longitude}'::numeric, '#{my_table.latitude}'::numeric), 4326)::geography")

答案 2 :(得分:0)

为什么不使用Active Record?

根据您的方案,您似乎从记录中的其他属性中获取值,以填充第三个location属性。

假设您的应用程序中有一个由此表支持的模型,您有几个选择:

  1. 在模型上创建location方法,该方法根据坐标生成并返回位置。这里的一个优点是数据库中的数据重复较少,代价是能够针对生成的位置进行原生搜索,并且如果该位置的生成成本高昂,可能会节省成本。
  2. 每次保存(创建或更新)记录时,添加before_save回调以生成位置。如果生成代价昂贵并且您经常更新记录,这可能会导致问题...但是,通过在生成位置之前检查坐标是否已更改,可以解决此问题。
  3. 但是,这两种解决方案都要求您在模型中编写方法以从坐标生成位置,或者至少调用一些第三方代码为您执行此操作。

答案 3 :(得分:0)

好的,所以我尝试使用这种方法(它似乎按预期工作):

@connection = ActiveRecord::Base.connection.raw_connection
@connection.prepare('fill_column_location', "UPDATE my_table SET location = ST_SetSRID(ST_MakePoint($1::numeric, $2::numeric), 4326)::geography WHERE id=$3")
fill_column_location = @connection.exec_prepared('fill_column_location', [my_table.longtmp, my_table.latitmp, id])

我不知道这样做是否有任何危险或缺陷。

答案 4 :(得分:0)

使用sanatize_sql_array在2020年为我工作。在这种情况下,我认为您会做类似的事情:

sql = ActiveRecord::Base.sanatize_sql_array([
  "UPDATE my_table SET location = ST_SetSRID(ST_MakePoint('?'::numeric, '?'::numeric), 4326)::geography WHERE id=?", 
  my_table.longitude, 
  my_table.latitude, 
  id
])

ActiveRecord::Base.connection.execute(sql)

文档:https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html#method-i-sanitize_sql_array