From 549e9a373955b04be19d611d90df294a9aeded07 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Sat, 2 Jul 2022 21:00:09 +0300 Subject: [PATCH] Fix recreating indexes for materialized views within custom schemas --- lib/scenic/adapters/postgres/indexes.rb | 14 ++++- spec/scenic/adapters/postgres_spec.rb | 77 ++++++++++++++++++++----- 2 files changed, 74 insertions(+), 17 deletions(-) diff --git a/lib/scenic/adapters/postgres/indexes.rb b/lib/scenic/adapters/postgres/indexes.rb index 8ce36de6..9c35edba 100644 --- a/lib/scenic/adapters/postgres/indexes.rb +++ b/lib/scenic/adapters/postgres/indexes.rb @@ -23,6 +23,7 @@ def on(name) delegate :quote_table_name, to: :connection def indexes_on(name) + schema, table = extract_schema_and_table(name.to_s) connection.execute(<<-SQL) SELECT t.relname as object_name, @@ -34,8 +35,8 @@ def indexes_on(name) LEFT JOIN pg_namespace n ON n.oid = i.relnamespace WHERE i.relkind = 'i' AND d.indisprimary = 'f' - AND t.relname = '#{name}' - AND n.nspname = ANY (current_schemas(false)) + AND t.relname = #{connection.quote(table)} + AND n.nspname = #{schema ? connection.quote(schema) : 'ANY (current_schemas(false))'} ORDER BY i.relname SQL end @@ -47,6 +48,15 @@ def index_from_database(result) definition: result["definition"], ) end + + def extract_schema_and_table(string) + schema, table = string.scan(/[^".]+|"[^"]*"/) + if table.nil? + table = schema + schema = nil + end + [schema, table] + end end end end diff --git a/spec/scenic/adapters/postgres_spec.rb b/spec/scenic/adapters/postgres_spec.rb index 0fa438ec..5848741f 100644 --- a/spec/scenic/adapters/postgres_spec.rb +++ b/spec/scenic/adapters/postgres_spec.rb @@ -52,6 +52,46 @@ module Adapters end end + describe "#update_materialized_view" do + it "handles views with custom schemas" do + adapter = Postgres.new + connection = ActiveRecord::Base.connection + previous_search_path = connection.select_value("SHOW search_path") + + begin + connection.execute <<-SQL + CREATE SCHEMA scenic; + SET search_path TO scenic, public; + SQL + + adapter.create_materialized_view( + "scenic.greetings", + "SELECT text 'hi' AS greeting", + ) + connection.add_index("scenic.greetings", :greeting, + name: "index_greetings_on_greeting", unique: true) + + silence_stream(STDOUT) do + adapter.update_materialized_view( + "scenic.greetings", + "SELECT text 'hello' AS greeting", + ) + end + + view = adapter.views.first + expect(view.name).to eq("scenic.greetings") + expect(view.materialized).to eq true + + index = connection.indexes("scenic.greetings").first + expect(index.columns).to eq(["greeting"]) + expect(index.unique).to eq true + ensure + connection.drop_schema("scenic") + connection.execute("SET search_path TO #{previous_search_path}") + end + end + end + describe "#replace_view" do it "successfully replaces a view" do adapter = Postgres.new @@ -186,21 +226,28 @@ module Adapters context "with views in non public schemas" do it "returns also the non public views" do adapter = Postgres.new - - ActiveRecord::Base.connection.execute <<-SQL - CREATE VIEW parents AS SELECT text 'Joe' AS name - SQL - - ActiveRecord::Base.connection.execute <<-SQL - CREATE SCHEMA scenic; - CREATE VIEW scenic.parents AS SELECT text 'Maarten' AS name; - SET search_path TO scenic, public; - SQL - - expect(adapter.views.map(&:name)).to eq [ - "parents", - "scenic.parents", - ] + connection = ActiveRecord::Base.connection + previous_search_path = connection.select_value("SHOW search_path") + + begin + connection.execute <<-SQL + CREATE VIEW parents AS SELECT text 'Joe' AS name + SQL + + connection.execute <<-SQL + CREATE SCHEMA scenic; + CREATE VIEW scenic.parents AS SELECT text 'Maarten' AS name; + SET search_path TO scenic, public; + SQL + + expect(adapter.views.map(&:name)).to eq [ + "parents", + "scenic.parents", + ] + ensure + connection.drop_schema("scenic") + connection.execute("SET search_path TO #{previous_search_path}") + end end end end