Current section

Files

Jump to
ecto_foundationdb lib ecto adapters foundationdb ecto_adapter_schema.ex
Raw

lib/ecto/adapters/foundationdb/ecto_adapter_schema.ex

defmodule Ecto.Adapters.FoundationDB.EctoAdapterSchema do
@moduledoc false
@behaviour Ecto.Adapter.Schema
alias EctoFoundationDB.Exception.IncorrectTenancy
alias EctoFoundationDB.Future
alias EctoFoundationDB.Layer.Fields
alias EctoFoundationDB.Layer.IndexInventory
alias EctoFoundationDB.Layer.Tx
alias EctoFoundationDB.Schema
alias EctoFoundationDB.Tenant
@impl Ecto.Adapter.Schema
def autogenerate(:binary_id), do: Ecto.UUID.generate()
def autogenerate(type),
do: raise("FoundationDB Adapter does not support autogenerating #{type}")
@impl Ecto.Adapter.Schema
def insert_all(
adapter_meta,
schema_meta,
_header,
entries,
_on_conflict,
_returning,
_placeholders,
options
) do
%{source: source, schema: schema, prefix: tenant, context: context} =
assert_tenancy!(schema_meta)
entries =
Enum.map(entries, fn data_object ->
pk_field = Fields.get_pk_field!(schema)
pk = data_object[pk_field]
future = Future.before_transactional(schema)
{{pk_field, pk}, future, data_object}
end)
num_ins =
IndexInventory.transactional(tenant, adapter_meta, source, fn tx, idxs, partial_idxs ->
Tx.insert_all(
tenant,
tx,
{schema, source, context},
entries,
{idxs, partial_idxs},
options
)
end)
{num_ins, nil}
end
@impl Ecto.Adapter.Schema
def insert(adapter_meta, schema_meta, data_object, on_conflict, returning, options) do
{_count, nil} =
insert_all(
adapter_meta,
schema_meta,
nil,
[data_object],
on_conflict,
returning,
[],
options
)
{:ok, []}
end
@impl Ecto.Adapter.Schema
def update(
adapter_meta,
schema_meta,
update_data,
filters,
_returning,
options
) do
%{source: source, schema: schema, prefix: tenant, context: context} =
assert_tenancy!(schema_meta)
pk_field = Fields.get_pk_field!(schema)
pk = filters[pk_field]
future = Future.before_transactional(schema)
res =
IndexInventory.transactional(tenant, adapter_meta, source, fn tx, idxs, partial_idxs ->
Tx.update_pks(
tenant,
tx,
{schema, source, context},
pk_field,
[{pk, future}],
update_data,
{idxs, partial_idxs},
options
)
end)
case res do
1 ->
{:ok, []}
0 ->
{:error, :stale}
end
end
@impl Ecto.Adapter.Schema
def delete(
adapter_meta,
schema_meta,
filters,
_returning,
_options
) do
%{source: source, schema: schema, prefix: tenant, context: context} =
assert_tenancy!(schema_meta)
pk_field = Fields.get_pk_field!(schema)
pk = filters[pk_field]
future = Future.before_transactional(schema)
res =
IndexInventory.transactional(tenant, adapter_meta, source, fn tx, idxs, partial_idxs ->
Tx.delete_pks(tenant, tx, {schema, source, context}, [{pk, future}], {idxs, partial_idxs})
end)
case res do
1 ->
{:ok, []}
0 ->
{:error, :stale}
end
end
def watch(_module, _repo, struct, {adapter_meta, options}) do
# This is not an Ecto callback, so we have to construct our own schema_meta
schema_meta = %{
schema: struct.__struct__,
source: Ecto.get_meta(struct, :source),
prefix: Keyword.get(options, :prefix, Ecto.get_meta(struct, :prefix))
}
%{schema: schema, source: source, context: context, prefix: tenant} =
assert_tenancy!(schema_meta)
pk_field = Fields.get_pk_field!(schema)
pk = Map.get(struct, pk_field)
IndexInventory.transactional(tenant, adapter_meta, source, fn tx, _idxs, _partial_idxs ->
future_ref = Tx.watch(tenant, tx, {schema, source, context}, {pk_field, pk}, options)
Future.new_watch(schema, future_ref, fn _ -> {schema, pk, options} end)
end)
end
defp assert_tenancy!(schema_meta = %{source: source, schema: schema, prefix: tenant}) do
schema_meta = Map.put(schema_meta, :context, Schema.get_context!(source, schema))
case Tx.safe?(tenant) do
{false, :missing_tenant} ->
raise IncorrectTenancy, """
FoundationDB Adapter is expecting the struct for schema \
#{inspect(schema)} to include a tenant in the prefix metadata, \
but a nil prefix was provided.
Call `Ecto.Adapters.FoundationDB.usetenant(struct, tenant)` before inserting.
Or use the option `prefix: tenant` on the call to your Repo.
"""
{true, tenant = %Tenant{}} ->
Map.put(schema_meta, :prefix, tenant)
end
end
end