blob: f2efcaa5ea3d28cd8676eb6d810c3f8ebddb7a7e [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_partition).
-export([
extract/1,
from_docid/1,
is_member/2,
start_key/1,
end_key/1,
shard_key/1,
validate_dbname/2,
validate_docid/1,
validate_partition/1,
hash/1
]).
-include_lib("couch/include/couch_db.hrl").
extract(Value) when is_binary(Value) ->
case binary:split(Value, <<":">>) of
[Partition, Rest] ->
{Partition, Rest};
_ ->
undefined
end;
extract(_) ->
undefined.
from_docid(DocId) ->
case extract(DocId) of
undefined ->
throw({illegal_docid, <<"Doc id must be of form partition:id">>});
{Partition, _} ->
Partition
end.
is_member(DocId, Partition) ->
case extract(DocId) of
{Partition, _} ->
true;
_ ->
false
end.
start_key(Partition) ->
<<Partition/binary, ":">>.
end_key(Partition) ->
<<Partition/binary, ";">>.
shard_key(Partition) ->
<<Partition/binary, ":foo">>.
validate_dbname(DbName, Options) when is_list(DbName) ->
validate_dbname(?l2b(DbName), Options);
validate_dbname(DbName, Options) when is_binary(DbName) ->
Props = couch_util:get_value(props, Options, []),
IsPartitioned = couch_util:get_value(partitioned, Props, false),
if not IsPartitioned -> ok; true ->
DbsDbName = config:get("mem3", "shards_db", "_dbs"),
NodesDbName = config:get("mem3", "nodes_db", "_nodes"),
UsersDbSuffix = config:get("couchdb", "users_db_suffix", "_users"),
Suffix = couch_db:dbname_suffix(DbName),
SysDbNames = [
iolist_to_binary(DbsDbName),
iolist_to_binary(NodesDbName)
| ?SYSTEM_DATABASES
],
Suffices = [
<<"_replicator">>,
<<"_users">>,
iolist_to_binary(UsersDbSuffix)
],
IsSysDb = lists:member(DbName, SysDbNames)
orelse lists:member(Suffix, Suffices),
if not IsSysDb -> ok; true ->
throw({bad_request, <<"Cannot partition a system database">>})
end
end.
validate_docid(<<"_design/", _/binary>>) ->
ok;
validate_docid(<<"_local/", _/binary>>) ->
ok;
validate_docid(DocId) when is_binary(DocId) ->
% When this function is called we already know that
% DocId is already valid thus we only need to
% ensure that the partition exists and is not empty.
case extract(DocId) of
undefined ->
throw({illegal_docid, <<"Doc id must be of form partition:id">>});
{Partition, PartitionedDocId} ->
validate_partition(Partition),
couch_doc:validate_docid(PartitionedDocId)
end.
validate_partition(<<>>) ->
throw({illegal_partition, <<"Partition must not be empty">>});
validate_partition(Partition) when is_binary(Partition) ->
case Partition of
<<"_", _/binary>> ->
Msg1 = <<"Partition must not start with an underscore">>,
throw({illegal_partition, Msg1});
_ ->
ok
end,
case couch_util:validate_utf8(Partition) of
true ->
ok;
false ->
Msg2 = <<"Partition must be valid UTF-8">>,
throw({illegal_partition, Msg2})
end,
case extract(Partition) of
{_, _} ->
Msg3 = <<"Partition must not contain a colon">>,
throw({illegal_partition, Msg3});
undefined ->
ok
end;
validate_partition(_) ->
throw({illegal_partition, <<"Partition must be a string">>}).
% Document ids that start with an underscore
% (i.e., _local and _design) do not contain a
% partition and thus do not use the partition
% hashing.
hash(<<"_", _/binary>> = DocId) ->
erlang:crc32(DocId);
hash(DocId) when is_binary(DocId) ->
erlang:crc32(from_docid(DocId)).