View Source Electric.Replication.Eval.Env (electric v0.9.5)

Evaluation environment for parsing PostgreSQL expressions.

We have a need to parse PG expressions, and then be able to execute them in Electric without reaching for Postgres on every operation. This is achieved by parsing Postgres expressions, however Postgres has a lot of features we're unlikely to support, and, moreover, very dynamic: new features, new data types - everything can be redefined, including so-called "preferred" data types, new type categories, implicit casts, etc. Redefining all this for normal types is quite unlikely in the wild, but may be later common for custom types.

Parsing PG expressions, we need to know what we support. It's then reasonable to have a good set of defaults we know how to deal with, but leave an escape hatch so that until we add support for pulling all this data from PG, we have a working system. Even afterwards, a set of PG functions using, say PLSQL is likely to use default PG functions, which we still need to know how to execute.

This module defines a struct that contains information, relevant to parsing PG statements into something Electric can understand, while respecting PG types and function/operator overload selections.

It's also worth noting that our defaults, especially implicit casts specifically omit any "system" types (e.g. regclass or pg_ndistinct) because we're essentially never going to have enough context in Electric to be able to correctly utilize those types.

Summary

Functions

Check if a list of inputs can be implicitly coerced to a list of targets.

Create a new empty environment, without any default functions or explicit casts

Get type category for the given type (possibly non-basic).

Check if one type is implicitly castable to another type in this environment.

Check if type is preferred within the type category.

Create a new environment with known defaults, merging in provided keys

Parse an unknown value constant as a known type in the current environment.

Types

@type basic_type() :: atom()
@type basic_type_registry() :: %{required(basic_type()) => type_info()}
@type cast_function() :: {module :: module(), function :: atom()}
@type cast_key() :: {from :: basic_type(), to :: basic_type()}
@type cast_registry() :: %{required(cast_key()) => cast_function()}
@type env_property() ::
  {:funcs, funcs()}
  | {:operators, funcs()}
  | {:explicit_casts, cast_registry()}
  | {:implicit_casts, implicit_cast_registry()}
  | {:known_basic_types, basic_type_registry()}
@type flat_pg_type() :: basic_type() | {:composite, map()} | :record | {:enum, term()}
@type func() :: %{
  optional(:strict?) => boolean(),
  optional(:immutable?) => boolean(),
  args: [pg_type()],
  returns: pg_type(),
  implementation: {module(), atom()} | (... -> any()),
  name: String.t()
}
@type func_id() :: {name :: String.t(), arity :: non_neg_integer()}
@type funcs() :: %{required(func_id()) => [func(), ...]}
Link to this type

implicit_cast_registry()

View Source
@type implicit_cast_registry() :: %{required(cast_key()) => cast_function() | :as_is}
@type pg_type() ::
  flat_pg_type()
  | {:array, flat_pg_type()}
  | {:range, flat_pg_type()}
  | {:multirange, flat_pg_type()}
@type t() :: %Electric.Replication.Eval.Env{
  explicit_casts: cast_registry(),
  funcs: funcs(),
  implicit_casts: implicit_cast_registry(),
  known_basic_types: basic_type_registry(),
  operators: funcs()
}
@type type_info() :: %{category: atom(), preferred?: boolean()}

Functions

Link to this function

can_implicitly_coerce_types?(env, inputs, targets)

View Source
@spec can_implicitly_coerce_types?(t(), [pg_type()], [pg_type()]) :: boolean()

Check if a list of inputs can be implicitly coerced to a list of targets.

Note that other functions may not exactly support all of types

Create a new empty environment, without any default functions or explicit casts

Link to this function

get_type_category(arg1, name)

View Source
@spec get_type_category(t(), pg_type()) :: atom()

Get type category for the given type (possibly non-basic).

Link to this function

get_unified_coercion_targets(env, inputs, targets, return_type \\ nil)

View Source
Link to this function

implicitly_castable?(arg1, same, same)

View Source
@spec implicitly_castable?(t(), basic_type(), basic_type()) :: boolean()

Check if one type is implicitly castable to another type in this environment.

Link to this function

is_preferred?(env, type)

View Source

Check if type is preferred within the type category.

@spec new([env_property()]) :: t()

Create a new environment with known defaults, merging in provided keys

Link to this function

parse_const(env, value, x)

View Source
@spec parse_const(t(), String.t() | nil, pg_type()) :: {:ok, term()} | :error

Parse an unknown value constant as a known type in the current environment.