Packages
electric
1.4.14
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/replication/postgres_interop/casting.ex
defmodule Electric.Replication.PostgresInterop.Casting do
@int2_range -32768..32767
@int4_range -2_147_483_648..2_147_483_647
@int8_range -9_223_372_036_854_775_808..9_223_372_036_854_775_807
defguard is_pg_int2(x) when is_integer(x) and x in @int2_range
defguard is_pg_int4(x) when is_integer(x) and x in @int4_range
defguard is_pg_int8(x) when is_integer(x) and x in @int8_range
def values_distinct?(nil, nil, _), do: false
def values_distinct?(v1, v2, _) when is_nil(v1) or is_nil(v2), do: true
def values_distinct?(_, _, plain_comparison), do: plain_comparison
def values_not_distinct?(v1, v2, plain_comparison),
do: not values_distinct?(v1, v2, plain_comparison)
def parse_int2(input) do
case String.to_integer(input) do
x when is_pg_int2(x) -> x
end
end
def parse_int4(input) do
case String.to_integer(input) do
x when is_pg_int4(x) -> x
end
end
def parse_int8(input) do
case String.to_integer(input) do
x when is_pg_int8(x) -> x
end
end
def parse_float8(input) do
case Float.parse(input) do
{value, ""} -> value
end
end
# Yes, Postgres really allows all of these in `SELECT 'tru'::boolean`
def parse_bool(x) when x in ~w|t tr tru true|, do: true
def parse_bool(x) when x in ~w|f fa fal fals false|, do: false
def parse_uuid(maybe_uuid) do
{:ok, value} = Ecto.UUID.dump(maybe_uuid)
Ecto.UUID.load!(value)
end
def parse_date("epoch"), do: Date.from_iso8601!("1970-01-01")
def parse_date(maybe_date) do
case Date.from_iso8601!(String.trim(maybe_date)) do
# PG doesn't support years <= 0, so neither do we
%Date{year: year} = date when year > 0 -> date
end
end
def parse_time(maybe_time) do
trimmed = maybe_time |> String.trim() |> String.upcase()
case Time.from_iso8601(trimmed) do
{:ok, time} ->
time
{:error, :invalid_format} ->
parse_am_pm_time(trimmed)
end
end
defp parse_am_pm_time(maybe_time) do
case String.split_at(maybe_time, -2) do
{time, x} when x in ["AM", "PM"] ->
case {Time.from_iso8601!(String.trim(time)), x} do
{%Time{hour: hour} = time, "AM"} when hour <= 12 -> time
{%Time{hour: hour} = time, "PM"} when hour <= 12 -> Time.add(time, 12, :hour)
end
end
end
def parse_timestamp("epoch"), do: DateTime.from_unix!(0) |> DateTime.to_naive()
def parse_timestamp(maybe_timestamp) do
NaiveDateTime.from_iso8601!(maybe_timestamp)
end
def parse_timestamptz("epoch"), do: DateTime.from_unix!(0) |> DateTime.to_naive()
def parse_timestamptz(maybe_timestamp) do
{:ok, datetime, _} = DateTime.from_iso8601(maybe_timestamp)
datetime
end
@doc """
LIKE function from SQL. Case sensitive by default.
## Examples
iex> like?("hello", "hell_")
true
iex> like?("helloo", "hell_")
false
iex> like?("helloo", "%o_")
true
iex> like?("HELLO", "hello")
false
iex> like?("HELLO", "hello", true)
true
"""
def like?(text, pattern, ignore_case? \\ false) do
pattern
|> String.split(~r/(?<!\\)[_%]/, include_captures: true, trim: true)
|> Enum.map_join(fn
"%" -> ".*"
"_" -> "."
text -> Regex.escape(text)
end)
|> then(&("^" <> &1 <> "$"))
|> Regex.compile!(if ignore_case?, do: [:caseless], else: [])
|> Regex.match?(text)
end
def ilike?(text, pattern), do: like?(text, pattern, true)
@doc """
The Postgres OR operator, which has some specific behaviour when
comparing NULLs with booleans.
## Examples
iex> pg_or(true, false)
true
iex> pg_or(false, false)
false
iex> pg_or(nil, true)
true
iex> pg_or(nil, false)
nil
iex> pg_or(nil, nil)
nil
"""
@spec pg_or(boolean() | nil, boolean() | nil) :: boolean() | nil
def pg_or(a, b)
def pg_or(nil, true), do: true
def pg_or(nil, _), do: nil
def pg_or(true, nil), do: true
def pg_or(_, nil), do: nil
def pg_or(a, b), do: Kernel.or(a, b)
@doc """
The Postgres AND operator, which has some specific behaviour when
comparing NULLs with booleans.
## Examples
iex> pg_and(true, true)
true
iex> pg_and(true, false)
false
iex> pg_and(false, false)
false
iex> pg_and(nil, true)
nil
iex> pg_and(nil, false)
false
iex> pg_and(nil, nil)
nil
"""
@spec pg_and(boolean() | nil, boolean() | nil) :: boolean() | nil
def pg_and(a, b)
def pg_and(nil, false), do: false
def pg_and(nil, _), do: nil
def pg_and(false, nil), do: false
def pg_and(_, nil), do: nil
def pg_and(a, b), do: Kernel.and(a, b)
end