mat_views 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,102 +21,106 @@ module MatViews
21
21
  # 2. In a transaction: rename original → old, tmp → original, drop old.
22
22
  # 3. Recreate declared unique indexes (if any).
23
23
  #
24
- # Supports optional row count strategies:
25
- # - `:estimated` approximate, using `pg_class.reltuples`
26
- # - `:exact` → accurate, using `COUNT(*)`
27
- # - `nil` → skip row count
24
+ # Options:
25
+ # - `row_count_strategy:` (Symbol, default: :none) → one of `:estimated`, `:exact`, or `:none or nil` to control row count reporting
28
26
  #
29
- # @return [MatViews::ServiceResponse]
27
+ # Returns a {MatViews::ServiceResponse}
30
28
  #
31
- # @example
32
- # svc = MatViews::Services::SwapRefresh.new(defn, row_count_strategy: :exact)
33
- # svc.run
29
+ # @see MatViews::Services::ConcurrentRefresh
30
+ # @see MatViews::Services::RegularRefresh
31
+ #
32
+ # @example Direct usage
33
+ # svc = MatViews::Services::SwapRefresh.new(definition, **options)
34
+ # response = svc.run
35
+ # response.success? # => true/false
36
+ #
37
+ # @example via job, this is the typical usage and will create a run record in the DB
38
+ # When definition.refresh_strategy == "concurrent"
39
+ # MatViews::Jobs::Adapter.enqueue(MatViews::Services::SwapRefresh, definition.id, **options)
34
40
  #
35
41
  class SwapRefresh < BaseService
36
- ##
37
- # Row count strategy (`:estimated`, `:exact`, `nil`).
38
- #
39
- # @return [Symbol, nil]
40
- attr_reader :row_count_strategy
41
-
42
- ##
43
- # @param definition [MatViews::MatViewDefinition]
44
- # @param row_count_strategy [Symbol, nil]
45
- def initialize(definition, row_count_strategy: :estimated)
46
- super(definition)
47
- @row_count_strategy = row_count_strategy
48
- end
42
+ private
49
43
 
50
44
  ##
51
45
  # Execute the swap refresh.
52
46
  #
53
47
  # @return [MatViews::ServiceResponse]
54
- def run
55
- prep = prepare!
56
- return prep if prep
57
-
58
- create_sql = %(CREATE MATERIALIZED VIEW #{q_tmp} AS #{definition.sql} WITH DATA)
59
- steps = [create_sql]
60
- conn.execute(create_sql)
61
-
62
- steps.concat(swap_index)
63
-
64
- payload = { view: "#{schema}.#{rel}" }
65
- payload[:row_count] = fetch_rows_count if row_count_strategy.present?
66
-
67
- ok(:updated, payload: payload, meta: { steps: steps, row_count_strategy: row_count_strategy, swap: true })
68
- rescue StandardError => e
69
- error_response(e,
70
- meta: {
71
- steps: steps,
72
- backtrace: Array(e.backtrace),
73
- row_count_strategy: row_count_strategy,
74
- swap: true
75
- },
76
- payload: { view: "#{schema}.#{rel}" })
77
- end
48
+ # - `status: :updated` on success, with `response` containing:
49
+ # - `view` - the qualified view name
50
+ # - `row_count_before` - if requested and available
51
+ # - `row_count_after` - if requested and available
52
+ # - `status: :error` with `error` on failure, with `error` containing:
53
+ # - serlialized exception class, message, and backtrace in a hash
54
+ #
55
+ def _run
56
+ self.response = { view: "#{schema}.#{rel}" }
78
57
 
79
- private
58
+ response[:row_count_before] = fetch_rows_count
59
+ response[:sql] = swap_view
60
+ response[:row_count_after] = fetch_rows_count
80
61
 
81
- # ────────────────────────────────────────────────────────────────
82
- # internal
83
- # ────────────────────────────────────────────────────────────────
62
+ ok(:updated)
63
+ end
84
64
 
85
65
  ##
86
66
  # Ensure name validity and existence of original view.
87
67
  #
88
68
  # @api private
89
69
  # @return [MatViews::ServiceResponse, nil]
90
- def prepare!
91
- return err("Invalid view name format: #{definition.name.inspect}") unless valid_name?
92
- return err("Materialized view #{schema}.#{rel} does not exist") unless view_exists?
70
+ def prepare
71
+ raise_err "Invalid view name format: #{definition.name.inspect}" unless valid_name?
72
+ raise_err "Materialized view #{schema}.#{rel} does not exist" unless view_exists?
93
73
 
94
74
  nil
95
75
  end
96
76
 
77
+ ##
78
+ # Assign the request parameters.
79
+ # Called by {#run} before {#prepare}.
80
+ #
81
+ # @api private
82
+ # @return [void]
83
+ #
84
+ def assign_request
85
+ self.request = { row_count_strategy: row_count_strategy, swap: true }
86
+ end
87
+
97
88
  ##
98
89
  # Perform rename/drop/index recreation in a transaction.
99
90
  #
100
91
  # @api private
101
92
  # @return [Array<String>] SQL steps executed
102
- def swap_index
103
- steps = []
93
+ def swap_view
94
+ conn.execute(create_temp_view_sql)
95
+ steps = [
96
+ move_current_to_old_sql,
97
+ move_temp_to_current_sql,
98
+ drop_old_view_sql,
99
+ recreate_declared_unique_indexes_sql
100
+ ].compact
104
101
  conn.transaction do
105
- rename_orig_sql = %(ALTER MATERIALIZED VIEW #{qualified_rel} RENAME TO #{conn.quote_column_name(old_rel)})
106
- steps << rename_orig_sql
107
- conn.execute(rename_orig_sql)
102
+ steps.each { |step| conn.execute(step) }
103
+ end
108
104
 
109
- rename_tmp_sql = %(ALTER MATERIALIZED VIEW #{q_tmp} RENAME TO #{conn.quote_column_name(rel)})
110
- steps << rename_tmp_sql
111
- conn.execute(rename_tmp_sql)
105
+ # prepend the create step
106
+ steps.unshift(create_temp_view_sql)
107
+ steps
108
+ end
112
109
 
113
- drop_old_sql = %(DROP MATERIALIZED VIEW #{q_old})
114
- steps << drop_old_sql
115
- conn.execute(drop_old_sql)
110
+ def create_temp_view_sql
111
+ @create_temp_view_sql ||= %(CREATE MATERIALIZED VIEW #{q_tmp} AS #{definition.sql} WITH DATA)
112
+ end
116
113
 
117
- recreate_declared_unique_indexes!(schema:, rel:, steps:)
118
- end
119
- steps
114
+ def move_current_to_old_sql
115
+ %(ALTER MATERIALIZED VIEW #{qualified_rel} RENAME TO #{conn.quote_column_name(old_rel)})
116
+ end
117
+
118
+ def move_temp_to_current_sql
119
+ %(ALTER MATERIALIZED VIEW #{q_tmp} RENAME TO #{conn.quote_column_name(rel)})
120
+ end
121
+
122
+ def drop_old_view_sql
123
+ %(DROP MATERIALIZED VIEW #{q_old})
120
124
  end
121
125
 
122
126
  ##
@@ -159,62 +163,16 @@ module MatViews
159
163
  # Recreate declared unique indexes on the swapped-in view.
160
164
  #
161
165
  # @api private
162
- # @param schema [String]
163
- # @param rel [String]
164
- # @param steps [Array<String>] collected SQL
165
- def recreate_declared_unique_indexes!(schema:, rel:, steps:)
166
+ # @return [String] SQL statements to execute
167
+ def recreate_declared_unique_indexes_sql
166
168
  cols = Array(definition.unique_index_columns).map(&:to_s).reject(&:empty?)
167
- return if cols.empty?
169
+ return nil if cols.empty?
168
170
 
169
- quoted_cols = cols.map { |c| conn.quote_column_name(c) }.join(', ')
171
+ quoted_cols = cols.map { |col| conn.quote_column_name(col) }.join(', ')
170
172
  idx_name = conn.quote_column_name("#{rel}_uniq_#{cols.join('_')}")
171
173
  q_rel = conn.quote_table_name("#{schema}.#{rel}")
172
174
 
173
- sql = %(CREATE UNIQUE INDEX #{idx_name} ON #{q_rel} (#{quoted_cols}))
174
- steps << sql
175
- conn.execute(sql)
176
- end
177
-
178
- # ────────────────────────────────────────────────────────────────
179
- # rows counting
180
- # ────────────────────────────────────────────────────────────────
181
-
182
- ##
183
- # Fetch the row count based on the configured strategy.
184
- #
185
- # @api private
186
- # @return [Integer, nil]
187
- def fetch_rows_count
188
- case row_count_strategy
189
- when :estimated then estimated_rows_count
190
- when :exact then exact_rows_count
191
- end
192
- end
193
-
194
- ##
195
- # Approximate row count via `pg_class.reltuples`.
196
- #
197
- # @api private
198
- # @return [Integer]
199
- def estimated_rows_count
200
- conn.select_value(<<~SQL).to_i
201
- SELECT COALESCE(c.reltuples::bigint, 0)
202
- FROM pg_class c
203
- JOIN pg_namespace n ON n.oid = c.relnamespace
204
- WHERE c.relkind IN ('m','r','p')
205
- AND n.nspname = #{conn.quote(schema)}
206
- AND c.relname = #{conn.quote(rel)}
207
- LIMIT 1
208
- SQL
209
- end
210
-
211
- ##
212
- # Accurate row count via `COUNT(*)`.
213
- #
214
- # @api private
215
- # @return [Integer]
216
- def exact_rows_count
217
- conn.select_value("SELECT COUNT(*) FROM #{qualified_rel}").to_i
175
+ %(CREATE UNIQUE INDEX #{idx_name} ON #{q_rel} (#{quoted_cols}))
218
176
  end
219
177
  end
220
178
  end
@@ -17,5 +17,5 @@ module MatViews
17
17
  # - PATCH: Backwards-compatible bug fixes
18
18
  #
19
19
  # @return [String] the current gem version
20
- VERSION = '0.1.2'
20
+ VERSION = '0.2.0'
21
21
  end
data/lib/mat_views.rb CHANGED
@@ -5,6 +5,7 @@
5
5
  # This source code is licensed under the MIT license found in the
6
6
  # LICENSE file in the root directory of this source tree.
7
7
 
8
+ require 'ext/exception'
8
9
  require 'mat_views/version'
9
10
  require 'mat_views/engine'
10
11
  require 'mat_views/configuration'
@@ -40,7 +41,7 @@ module MatViews
40
41
  class << self
41
42
  # Global configuration for MatViews
42
43
  # @return [MatViews::Configuration]
43
- attr_accessor :configuration
44
+ attr_reader :configuration
44
45
 
45
46
  # Configure MatViews via block.
46
47
  #
@@ -50,7 +51,7 @@ module MatViews
50
51
  # config.job_queue = :materialized
51
52
  # end
52
53
  def configure
53
- self.configuration ||= Configuration.new
54
+ @configuration ||= Configuration.new
54
55
  yield(configuration)
55
56
  end
56
57
  end
data/lib/tasks/helpers.rb CHANGED
@@ -51,12 +51,12 @@ module MatViews
51
51
  end
52
52
 
53
53
  # Parse row count strategy from arg or ROW_COUNT_STRATEGY env.
54
- # Defaults to :estimated if blank.
54
+ # Defaults to :none if blank.
55
55
  def parse_row_count_strategy(arg)
56
- s = (arg || ENV.fetch('ROW_COUNT_STRATEGY', nil)).to_s.strip
57
- return :estimated if s.empty?
56
+ str = (arg || ENV.fetch('ROW_COUNT_STRATEGY', nil)).to_s.strip
57
+ return :none if str.empty?
58
58
 
59
- s.to_sym
59
+ str.to_sym
60
60
  end
61
61
 
62
62
  # Check if a materialized view exists in schema.
@@ -78,14 +78,15 @@ module MatViews
78
78
  # @return [MatViews::MatViewDefinition]
79
79
  # @raise [RuntimeError] if no definition found or mismatch with DB
80
80
  def find_definition_by_name!(raw_name)
81
- raise 'view_name is required' if raw_name.nil? || raw_name.to_s.strip.empty?
81
+ raw_name_string = raw_name&.to_s&.strip
82
+ raise 'view_name is required' unless raw_name_string && !raw_name_string.empty?
82
83
 
83
84
  schema, rel =
84
- if raw_name.to_s.include?('.')
85
- parts = raw_name.to_s.split('.', 2)
85
+ if raw_name_string.include?('.')
86
+ parts = raw_name_string.split('.', 2)
86
87
  [parts[0], parts[1]]
87
88
  else
88
- [nil, raw_name.to_s]
89
+ [nil, raw_name_string]
89
90
  end
90
91
 
91
92
  defn = MatViews::MatViewDefinition.find_by(name: rel)
@@ -126,13 +127,13 @@ module MatViews
126
127
  #
127
128
  # @param definition_id [Integer] MatViewDefinition ID
128
129
  # @param force [Boolean] whether to force creation
130
+ # @param row_count_strategy [Symbol] :estimated or :exact or :none
129
131
  # @return [void]
130
- def enqueue_create!(definition_id, force)
131
- q = MatViews.configuration.job_queue || :default
132
+ def enqueue_create(definition_id, force, row_count_strategy)
132
133
  MatViews::Jobs::Adapter.enqueue(
133
134
  MatViews::CreateViewJob,
134
- queue: q,
135
- args: [definition_id, force]
135
+ queue: MatViews.configuration.job_queue || :default,
136
+ args: [definition_id, force, row_count_strategy]
136
137
  )
137
138
  end
138
139
 
@@ -144,11 +145,10 @@ module MatViews
144
145
  #
145
146
  # This method allows scheduling a refresh operation with the specified row count strategy.
146
147
  # It uses the configured job adapter to enqueue the job.
147
- def enqueue_refresh!(definition_id, row_count_strategy)
148
- q = MatViews.configuration.job_queue || :default
148
+ def enqueue_refresh(definition_id, row_count_strategy)
149
149
  MatViews::Jobs::Adapter.enqueue(
150
150
  MatViews::RefreshViewJob,
151
- queue: q,
151
+ queue: MatViews.configuration.job_queue || :default,
152
152
  args: [definition_id, row_count_strategy]
153
153
  )
154
154
  end
@@ -168,16 +168,16 @@ module MatViews
168
168
  #
169
169
  # @param definition_id [Integer] MatViewDefinition ID
170
170
  # @param cascade [Boolean] whether to drop with CASCADE
171
+ # @param row_count_strategy [Symbol] :estimated or :exact or :none
171
172
  # @return [void]
172
173
  #
173
174
  # This method schedules a job to delete the materialized view, optionally with CASCADE.
174
175
  # It uses the configured job adapter to enqueue the job.
175
- def enqueue_delete!(definition_id, cascade)
176
- q = MatViews.configuration.job_queue || :default
176
+ def enqueue_delete(definition_id, cascade, row_count_strategy)
177
177
  MatViews::Jobs::Adapter.enqueue(
178
178
  MatViews::DeleteViewJob,
179
- queue: q,
180
- args: [definition_id, cascade]
179
+ queue: MatViews.configuration.job_queue || :default,
180
+ args: [definition_id, cascade, row_count_strategy]
181
181
  )
182
182
  end
183
183
  end
@@ -10,33 +10,36 @@ namespace :mat_views do
10
10
  # ───────────── CREATE ─────────────
11
11
 
12
12
  desc 'Enqueue a CREATE for a specific view by its name (optionally schema-qualified)'
13
- task :create_by_name, %i[view_name force yes] => :environment do |_t, args|
13
+ task :create_by_name, %i[view_name force row_count_strategy yes] => :environment do |_t, args|
14
+ rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
14
15
  force = helpers.parse_force?(args[:force])
15
16
  skip = helpers.skip_confirm?(args[:yes])
16
17
  defn = helpers.find_definition_by_name!(args[:view_name])
17
18
 
18
- helpers.confirm!("Enqueue CREATE for view=#{defn.name} (id=#{defn.id}), force=#{force}", skip: skip)
19
- helpers.enqueue_create!(defn.id, force)
20
- helpers.logger.info("[mat_views] Enqueued CreateViewJob for definition ##{defn.id} (#{defn.name}), force=#{force}")
19
+ helpers.confirm!("Enqueue CREATE for view=#{defn.name} (id=#{defn.id}), force=#{force}, row_count_strategy=#{rcs}", skip: skip)
20
+ helpers.enqueue_create(defn.id, force, rcs)
21
+ helpers.logger.info("[mat_views] Enqueued CreateViewJob for definition ##{defn.id} (#{defn.name}), force=#{force}, row_count_strategy=#{rcs}.")
21
22
  end
22
23
 
23
24
  desc 'Enqueue a CREATE for a specific view by its definition ID'
24
- task :create_by_id, %i[definition_id force yes] => :environment do |_t, args|
25
+ task :create_by_id, %i[definition_id force row_count_strategy yes] => :environment do |_t, args|
25
26
  raise 'mat_views:create_by_id requires a definition_id parameter' if args[:definition_id].to_s.strip.empty?
26
27
 
28
+ rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
27
29
  force = helpers.parse_force?(args[:force])
28
30
  skip = helpers.skip_confirm?(args[:yes])
29
31
 
30
32
  defn = MatViews::MatViewDefinition.find_by(id: args[:definition_id])
31
33
  raise "No MatViews::MatViewDefinition found for id=#{args[:definition_id]}" unless defn
32
34
 
33
- helpers.confirm!("Enqueue CREATE for id=#{defn.id} (#{defn.name}), force=#{force}", skip: skip)
34
- helpers.enqueue_create!(defn.id, force)
35
- helpers.logger.info("[mat_views] Enqueued CreateViewJob for definition ##{defn.id} (#{defn.name}), force=#{force}")
35
+ helpers.confirm!("Enqueue CREATE for id=#{defn.id} (#{defn.name}), force=#{force}, row_count_strategy=#{rcs}", skip: skip)
36
+ helpers.enqueue_create(defn.id, force, rcs)
37
+ helpers.logger.info("[mat_views] Enqueued CreateViewJob for definition ##{defn.id} (#{defn.name}), force=#{force}, row_count_strategy=#{rcs}.")
36
38
  end
37
39
 
38
40
  desc 'Enqueue CREATE jobs for ALL defined materialized views'
39
- task :create_all, %i[force yes] => :environment do |_t, args|
41
+ task :create_all, %i[force row_count_strategy yes] => :environment do |_t, args|
42
+ rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
40
43
  force = helpers.parse_force?(args[:force])
41
44
  skip = helpers.skip_confirm?(args[:yes])
42
45
 
@@ -47,9 +50,9 @@ namespace :mat_views do
47
50
  next
48
51
  end
49
52
 
50
- helpers.confirm!("Enqueue CREATE for ALL (#{count}) views, force=#{force}", skip: skip)
51
- scope.find_each { |defn| helpers.enqueue_create!(defn.id, force) }
52
- helpers.logger.info("[mat_views] Enqueued #{count} CreateViewJob(s), force=#{force}.")
53
+ helpers.confirm!("Enqueue CREATE for ALL (#{count}) views, force=#{force}, row_count_strategy=#{rcs}", skip: skip)
54
+ scope.find_each { |defn| helpers.enqueue_create(defn.id, force, rcs) }
55
+ helpers.logger.info("[mat_views] Enqueued #{count} CreateViewJob(s), force=#{force}, row_count_strategy=#{rcs}.")
53
56
  end
54
57
 
55
58
  # ───────────── REFRESH ─────────────
@@ -61,8 +64,8 @@ namespace :mat_views do
61
64
  defn = helpers.find_definition_by_name!(args[:view_name])
62
65
 
63
66
  helpers.confirm!("Enqueue REFRESH for view=#{defn.name} (id=#{defn.id}), row_count_strategy=#{rcs}", skip: skip)
64
- helpers.enqueue_refresh!(defn.id, rcs)
65
- helpers.logger.info("[mat_views] Enqueued RefreshViewJob for definition ##{defn.id} (#{defn.name}), row_count_strategy=#{rcs}")
67
+ helpers.enqueue_refresh(defn.id, rcs)
68
+ helpers.logger.info("[mat_views] Enqueued RefreshViewJob for definition ##{defn.id} (#{defn.name}), row_count_strategy=#{rcs}.")
66
69
  end
67
70
 
68
71
  desc 'Enqueue a REFRESH for a specific view by its definition ID'
@@ -76,8 +79,8 @@ namespace :mat_views do
76
79
  raise "No MatViews::MatViewDefinition found for id=#{args[:definition_id]}" unless defn
77
80
 
78
81
  helpers.confirm!("Enqueue REFRESH for id=#{defn.id} (#{defn.name}), row_count_strategy=#{rcs}", skip: skip)
79
- helpers.enqueue_refresh!(defn.id, rcs)
80
- helpers.logger.info("[mat_views] Enqueued RefreshViewJob for definition ##{defn.id} (#{defn.name}), row_count_strategy=#{rcs}")
82
+ helpers.enqueue_refresh(defn.id, rcs)
83
+ helpers.logger.info("[mat_views] Enqueued RefreshViewJob for definition ##{defn.id} (#{defn.name}), row_count_strategy=#{rcs}.")
81
84
  end
82
85
 
83
86
  desc 'Enqueue REFRESH jobs for ALL defined materialized views'
@@ -93,40 +96,43 @@ namespace :mat_views do
93
96
  end
94
97
 
95
98
  helpers.confirm!("Enqueue REFRESH for ALL (#{count}) views, row_count_strategy=#{rcs}", skip: skip)
96
- scope.find_each { |defn| helpers.enqueue_refresh!(defn.id, rcs) }
99
+ scope.find_each { |defn| helpers.enqueue_refresh(defn.id, rcs) }
97
100
  helpers.logger.info("[mat_views] Enqueued #{count} RefreshViewJob(s), row_count_strategy=#{rcs}.")
98
101
  end
99
102
 
100
103
  # ───────────── DELETE ─────────────
101
104
 
102
105
  desc 'Enqueue a DELETE (DROP MATERIALIZED VIEW) for a specific view by its name (optionally schema-qualified)'
103
- task :delete_by_name, %i[view_name cascade yes] => :environment do |_t, args|
106
+ task :delete_by_name, %i[view_name cascade row_count_strategy yes] => :environment do |_t, args|
104
107
  cascade = helpers.parse_cascade?(args[:cascade])
108
+ rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
105
109
  skip = helpers.skip_confirm?(args[:yes])
106
110
  defn = helpers.find_definition_by_name!(args[:view_name])
107
111
 
108
- helpers.confirm!("Enqueue DELETE for view=#{defn.name} (id=#{defn.id}), cascade=#{cascade}", skip: skip)
109
- helpers.enqueue_delete!(defn.id, cascade)
110
- helpers.logger.info("[mat_views] Enqueued DeleteViewJob for definition ##{defn.id} (#{defn.name}), cascade=#{cascade}")
112
+ helpers.confirm!("Enqueue DELETE for view=#{defn.name} (id=#{defn.id}), cascade=#{cascade}, row_count_strategy=#{rcs}", skip: skip)
113
+ helpers.enqueue_delete(defn.id, cascade, rcs)
114
+ helpers.logger.info("[mat_views] Enqueued DeleteViewJob for definition ##{defn.id} (#{defn.name}), cascade=#{cascade}, row_count_strategy=#{rcs}.")
111
115
  end
112
116
 
113
117
  desc 'Enqueue a DELETE (DROP MATERIALIZED VIEW) for a specific view by its definition ID'
114
- task :delete_by_id, %i[definition_id cascade yes] => :environment do |_t, args|
118
+ task :delete_by_id, %i[definition_id cascade row_count_strategy yes] => :environment do |_t, args|
115
119
  raise 'mat_views:delete_by_id requires a definition_id parameter' if args[:definition_id].to_s.strip.empty?
116
120
 
121
+ rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
117
122
  cascade = helpers.parse_cascade?(args[:cascade])
118
123
  skip = helpers.skip_confirm?(args[:yes])
119
124
 
120
125
  defn = MatViews::MatViewDefinition.find_by(id: args[:definition_id])
121
126
  raise "No MatViews::MatViewDefinition found for id=#{args[:definition_id]}" unless defn
122
127
 
123
- helpers.confirm!("Enqueue DELETE for id=#{defn.id} (#{defn.name}), cascade=#{cascade}", skip: skip)
124
- helpers.enqueue_delete!(defn.id, cascade)
125
- helpers.logger.info("[mat_views] Enqueued DeleteViewJob for definition ##{defn.id} (#{defn.name}), cascade=#{cascade}")
128
+ helpers.confirm!("Enqueue DELETE for id=#{defn.id} (#{defn.name}), cascade=#{cascade}, row_count_strategy=#{rcs}", skip: skip)
129
+ helpers.enqueue_delete(defn.id, cascade, rcs)
130
+ helpers.logger.info("[mat_views] Enqueued DeleteViewJob for definition ##{defn.id} (#{defn.name}), cascade=#{cascade}, row_count_strategy=#{rcs}.")
126
131
  end
127
132
 
128
133
  desc 'Enqueue DELETE jobs for ALL defined materialized views'
129
- task :delete_all, %i[cascade yes] => :environment do |_t, args|
134
+ task :delete_all, %i[cascade row_count_strategy yes] => :environment do |_t, args|
135
+ rcs = helpers.parse_row_count_strategy(args[:row_count_strategy])
130
136
  cascade = helpers.parse_cascade?(args[:cascade])
131
137
  skip = helpers.skip_confirm?(args[:yes])
132
138
 
@@ -137,9 +143,9 @@ namespace :mat_views do
137
143
  next
138
144
  end
139
145
 
140
- helpers.confirm!("Enqueue DELETE for ALL (#{count}) views, cascade=#{cascade}", skip: skip)
141
- scope.find_each { |defn| helpers.enqueue_delete!(defn.id, cascade) }
142
- helpers.logger.info("[mat_views] Enqueued #{count} DeleteViewJob(s), cascade=#{cascade}.")
146
+ helpers.confirm!("Enqueue DELETE for ALL (#{count}) views, cascade=#{cascade}, row_count_strategy=#{rcs}", skip: skip)
147
+ scope.find_each { |defn| helpers.enqueue_delete(defn.id, cascade, rcs) }
148
+ helpers.logger.info("[mat_views] Enqueued #{count} DeleteViewJob(s), cascade=#{cascade}, row_count_strategy=#{rcs}.")
143
149
  end
144
150
  end
145
151
  # rubocop:enable Metrics/BlockLength
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mat_views
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nitesh Purohit
@@ -49,6 +49,7 @@ files:
49
49
  - app/models/mat_views/mat_view_definition.rb
50
50
  - app/models/mat_views/mat_view_run.rb
51
51
  - config/routes.rb
52
+ - lib/ext/exception.rb
52
53
  - lib/generators/mat_views/install/install_generator.rb
53
54
  - lib/generators/mat_views/install/templates/create_mat_view_definitions.rb
54
55
  - lib/generators/mat_views/install/templates/create_mat_view_runs.rb
@@ -89,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
90
  - !ruby/object:Gem::Version
90
91
  version: '0'
91
92
  requirements: []
92
- rubygems_version: 3.6.8
93
+ rubygems_version: 3.6.9
93
94
  specification_version: 4
94
95
  summary: Manage and refresh PostgreSQL materialized views in Rails
95
96
  test_files: []