Merge pull request #4581 from apache/otp-26-compat

OTP 26 compatibility
diff --git a/.gitignore b/.gitignore
index 99a5285..eaa50a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -121,6 +121,7 @@
 src/mango/test/*.pyc
 src/mango/nosetests.xml
 src/mango/venv/
+src/mango/.hypothesis/
 src/jwtf/.rebar3/
 test/javascript/junit.xml
 
diff --git a/rel/reltool.config b/rel/reltool.config
index 41d45ef..d84ef59 100644
--- a/rel/reltool.config
+++ b/rel/reltool.config
@@ -73,7 +73,6 @@
     {boot_rel, "couchdb"},
     {profile, embedded},
     {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)"]},
-    {excl_archive_filters, [".*"]},
     {incl_cond, exclude},
 
     %% stdlib
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index 689af7c..53abc73 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -1428,7 +1428,7 @@
     {"X-Couch-Stack-Hash", stack_hash(Stack)}.
 
 stack_hash(Stack) ->
-    erlang:crc32(term_to_binary(Stack)).
+    erlang:crc32(?term_to_bin(Stack)).
 
 %% @doc CouchDB uses a chunked transfer-encoding to stream responses to
 %% _all_docs, _changes, _view and other similar requests. This configuration
diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl
index fd3d755..9df3a88 100644
--- a/src/chttpd/src/chttpd_misc.erl
+++ b/src/chttpd/src/chttpd_misc.erl
@@ -295,7 +295,7 @@
     Nodes = lists:sort([node() | erlang:nodes()]),
     lists:nth(1 + Checksum rem length(Nodes), Nodes);
 choose_node(Key) ->
-    choose_node(term_to_binary(Key)).
+    choose_node(?term_to_bin(Key)).
 
 handle_reload_query_servers_req(#httpd{method = 'POST'} = Req) ->
     chttpd:validate_ctype(Req, "application/json"),
diff --git a/src/couch/include/couch_db.hrl b/src/couch/include/couch_db.hrl
index df81cfb..9c1df21 100644
--- a/src/couch/include/couch_db.hrl
+++ b/src/couch/include/couch_db.hrl
@@ -33,6 +33,7 @@
 -define(i2b(V), couch_util:integer_to_boolean(V)).
 -define(b2i(V), couch_util:boolean_to_integer(V)).
 -define(term_to_bin(T), term_to_binary(T, [{minor_version, 1}])).
+-define(term_to_bin(T, O), term_to_binary(T, O ++ [{minor_version, 1}])).
 -define(term_size(T), erlang:external_size(T, [{minor_version, 1}])).
 
 -define(DEFAULT_ATTACHMENT_CONTENT_TYPE, <<"application/octet-stream">>).
diff --git a/src/couch/src/couch_file.erl b/src/couch/src/couch_file.erl
index c9d7f2a..74d4df2 100644
--- a/src/couch/src/couch_file.erl
+++ b/src/couch/src/couch_file.erl
@@ -399,7 +399,7 @@
     end.
 
 write_header(Fd, Data) ->
-    Bin = term_to_binary(Data),
+    Bin = ?term_to_bin(Data),
     Md5 = couch_hash:md5_hash(Bin),
     % now we assemble the final header binary and write to disk
     FinalBin = <<Md5/binary, Bin/binary>>,
diff --git a/src/couch/src/couch_native_process.erl b/src/couch/src/couch_native_process.erl
index 69fbcb1..3ca7dd1 100644
--- a/src/couch/src/couch_native_process.erl
+++ b/src/couch/src/couch_native_process.erl
@@ -392,7 +392,7 @@
     BindFuns = bindings(State, Sig),
     {Sig, makefun(State, Source, BindFuns)}.
 makefun(State, Source, {DDoc}) ->
-    Sig = couch_hash:md5_hash(lists:flatten([Source, term_to_binary(DDoc)])),
+    Sig = couch_hash:md5_hash(lists:flatten([Source, ?term_to_bin(DDoc)])),
     BindFuns = bindings(State, Sig, {DDoc}),
     {Sig, makefun(State, Source, BindFuns)};
 makefun(_State, Source, BindFuns) when is_list(BindFuns) ->
diff --git a/src/couch/src/couch_query_servers.erl b/src/couch/src/couch_query_servers.erl
index 6752980..151bdc8 100644
--- a/src/couch/src/couch_query_servers.erl
+++ b/src/couch/src/couch_query_servers.erl
@@ -377,7 +377,7 @@
 approx_count_distinct(reduce, KVs) ->
     lists:foldl(
         fun([[Key, _Id], _Value], Filter) ->
-            hyper:insert(term_to_binary(Key), Filter)
+            hyper:insert(?term_to_bin(Key), Filter)
         end,
         hyper:new(11),
         KVs
diff --git a/src/couch/test/eunit/couch_flags_config_tests.erl b/src/couch/test/eunit/couch_flags_config_tests.erl
index 0570771..2657629 100644
--- a/src/couch/test/eunit/couch_flags_config_tests.erl
+++ b/src/couch/test/eunit/couch_flags_config_tests.erl
@@ -96,7 +96,7 @@
      || {Rules, Expected} <- Cases
     ].
 
-flags({{_Pattern}, {_Pattern, _Size, Flags}}) ->
+flags({{_}, {_, _Size, Flags}}) ->
     Flags.
 
 test_id(Items, ExpectedResult) ->
diff --git a/src/couch_epi/src/couch_epi.hrl b/src/couch_epi/src/couch_epi.hrl
index a8bd1d5..8fa3335 100644
--- a/src/couch_epi/src/couch_epi.hrl
+++ b/src/couch_epi/src/couch_epi.hrl
@@ -13,3 +13,6 @@
 -record(couch_epi_spec, {
     behaviour, app, kind, options, key, value, codegen, type
 }).
+
+% Copied from `couch_db.hrl` which cannot be included in this application
+-define(term_to_bin(T), term_to_binary(T, [{minor_version, 1}])).
diff --git a/src/couch_epi/src/couch_epi_util.erl b/src/couch_epi/src/couch_epi_util.erl
index 2c86a96..9f34dca 100644
--- a/src/couch_epi/src/couch_epi_util.erl
+++ b/src/couch_epi/src/couch_epi_util.erl
@@ -12,6 +12,8 @@
 
 -module(couch_epi_util).
 
+-include("couch_epi.hrl").
+
 -export([module_version/1, hash/1, module_exists/1]).
 
 -compile([nowarn_deprecated_function]).
@@ -22,7 +24,7 @@
     VSNs.
 
 hash(Term) ->
-    <<SigInt:128/integer>> = couch_hash:md5_hash(term_to_binary(Term)),
+    <<SigInt:128/integer>> = couch_hash:md5_hash(?term_to_bin(Term)),
     lists:flatten(io_lib:format("\"~.36B\"", [SigInt])).
 
 module_exists(Module) ->
diff --git a/src/couch_log/src/couch_log_formatter.erl b/src/couch_log/src/couch_log_formatter.erl
index 91b64ad..cc8b5d8 100644
--- a/src/couch_log/src/couch_log_formatter.erl
+++ b/src/couch_log/src/couch_log_formatter.erl
@@ -467,12 +467,14 @@
     %% https://www.rfc-editor.org/rfc/rfc5424.html#section-6.3
     %% iut="3" eventSource="Application" eventID="1011"
     string:join(
-        maps:fold(
-            fun(K, V, Acc) ->
-                [to_str(K, V) | Acc]
-            end,
-            [],
-            Meta
+        lists:sort(
+            maps:fold(
+                fun(K, V, Acc) ->
+                    [to_str(K, V) | Acc]
+                end,
+                [],
+                Meta
+            )
         ),
         " "
     ).
diff --git a/src/couch_log/src/couch_log_trunc_io.erl b/src/couch_log/src/couch_log_trunc_io.erl
index 9736e87..c330edb 100644
--- a/src/couch_log/src/couch_log_trunc_io.erl
+++ b/src/couch_log/src/couch_log_trunc_io.erl
@@ -352,7 +352,7 @@
         _ -> {"...", 3}
     end;
 map_body(Map, Max, Options) ->
-    case maps:to_list(Map) of
+    case lists:sort(maps:to_list(Map)) of
         [] ->
             {[], 0};
         [{Key, Value} | Rest] ->
diff --git a/src/couch_log/test/eunit/couch_log_formatter_test.erl b/src/couch_log/test/eunit/couch_log_formatter_test.erl
index 29d5497..cdb7eae 100644
--- a/src/couch_log/test/eunit/couch_log_formatter_test.erl
+++ b/src/couch_log/test/eunit/couch_log_formatter_test.erl
@@ -38,9 +38,8 @@
         bar => "barStr",
         baz => baz
     }),
-    % NOTE: this currently hardcodes the ordering of the keys, however, map
-    % key order is not guaranteed and this may break.
-    Formatted = "[foo=123 baz=\"baz\" bar=\"barStr\"]",
+    % Rely on `couch_log_formatter:format_meta/1` to sort keys
+    Formatted = "[bar=\"barStr\" baz=\"baz\" foo=123]",
     ?assertEqual(Formatted, lists:flatten(Entry#log_entry.msg)).
 
 format_reason_test() ->
diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl
index c0f6ff4..1a4a3eb 100644
--- a/src/couch_mrview/src/couch_mrview.erl
+++ b/src/couch_mrview/src/couch_mrview.erl
@@ -261,7 +261,7 @@
 query_all_docs(Db, Args0, Callback, Acc) ->
     Sig = couch_util:with_db(Db, fun(WDb) ->
         {ok, Info} = couch_db:get_db_info(WDb),
-        couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Info)))
+        couch_index_util:hexsig(couch_hash:md5_hash(?term_to_bin(Info)))
     end),
     Args1 = Args0#mrargs{view_type = map},
     Args2 = couch_mrview_util:validate_all_docs_args(Db, Args1),
diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl
index 440eb72..5913aa3 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -265,7 +265,7 @@
         partitioned = Partitioned
     },
     SigInfo = {Views, Language, DesignOpts, couch_index_util:sort_lib(Lib)},
-    {ok, IdxState#mrst{sig = couch_hash:md5_hash(term_to_binary(SigInfo))}}.
+    {ok, IdxState#mrst{sig = couch_hash:md5_hash(?term_to_bin(SigInfo))}}.
 
 set_view_type(_Args, _ViewName, []) ->
     throw({not_found, missing_named_view});
@@ -315,7 +315,7 @@
     UpdateSeq = couch_db:get_update_seq(Db),
     PurgeSeq = couch_db:get_purge_seq(Db),
     Term = view_sig_term(BaseSig, UpdateSeq, PurgeSeq),
-    couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Term)));
+    couch_index_util:hexsig(couch_hash:md5_hash(?term_to_bin(Term)));
 view_sig(Db, State, {_Nth, _Lang, View}, Args) ->
     view_sig(Db, State, View, Args);
 view_sig(_Db, State, View, Args0) ->
@@ -327,7 +327,7 @@
         extra = []
     },
     Term = view_sig_term(Sig, UpdateSeq, PurgeSeq, Args),
-    couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Term))).
+    couch_index_util:hexsig(couch_hash:md5_hash(?term_to_bin(Term))).
 
 view_sig_term(BaseSig, UpdateSeq, PurgeSeq) ->
     {BaseSig, UpdateSeq, PurgeSeq}.
@@ -1114,7 +1114,7 @@
     KSI = proplists:get_value(<<"keyseq_indexed">>, DesignOpts, false),
     Views = [old_view_format(V, SI, KSI) || V <- State#mrst.views],
     SigInfo = {Views, Language, DesignOpts, couch_index_util:sort_lib(Lib)},
-    couch_hash:md5_hash(term_to_binary(SigInfo)).
+    couch_hash:md5_hash(?term_to_bin(SigInfo)).
 
 old_view_format(View, SI, KSI) ->
     {
diff --git a/src/couch_pse_tests/src/cpse_util.erl b/src/couch_pse_tests/src/cpse_util.erl
index b0e5b0c..c5aac94 100644
--- a/src/couch_pse_tests/src/cpse_util.erl
+++ b/src/couch_pse_tests/src/cpse_util.erl
@@ -366,11 +366,11 @@
     }}.
 
 gen_rev(A, DocId, {Pos, Rev}, Body, Atts) when A == update; A == delete ->
-    NewRev = couch_hash:md5_hash(term_to_binary({DocId, Rev, Body, Atts})),
+    NewRev = couch_hash:md5_hash(?term_to_bin({DocId, Rev, Body, Atts})),
     {Pos + 1, [NewRev, Rev]};
 gen_rev(conflict, DocId, _, Body, Atts) ->
     UUID = couch_uuids:random(),
-    NewRev = couch_hash:md5_hash(term_to_binary({DocId, UUID, Body, Atts})),
+    NewRev = couch_hash:md5_hash(?term_to_bin({DocId, UUID, Body, Atts})),
     {1, [NewRev]}.
 
 prep_atts(_Db, []) ->
diff --git a/src/couch_replicator/src/couch_replicator_ids.erl b/src/couch_replicator/src/couch_replicator_ids.erl
index 86fe1f2..0c381b0 100644
--- a/src/couch_replicator/src/couch_replicator_ids.erl
+++ b/src/couch_replicator/src/couch_replicator_ids.erl
@@ -120,7 +120,7 @@
                 true ->
                     [<<"winning_revs_only">>]
             end,
-    couch_util:to_hex(couch_hash:md5_hash(term_to_binary(Base3))).
+    couch_util:to_hex(couch_hash:md5_hash(?term_to_bin(Base3))).
 
 maybe_append_options(Options, RepOptions) ->
     lists:foldl(
diff --git a/src/couch_replicator/src/couch_replicator_utils.erl b/src/couch_replicator/src/couch_replicator_utils.erl
index 40d1885..0aff4e9 100644
--- a/src/couch_replicator/src/couch_replicator_utils.erl
+++ b/src/couch_replicator/src/couch_replicator_utils.erl
@@ -140,7 +140,8 @@
             {undefined, undefined}
     catch
         % Tolerate invalid B64 values here to avoid crashing replicator
-        error:function_clause ->
+        % Changed to `missing_padding` in OTP 26
+        error:Reason when Reason =:= function_clause; Reason =:= missing_padding ->
             {undefined, undefined}
     end.
 
diff --git a/src/ddoc_cache/src/ddoc_cache_value.erl b/src/ddoc_cache/src/ddoc_cache_value.erl
index 59585ee..825fd45 100644
--- a/src/ddoc_cache/src/ddoc_cache_value.erl
+++ b/src/ddoc_cache/src/ddoc_cache_value.erl
@@ -17,8 +17,10 @@
     unwrap/1
 ]).
 
+-include_lib("couch/include/couch_db.hrl").
+
 wrap(Value) ->
-    {?MODULE, term_to_binary(Value)}.
+    {?MODULE, ?term_to_bin(Value)}.
 
 unwrap({?MODULE, Bin}) when is_binary(Bin) ->
     binary_to_term(Bin).
diff --git a/src/dreyfus/src/dreyfus_index.erl b/src/dreyfus/src/dreyfus_index.erl
index 69f44b5..2184a31 100644
--- a/src/dreyfus/src/dreyfus_index.erl
+++ b/src/dreyfus/src/dreyfus_index.erl
@@ -327,7 +327,7 @@
                     Sig = ?l2b(
                         couch_util:to_hex(
                             couch_hash:md5_hash(
-                                term_to_binary({Analyzer, Def})
+                                ?term_to_bin({Analyzer, Def})
                             )
                         )
                     ),
diff --git a/src/fabric/src/fabric_view_changes.erl b/src/fabric/src/fabric_view_changes.erl
index 693e243..fd439c5 100644
--- a/src/fabric/src/fabric_view_changes.erl
+++ b/src/fabric/src/fabric_view_changes.erl
@@ -422,7 +422,7 @@
 pack_seqs(Workers) ->
     SeqList = [{N, R, S} || {#shard{node = N, range = R}, S} <- Workers],
     SeqSum = lists:sum([seq(S) || {_, _, S} <- SeqList]),
-    Opaque = couch_util:encodeBase64Url(term_to_binary(SeqList, [compressed])),
+    Opaque = couch_util:encodeBase64Url(?term_to_bin(SeqList, [compressed])),
     ?l2b([integer_to_list(SeqSum), $-, Opaque]).
 
 seq({Seq, _Uuid, _Node}) -> Seq;
diff --git a/src/mango/src/mango_idx.erl b/src/mango/src/mango_idx.erl
index 9ce1ef9..011754f 100644
--- a/src/mango/src/mango_idx.erl
+++ b/src/mango/src/mango_idx.erl
@@ -347,7 +347,7 @@
 
 gen_name(Idx, Opts0) ->
     Opts = lists:usort(Opts0),
-    TermBin = term_to_binary({Idx, Opts}),
+    TermBin = ?term_to_bin({Idx, Opts}),
     Sha = crypto:hash(sha, TermBin),
     mango_util:enc_hex(Sha).
 
diff --git a/src/mem3/src/mem3_hash.erl b/src/mem3/src/mem3_hash.erl
index ccaab72..6dfe3f4 100644
--- a/src/mem3/src/mem3_hash.erl
+++ b/src/mem3/src/mem3_hash.erl
@@ -21,6 +21,7 @@
 ]).
 
 -include_lib("mem3/include/mem3.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 calculate(#shard{opts = Opts}, DocId) ->
     Props = couch_util:get_value(props, Opts, []),
@@ -53,7 +54,7 @@
 crc32(Item) when is_binary(Item) ->
     erlang:crc32(Item);
 crc32(Item) ->
-    erlang:crc32(term_to_binary(Item)).
+    erlang:crc32(?term_to_bin(Item)).
 
 get_hash_fun_int(Opts) when is_list(Opts) ->
     case lists:keyfind(hash, 1, Opts) of
diff --git a/src/mem3/src/mem3_rep.erl b/src/mem3/src/mem3_rep.erl
index 010d213..d186f9e 100644
--- a/src/mem3/src/mem3_rep.erl
+++ b/src/mem3/src/mem3_rep.erl
@@ -152,7 +152,7 @@
     <<>>.
 
 local_id_hash(Thing) ->
-    couch_util:encodeBase64Url(couch_hash:md5_hash(term_to_binary(Thing))).
+    couch_util:encodeBase64Url(couch_hash:md5_hash(?term_to_bin(Thing))).
 
 make_purge_id(SourceUUID, TargetUUID) ->
     <<"_local/purge-mem3-", SourceUUID/binary, "-", TargetUUID/binary>>.
diff --git a/src/mem3/src/mem3_util.erl b/src/mem3/src/mem3_util.erl
index f05fe73..520f546 100644
--- a/src/mem3/src/mem3_util.erl
+++ b/src/mem3/src/mem3_util.erl
@@ -360,7 +360,7 @@
 rotate_list(_Key, []) ->
     [];
 rotate_list(Key, List) when not is_binary(Key) ->
-    rotate_list(term_to_binary(Key), List);
+    rotate_list(?term_to_bin(Key), List);
 rotate_list(Key, List) ->
     {H, T} = lists:split(erlang:crc32(Key) rem length(List), List),
     T ++ H.
diff --git a/src/nouveau/src/nouveau_util.erl b/src/nouveau/src/nouveau_util.erl
index 5015b8f..b7da7e8 100644
--- a/src/nouveau/src/nouveau_util.erl
+++ b/src/nouveau/src/nouveau_util.erl
@@ -72,7 +72,7 @@
                         couch_util:to_hex(
                             crypto:hash(
                                 sha256,
-                                term_to_binary(
+                                ?term_to_bin(
                                     {DefaultAnalyzer, FieldAnalyzers, Def}
                                 )
                             )
diff --git a/src/smoosh/src/smoosh_persist.erl b/src/smoosh/src/smoosh_persist.erl
index 886f18e..2feab7e 100644
--- a/src/smoosh/src/smoosh_persist.erl
+++ b/src/smoosh/src/smoosh_persist.erl
@@ -19,6 +19,7 @@
 ]).
 
 -include_lib("kernel/include/file.hrl").
+-include_lib("couch/include/couch_db.hrl").
 
 -define(SUFFIX, ".smooshq").
 
@@ -183,7 +184,7 @@
     Path = file_path("foochan"),
     ?assertEqual(ok, write(#{<<"a">> => 1}, Path)),
 
-    ok = file:write_file(Path, term_to_binary(foo), [raw]),
+    ok = file:write_file(Path, ?term_to_bin(foo), [raw]),
     ?assertEqual({error, term_not_a_map}, read(Path)),
 
     ok = file:write_file(Path, <<"42">>, [raw]),