Packages
electric
1.3.2
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.16
1.4.16-beta-1
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.14
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
retired
1.1.5
retired
1.1.4
retired
1.1.3
retired
1.1.2
1.1.1
1.1.0
1.0.24
1.0.23
1.0.22
1.0.21
1.0.20
1.0.19
1.0.18
1.0.17
1.0.15
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-beta.23
1.0.0-beta.22
1.0.0-beta.20
1.0.0-beta.19
1.0.0-beta.18
1.0.0-beta.17
1.0.0-beta.16
1.0.0-beta.15
1.0.0-beta.14
1.0.0-beta.13
1.0.0-beta.12
1.0.0-beta.11
1.0.0-beta.10
1.0.0-beta.9
1.0.0-beta.8
1.0.0-beta.7
1.0.0-beta.6
1.0.0-beta.5
1.0.0-beta.4
1.0.0-beta.3
1.0.0-beta.2
1.0.0-beta.1
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.5.2
0.4.4
Postgres sync engine. Sync little subsets of your Postgres data into local apps and services.
Current section
Files
Jump to
Current section
Files
lib/electric/shapes/shape/subset.ex
defmodule Electric.Shapes.Shape.Subset do
alias Electric.Postgres.Inspector
alias Electric.Replication.Eval.Env
alias Electric.Replication.Eval.Parser
alias Electric.Shapes.Shape.Validators
defstruct [
:order_by,
:limit,
:offset,
:where
]
@schema_options [
order_by: [type: :string],
limit: [type: :pos_integer],
offset: [type: :non_neg_integer],
where: [type: :string],
params: [type: {:map, :string, :string}, default: %{}]
]
|> NimbleOptions.new!()
def new(shape, fields, opts) do
inspector = Access.fetch!(opts, :inspector)
with {:ok, fields} <- NimbleOptions.validate(Map.new(fields), @schema_options),
{:ok, columns} <- load_column_info(shape, inspector),
:ok <- validate_order_by(fields[:order_by], columns),
refs = Inspector.columns_to_expr(columns),
{:ok, where} <- validate_where_clause(fields[:where], fields[:params], refs) do
{:ok,
%__MODULE__{
order_by: fields[:order_by],
limit: fields[:limit],
offset: fields[:offset],
where: where
}}
else
{:error, %NimbleOptions.ValidationError{message: reason, key: key}} ->
{:error, {key, reason}}
{:error, reason} ->
{:error, reason}
end
end
defp load_column_info(%{root_table_id: table_oid, root_table: table}, inspector) do
case Inspector.load_column_info(table_oid, inspector) do
:table_not_found ->
{:error,
{:order_by,
"Table #{Electric.Utils.inspect_relation(table)} does not exist. " <>
"If the table name contains capitals or special characters you must quote it."}}
{:ok, columns} ->
{:ok, columns}
end
end
defp validate_order_by(nil, _columns), do: :ok
defp validate_order_by(order_by, columns) do
case Parser.validate_order_by(order_by, columns) do
:ok -> :ok
{:error, reason} -> {:error, {:order_by, reason}}
end
end
defp validate_where_clause(nil, _params, _refs), do: {:ok, nil}
defp validate_where_clause(where, params, refs) do
# Use an env with allow_enums: true to allow enum types in WHERE clauses for subsets.
# Enum values are validated by Postgres at query time rather than during parsing.
env = %{Env.new() | allow_enums: true}
with {:ok, where} <- Parser.parse_query(where),
{:ok, subqueries} <- Parser.extract_subqueries(where),
:ok <- assert_no_subqueries(subqueries),
:ok <- Validators.validate_parameters(params),
{:ok, where} <- Parser.validate_where_ast(where, params: params, refs: refs, env: env),
{:ok, where} <- Validators.validate_where_return_type(where) do
{:ok, where}
else
{:error, {part, reason}} -> {:error, {part, reason}}
{:error, reason} -> {:error, {:where, reason}}
end
end
defp assert_no_subqueries([]), do: :ok
defp assert_no_subqueries(_), do: {:error, "Subqueries are not allowed in subsets"}
end