Current section
Files
Jump to
Current section
Files
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.Exception.Unsupported
alias EctoFoundationDB.Layer.Fields
alias EctoFoundationDB.Layer.IndexInventory
alias EctoFoundationDB.Layer.Tx
alias EctoFoundationDB.Schema
@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 = %{opts: adapter_opts},
schema_meta,
_header,
entries,
_on_conflict,
_returning,
_placeholders,
_options
) do
%{source: source, schema: schema, prefix: tenant, context: context} =
assert_tenancy!(adapter_opts, schema_meta)
entries =
Enum.map(entries, fn data_object ->
pk_field = Fields.get_pk_field!(schema)
pk = data_object[pk_field]
{{pk_field, pk}, data_object}
end)
num_ins =
IndexInventory.transactional(tenant, adapter_meta, source, fn tx, idxs, partial_idxs ->
Tx.insert_all(tx, {schema, source, context}, entries, idxs, partial_idxs)
end)
{num_ins, nil}
end
@impl Ecto.Adapter.Schema
def insert(adapter_meta, schema_meta, data_object, on_conflict, returning, options) do
{1, nil} =
insert_all(
adapter_meta,
schema_meta,
nil,
[data_object],
on_conflict,
returning,
[],
options
)
{:ok, []}
end
@impl Ecto.Adapter.Schema
def update(
adapter_meta = %{opts: adapter_opts},
schema_meta,
update_data,
filters,
_returning,
_options
) do
%{source: source, schema: schema, prefix: tenant, context: context} =
assert_tenancy!(adapter_opts, schema_meta)
pk_field = Fields.get_pk_field!(schema)
pk = filters[pk_field]
res =
IndexInventory.transactional(tenant, adapter_meta, source, fn tx, idxs, partial_idxs ->
Tx.update_pks(
tx,
{schema, source, context},
pk_field,
[pk],
update_data,
idxs,
partial_idxs
)
end)
case res do
1 ->
{:ok, []}
0 ->
{:error, :stale}
end
end
@impl Ecto.Adapter.Schema
def delete(
adapter_meta = %{opts: adapter_opts},
schema_meta,
filters,
_returning,
_options
) do
%{source: source, schema: schema, prefix: tenant, context: context} =
assert_tenancy!(adapter_opts, schema_meta)
pk_field = Fields.get_pk_field!(schema)
pk = filters[pk_field]
res =
IndexInventory.transactional(tenant, adapter_meta, source, fn tx, idxs, partial_idxs ->
Tx.delete_pks(tx, {schema, source, context}, [pk], idxs, partial_idxs)
end)
case res do
1 ->
{:ok, []}
0 ->
{:error, :stale}
end
end
defp assert_tenancy!(
_adapter_opts,
schema_meta = %{source: source, schema: schema}
) do
schema_meta =
%{schema: schema, prefix: tenant, context: context} =
Map.put(schema_meta, :context, Schema.get_context!(source, schema))
case Tx.safe?(tenant, Schema.get_option(context, :usetenant)) do
{false, :unused_tenant} ->
raise IncorrectTenancy, """
FoundatioDB Adapter is expecting the struct for schema \
#{inspect(schema)} to specify no tentant in the prefix metadata, \
but a non-nil prefix was provided.
Add `usetenant: true` to your schema's `@schema_context`.
Also be sure to remove the option `prefix: tenant` on the call to your Repo.
Alternatively, remove the call to \
`Ecto.Adapters.FoundationDB.usetenant(struct, tenant)` before inserting.
"""
{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(struxt, tenant)` before inserting.
Or use the option `prefix: tenant` on the call to your Repo.
Alternatively, remove `usetenant: true` from your schema's \
`@schema_context` if you do not want to use a tenant for this schema.
"""
{false, :tenant_only} ->
raise Unsupported, "Non-tenant transactions are not yet implemented."
true ->
schema_meta
end
end
end