2015-09-18 10:02:51 +00:00
|
|
|
%% -*- Mode: Erlang -*-
|
2015-09-18 09:22:03 +00:00
|
|
|
%% -------------------------------------------------------------------
|
|
|
|
%%
|
|
|
|
%% Copyright (c) 2015 Basho Technologies, Inc.
|
|
|
|
%%
|
|
|
|
%% This file is provided to you 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.
|
|
|
|
%%
|
|
|
|
%% -------------------------------------------------------------------
|
2015-09-21 11:46:19 +00:00
|
|
|
%% @doc A util module for riak_ts basic CREATE TABLE Actions
|
|
|
|
-module(timeseries_util).
|
2015-09-18 09:22:03 +00:00
|
|
|
|
|
|
|
-compile(export_all).
|
|
|
|
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
-define(MAXVARCHARLEN, 16).
|
|
|
|
-define(MAXTIMESTAMP, trunc(math:pow(2, 63))).
|
|
|
|
-define(MAXFLOAT, math:pow(2, 63)).
|
|
|
|
-define(MULTIPLECLUSTERSIZE, 3).
|
2015-09-18 09:22:03 +00:00
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
confirm_create(ClusterType, DDL, Expected) ->
|
2015-09-18 09:22:03 +00:00
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
[Node | _] =build_cluster(ClusterType),
|
2015-09-18 09:22:03 +00:00
|
|
|
|
2015-09-21 11:46:19 +00:00
|
|
|
Props = io_lib:format("{\\\"props\\\": {\\\"n_val\\\": 3, \\\"table_def\\\": \\\"~s\\\"}}", [DDL]),
|
|
|
|
Got = rt:admin(Node, ["bucket-type", "create", get_bucket(), lists:flatten(Props)]),
|
|
|
|
?assertEqual(Expected, Got),
|
2015-09-18 09:22:03 +00:00
|
|
|
|
|
|
|
pass.
|
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
confirm_activate(ClusterType, DDL, Expected) ->
|
2015-09-22 20:15:26 +00:00
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
[Node | Rest] = build_cluster(ClusterType),
|
|
|
|
ok = maybe_stop_a_node(ClusterType, Rest),
|
2015-09-18 10:02:51 +00:00
|
|
|
{ok, _} = create_bucket(Node, DDL),
|
|
|
|
Got = activate_bucket(Node, DDL),
|
2015-09-21 11:46:19 +00:00
|
|
|
?assertEqual(Expected, Got),
|
2015-09-18 09:22:03 +00:00
|
|
|
|
|
|
|
pass.
|
|
|
|
|
2015-09-30 14:15:00 +00:00
|
|
|
confirm_put(ClusterType, TestType, DDL, Obj) ->
|
2015-09-22 20:15:26 +00:00
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
[Node | _] = build_cluster(ClusterType),
|
|
|
|
|
|
|
|
case TestType of
|
|
|
|
normal ->
|
|
|
|
io:format("1 - Creating and activating bucket~n"),
|
|
|
|
{ok, _} = create_bucket(Node, DDL),
|
|
|
|
{ok, _} = activate_bucket(Node, DDL);
|
|
|
|
no_ddl ->
|
|
|
|
io:format("1 - NOT Creating or activating bucket - failure test~n"),
|
|
|
|
ok
|
|
|
|
end,
|
2015-09-22 11:39:37 +00:00
|
|
|
Bucket = list_to_binary(get_bucket()),
|
2015-09-24 10:17:49 +00:00
|
|
|
io:format("2 - writing to bucket ~p with:~n- ~p~n", [Bucket, Obj]),
|
2015-09-22 11:39:37 +00:00
|
|
|
C = rt:pbc(Node),
|
2015-09-30 14:15:00 +00:00
|
|
|
riakc_ts:put(C, Bucket, Obj).
|
2015-09-18 09:22:03 +00:00
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
confirm_select(ClusterType, TestType, DDL, Data, Qry, Expected) ->
|
2015-09-22 20:15:26 +00:00
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
[Node | _] = build_cluster(ClusterType),
|
2015-09-22 20:15:26 +00:00
|
|
|
|
2015-09-24 10:17:49 +00:00
|
|
|
case TestType of
|
|
|
|
normal ->
|
|
|
|
io:format("1 - Create and activate the bucket~n"),
|
|
|
|
{ok, _} = create_bucket(Node, DDL),
|
|
|
|
{ok, _} = activate_bucket(Node, DDL);
|
|
|
|
no_ddl ->
|
|
|
|
io:format("1 - NOT Creating or activating bucket - failure test~n"),
|
|
|
|
ok
|
|
|
|
end,
|
2015-09-22 20:15:26 +00:00
|
|
|
|
|
|
|
Bucket = list_to_binary(get_bucket()),
|
|
|
|
io:format("2 - writing to bucket ~p with:~n- ~p~n", [Bucket, Data]),
|
|
|
|
C = rt:pbc(Node),
|
|
|
|
ok = riakc_ts:put(C, Bucket, Data),
|
|
|
|
|
|
|
|
io:format("3 - Now run the query ~p~n", [Qry]),
|
2015-09-24 10:17:49 +00:00
|
|
|
Got = riakc_ts:query(C, Qry),
|
|
|
|
io:format("Got is ~p~n", [Got]),
|
|
|
|
?assertEqual(Expected, Got),
|
2015-09-18 09:22:03 +00:00
|
|
|
pass.
|
|
|
|
|
|
|
|
%%
|
|
|
|
%% Helper funs
|
|
|
|
%%
|
|
|
|
|
2015-09-21 11:46:19 +00:00
|
|
|
activate_bucket(Node, _DDL) ->
|
|
|
|
rt:admin(Node, ["bucket-type", "activate", get_bucket()]).
|
2015-09-18 09:22:03 +00:00
|
|
|
|
|
|
|
create_bucket(Node, DDL) ->
|
|
|
|
Props = io_lib:format("{\\\"props\\\": {\\\"n_val\\\": 3, " ++
|
2015-09-21 11:46:19 +00:00
|
|
|
"\\\"table_def\\\": \\\"~s\\\"}}", [DDL]),
|
|
|
|
rt:admin(Node, ["bucket-type", "create", get_bucket(),
|
2015-09-18 09:22:03 +00:00
|
|
|
lists:flatten(Props)]).
|
|
|
|
|
|
|
|
%% @ignore
|
2015-09-24 10:17:49 +00:00
|
|
|
maybe_stop_a_node(one_down, [H | _T]) ->
|
|
|
|
ok = rt:stop(H);
|
|
|
|
maybe_stop_a_node(_, _) ->
|
|
|
|
ok.
|
|
|
|
|
|
|
|
build_cluster(single) -> build_c2(1);
|
|
|
|
build_cluster(multiple) -> build_c2(?MULTIPLECLUSTERSIZE);
|
|
|
|
build_cluster(one_down) -> build_c2(?MULTIPLECLUSTERSIZE).
|
|
|
|
|
|
|
|
-spec build_c2(non_neg_integer()) -> [node()].
|
|
|
|
build_c2(Size) ->
|
2015-09-18 09:22:03 +00:00
|
|
|
lager:info("Building cluster of ~p~n", [Size]),
|
2015-09-24 10:17:49 +00:00
|
|
|
build_c2(Size, []).
|
|
|
|
-spec build_c2(non_neg_integer(), list()) -> [node()].
|
|
|
|
build_c2(Size, Config) ->
|
2015-09-18 09:22:03 +00:00
|
|
|
[_Node1|_] = Nodes = rt:deploy_nodes(Size, Config),
|
|
|
|
rt:join_cluster(Nodes),
|
|
|
|
Nodes.
|
|
|
|
|
2015-09-21 11:46:19 +00:00
|
|
|
get_bucket() ->
|
2015-09-18 09:22:03 +00:00
|
|
|
"GeoCheckin".
|
|
|
|
|
2015-09-22 20:15:26 +00:00
|
|
|
get_valid_qry() ->
|
2015-09-29 15:24:44 +00:00
|
|
|
"select * from GeoCheckin Where time > 1 and time < 10 and myfamily = 'family1' and myseries ='seriesX'".
|
2015-09-24 10:17:49 +00:00
|
|
|
|
|
|
|
get_invalid_qry(borked_syntax) ->
|
|
|
|
"selectah * from GeoCheckin Where time > 1 and time < 10";
|
|
|
|
get_invalid_qry(key_not_covered) ->
|
|
|
|
"select * from GeoCheckin Where time > 1 and time < 10";
|
|
|
|
get_invalid_qry(invalid_operator) ->
|
2015-09-29 15:24:44 +00:00
|
|
|
"select * from GeoCheckin Where time > 1 and time < 10 and myfamily = 'family1' and myseries ='seriesX' and weather > 'bob'";
|
2015-09-24 10:17:49 +00:00
|
|
|
get_invalid_qry(field_comparison) ->
|
2015-09-29 15:24:44 +00:00
|
|
|
"select * from GeoCheckin Where time > 1 and time < 10 and myfamily = 'family1' and myseries ='seriesX' and weather = family1";
|
2015-09-24 10:17:49 +00:00
|
|
|
get_invalid_qry(type_error) ->
|
2015-09-29 15:24:44 +00:00
|
|
|
"select * from GeoCheckin Where time > 1 and time < 10 and myfamily = 'family1' and myseries ='seriesX' and weather = true".
|
2015-09-22 20:15:26 +00:00
|
|
|
|
|
|
|
get_valid_select_data() ->
|
2015-09-29 15:24:44 +00:00
|
|
|
Family = <<"family1">>,
|
|
|
|
Series = <<"seriesX">>,
|
2015-09-22 20:15:26 +00:00
|
|
|
Times = lists:seq(1, 10),
|
|
|
|
[[Family, Series, X, get_varchar(), get_float()] || X <- Times].
|
|
|
|
|
2015-10-01 11:02:07 +00:00
|
|
|
get_cols(docs) ->
|
|
|
|
[<<"myfamily">>,
|
|
|
|
<<"myseries">>,
|
|
|
|
<<"time">>,
|
|
|
|
<<"weather">>,
|
|
|
|
<<"temperature">>].
|
|
|
|
|
|
|
|
%% Convert put data to result rows exclusive of the first and last records.
|
|
|
|
exclusive_result_from_data(Data) ->
|
|
|
|
[_|Tail] = remove_last([list_to_tuple(R) || R <- Data]),
|
|
|
|
Tail.
|
|
|
|
|
|
|
|
remove_last(Data) ->
|
|
|
|
lists:reverse(tl(lists:reverse(Data))).
|
|
|
|
|
2015-09-18 09:22:03 +00:00
|
|
|
%% a valid DDL - the one used in the documents
|
|
|
|
get_ddl(docs) ->
|
|
|
|
_SQL = "CREATE TABLE GeoCheckin (" ++
|
|
|
|
"myfamily varchar not null, " ++
|
|
|
|
"myseries varchar not null, " ++
|
|
|
|
"time timestamp not null, " ++
|
|
|
|
"weather varchar not null, " ++
|
|
|
|
"temperature float, " ++
|
|
|
|
"PRIMARY KEY ((quantum(time, 15, 'm'), myfamily, myseries), " ++
|
|
|
|
"time, myfamily, myseries))";
|
|
|
|
%% another valid DDL - one with all the good stuff like
|
|
|
|
%% different types and optional blah-blah
|
|
|
|
get_ddl(variety) ->
|
|
|
|
_SQL = "CREATE TABLE GeoCheckin (" ++
|
|
|
|
"myfamily varchar not null, " ++
|
|
|
|
"myseries varchar not null, " ++
|
|
|
|
"time timestamp not null, " ++
|
|
|
|
"myint integer not null, " ++
|
|
|
|
"myfloat float not null, " ++
|
|
|
|
"mybool boolean not null, " ++
|
|
|
|
"mytimestamp timestamp not null, " ++
|
|
|
|
"myany any not null, " ++
|
|
|
|
"myoptional integer, " ++
|
|
|
|
"PRIMARY KEY ((quantum(time, 15, 'm'), myfamily, myseries), " ++
|
|
|
|
"time, myfamily, myseries))";
|
|
|
|
%% an invalid TS DDL becuz family and series not both in key
|
|
|
|
get_ddl(shortkey_fail) ->
|
|
|
|
_SQL = "CREATE TABLE GeoCheckin (" ++
|
|
|
|
"myfamily varchar not null, " ++
|
|
|
|
"myseries varchar not null, " ++
|
|
|
|
"time timestamp not null, " ++
|
|
|
|
"weather varchar not null, " ++
|
|
|
|
"temperature float, " ++
|
|
|
|
"PRIMARY KEY ((quantum(time, 15, 'm'), myfamily), " ++
|
|
|
|
"time, myfamily))";
|
|
|
|
%% an invalid TS DDL becuz partition and local keys dont cover the same space
|
|
|
|
get_ddl(splitkey_fail) ->
|
|
|
|
_SQL = "CREATE TABLE GeoCheckin (" ++
|
|
|
|
"myfamily varchar not null, " ++
|
|
|
|
"myseries varchar not null, " ++
|
|
|
|
"time timestamp not null, " ++
|
|
|
|
"weather varchar not null, " ++
|
|
|
|
"temperature float, " ++
|
|
|
|
"PRIMARY KEY ((quantum(time, 15, 'm'), myfamily, myseries), " ++
|
|
|
|
"time, myfamily, myseries, temperature))";
|
|
|
|
%% another invalid TS DDL because family/series must be varchar
|
|
|
|
%% or is this total bollox???
|
|
|
|
get_ddl(keytype_fail_mebbies_or_not_eh_check_it_properly_muppet_boy) ->
|
|
|
|
_SQL = "CREATE TABLE GeoCheckin (" ++
|
|
|
|
"myfamily integer not null, " ++
|
|
|
|
"myseries varchar not null, " ++
|
|
|
|
"time timestamp not null, " ++
|
|
|
|
"weather varchar not null, " ++
|
|
|
|
"temperature float, " ++
|
|
|
|
"PRIMARY KEY ((quantum(time, 15, 'm'), myfamily, myseries), " ++
|
|
|
|
"time, myfamily, myseries))".
|
|
|
|
|
2015-09-21 11:46:19 +00:00
|
|
|
get_valid_obj() ->
|
2015-09-22 11:39:37 +00:00
|
|
|
[get_varchar(),
|
2015-09-18 09:22:03 +00:00
|
|
|
get_varchar(),
|
|
|
|
get_timestamp(),
|
|
|
|
get_varchar(),
|
2015-09-22 11:39:37 +00:00
|
|
|
get_float()].
|
|
|
|
|
|
|
|
get_invalid_obj() ->
|
|
|
|
[get_varchar(),
|
|
|
|
get_integer(), % this is the duff field
|
|
|
|
get_timestamp(),
|
|
|
|
get_varchar(),
|
|
|
|
get_float()].
|
2015-09-18 09:22:03 +00:00
|
|
|
|
|
|
|
get_varchar() ->
|
|
|
|
Len = random:uniform(?MAXVARCHARLEN),
|
2015-09-22 11:39:37 +00:00
|
|
|
String = get_string(Len),
|
|
|
|
list_to_binary(String).
|
|
|
|
|
2015-09-18 09:22:03 +00:00
|
|
|
get_string(Len) ->
|
|
|
|
get_s(Len, []).
|
|
|
|
|
2015-09-22 11:39:37 +00:00
|
|
|
get_integer() ->
|
|
|
|
get_timestamp().
|
|
|
|
|
2015-09-18 09:22:03 +00:00
|
|
|
get_s(0, Acc) ->
|
|
|
|
Acc;
|
|
|
|
get_s(N, Acc) when is_integer(N) andalso N > 0 ->
|
|
|
|
get_s(N - 1, [random:uniform(255) | Acc]).
|
|
|
|
|
|
|
|
get_timestamp() ->
|
|
|
|
random:uniform(?MAXTIMESTAMP).
|
|
|
|
|
|
|
|
get_float() ->
|
|
|
|
F1 = random:uniform(trunc(?MAXFLOAT)),
|
|
|
|
F2 = random:uniform(trunc(?MAXFLOAT)),
|
2015-09-22 11:39:37 +00:00
|
|
|
F1 - F2 + random:uniform().
|