diff --git a/lib/scenic/adapters/postgres.rb b/lib/scenic/adapters/postgres.rb index 9941832..48cb336 100644 --- a/lib/scenic/adapters/postgres.rb +++ b/lib/scenic/adapters/postgres.rb @@ -14,7 +14,7 @@ module Scenic module Adapters # An adapter for managing Postgres views. # - # These methods are used interally by Scenic and are not intended for direct + # These methods are used internally by Scenic and are not intended for direct # use. Methods that alter database schema are intended to be called via # {Statements}, while {#refresh_materialized_view} is called via # {Scenic.database}. @@ -124,7 +124,7 @@ def drop_view(name) # @param sql_definition The SQL schema that defines the materialized view. # @param no_data [Boolean] Default: false. Set to true to create # materialized view without running the associated query. You will need - # to perform a non-concurrent refresh to populate with data. + # to perform a refresh to populate with data. # # This is typically called in a migration via {Statements#create_view}. # @@ -154,7 +154,7 @@ def create_materialized_view(name, sql_definition, no_data: false) # @param sql_definition The SQL schema for the updated view. # @param no_data [Boolean] Default: false. Set to true to create # materialized view without running the associated query. You will need - # to perform a non-concurrent refresh to populate with data. + # to perform a refresh to populate with data. # # @raise [MaterializedViewsNotSupportedError] if the version of Postgres # in use does not support materialized views. @@ -193,7 +193,10 @@ def drop_materialized_view(name) # refreshed without locking the view for select but requires that the # table have at least one unique index that covers all rows. Attempts to # refresh concurrently without a unique index will raise a descriptive - # error. + # error. This option is ignored if the view is not populated, as it + # would cause an error to be raised by Postgres. Default: false. + # @param cascade [Boolean] Whether to refresh dependent materialized + # views. Default: false. # # @raise [MaterializedViewsNotSupportedError] if the version of Postgres # in use does not support materialized views. @@ -205,26 +208,29 @@ def drop_materialized_view(name) # Scenic.database.refresh_materialized_view(:search_results) # @example Concurrent refresh # Scenic.database.refresh_materialized_view(:posts, concurrently: true) + # @example Cascade refresh + # Scenic.database.refresh_materialized_view(:posts, cascade: true) # # @return [void] def refresh_materialized_view(name, concurrently: false, cascade: false) raise_unless_materialized_views_supported + if concurrently + raise_unless_concurrent_refresh_supported + end + if cascade refresh_dependencies_for(name, concurrently: concurrently) end - if concurrently - raise_unless_concurrent_refresh_supported + if concurrently && populated?(name) execute "REFRESH MATERIALIZED VIEW CONCURRENTLY #{quote_table_name(name)};" else execute "REFRESH MATERIALIZED VIEW #{quote_table_name(name)};" end end - # True if supplied relation name is populated. Useful for checking the - # state of materialized views which may error if created `WITH NO DATA` - # and used before they are refreshed. True for all other relation types. + # True if supplied relation name is populated. # # @param name The name of the relation # @@ -235,7 +241,7 @@ def refresh_materialized_view(name, concurrently: false, cascade: false) def populated?(name) raise_unless_materialized_views_supported - schemaless_name = name.split(".").last + schemaless_name = name.to_s.split(".").last sql = "SELECT relispopulated FROM pg_class WHERE relname = '#{schemaless_name}'" relations = execute(sql) diff --git a/spec/scenic/adapters/postgres_spec.rb b/spec/scenic/adapters/postgres_spec.rb index 9ef4da0..90550f6 100644 --- a/spec/scenic/adapters/postgres_spec.rb +++ b/spec/scenic/adapters/postgres_spec.rb @@ -149,6 +149,14 @@ module Adapters adapter.refresh_materialized_view(:tests, concurrently: true) }.to raise_error e end + + it "falls back to non-concurrent refresh if not populated" do + adapter = Postgres.new + adapter.create_materialized_view(:testing, "SELECT unnest('{1, 2}'::int[])", no_data: true) + + expect { adapter.refresh_materialized_view(:testing, concurrently: true) } + .not_to raise_error + end end end