module PGTrunk::Operations::Rules

@private Namespace for operations with rules

@!parse

class ActiveRecord::Migration
  # Create a rule
  #
  # @param [#to_s] table (nil) The qualified name of the table
  # @param [#to_s] name (nil) The name of the rule (unique within the table)
  # @option options [Boolean] :replace_existing (false) If the rule should overwrite an existing one
  # @option options [Symbol] :event (nil) The type of the query the rule is applied to.
  #   Supported values: :update, :insert, :delete
  # @option options [Symbol] :kind (:also) The kind of the rule (either :also or :instead).
  #   In case of `instead` the original query wouldn't be executed, only the `command` is.
  # @option options [String] :where (nil) The condition (SQL) for the rule to be applied.
  # @option options [String] :command (nil) The SQL command to be added by the rule.
  # @yield [r] the block with the rule's definition
  # @yieldparam Object receiver of methods specifying the rule
  # @return [void]
  #
  # @notice `SELECT` rules are not supported by the gem.
  #
  # To create a rule you must define table, and event (operation) for the rule.
  # Usually you also supposed to define a command, but in case the `kind` is set
  # to `:instead`, missing the command would provide `INSTEAD DO NOTHING` rule.
  #
  # ```ruby
  # create_rule "users" do |r|
  #   r.event :insert
  #   r.kind :instead
  #   r.comment "Forbid insertion to the table"
  # SQL
  # ```
  #
  # By default the kind is set to `:also`, in this case the `command` is needed as well:
  #
  # ```ruby
  # create_rule "users", "_count_insertion" do |r|
  #   r.event :insert
  #   r.command <<~SQL
  #     UPDATE counters SET user_inserts = user_inserts + 1
  #   SQL
  #   r.comment "Count insertion to the table"
  # SQL
  # ```
  #
  # With a `when` option you can also specify a condition:
  #
  # ```ruby
  # create_rule "users", "_forbid_grants" do |r|
  #   r.event :update
  #   r.kind :instead
  #   r.where "NOT old.admin AND new.admin"
  #   r.comment "Forbid granting admin rights"
  # SQL
  # ```
  #
  # With a `replace_existing: true` option,
  # the rule will be created using the `CREATE OR REPLACE` clause.
  # In this case the migration is irreversible because we
  # don't know if and how to restore the previous definition.
  #
  # ```ruby
  # create_rule "users", "_forbid_insertion", replace_existing: true do |r|
  #   r.event :insert
  #   r.kind :instead
  #   r.comment "Forbid insertion to the table"
  # SQL
  # ```
  def create_rule(table, name = nil, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Drop a rule
  #
  # @param [#to_s] table (nil) The qualified name of the table
  # @param [#to_s] name (nil) The name of the rule (unique within the table)
  # @option options [Boolean] :if_exists (false) Suppress the error when the rule is absent.
  # @option options [Symbol] :force (:restrict) Define how to process dependent objects
  #   Supported values: :restrict (default), :cascade (for cascade deletion)
  # @option options [Symbol] :event (nil) The type of the query the rule is applied to.
  #   Supported values: :update, :insert, :delete
  # @option options [Symbol] :kind (:also) The kind of the rule (either :also or :instead).
  #   In case of `instead` the original query wouldn't be executed, only the `command` is.
  # @option options [String] :where (nil) The condition (SQL) for the rule to be applied.
  # @option options [String] :command (nil) The SQL command to be added by the rule.
  # @yield [r] the block with the rule's definition
  # @yieldparam Object receiver of methods specifying the rule
  # @return [void]
  #
  # The rule can be identified by the table and explicit name
  #
  # ```ruby
  # drop_rule :users, "_forbid_insertion"
  # ```
  #
  # Alternatively the name can be got from kind and event.
  #
  # ```ruby
  # drop_rule :users do |r|
  #   r.event :insert
  #   r.kind :instead
  #   r.comment "Forbid insertion to the table"
  # end
  # ```
  #
  # To made operation reversible all the necessary parameters must be provided
  # like in the `create_rule` operation:
  #
  # ```ruby
  # drop_rule "users", "_count_insertion" do |r|
  #   r.event :insert
  #   r.command <<~SQL
  #     UPDATE counters SET user_inserts = user_inserts + 1
  #   SQL
  #   r.comment "Count insertion to the table"
  # SQL
  # ```
  #
  # The operation can be called with `if_exists` option.
  #
  # ```ruby
  # drop_rule :users, if_exists: true do |r|
  #   # event and kind here are used to define a name
  #   r.event :insert
  #   r.kind :instead
  # end
  # ```
  #
  # With the `force: :cascade` option the operation would remove
  # all the objects that use the rule.
  #
  # ```ruby
  # drop_rule :users, force: :cascade do |r|
  #   r.event :insert
  #   r.kind :instead
  # end
  # ```
  #
  # In both cases the operation becomes irreversible due to
  # uncertainty of the previous state of the database.
  def drop_rule(table, name = nil, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Rename a rule
  #
  # @param [#to_s] table (nil) The qualified name of the table
  # @param [#to_s] name (nil) The current name of the rule
  # @option options [#to_s] :to (nil) The new name for the rule
  # @yield [c] the block with the constraint's definition
  # @yieldparam Object receiver of methods specifying the constraint
  # @return [void]
  #
  # A rule can be identified by the table and explicit name
  #
  # ```ruby
  # rename_rule :users, "_forbid_insertion", to: "_skip_insertion"
  # ```
  #
  # Alternatively the name can be got from the event and kind.
  #
  # ```ruby
  # rename_rule :users, to: "_skip_insertion" do |r|
  #   r.event :insert
  #   r.kind :instead
  # end
  # ```
  #
  # The name can be reset to auto-generated when
  # the `:to` option is missed or blank:
  #
  # ```ruby
  # rename_rule :users, "_skip_insertion" do |r|
  #   r.event :insert
  #   r.kind :instead
  # end
  # ```
  #
  # The operation is always reversible.
  def rename_rule(table, name = nil, **options, &block); end
end