| % 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)). |