module PGTrunk::Operations::Procedures

@private Namespace for operations with procedures

@!parse

class ActiveRecord::Migration
  # Modify a procedure
  #
  # @param [#to_s] name (nil) The qualified name of the procedure
  # @option options [Boolean] :if_exists (false) Suppress the error when the procedure is absent
  # @yield [p] the block with the procedure's definition
  # @yieldparam Object receiver of methods specifying the procedure
  # @return [void]
  #
  # The operation changes the procedure without dropping it
  # (which is useful when there are other objects
  # using the function and you don't want to change them all).
  #
  # You can change any property except for the name
  # (use `rename_function` instead) and `language`.
  #
  # ```ruby
  # change_procedure "metadata.set_foo(a int)" do |p|
  #   p.body <<~SQL
  #     SET foo = a
  #   SQL
  #   p.security :invoker
  #   p.comment "Multiplies 2 integers"
  # SQL
  # ```
  #
  # The example above is not invertible because of uncertainty
  # about the previous state of body and comment.
  # To define them, use a from options (available in a block syntax only):
  #
  # ```ruby
  # change_procedure "metadata.set_foo(a int)" do |p|
  #   p.body <<~SQL, from: <<~SQL
  #     SET foo = a
  #   SQL
  #     SET foo = -a
  #   SQL
  #   p.comment <<~MSG, from: <<~MSG
  #     Multiplies 2 integers
  #   MSG
  #     Multiplies ints
  #   MSG
  #   p.security :invoker
  # SQL
  # ```
  #
  # Like in the other operations, the procedure can be
  # identified by a qualified name (with types of arguments).
  # If it has no overloaded implementations,
  # the plain name is supported as well.
  def change_procedure(name, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Create a procedure
  #
  # @param [#to_s] name (nil)
  #   The qualified name of the procedure with arguments and returned value type
  # @option options [Boolean] :replace_existing (false) If the procedure should overwrite an existing one
  # @option options [#to_s] :language ("sql") The language (like "sql" or "plpgsql")
  # @option options [#to_s] :body (nil) The body of the procedure
  # @option options [Symbol] :security (:invoker) Define the role under which the procedure is invoked
  #   Supported values: :invoker (default), :definer
  # @option options [#to_s] :comment The description of the procedure
  # @yield [p] the block with the procedure's definition
  # @yieldparam Object receiver of methods specifying the procedure
  # @return [void]
  #
  # The syntax of the operation is the same as for `create_function`,
  # but with only `security` option available. Notice, that
  # procedures cannot return values so you're expected not to
  # define a returned value as well.
  #
  # The procedure can be created either using inline syntax
  #
  # ```ruby
  # create_procedure "metadata.set_foo(a int)",
  #                  language: :sql,
  #                  body: "SET foo = a",
  #                  comment: "Sets foo value"
  # ```
  #
  # or using a block:
  #
  # ```ruby
  # create_procedure "metadata.set_foo(a int)" do |p|
  #   p.language "sql" # (default)
  #   p.body <<~SQL
  #     SET foo = a
  #   SQL
  #   p.security :invoker # (default), also :definer
  #   p.comment "Multiplies 2 integers"
  # SQL
  # ```
  #
  # With a `replace_existing: true` option,
  # it 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 its previous definition.
  #
  # ```ruby
  # create_procedure "set_foo(a int)",
  #                  body: "SET foo = a",
  #                  replace_existing: true
  # ```
  #
  # A procedure without arguments is supported as well
  #
  # ```ruby
  # # the same as "do_something()"
  # create_procedure "do_something" do |p|
  #   # ...
  # end
  # ```
  def create_procedure(name, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Drop a procedure
  #
  # @param [#to_s] name (nil)
  #   The qualified name of the procedure with arguments and returned value type
  # @option options [Boolean] :if_exists (false) Suppress the error when the procedure is absent
  # @option options [#to_s] :language ("sql") The language (like "sql" or "plpgsql")
  # @option options [#to_s] :body (nil) The body of the procedure
  # @option options [Symbol] :security (:invoker) Define the role under which the procedure is invoked
  #   Supported values: :invoker (default), :definer
  # @option options [#to_s] :comment The description of the procedure
  # @yield [p] the block with the procedure's definition
  # @yieldparam Object receiver of methods specifying the procedure
  # @return [void]
  #
  # A procedure can be dropped by a plain name:
  #
  # ```ruby
  # drop_procedure "set_foo"
  # ```
  #
  # If several overloaded procedures have the name,
  # then you must specify the signature having
  # types of attributes at least:
  #
  # ```ruby
  # drop_procedure "set_foo(int)"
  # ```
  #
  # In both cases above the operation is irreversible. To make it
  # inverted you have to provide a full signature along with
  # the body definition. The other options are supported as well:
  #
  # ```ruby
  # drop_procedure "metadata.set_foo(a int)" do |p|
  #   p.language "sql" # (default)
  #   p.body <<~SQL
  #     SET foo = a
  #   SQL
  #   p.security :invoker # (default), also :definer
  #   p.comment "Multiplies 2 integers"
  # SQL
  # ```
  #
  # The operation can be called with `if_exists` option. In this case
  # it would do nothing when no procedure existed.
  #
  # ```ruby
  # drop_procedure "metadata.set_foo(a int)", if_exists: true
  # ```
  #
  # Notice, that this option make the operation irreversible because of
  # uncertainty about the previous state of the database.
  def drop_procedure(name, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Change the name and/or schema of a procedure
  #
  # @param [#to_s] :name (nil) The qualified name of the procedure
  # @option options [#to_s] :to (nil) The new qualified name for the procedure
  # @return [void]
  #
  # A procedure can be renamed by changing both the name
  # and the schema (namespace) it belongs to.
  #
  # If there are no overloaded procedures, then you can use a plain name:
  #
  # ```ruby
  # rename_procedure "math.set_foo", to: "public.foo_setup"
  # ```
  #
  # otherwise the types of attributes must be explicitly specified.
  #
  # ```ruby
  # rename_procedure "math.set_foo(int)", to: "public.foo_setup"
  # ```
  #
  # Any specification of attributes in `to:` option
  # is ignored because they cannot be changed anyway.
  #
  # The operation is always reversible.
  def rename_procedure(name, to:); end
end