Rails не заполняет материализованное представление Postgres по умолчанию
У меня есть следующие миграции:
execute <<-SQL
CREATE TABLE test_table(
name char(20)
);
INSERT INTO test_table(name) values ('test name');
CREATE MATERIALIZED VIEW test AS
SELECT * from test_table
WITH DATA;
SQL
Обратите внимание, что я добавляю "С ДАННЫМИ". Это не заполняет данные (поскольку я получаю ошибку "материализованное представление не было заполнено", когда я пытаюсь выполнить одновременное обновление представления) и добавляет "БЕЗ ДАННЫХ" в structure.sql
:
CREATE MATERIALIZED VIEW public.test AS
SELECT test_table.name
FROM public.test_table
WITH NO DATA;
Что я делаю неправильно? Документация Postgres о материализованных представлениях гласит, что запрос выполняется и используется для заполнения представления во время выполнения команды (если не используется WITH NO DATA). Поэтому даже без указания "WITH DATA" он должен генерировать "WITH DATA" по умолчанию, но я вместо этого получите "БЕЗ ДАННЫХ". Когда я использую сценическую библиотеку, она делает то же самое...
Моя проблема похожа на миграцию ActiveRecord, не заполняющую материализованное представление Postgres
РЕДАКТИРОВАТЬ Я узнал, что данные на самом деле заполняются в среде разработки (хотя она устанавливает WITH NO DATA
в structure.sql
). Проблема в тестовой среде, где она иногда не заполняет данные. Все еще расследую почему... Но structure.sql
это точно не правильно, к сожалению.
0 ответов
Я надеюсь, что это не слишком поздно, чтобы быть полезным, на восемнадцать месяцев, но вот как я решил эту проблему.
Предполагая, что вы используете (настоятельно рекомендуется!) scenic
драгоценный камень
Предположим, ваша материализованная модель с поддержкой представлений выглядит так. Я изменилrefresh
метод немного взять concurrently
как аргумент.
class Foo < ApplicationRecord
# if you're using the excellent "scenic" gem
def self.refresh(concurrently:)
Scenic.database.refresh_materialized_view(table_name, concurrently: concurrently, cascade: false)
end
# if you're not using the excellent "scenic" gem (why not?)
# have not tested this but should work
def self.refresh(concurrently:)
concurrently_string = concurrently ? "CONCURRENTLY" : ""
ActiveRecord::Base.connection.execute(
"REFRESH MATERIALIZED VIEW #{table_name} #{concurrently_string} WITH DATA"
)
end
private
def readonly?
true
end
end
Затем в spec/rails_helper.rb
, просто сделайте это для каждой из ваших материализованных моделей с поддержкой просмотра:
config.before(:suite) do
Foo.refresh(concurrently: false)
Bar.refresh(concurrently: false)
# etc.
end
Обратите внимание, что в ваших индивидуальных тестах вам все равно может потребоваться позвонить #refresh
в некоторых случаях вручную, если вы хотите, чтобы они отражали данные, которые вы вставили в базовые таблицы.