View Source Vtc.Ecto.Postgres.PgFramestamp.Range (vtc v0.17.5)
Defines a custom Range type for dealing with Framestamp ranges.
The new range types are defined as follows:
CREATE TYPE framestamp_range AS RANGE (
subtype = framestamp,
subtype_diff = framestamp_range_private.subtype_diff
canonical = framestamp_range_private.canonical
);
Framestamp ranges can be created in SQL expressions like so:
SELECT framestamp_range(stamp_1, stamp_2, '[)')
Framestamp fastranges can be created in SQL expressions like so:
SELECT framestamp_fastrange(stamp_1, stamp_2)
Indexing
framestamp_range
is currently VERY slow when using a GiST index, consider using a framestamp_fastrange instead.
canonicalization
Canonicalization
Postgres framestamp_range
values are ALWAYS coerced to exclusive out ranges. That
means that even if a Framestamp.Range has :out_type
set to
:inclusive
when it is sent to the database, it will come back from the database
with :out_type
set to :exclusive
, and the :out
field will be adjusted
accordingly.
Further, when a Range operation, like a union, would result in an in-and-out point with different framerates, the higher rate will always be selected.
This unlike the application behavior of Vtc.Framestamp.Range
, which always inherits
the rate of the value that appears on the left side. This behavior may be updated to
match Vtc's application behavior in the future.
framestamp-fast-range
Framestamp Fast Range
In addition to framestamp_range
, a framestamp_fastrange
type is defined as well:
CREATE TYPE framestamp_fastrange AS RANGE (
subtype = double precision,
subtype_diff = float8mi
);
Fast ranges are meant to support GiST indexing, as in most cases, framestamp_range
will be VERY slow to index.
Frame-accurate
Vtc's position is that rational values are necessary for frame-accurate timecode manipulation, so why does it put forth a float-based range type?
The main risk of using floats is unpredictable floating-point errors stacking up during arithmetic operations so that when you go to compare two values that SHOULD be equal, they aren't.
However, if you do all calculations with rational values -- up to the point where you need to compare them -- it is safe to cast to floats for the comparison operation, as equal rational values will always cast to the same float value.
FastRanges should ONLY be used for comparisons, and should NEVER be adjusted once built.
For more on FastRanges, see PgFramestamp.FastRange, which can be used to help serialize and deserialize the type.
field-migrations
Field migrations
You can create framestamp_range
fields during a migration like so:
alias Vtc.Framerate
create table("framestamp_ranges") do
add(:a, Framestamp.Range.type())
add(:b, Framestamp.Range.type())
end
Framestamp.Range re-exports the Ecto.Type
implementation
of this module, and can be used any place this module would be used.
schema-fields
Schema fields
Then in your schema module:
defmodule MyApp.FramestampRanges do
@moduledoc false
use Ecto.Schema
alias Vtc.Framestamp
@type t() :: %__MODULE__{
a: Framestamp.Range.t(),
b: Framestamp.Range.t()
}
schema "rationals_01" do
field(:a, Framestamp.Range)
field(:b, Framestamp.Range)
end
changesets
Changesets
With the above setup, changesets should just work:
def changeset(schema, attrs) do
schema
|> Changeset.cast(attrs, [:a, :b])
|> Changeset.validate_required([:a, :b])
end
Framestamp.Range values can be cast from the following values in changesets:
- Framerate structs.
fragments
Fragments
Framestamp.Range values must be explicitly cast using type/2:
stamp_in = Framestamp.with_frames!("01:00:00:00", Rates.f23_98())
stamp_out = Framestamp.with_frames!("02:00:00:00", Rates.f23_98())
stamp_range = Framestamp.Range.new!(stamp_in, stamp_out)
query = Query.from(
f in fragment("SELECT ? as r", type(^stamp_range, Framerate.Range)), select: f.r
)
Link to this section Summary
Types
Type of the raw composite value that will be sent to / received from the database.
Ecto Migrations
The database type for PgFramerate.
Functions
Callback implementation for Ecto.Type.embed_as/1
.
Callback implementation for Ecto.Type.equal?/2
.
Link to this section Types
@type db_record() :: %Postgrex.Range{ lower: Vtc.Ecto.Postgres.PgFramestamp.db_record(), lower_inclusive: boolean(), upper: Vtc.Ecto.Postgres.PgFramestamp.db_record(), upper_inclusive: boolean() }
Type of the raw composite value that will be sent to / received from the database.
Link to this section Ecto Migrations
@spec type() :: atom()
The database type for PgFramerate.
Can be used in migrations as the fields type.
Link to this section Functions
Callback implementation for Ecto.Type.embed_as/1
.
Callback implementation for Ecto.Type.equal?/2
.