好的,我有一个使用字符串插值的 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变量?
使用
sanatize_sql_array
在 2020 年对我有用。我认为在这种情况下你会这样做:
sql = ActiveRecord::Base.sanitize_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)
这种方式的字符串插值非常危险,可能会让您的服务器遭受 SQL 注入攻击。相反,使用 ActiveRecord 的查询接口 来构建查询,如下所示:
my_table = MyTable.find(id)
my_table.update(location: "ST_SetSRID(ST_MakePoint('#{my_table.longitude}'::numeric, '#{my_table.latitude}'::numeric), 4326)::geography")
为什么不使用 Active Record?
在您的场景中,您似乎从记录中的其他属性中获取值来填充第三个
location
属性。
假设您的应用程序中有一个由该表支持的模型,您有几个选择:
location
方法,该方法根据坐标生成并返回位置。这里的一个优点是数据库中的数据重复较少,但代价是能够针对生成的位置进行本机搜索,并且如果生成该位置的成本很高,则可能会节省成本。before_save
回调以在每次保存(创建或更新)记录时生成位置。如果生成成本昂贵并且您经常更新记录,这可能会导致问题......但是有一些方法可以通过在生成位置之前检查坐标是否已更改来解决此问题。但是,这两种解决方案都要求您在模型中编写一个方法来根据坐标生成位置,或者至少调用一些第三方代码来为您执行此操作。
好吧,所以我尝试使用这种方法(它似乎按预期工作):
@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])
我不知道这样做是否有任何危险或缺陷。
尝试“?”而不是插值
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 )