我可以执行原始sql查询,利用预准备语句,而不是使用ActiveRecord :: Relation :: QueryAttribute吗?

时间:2018-04-20 18:50:30

标签: ruby-on-rails postgresql activerecord prepared-statement

我想做upsert。 Rails还没有支持这个。查询是这样的:

INSERT INTO foos (thing_id, bar_id) VALUES (1, 2)
ON CONFLICT (thing_id, bar_id) DO NOTHING

我可以使用self.class.connection.executeexec_insert轻松完成此操作。但我也希望利用准备好的陈述。我想我可以这样做:

thing_id = ActiveRecord::Relation::QueryAttribute.new("thing_id", thing.id, ActiveRecord::Type::Integer.new)
bar_id = ActiveRecord::Relation::QueryAttribute.new("bar_id", id, ActiveRecord::Type::Integer.new)

self.class.connection.exec_insert(<<-SQL, nil, [thing_id, bar_id])
  INSERT INTO foos (thing_id, bar_id) VALUES ($1, $2)
  ON CONFLICT (thing_id, bar_id) DO NOTHING
SQL

但是当我试验这个时,似乎没有创建预备语句。

我试过这种风格:

query = <<-SQL
  INSERT INTO foos (thing_id, bar_id) VALUES ($1, $2)
  ON CONFLICT (thing_id, bar_id) DO NOTHING
SQL

connection = ActiveRecord::Base.connection.raw_connection
connection.prepare('some_name', query)
st = connection.exec_prepared('some_name', [ thing.id, id ])

它确实创建了一个准备好的声明。但是,第二次运行时,postgres抱怨创建一个具有相同名称的预准备语句。所以,rails&#39;准备好的报表管理发生在高于此水平的水平上,我无法在此处利用它。我必须在这里手动管理它。我不能确信我能做到这一点,即使我能做到这一点也会很冗长。

execute和朋友不接受("foo=?", 1)接受的where api。

有没有办法利用rails&#39;原始SQL的自动化预处理语句管理?

1 个答案:

答案 0 :(得分:3)

#exec_query接受一个可选的关键字参数prepare,默认为false。看看method definition(和here)。

给出以下表格定义:

CREATE TABLE foos (
    thing_id INT,
    bar_id INT,
    UNIQUE (thing_id, bar_id)
);

我测试了以下内容:

conn = ActiveRecord::Base.connection
stmt = 'INSERT INTO foos (thing_id, bar_id) VALUES ($1, $2) ' \
       'ON CONFLICT (thing_id, bar_id) DO NOTHING'

# "Old" bind parameters
binds = [[nil, 1], [nil, 2]]
conn.exec_query stmt, 'SQL', binds, prepare: true
conn.exec_query stmt, 'SQL', binds, prepare: true

# "New" bind parameters
type  = ActiveModel::Type::Integer.new limit: 4
binds = [
  ActiveRecord::Relation::QueryAttribute.new('thing_id', 1, type),
  ActiveRecord::Relation::QueryAttribute.new('bar_id',   2, type)
]
conn.exec_query stmt, 'SQL', binds, prepare: true
conn.exec_query stmt, 'SQL', binds, prepare: true

这两种绑定参数样式都适用于ActiveRecord 5.2.0和pg 1.0.0。多个使用相同值的INSERT语句最终将仅插入一行,而不会引发任何错误。我检查了Postgres日志,并且只有一个“解析”(在第一个INSERT之前),因此看来准备好的语句机制正在正确使用。