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

Joh*_*hir 7 postgresql activerecord ruby-on-rails prepared-statement

我想做一个upsert.Rails还不支持这个.查询是这样的:

INSERT INTO foos (thing_id, bar_id) VALUES (1, 2)
ON CONFLICT (thing_id, bar_id) DO NOTHING
Run Code Online (Sandbox Code Playgroud)

我可以用self.class.connection.execute或轻松做到这一点exec_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
Run Code Online (Sandbox Code Playgroud)

但是当我试验这个时,似乎没有创建一个准备好的语句.

我试过这种风格:

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 ])
Run Code Online (Sandbox Code Playgroud)

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

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

有没有办法利用rails的自动化准备语句管理原始SQL?

Ste*_*eve 5

#exec_query接受一个可选的关键字参数,prepare默认为 false。看看方法定义(和这里)。

鉴于下表定义:

CREATE TABLE foos (
    thing_id INT,
    bar_id INT,
    UNIQUE (thing_id, bar_id)
);
Run Code Online (Sandbox Code Playgroud)

我测试了以下内容:

CREATE TABLE foos (
    thing_id INT,
    bar_id INT,
    UNIQUE (thing_id, bar_id)
);
Run Code Online (Sandbox Code Playgroud)

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