Skip to content

Instantly share code, notes, and snippets.

@segun
Created September 3, 2014 23:17
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save segun/0dd1d45874d350657083 to your computer and use it in GitHub Desktop.
Save segun/0dd1d45874d350657083 to your computer and use it in GitHub Desktop.
Mnesia Add New Column In Production Environment Without Losing Data
%%%-------------------------------------------------------------------
%%% @author aardvocate
%%% @copyright (C) 2014, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 03. Sep 2014 11:02 PM
%%%-------------------------------------------------------------------
%%%
%%% This module provides a method will add new mnesia columns to an already running mnesia instance without losing data.
%%% This has only been tested on Tables with very small data. If you have a Table with millions of sensitive data,
%%% make sure you KNOW what you are doing before runing this module. I advice you take a backup first for starters.
%%% Also this module is basic and barebones, it doesn't take into consideration for instance mnesia running on multiple nodes,
%%% on multiple machines. If you have erlang on multiple nodes, one solution is to run the module on each of your nodes.
%%% This module did not use LOCKING, so it is better to make sure no other process is writing to your tables while this module
%%% is running. Alternately, you can replace the qlc: aspects with mnesia:read/ and use locks as you wish. But also take
%%% note that if you have 100M rows in the mnesia table, this might take a while to run. You can put this in a gen_server:cast
%%% so as to make it non-blocking.
-module(mnesia_utils).
-author("segun").
%% API
-export([start/1, add_column/4]).
-include_lib("stdlib/include/qlc.hrl").
start(MnesiaDir) ->
application:set_env(mnesia, dir, MnesiaDir),
mnesia:start().
add_column(TableName, ExistingColumns, NewColum, NewColumnDefaulValue) ->
ReadFun = fun() ->
Query = qlc:q([R || R <- mnesia:table(TableName)]),
qlc:e(Query)
end,
{atomic, Records} = mnesia:transaction(ReadFun),
mnesia:delete_table(TableName),
Columns = lists:reverse([NewColum | lists:reverse(ExistingColumns)]),
mnesia:create_table(TableName, [{attributes, Columns}]),
MnesiaData = generate_mnesia_data(Records, NewColumnDefaulValue),
F = fun() -> lists:foreach(fun mnesia:write/1, MnesiaData) end,
mnesia:transaction(F).
prepend(X, Tuple) ->
TupleAsList = lists:reverse(tuple_to_list(Tuple)),
list_to_tuple(lists:reverse([X | TupleAsList])).
generate_mnesia_data(Records, Default) ->
generate_mnesia_data(Records, Default, []).
generate_mnesia_data([H | Records], Default, Acc) ->
NewRecord = prepend(Default, H),
generate_mnesia_data(Records, Default, [NewRecord | Acc]);
generate_mnesia_data([], _Default, Acc) ->
lists:reverse(Acc).
@oladipo
Copy link

oladipo commented May 2, 2017

Did you ever consider using mnesia:transform_table/3 ?

http://erlang.org/doc/man/mnesia.html#transform_table-3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment