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 в некоторых случаях вручную, если вы хотите, чтобы они отражали данные, которые вы вставили в базовые таблицы.

Другие вопросы по тегам