module PGTrunk::Operations::Functions

@private Namespace for operations with functions

@!parse

class ActiveRecord::Migration
  # Modify a function
  #
  # @param [#to_s] name (nil) The qualified name of the function
  # @option options [Boolean] :if_exists (false) Suppress the error when the function is absent
  # @yield [f] the block with the function's definition
  # @yieldparam Object receiver of methods specifying the function
  # @return [void]
  #
  # The operation changes the function without dropping it
  # (which can be necessary 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_function "math.mult(int, int)" do |f|
  #   f.volatility :immutable, from: :stable
  #   f.parallel :safe, from: :restricted
  #   f.security :invoker
  #   f.leakproof true
  #   f.strict true
  #   f.cost 5.0
  #   # f.rows 1 (supported for functions returning sets of rows)
  # SQL
  # ```
  #
  # The example above is not invertible because of uncertainty
  # about the previous volatility, parallelism, and cost.
  # To define them, use a from options (available in a block syntax only):
  #
  # ```ruby
  # change_function "math.mult(a int, b int)" do |f|
  #   f.body <<~SQL, from: <<~SQL
  #     SELECT a * b;
  #   SQL
  #     SELECT min(a * b, 1);
  #   SQL
  #   f.volatility :immutable, from: :volatile
  #   f.parallel :safe, from: :unsafe
  #   f.leakproof true
  #   f.strict true
  #   f.cost 5.0, from: 100.0
  # SQL
  # ```
  #
  # Like in the other operations, the function 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_function(name, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Create a function
  #
  # @param [#to_s] name (nil)
  #   The qualified name of the function with arguments and returned value type
  # @option options [Boolean] :replace_existing (false) If the function 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 function
  # @option options [Symbol] :volatility (:volatile) The volatility of the function.
  #   Supported values: :volatile (default), :stable, :immutable
  # @option options [Symbol] :parallel (:unsafe) The safety of parallel execution.
  #   Supported values: :unsafe (default), :restricted, :safe
  # @option options [Symbol] :security (:invoker) Define the role under which the function is invoked
  #   Supported values: :invoker (default), :definer
  # @option options [Boolean] :leakproof (false) If the function is leakproof
  # @option options [Boolean] :strict (false) If the function is strict
  # @option options [Float] :cost (nil) The cost estimation for the function
  # @option options [Integer] :rows (nil) The number of rows returned by a function
  # @option options [#to_s] :comment The description of the function
  # @yield [f] the block with the function's definition
  # @yieldparam Object receiver of methods specifying the function
  # @return [void]
  #
  # The function can be created either using inline syntax
  #
  # ```ruby
  # create_function "math.mult(a int, b int) int",
  #                 language: :sql,
  #                 body: "SELECT a * b",
  #                 volatility: :immutable,
  #                 leakproof: true,
  #                 comment: "Multiplies 2 integers"
  # ```
  #
  # or using a block:
  #
  # ```ruby
  # create_function "math.mult(a int, b int) int" do |f|
  #   f.language "sql" # (default)
  #   f.body <<~SQL
  #     SELECT a * b;
  #   SQL
  #   f.volatility :immutable # :stable, :volatile (default)
  #   f.parallel :safe        # :restricted, :unsafe (default)
  #   f.security :invoker     # (default), also :definer
  #   f.leakproof true
  #   f.strict true
  #   f.cost 5.0
  #   # f.rows 1 (supported for functions returning sets of rows)
  #   f.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_function "math.mult(a int, b int) int",
  #                 body: "SELECT a * b",
  #                 replace_existing: true
  # ```
  #
  # We presume a function without arguments should have
  # no arguments and return `void` like
  #
  # ```ruby
  # # the same as "do_something() void"
  # create_function "do_something" do |f|
  #   # ...
  # end
  # ```
  def create_function(name, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Drop a function
  #
  # @param [#to_s] name (nil)
  #   The qualified name of the function with arguments and returned value type
  # @option options [Boolean] :if_exists (false) Suppress the error when the function is absent
  # @option options [Symbol] :force (:restrict) How to process dependent objects
  #   Supported values: :restrict (default), :cascade
  # @option options [#to_s] :language ("sql") The language (like "sql" or "plpgsql")
  # @option options [#to_s] :body (nil) The body of the function
  # @option options [Symbol] :volatility (:volatile) The volatility of the function.
  #   Supported values: :volatile (default), :stable, :immutable
  # @option options [Symbol] :parallel (:unsafe) The safety of parallel execution.
  #   Supported values: :unsafe (default), :restricted, :safe
  # @option options [Symbol] :security (:invoker) Define the role under which the function is invoked
  #   Supported values: :invoker (default), :definer
  # @option options [Boolean] :leakproof (false) If the function is leakproof
  # @option options [Boolean] :strict (false) If the function is strict
  # @option options [Float] :cost (nil) The cost estimation for the function
  # @option options [Integer] :rows (nil) The number of rows returned by a function
  # @option options [#to_s] :comment The description of the function
  # @yield [f] the block with the function's definition
  # @yieldparam Object receiver of methods specifying the function
  # @return [void]
  #
  # A function can be dropped by a plain name:
  #
  # ```ruby
  # drop_function "multiply"
  # ```
  #
  # If several overloaded functions have the name,
  # then you must specify the signature having
  # types of attributes at least:
  #
  # ```ruby
  # drop_function "multiply(int, 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_function "math.mult(a int, b int) int" do |f|
  #   f.language "sql" # (default)
  #   f.body <<~SQL
  #     SELECT a * b;
  #   SQL
  #   f.volatility :immutable # :stable, :volatile (default)
  #   f.parallel :safe        # :restricted, :unsafe (default)
  #   f.security :invoker     # (default), also :definer
  #   f.leakproof true
  #   f.strict true
  #   f.cost 5.0
  #   # f.rows 1 (supported for functions returning sets of rows)
  #   f.comment "Multiplies 2 integers"
  # end
  # ```
  #
  # The operation can be called with `if_exists` option. In this case
  # it would do nothing when no function existed.
  #
  # ```ruby
  # drop_function "math.multiply(integer, integer)", if_exists: true
  # ```
  #
  # Another operation-specific option `force: :cascade` enables
  # to drop silently any object depending on the function.
  #
  # ```ruby
  # drop_function "math.multiply(integer, integer)", force: :cascade
  # ```
  #
  # Both options make the operation irreversible because of
  # uncertainty about the previous state of the database.
  def drop_function(name, **options, &block); end
end

@!parse

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