blob: a6471b4f5c4b4f77f5e1d58356aa593909eeba52 [file] [log] [blame]
% Licensed under the Apache License, Version 2.0 (the "License"); you may not
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
% License for the specific language governing permissions and limitations under
% the License.
-module(couch_eval).
-export([
acquire_map_context/6,
release_map_context/1,
map_docs/2,
with_context/2,
try_compile/4
]).
-include_lib("couch/include/couch_db.hrl").
-type db_name() :: binary().
-type doc_id() :: binary().
-type ddoc_id() :: binary().
-type language() :: binary().
-type sig() :: binary().
-type lib() :: any().
-type map_fun() :: binary().
-type map_funs() :: [map_fun()].
-type result() :: {doc_id(), [[{any(), any()}]]}.
-type api_mod() :: atom().
-type context() :: {api_mod(), any()}.
-type function_type() :: binary() | atom().
-type function_name() :: binary().
-type function_src() :: binary().
-type error(_Error) :: no_return().
-type context_opts() :: #{
db_name := db_name(),
ddoc_id => ddoc_id(),
language => language(),
sig => sig(),
lib => lib(),
map_funs => map_funs(),
api_mod => api_mod()
}.
-type with_context_opts() :: #{
language := language()
}.
-callback acquire_map_context(context_opts()) -> {ok, any()} | {error, any()}.
-callback release_map_context(context()) -> ok | {error, any()}.
-callback map_docs(context(), [doc()]) -> {ok, [result()]} | {error, any()}.
-callback acquire_context() -> {ok, any()} | {error, any()}.
-callback release_context(context()) -> ok | {error, any()}.
-callback try_compile(context(), function_type(), function_name(), function_src()) -> ok.
-spec acquire_map_context(
db_name(),
ddoc_id(),
language(),
sig(),
lib(),
map_funs()
) ->
{ok, context()}
| error({invalid_eval_api_mod, Language :: binary()})
| error({unknown_eval_api_language, Language :: binary()}).
acquire_map_context(DbName, DDocId, Language, Sig, Lib, MapFuns) ->
ApiMod = get_api_mod(Language),
CtxOpts = #{
db_name => DbName,
ddoc_id => DDocId,
language => Language,
sig => Sig,
lib => Lib,
map_funs => MapFuns
},
case ApiMod:acquire_map_context(CtxOpts) of
{ok, Ctx} ->
{ok, {ApiMod, Ctx}};
{error, Error} ->
{error, Error}
end.
-spec release_map_context(context()) -> ok | {error, any()}.
release_map_context(nil) ->
ok;
release_map_context({ApiMod, Ctx}) ->
ApiMod:release_map_context(Ctx).
-spec map_docs(context(), [doc()]) -> {ok, result()} | {error, any()}.
map_docs({ApiMod, Ctx}, Docs) ->
ApiMod:map_docs(Ctx, Docs).
-spec with_context(with_context_opts(), function()) ->
any()
| error({invalid_eval_api_mod, Language :: binary()})
| error({unknown_eval_api_language, Language :: binary()}).
with_context(#{language := Language}, Fun) ->
{ok, Ctx} = acquire_context(Language),
try
Fun(Ctx)
after
release_context(Ctx)
end.
-spec try_compile(context(), function_type(), function_name(), function_src()) -> ok.
try_compile({_ApiMod, _Ctx}, reduce, <<_/binary>>, disabled) ->
% Reduce functions may be disabled. Accept that as a valid configuration.
ok;
try_compile({ApiMod, Ctx}, FuncType, FuncName, FuncSrc) ->
ApiMod:try_compile(Ctx, FuncType, FuncName, FuncSrc).
acquire_context(Language) ->
ApiMod = get_api_mod(Language),
{ok, Ctx} = ApiMod:acquire_context(),
{ok, {ApiMod, Ctx}}.
release_context(nil) ->
ok;
release_context({ApiMod, Ctx}) ->
ApiMod:release_context(Ctx).
get_api_mod(Language) when is_binary(Language) ->
try
LangStr = binary_to_list(Language),
ModStr =
case LangStr of
"javascript" ->
config:get("couch_eval.languages", LangStr, "couch_js");
"query" ->
config:get("couch_eval.languages", LangStr, "mango_eval");
_ ->
config:get("couch_eval.languages", LangStr)
end,
case ModStr of
_ -> list_to_existing_atom(ModStr);
undefined -> erlang:error({unknown_eval_api_language, Language})
end
catch error:badarg ->
erlang:error({invalid_eval_api_mod, Language})
end.