Skip to content

Commit

Permalink
Add some mnesia functions without testing, wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
shahryarjb committed Mar 3, 2024
1 parent 0908155 commit 2894719
Show file tree
Hide file tree
Showing 10 changed files with 494 additions and 2 deletions.
2 changes: 1 addition & 1 deletion guidance/guarded-struct.livemd
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Adding new Copyright (c) [2023] [Shahryar Tavakkoli at [Mishka Group](https://gi
* [Define a nested and complex struct](#define-a-nested-and-complex-struct)
* [Error and data output sample](#data-and-error-output-sample)
* [Set config to show error inside defexception](#set-config-to-show-error-inside-defexception)
* [Error defexception modules](#authorized_fields-option-to-limit-user-input)
* [Error defexception modules](#set-config-to-show-error-inside-defexception)
* [authorized_fields option to limit user input](#authorized_fields-option-to-limit-user-input)
* [Call external struct/structs module](#call-external-struct/structs-module)
* [List of structs](#list-of-structs)
Expand Down
15 changes: 15 additions & 0 deletions lib/helper/extra.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Helper.Extra do
@alphabet Enum.concat([?0..?9, ?A..?Z, ?a..?z])

# Do not use for security and sensitive cases
@spec randstring(integer) :: binary
def randstring(count) do
:rand.seed(:exsplus, :os.timestamp())
Expand All @@ -14,4 +15,18 @@ defmodule Helper.Extra do
defp random_char_from_alphabet() do
Enum.random(@alphabet)
end

def app_started?(name) do
Application.started_applications()
|> Enum.map(fn {app, _, _} -> app end)
|> Enum.member?(name)
end

def elixir_to_erlang_guard(:or), do: :orelse
def elixir_to_erlang_guard(:and), do: :andalso
def elixir_to_erlang_guard(:<=), do: :"=<"
def elixir_to_erlang_guard(:!=), do: :"/="
def elixir_to_erlang_guard(:===), do: :"=:="
def elixir_to_erlang_guard(:!==), do: :"=/="
def elixir_to_erlang_guard(term), do: term
end
13 changes: 13 additions & 0 deletions lib/modules/mnesia_assistant/error.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
defmodule MnesiaAssistant.Error do
alias :mnesia, as: Mnesia
# types link https://www.erlang.org/doc/man/mnesia#data-types

def error_description(error), do: Mnesia.error_description(error)

def error({:atomic, :ok}), do: {:ok, :atomic}

def error(error) do
{err_msg, _} = Mnesia.error_description(error)
{:error, error, err_msg}
end
end
33 changes: 33 additions & 0 deletions lib/modules/mnesia_assistant/mnesia_assistant.ex
Original file line number Diff line number Diff line change
@@ -1,2 +1,35 @@
defmodule MnesiaAssistant do
alias MnesiaAssistant.{Information, Schema}

def initial() do
# TODO: add __using__ to create and start Mnesia
# application ensure_all_started
# ensure dir exists
# Store init schema
# start mnesia server
# Store init_tables
# Store ensure tables loaded
end

################################################################
######################### Public Apis ##########################
################################################################

################# Global functions Public Apis #################
def start(), do: Application.start(:mnesia)

def set_dir(dir), do: Application.put_env(:mnesia, :dir, dir)

def stop(), do: Application.stop(:mnesia)

def started?(), do: Helper.Extra.app_started?(:mnesia)

############### Information functions Public Apis ###############
defdelegate info(), to: Information

defdelegate info(type), to: Information

defdelegate set_debug_level(level), to: Information

defdelegate schema(), to: Schema
end
9 changes: 9 additions & 0 deletions lib/modules/mnesia_assistant/operation/backup_and_restore.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule MnesiaAssistant.BackupAndRestore do
alias :mnesia, as: Mnesia

def load_textfile(path), do: Mnesia.load_textfile(path)

def dump_to_textfile(path), do: Mnesia.dump_to_textfile(path)

def set_master_nodes(nodes) when is_list(nodes), do: Mnesia.set_master_nodes(nodes)
end
53 changes: 53 additions & 0 deletions lib/modules/mnesia_assistant/operation/information.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule MnesiaAssistant.Information do
alias :mnesia, as: Mnesia
@debug_level_types [:none, :verbose, :debug, :trace, false, true]

@system_types [
:all,
:access_module,
:auto_repair,
:backup_module,
:checkpoints,
:event_module,
:db_nodes,
:debug,
:directory,
:dump_log_load_regulation,
:dump_log_time_threshold,
:dump_log_update_in_place,
:dump_log_write_threshold,
:extra_db_nodes,
:fallback_activated,
:held_locks,
:is_running,
:local_tables,
:lock_queue,
:log_version,
:master_node_tables,
:protocol_version,
:running_db_nodes,
:schema_location,
:subscribers,
:tables,
:transactions,
:transaction_failures,
:transaction_commits,
:transaction_restarts,
:transaction_log_writes,
:use_dir,
:version
]

def info(), do: Mnesia.info()

def info({:system, type}) when type in @system_types, do: Mnesia.system_info(type)

def info(type) when type in @system_types, do: Mnesia.system_info(type)

def info(:schema), do: Mnesia.schema()

def system_info(type) when is_atom(type), do: Mnesia.system_info(type)

def set_debug_level(level) when level in @debug_level_types,
do: Mnesia.set_debug_level(level)
end
151 changes: 151 additions & 0 deletions lib/modules/mnesia_assistant/operation/query.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
defmodule MnesiaAssistant.Query do
alias :mnesia, as: Mnesia
@table_lock_types [:read, :write, :sticky_write]

def read({table, key}), do: Mnesia.read(table, key)

def read(table, key), do: Mnesia.read(table, key)

def read(table, key, lock_type) when lock_type in @table_lock_types,
do: Mnesia.read(table, key, lock_type)

# mnesia:index_read(person, 36, age)
def index_read(table, key, attr), do: Mnesia.index_read(table, key, attr)

def first(table), do: Mnesia.first(table) |> check_nil_end_of_table()

def last(table), do: Mnesia.last(table) |> check_nil_end_of_table()

def next(table, key), do: Mnesia.next(table, key) |> check_nil_end_of_table()

def prev(table, key), do: Mnesia.prev(table, key) |> check_nil_end_of_table()

# MatchHead = #person{name='$1', sex=male, age='$2', _='_'},
# Guard = {'>', '$2', 30},
# Result = '$1',
# mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),
def delete(table, key, lock_type) when lock_type in [:write, :sticky_write] do
Mnesia.delete(table, key, lock_type)
end

def delete(table, key), do: Mnesia.delete({table, key})

def write(name, data, lock_type \\ :write) when lock_type in [:write, :sticky_write] do
Mnesia.write(name, data, lock_type)
end

def select(table, match_fields, conds, lock_type, opts)
when is_list(match_fields) and is_list(conds) and lock_type in @table_lock_types do
result_type = Keyword.get(opts, :result_type, [:"$$"])
lock_type = Keyword.get(opts, :lock_type)
converted = select_converter(table, match_fields, conds, result_type)

cond do
is_nil(lock_type) -> Mnesia.select(table, converted, lock_type)
!is_nil(lock_type) and lock_type in @table_lock_types -> Mnesia.select(table, converted)
true -> raise "The input of the select function is wrong. Do according to the document"
end
end

# Mnesia.match_object({Person, :_, "Marge Simpson", :_})
def match_object(table, pattern) when is_list(pattern) do
pattern = ([table] ++ [pattern]) |> List.to_tuple()
Mnesia.match_object(pattern)
end

def match_object(table, pattern, lock_type)
when is_list(pattern) and lock_type in @table_lock_types do
pattern = ([table] ++ [pattern]) |> List.to_tuple()
Mnesia.match_object(table, pattern, lock_type)
end

# Fun = fun({account, _AccountID, Balance}, Acc) -> Acc + Balance end,
# InitialAcc = 0
# mnesia:foldl(Fun, InitialAcc, account) end)
def foldl(table, initial_acc, foldl_fun) when is_function(foldl_fun) do
Mnesia.foldl(foldl_fun, initial_acc, table)
end

def foldl(table, initial_acc, foldl_fun, lock_type)
when is_function(foldl_fun) and lock_type in @table_lock_types do
Mnesia.foldl(foldl_fun, initial_acc, table, lock_type)
end

def foldr(table, initial_acc, foldr_fun) when is_function(foldr_fun) do
Mnesia.foldr(foldr_fun, initial_acc, table)
end

def foldr(table, initial_acc, foldr_fun, lock_type)
when is_function(foldr_fun) and lock_type in @table_lock_types do
Mnesia.foldr(foldr_fun, initial_acc, table, lock_type)
end

def dirty_match_object(table, pattern) when is_list(pattern) do
pattern = ([table] ++ [pattern]) |> List.to_tuple()
Mnesia.dirty_match_object(pattern)
end

def dirty_match_object(pattern) when is_tuple(pattern), do: Mnesia.dirty_match_object(pattern)

def dirty_read(module, key), do: Mnesia.dirty_read(module, key)

def dirty_index_read(table, key, attr), do: Mnesia.dirty_index_read(table, key, attr)

def dirty_first(table), do: Mnesia.dirty_first(table) |> check_nil_end_of_table()

def dirty_last(table), do: Mnesia.dirty_last(table) |> check_nil_end_of_table()

def dirty_next(table, key), do: Mnesia.dirty_next(table, key) |> check_nil_end_of_table()

def dirty_prev(table, key), do: Mnesia.dirty_prev(table, key) |> check_nil_end_of_table()

def dirty_delete(table, key), do: Mnesia.dirty_delete(table, key)

def dirty_delete({table, key}), do: Mnesia.dirty_delete(table, key)

def dirty_write(name, data), do: Mnesia.dirty_write(name, data)

def is_transaction?(), do: Mnesia.is_transaction()

def abort(reason), do: Mnesia.abort(reason)

def transaction(transaction_fn) when is_function(transaction_fn) do
Mnesia.transaction(transaction_fn)
end

def transaction(transaction_fn, retries)
when is_function(transaction_fn) and (is_integer(retries) or retries == :infinity) do
Mnesia.transaction(transaction_fn, retries)
end

def transaction(transaction_fn, attrs) when is_function(transaction_fn) and is_list(attrs) do
Mnesia.transaction(transaction_fn, attrs)
end

def transaction(transaction_fn, attrs, retries)
when is_function(transaction_fn) and is_list(attrs) and
(is_integer(retries) or retries == :infinity) do
Mnesia.transaction(transaction_fn, attrs, retries)
end

def sync_dirty(sync_dirty_fn) when is_function(sync_dirty_fn),
do: Mnesia.sync_dirty(sync_dirty_fn)

def sync_dirty(sync_dirty_fn, args) when is_function(sync_dirty_fn) and is_list(args) do
Mnesia.sync_dirty(sync_dirty_fn, args)
end

defp check_nil_end_of_table(:"$end_of_table"), do: nil
defp check_nil_end_of_table(value), do: value

defp select_converter(table, match_fields, conds, result_type) do
fields_pattern = ([table] ++ match_fields) |> List.to_tuple()

conds =
Enum.map(conds, fn {cond, field, value} ->
{Helper.Extra.elixir_to_erlang_guard(cond), field, value}
end)

[{fields_pattern, conds, result_type}]
end
end
21 changes: 21 additions & 0 deletions lib/modules/mnesia_assistant/operation/schema.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule MnesiaAssistant.Schema do
alias :mnesia, as: Mnesia

# Schema
def schema(), do: Mnesia.schema()

def create(nodes \\ [node()]) when is_list(nodes) do
create_dir()
Mnesia.create_schema([node()])
end

def delete_schema(nodes \\ [node()]), do: Mnesia.delete_schema(nodes)

defp create_dir() do
path = Application.get_env(:mnesia, :dir)

with true <- !is_nil(path), false <- File.dir?(path) do
:ok = File.mkdir_p!(path)
end
end
end
Loading

0 comments on commit 2894719

Please sign in to comment.