如何通过 ActiveRecord 将空的 jsonb 值插入到 Ruby on Rails 中的 Postgres 表字段?

问题描述 投票:0回答:1

这是一个 SQL 片段:

CREATE TABLE jsonb_null (
  id bigserial PRIMARY KEY,
  value jsonb
);
INSERT INTO jsonb_null (value) VALUES ('"null"'), ('null'), (NULL);
SELECT id, value, jsonb_typeof(value) FROM jsonb_null ORDER BY id;

输出以下内容:

 id | value  | jsonb_typeof
----+--------+--------------
  1 | "null" | string
  2 | null   | null
  3 |        |
(3 rows)

我想要选项 (2),其中有一个空的 jsonb 值。我怎样才能插入这个?

如果我们使用 ActiveRecord 选择一个值,这是一个失败的示例。它不一定与插入有关,但表明了问题。

require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
  gem 'rails'
  gem 'pg'
end

require 'active_record'
require 'minitest/autorun'
require 'logger'

# `CREATE DATABASE rails_test;`
ActiveRecord::Base.establish_connection(adapter: 'postgresql', database: 'rails_test')
ActiveRecord::Base.logger = Logger.new($stdout)
ActiveRecord::Base.connection.execute(<<~SQL)
  DROP TABLE IF EXISTS jsonb_nulls;
  CREATE TABLE jsonb_nulls (
    id bigserial PRIMARY KEY,
    value jsonb
  );
  INSERT INTO jsonb_nulls (id, value) VALUES (1, '"null"'), (2, 'null'), (3, NULL);
SQL

class JsonbNull < ActiveRecord::Base; end

class JsonbNullTest < Minitest::Test
  def test_jsonb_null
    refute_equal JsonbNull.find(2).value, JsonbNull.find(3).value
  end
end
ruby-on-rails postgresql rails-activerecord
1个回答
0
投票

虽然 Postgres 对于 SQL null 和 JSONB null 有不同的类型,但 Ruby 数据库驱动程序没有,并且会将两者都转换为 nil。这就是测试通过并且应该会通过的原因。

如果要查询 JSONB null,请使用

jsonb_typeof = 'null'
。这不是您可以使用查询接口生成的查询,但您可以使用 SQL 字符串或 Arel 来完成。

JsonbNull.where("jsonb_typeof(value) = 'null'")

使用常规 ActiveRecord 方法(例如

create
)创建具有 JSONB null 值的记录将非常困难。

ActiveRecord 确实不遗余力地保护您免受自己的愚蠢行为的影响,并且会逃避您抛出的任何内容,以避免潜在的 SQL 注入攻击。这甚至包括诸如

Arel::Nodes::SqlLiteral
之类的类型,这些类型通常被查询方法视为可信。

irb(main):007:0> JsonbNull.create!(value: Arel.sql("'null'::jsonb"))                                                      TRANSACTION (0.3ms)  BEGIN                                                                                              JsonbNull Create (0.3ms)  INSERT INTO "jsonb_nulls" ("value", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["value", "\"'null'::jsonb\""], ["created_at", "2024-04-03 13:30:30.928161"], ["updated_at", "2024-04-03 13:30:30.928161"]]                                                                                                              TRANSACTION (9.5ms)  COMMIT                                                                                           

要实现这一点,您需要使用较低级别的方法并自己构建插入语句。

我会考虑是否有更简单的方法来实现你的目标。

© www.soinside.com 2019 - 2024. All rights reserved.