Browse Source

CLEANUP THE REPO

Namdak Tonpa 6 years ago
parent
commit
5285c26dba

+ 0 - 4
.gitignore

@@ -1,4 +0,0 @@
-ebin
-*.dets
-*.gz
-*.dump

+ 0 - 4
.travis.yml

@@ -1,4 +0,0 @@
-language: erlang
-otp_release:
-  - 19.3
-script: "curl -fsSL https://raw.github.com/synrc/mad/master/mad > mad && chmod +x mad && ./mad dep com"

+ 0 - 1
CNAME

@@ -1 +0,0 @@
-kvs.n2o.space

+ 0 - 15
LICENSE

@@ -1,15 +0,0 @@
-Copyright (c) 2014-2018 Maxim Sokhatsky <maxim@synrc.com>
-
-Permission to use, copy, modify, and/or distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and following permission notice appear in all copies:
-
-Software may only be used for the great good and the true happiness of all sentient beings.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

+ 0 - 323
README.md

@@ -1,323 +0,0 @@
-KVS: Erlang Abstract Term Database
-=================================
-[![Build Status](https://travis-ci.org/synrc/kvs.svg?branch=master)](https://travis-ci.org/synrc/kvs)
-
-Features
---------
-
-* Polymorphic Tuples aka Extensible Records
-* Managing Linked-Lists
-* Various Backends Support: Mnesia, Riak, KAI, Redis, MongoDB
-* Sequential Consistency via Feed Server
-* Basic Schema for Social Sites and Accounting
-* Extendable Schema
-* Supports Secondary Indexes for KAI, Mnesia, Riak and MongoDB
-* Change Backends on-the-fly
-* Supports Multiple backends at the same time
-* Xen Ready
-
-Usage
------
-
-In rebar.config:
-
-```erlang
-{kvs, ".*", {git, "git://github.com/synrc/kvs", "HEAD"}}
-```
-
-Redis also need to add:
-```erlang
-{eredis, ".*", {git, "git://github.com/wooga/eredis", {tag, "v1.0.6"} }}
-```
-
-MongoDB also need to add:
-
-```erlang
-{mongodb, ".*", {git, "git://github.com/comtihon/mongodb-erlang", {tag, "master"} }},
-{poolboy, ".*", {git, "git://github.com/devinus/poolboy", {tag, "master"} }}
-```
-
-In the above example poolboy is optional. MongoDB and Poolboy config example:
-
-```erlang
-{kvs, [
-  {mongo, [
-    {connection, [{database,<<"kvs">>}]},
-    {pool, [{size,10},{max_overflow,20}]}
-  ]}
-]}
-```
-
-To disable poolboy exclude {pool, ...} from your sys.config. More information on the 
-configuring MongoDB and Poolboy can be found here: https://github.com/comtihon/mongodb-erlang, https://github.com/devinus/poolboy.
-
-Models
-------
-
-We have built with KVS a number of applications and came up with schema samples.
-We grouped schemas by three categories. KVS hides database access behind backend drivers
-and provides high-level rich API to stored and extend the following data:
-
-* **Core** — Acl, Users, Subscriptions, Feeds, Entries, Comments
-* **Banking** — Account, Customer, Transaction, Item, Currency, Program, Card, Cashback
-* **Social** — Group, Meeting, Payment, Product, Purchase
-
-Applications
-------------
-
-This Framework provides also a **feed** application for sequential consistency
-and **cr** application for chain replication database on top of **kvs**.
-All write requests with given object key will be handled by single processes
-so you may not worry about concurrent changes in user feed tops.
-
-All write operations that are made to data with secondary indexes,
-i.e. not like linked lists could be potentially handled without feed_server.
-But some KV storages are not supporting secondary indexes so use these backends carefully.
-
-Store Backends
---------------
-
-Currently **kvs** includes following store backends:
-
-* Mnesia
-* Riak
-* KAI
-* Filesystem
-* Redis
-* MongoDB
-
-Configuring
------------
-
-First of all, you need to tune your backend in the kvs application:
-
-```erlang
-{kvs, [{dba,store_mnesia}]},
-```
-
-Try to check it:
-
-```erlang
-1> kvs:config(dba).
-store_kai
-
-2> kvs:version().
-{version,"KVS KAI PURE XEN"}
-```
-
-Create a database for a single node:
-
-```erlang
-3> kvs:join().
-[kvs] Mnesia Init
-ok
-```
-
-You can also create a database by joining to existing cluster:
-
-```erlang
-3> kvs:join('kvs@synrc.com').
-```
-
-In that case you don't need to initialize the database
-to check table packages included into the schema:
-
-```erlang
-4> kvs:dir().
-[{table,"id_seq"},
- {table,"subscription"},
- {table,"feed"},
- {table,"comment"},
- {table,"entry"},
- {table,"access"},
- {table,"acl"},
- {table,"user"}]
-```
-
-Operations
-----------
-
-Try to add some data:
-
-```erlang
-1> rr(kvs_user).
-2> kvs:put(#user{id="maxim@synrc.com"}).
-ok
-3> kvs:get(user,"maxim@synrc.com").
-#user{id = "maxim@synrc.com",container = feed,...}
-4> kvs:put(#user{id="doxtop@synrc.com"}).
-5> length(kvs:all(user)).
-2
-```
-
-Polymorphic Records
--------------------
-
-The data in KVS represented as plain Erlang records. The first element of the tuple, as usual, indicates the name of a bucket. And the second element usually corresponds
-to the index key field. Additional secondary indexes could be applied for stores
-that support 2i, e.g. kai, mnesia, riak, mongodb.
-
-Iterators
----------
-
-All record could be chained into the double-linked lists in the database.
-So you can inherit from the ITERATOR record just like that:
-
-```erlang
--record(iterator, {id,version,
-                   container,feed_id,prev,
-                   next,feeds=[],guard,etc}).
-```
-
-The layout of iterators are following:
-
-```erlang
-> lists:zip(lists:seq(1,length((kvs:table(operation))#table.fields)),
-            (kvs:table(operation))#table.fields).
-
-[{1,id},
- {2,version},
- {3,container},
- {4,feed_id},
- {5,prev},
- {6,next},
- {7,feeds},
- {8,guard},
- {9,etc},
- {10,body},
- {11,name},
- {12,status}]
-```
-
-This means your table will support add/remove linked list operations to lists.
-
-```erlang
-1> kvs:add(#user{id="mes@ua.fm"}).
-2> kvs:add(#user{id="dox@ua.fm"}).
-```
-
-Read the chain (undefined means all)
-
-```erlang
-3> kvs:entries(kvs:get(feed, user), user, undefined).
-[#user{id="mes@ua.fm"},
- #user{id="dox@ua.fm"}]
-```
-
-Read flat values by all keys from table:
-
-```erlang
-4> kvs:all(user).
-[#user{id="mes@ua.fm"},
- #user{id="dox@ua.fm"}]
-```
-
-Table can be traversed from any element in any directions with ```fold/6``` processing function.
-
-Function ```entries/3``` which read the elements from top of container can be implemented by reversing the ```fold/6``` result list:
-
-```erlang
-4> {ok,F2} = kvs:get(feed,"f").
-{ok,{feed,"f",2,2,aclver}}
-
-5> lists:reverse(kvs:fold(fun(A,Acc)->[A|Acc] end, [], entry, element(#container.top, F2),2,#iterator.prev)).
-[#entry{id = 2,container = feed,feed_id = "f",prev = 1, next = [],...},
- #entry{id = 1,container = feed,feed_id = "f",prev = [], next = 2,...}]
- ```
-
-Containers
-----------
-
-If you are using iterators records this automatically means you are using containers.
-Containers are just boxes for storing top/heads of the linked lists. Here is layout
-of containers:
-
-```erlang
--record(container, {id,top,count}).
-```
-
-```erlang
-> lists:zip(lists:seq(1,length((kvs:table(feed))#table.fields)),
-            (kvs:table(feed))#table.fields).
-[{1,id},
- {2,top},
- {3,count},
- {4,aclver}]
-```
-
-Extending Schema
-----------------
-
-Usually, you need only specify custom mnesia indexes and tables tuning.
-Riak, KAI and Redis backends don't need it. Group you table into table packages
-represented as modules with handle_notice API.
-
-```erlang
--module(kvs_feed).
--inclue_lib("kvs/include/metainfo.hrl").
-
-metainfo() -> 
-    #schema{name=kvs,tables=[
-        #table{name=feed,container=true,fields=record_info(fields,feed)},
-        #table{ name=entry,container=feed,fields=record_info(fields,entry),
-                keys=[feed_id,entry_id,from]},
-        #table{name=comment,container=feed,fields=record_info(fields,comment),
-                keys=[entry_id,author_id]} ]}.
-```
-
-And plug it into schema config:
-
-```erlang
-{kvs, {schema,[kvs_user,kvs_acl,kvs_feed,kvs_subscription]}},
-```
-
-And on database init
-
-```erlang
-1> kvs:join().
-```
-
-It will create your custom schema.
-
-Using KVS in real applications
-------------------------------
-
-Besides using KVS in production in a number of applications we have
-built on top of KVS several products. The first product is Chain
-Replication Database with XA protocol. And second is social Feed
-Server for web shops and social sites.
-
-### Chain Replication Database
-
-The **kvs** semantic is totally compatible with XA protocol.
-Adding the object with PUT means only putting to database
-while ADD operations provide linking to the chain's container.
-Also linking operation LINK is provided separately.
-
-```erlang
-dispatch({prepare,_,_,Tx}, #state{})  ->
-    kvs:info(?MODULE,"KVS PUT ~p:~p~n",[element(1,Tx),element(2,Tx)]),
-    kvs:put(Tx);
-
-dispatch({commit,_,_,Tx}, #state{})  ->
-    kvs:info(?MODULE,"KVS LINK ~p:~p~n",[element(1,Tx),element(2,Tx)]),
-    kvs:link(Tx);
-
-dispatch({rollback,_,_,Tx}, #state{})  ->
-    kvs:info(?MODULE,"KVS REMOVE ~p:~p~n",[element(1,Tx),element(2,Tx)]),
-    kvs:remove(Tx);
-```
-
-See: https://github.com/spawnproc/cr
-
-Credits
--------
-
-* Maxim Sokhatsky
-* Andrii Zadorozhnii
-* Vladimir Kirillov
-* Alex Kalenuk
-* Sergey Polkovnikov
-* Andrey Martemyanov
-
-OM A HUM

+ 0 - 14
include/acl.hrl

@@ -1,14 +0,0 @@
--ifndef(ACL_HRL).
--define(ACL_HRL, true).
-
--include("kvs.hrl").
-
--record(acl, {?CONTAINER}).
-
--record(access, {?ITERATOR(acl),
-        entry_id=[],
-        acl_id=[],
-        accessor=[],
-        action=[]}).
-
--endif.

+ 0 - 62
include/api.hrl

@@ -1,62 +0,0 @@
--ifndef(API_HRL).
--define(API_HRL, true).
-
--include("metainfo.hrl").
-
-% exports
-
--export([start/0,stop/0]).                                        % service
--export([destroy/0,join/0,join/1,init/2]).                        % schema change
--export([modules/0,containers/0,tables/0,table/1,version/0]).     % meta info
--export([create/1,add/1,link/1,unlink/1,remove/2]).               % chain ops
--export([put/1,delete/2,next_id/2]).                              % raw ops
--export([get/2,get/3,index/3]).                                   % read ops
--export([load_db/1,save_db/1]).                                   % import/export
-
-
-% service
-
--spec start() -> ok | {error,any()}.
--spec stop() -> stopped.
-
-% schema change
-
--spec destroy() -> ok.
--spec join() -> ok | {error,any()}.
--spec join(Node :: string()) -> [{atom(),any()}].
--spec init(Backend :: atom(), Module :: atom()) -> list(#table{}).
-
-% meta info
-
--spec modules() -> list(atom()).
--spec containers() -> list({atom(),list(atom())}).
--spec tables() -> list(#table{}).
--spec table(Tab :: atom()) -> #table{}.
--spec version() -> {version,string()}.
-
-% chain ops
-
--spec create(Container :: atom()) -> integer().
--spec add(Record :: tuple()) -> {ok,tuple()} | {error,exist} | {error,no_container} | {error,just_added} | {aborted,any()}.
--spec link(Record :: tuple()) -> {ok,tuple()} | {error, not_found} | {error,no_container} | {error,just_added}.
--spec unlink(Record :: tuple()) -> {ok,tuple()} | {error,no_container}.
--spec remove(Tab :: atom(), Key :: any()) -> ok | {error,any()}.
--spec next_id(Tab :: atom() | string(), Key :: integer()) -> integer().
-
-% raw ops
-
--spec put(Record :: tuple()) -> ok | {error,any()}.
--spec delete(Tab :: atom(), Key :: any()) -> ok | {error,any()}.
-
-% read ops
-
--spec get(Tab :: atom(), Key :: any()) -> {ok,any()} | {error,duplicated} | {error,not_found}.
--spec get(Tab :: atom(), Key :: any(), Value :: any()) -> {ok,any()} | {error,duplicated} | {error,not_found}.
--spec index(Tab :: atom(), Key :: any(), Value :: any()) -> list(tuple()).
-
-% import/export
-
--spec load_db(Path :: string()) -> list(ok | {error,any()}).
--spec save_db(Path :: string()) -> ok | {error,any()}.
-
--endif.

+ 0 - 15
include/comment.hrl

@@ -1,15 +0,0 @@
--ifndef(COMMENT_HRL).
--define(COMMENT_HRL, true).
-
--include("kvs.hrl").
-
--record(comment, {?ITERATOR(feed), % {comment_id, entry_id, feed_id}
-        comment_id=[],
-        entry_id=[],
-        content=[],
-        from=[],
-        created=[],
-        media = [],
-        parent=[]}).
-
--endif.

+ 0 - 9
include/config.hrl

@@ -1,9 +0,0 @@
--ifndef(CONFIG_HRL).
--define(CONFIG_HRL, true).
-
--record(config, {key, value=[]}).
-
--define(DBA, (application:get_env(kvs,dba,store_mnesia))).
--define(MQ, (kvs:config(kvs,mq,kvs))).
-
--endif.

+ 0 - 19
include/entry.hrl

@@ -1,19 +0,0 @@
--ifndef(ENTRY_HRL).
--define(ENTRY_HRL, true).
-
--include("kvs.hrl").
-
--record(entry, {?ITERATOR(feed), % {entry_id, feed_id}
-        entry_id=[],
-        from=[],
-        to=[],
-        title=[],
-        description=[],
-        created=[],
-        access=[],
-        shared=[],
-        starred=[],
-        media = [],
-        type = []}).
-
--endif.

+ 0 - 8
include/feed.hrl

@@ -1,8 +0,0 @@
--ifndef(FEED_HRL).
--define(FEED_HRL, true).
-
--include("kvs.hrl").
-
--record(feed, {?CONTAINER, aclver=[]}).
-
--endif.

+ 0 - 16
include/group.hrl

@@ -1,16 +0,0 @@
--ifndef(GROUP_HRL).
--define(GROUP_HRL, true).
-
--include("kvs.hrl").
-
--record(group,{?ITERATOR(feed),
-        name=[],
-        description=[],
-        scope=[],
-        creator=[],
-        created=[],
-        owner=[],
-        users_count = 0,
-        entries_count = 0}).
-
--endif.

+ 0 - 55
include/kvs.hrl

@@ -1,55 +0,0 @@
--ifndef(KVS_HRL).
--define(KVS_HRL, true).
-
--record(writer,  {id    = [] :: term(), % {p2p,_,_} | {muc,_}
-                  count =  0 :: integer(),
-                  cache = [] :: [] | tuple(),
-                  args  = [] :: term(),
-                  first = [] :: [] | tuple()}).
-
--record(reader,  {id    = [] :: term(), % phone_id | {p2p,_,_} | {muc,_,_}
-                  pos   = 0 :: [] | integer(),
-                  cache = [] :: [] | integer() | {atom(),term()},
-                  args  = [] :: term(),
-                  feed  = [] :: term(), % {p2p,_,_} | {muc,_} -- link to writer
-                  dir   =  0 :: 0 | 1}).
-
--define(CUR,  id =  [] :: term(),
-              top=  [] :: [] | integer(),
-              bot=  [] :: [] | integer(),
-              dir=   0 ::  0 | 1,
-              reader=  [] :: [] | tuple(),
-              writer=  [] :: [] | tuple()).
--record(cur,  {?CUR, left=0, right=0, args=[]::list(tuple()|integer()), money=0, status=[]}).
-
--define(ITER, id   = [] :: [] | integer(),
-              container=[] :: atom(),
-              feed = [] :: term(),
-              next = [] :: [] | integer(),
-              prev = [] :: [] | integer()).
-
--record(iter, {?ITER}).
-
--define(CONTAINER, id=[] :: [] | integer(),
-                   top=[] :: [] | integer(),
-                   rear=[] :: [] | integer(),
-                   count=0 :: integer()).
-
--define(ITERATOR(Container), id=[] :: [] | integer(),
-                             container=Container :: atom(),
-                             feed_id=[] :: term(),
-                             prev=[] :: [] | integer(),
-                             next=[] :: [] | integer(),
-                             feeds=[] :: list()).
-
--record(id_seq,    {thing, id}).
--record(container, {?CONTAINER}).
--record(iterator,  {?ITERATOR([])}).
--record(block,     {left,right,name,last}).
--record(log,       {?CONTAINER, name, acc}).
--record(operation, {?ITERATOR(log), body=[], name=[], status=[]}).
--record(kvs,       {mod = store_mnesia,cx}).
-
--compile({no_auto_import,[put/2]}).
-
--endif.

+ 0 - 9
include/metainfo.hrl

@@ -1,9 +0,0 @@
--ifndef(METAINFO_HRL).
--define(METAINFO_HRL, true).
-
--record(schema, {name,tables=[]}).
--record(table,  {name,container=feed,type=set,fields=[],keys=[],copy_type=application:get_env(kvs,mnesia_media,disc_copies),columns,order_by}).
--record(column, {name,type,key=false,ro=false,transform}).
--record(query,  {body,types=[],values=[],next_ph_num = 1}).
-
--endif.

+ 0 - 27
include/product.hrl

@@ -1,27 +0,0 @@
--ifndef(PRODUCT_HRL).
--define(PRODUCT_HRL, true).
-
--include("kvs.hrl").
-
--record(product, {?ITERATOR(feed),
-        ext_id                 :: term(),    % ext
-        vendor_id              :: integer(), % auto
-        categories             :: list(integer()), % admin
-        creator,
-        owner,
-        title,
-        brief,
-        cover,
-        publish_start_date     :: calendar:date_time(), % admin
-        publish_end_date       :: calendar:date_time(), % admin
-        price = 0              :: integer(),
-        currency               :: integer(),  % currency charge
-        retailer_price         :: integer(), % ext
-        our_price              :: integer(), % auto
-        fee                    :: integer(),  % net membership fee
-        enabled                :: boolean(), % admin
-        for_sale               :: boolean(),
-        created                :: calendar:date_time(), % auto
-        modify_date            :: calendar:date_time() }).
-
--endif.

+ 0 - 49
include/sql.hrl

@@ -1,49 +0,0 @@
--ifndef(MEKAO_HRL).
--define(MEKAO_HRL, true).
-
--include("metainfo.hrl").
-
-% MEKAO SQL
-
--record(sql_select,   {table,columns,where,order_by}).
--record(sql_insert,   {table,columns,values,returning}).
--record(sql_update,   {table,set,where,returning}).
--record(sql_delete,   {table,where,returning}).
--record(sql_settings, {placeholder,returning,is_null = fun(X) -> X == undefined end}).
-
--spec insert(entity(), table(), s()) -> {ok, b_query()} | {error, empty_insert}.
--spec select_pk(selector(), table(), s()) -> {ok, b_query()} | {error, pk_miss}.
--spec select(selector(), table(), s()) -> {ok, b_query()}.
--spec update_pk(selector(), table(), s()) -> {ok, b_query()} | {error, pk_miss} | {error, empty_update}.
--spec update_pk_diff( Old :: entity(), New :: entity(), table(), s()) -> {ok, b_query()} | {error, pk_miss} | {error, empty_update}.
--spec update(entity(), selector(), table(), s()) -> {ok, b_query()} | {error, empty_update}.
--spec delete_pk(selector(), table(), s()) -> {ok, b_query()} | {error, pk_miss}.
--spec delete(selector(), table(), s()) -> {ok, b_query()}.
--spec prepare_insert(entity(), table(), s()) -> p_query().
--spec prepare_select(selector(), table(), s()) -> p_query().
--spec prepare_delete(selector(), table(), s()) -> p_query().
--spec prepare_update(entity(), selector(), table(), s()) -> p_query().
--spec returning(insert | update | delete, table(), s()) -> iolist().
--spec build(p_query()) -> b_query().
-
--type b_query() :: 'query'(iolist()).
--type table()   :: #table{}.
--type column()  :: #column{}.
--type s()       :: #sql_settings{}.
--type entity()      :: tuple() | list().
--type selector()    :: tuple() | list(predicate(term())).
--type predicate(Value) :: Value | { '$predicate', '=' | '<>' | '>' | '>=' | '<' | '<=', Value}.
--type 'query'(Body) :: #query{body :: Body}.
--type p_query() :: 'query'( #sql_insert{}
-                          | #sql_select{}
-                          | #sql_update{}
-                          | #sql_delete{}
-                          ).
-
--export_type([
-    table/0, column/0, s/0,
-    p_query/0, b_query/0,
-    predicate/1
-]).
-
--endif.

+ 0 - 11
include/state.hrl

@@ -1,11 +0,0 @@
--ifndef(KVS_STATE_HRL).
--define(KVS_STATE_HRL, true).
-
--record(state, {
-        owner = "feed_owner",
-        type :: user | group | system | product,
-        feeds = [],
-        callback,
-        cached_feeds=[]}).
-
--endif.

+ 0 - 13
include/subscription.hrl

@@ -1,13 +0,0 @@
--ifndef(SUBSCRIPTION_HRL).
--define(SUBSCRIPTION_HRL, true).
-
--record(subscription,{
-        key=[],
-        who=[],
-        whom=[],
-        what=[],
-        how=[],
-        date=[],
-        note=[]}).
-
--endif.

+ 0 - 21
include/user.hrl

@@ -1,21 +0,0 @@
--ifndef(USER_HRL).
--define(USER_HRL, true).
--include("kvs.hrl").
--record(user, {?ITERATOR(feed),
-        email=[]::[]|binary(),
-        username=[],
-        password=[],
-        display_name=[],
-        register_date=[],
-        tokens = [],
-        images=[],
-        names=[],
-        surnames=[],
-        birth=[],
-        sex=[],
-        date=[],
-        status=[],
-        zone=[],
-        type=[],
-        new_field }).
--endif.

+ 0 - 66
index.html

@@ -1,66 +0,0 @@
-<html>
-
-<head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <meta name="description" content="" />
-    <meta name="author" content="Maxim Sokhatsky" />
-    <title>KVS</title>
-    <link rel="stylesheet" href="https://synrc.space/synrc.css?v=2" />
-</head>
-
-<body>
-
-<nav>
-    <a href='https://synrc.space/'>SYNRC</a>
-    <a href='https://n2o.space'>N2O</a>
-</nav>
-
-<header>
-    <a href="https://n2o.space"><img src="https://synrc.space/images/Synrc Neo.svg?v=1"></a>
-    <h1>KVS</h1>
-</header>
-
-<aside>
-    <article>
-        <section>
-            <h3>Motivation</h3>
-            <div>
-            </div>
-        </section>
-
-        <section>
-            <a name=plugin><h3>MANUAL</h3></a>
-            <div><ul>
-           <li><a href="man/kvs.htm">KVS</a></font></li>
-           <li><a href="man/kvs_stream.htm">STREAM</a></font></li>
-            </ul></div>
-            <br>
-            <div>
-                26 APR 2017 &copy; <a href="http://5ht.co/license.htm">DHARMA LICENSE</a>
-            </div>
-
-        </section>
-    </article>
-</aside>
-<main>
-    <section>
-
-    <section>
-        <a name=plugin><h3>Related Documents</h3></a>
-        <div>
-        <ul>
-           <li><a href="https://n2o.space/articles/web.htm">N2O: SERVER</a></li>
-           <li><a href="https://5ht.co/spawnproc.htm">N2O: PRIVATBANK</a></li>
-           <li><a href="https://synrc.com/apps/cr/doc/cr.htm">CR: DATABASE</a></li>
-        </ul></div>
-    </section>
-</main>
-
-<footer>
-    Made with <span class="heart">❤</span> for EMQ and N2O
-</footer>
-
-</body>
-</html>

+ 0 - 112
man/kvs.htm

@@ -1,112 +0,0 @@
-<html>
-
-<head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <meta name="description" content="" />
-    <meta name="author" content="Maxim Sokhatsky" />
-    <title>KVS</title>
-    <link rel="stylesheet" href="https://synrc.space/synrc.css" />
-</head>
-
-<body>
-
-<nav>
-    <a href='https://synrc.space/'>SYNRC</a>
-    <a href='https://n2o.space'>N2O</a>
-    <a href='https://kvs.n2o.space'>KVS</a>
-</nav>
-
-<header>
-    <a href="../index.html"><img src="../Synrc Neo.svg"></a>
-    <h1>KVS</h1>
-</header>
-
-<main>
-    <section>
-
-<h3>INTRO</h3>
-
-   <p>This module is used to manage different KV backends to your cloud of Erlang apps.
-      This is an unified abstraction layer to several databases: <b>redis</b>,
-      <b>mongo</b>, <b>mnesia</b>, <b>riak</b>, <b>aerospike</b>.</p>
-
-        <p><blockquote><p><ul>
-            <li><b><a href="#dir">dir/0</a></b> &mdash; table list.</li>
-            <li><b><a href="#ver">ver/0</a></b> &mdash; KVS version.</li>
-            <li><b><a href="#seq">seq/2</a></b> &mdash; generate new sequence id for table.</li>
-            <li><b><a href="#count">count/1</a></b> &mdash; records in table.</li>
-            <li><b><a href="#put">put/1</a></b> &mdash; put record using element 2 as key.</li>
-            <li><b><a href="#get">get/2</a></b> &mdash; get record by key from table.</li>
-            <li><b><a href="#delete">delete/1</a></b> &mdash; delete record from table.</li>
-            <li><b><a href="#index">index/3</a></b> &mdash; index by field and its value.</li>
-        </ul></p></blockquote></p>
-
- <p>You can change backend by setting application env. This behaves well on </p>
-
-<figure><code>
- application:set_env(kvs,dba,store_mnesia).
-
-</code></figure>
-
-    </section>
-    <section>
-
-<h3>RECORDS</h3>
-
-<figure><figcaption>KVS CORE</figcaption><code>
-    #ok { data= [] :: term() }.
-
- #error { data= [] :: term() }.
-
-   #cur { id=   [] :: term(),
-          top=  [] :: [] | integer(),
-          bot=  [] :: [] | integer(),
-          dir=   0 ::  0 | 1,
-          reader= [] :: [] | tuple(),
-          writer= [] :: [] | tuple(),
-          left=  0 :: integer(),
-          right= 0 :: inetegr() }.
-
-  #iter { id=   [] :: [] | integer(),
-          prev= [] :: [] | integer(),
-          next= [] :: [] | integer()).
-
-</code></figure>
-
-<p><ul>
-<li>id &mdash; Unique key of the cursor.</li>
-</ul></p>
-
-    </section>
-    <section>
-
-<h3>CONFIG</h3>
-
-<p>In sys.config you should specify kvs backend and list of modules
-   containing <b>metainfo/0</b> exported function.</p>
-
-<figure><code>
-  [{kvs, [{dba, store_mnesia},
-          {schema, [kvs]} ]}].
-
-</code></figure>
-
-    </section>
-    <section>
-
-<p>This module may refer to:
-<a href="http://erlang.org/doc/man/mnesia.html">mnesia</a></b>,
-<a href="kvs_stream.htm"><b>kvs_stream</b></a>.
-</p>
-
-    </section>
-</main>
-
-<footer>
-    2005&mdash;2017 &copy; Synrc Research Center
-</footer>
-
-</body>
-</html>

+ 0 - 406
man/kvs_stream.htm

@@ -1,406 +0,0 @@
-<html>
-
-<head>
-    <meta charset="utf-8" />
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <meta name="description" content="" />
-    <meta name="author" content="Maxim Sokhatsky" />
-    <title>KVS</title>
-    <link rel="stylesheet" href="https://synrc.space/synrc.css" />
-</head>
-
-<body>
-
-<nav>
-    <a href='https://synrc.space/'>SYNRC</a>
-    <a href='https://n2o.space'>N2O</a>
-    <a href='https://kvs.n2o.space'>KVS</a>
-</nav>
-
-<header>
-    <a href="../index.html"><img src="../Synrc Neo.svg"></a>
-    <h1>STREAM</h1>
-</header>
-
-<main>
-    <section>
-
-<h3>INTRO</h3>
-
-<p>The <b>kvs_stream</b> is intended to store, retrieve and manage doubly-linked lists
-   using simple key-value interface as underlying API.
-   The descriptor of the cursor consist mainly of two parts (reader and writer).
-   Writer holds the cached value of the top or bottom of the stream.
-   Reader holds the cached value of current cursor position.
-   Descriptor also tracks moving direction and distances to both ends,
-   which are updated during reader cursor repositioning.
-   Cursor should be stored in database.
-   If there is no cursor for some data then this data is not alive yet.
-   The data could be added only to list ends using writer cursor.
-   The data in list could be removed only by record id.
-   The list could not contain duplicates or even records with the same id.
-   When you consume the stream, the data is not deleted.
-   </p>
-
-        <p><blockquote><p><ul>
-            <li><b><a href="#new">new/0</a></b> &mdash; new list.</li>
-            <li><b><a href="#save">save/1</a></b> &mdash; store cursor to db.</li>
-            <li><b><a href="#load">load/1</a></b> &mdash; load cursor from db.</li>
-            <li><b><a href="#top">top/1</a></b> &mdash; top of the list.</li>
-            <li><b><a href="#bot">bot/1</a></b> &mdash; bottom of the list.</li>
-            <li><b><a href="#up">up/1</a></b> &mdash; up from bottom (default).</li>
-            <li><b><a href="#down">down/1</a></b> &mdash; down from top.</li>
-            <li><b><a href="#next">next/1</a></b> &mdash; move reader next.</li>
-            <li><b><a href="#prev">prev/1</a></b> &mdash; move reader prev.</li>
-            <li><b><a href="#take">take/1</a></b> &mdash; take N elements staring from reader.</li>
-            <li><b><a href="#drop">drop/1</a></b> &mdash; skip N elements staring from reader.</li>
-            <li><b><a href="#add">add/1</a></b> &mdash; add element to list.</li>
-            <li><b><a href="#remove">remove/1</a></b> &mdash; remove element from list.</li>
-            <li><b><a href="#cons">cons/1</a></b> &mdash; add element to top.</li>
-            <li><b><a href="#snoc">snoc/1</a></b> &mdash; add element to bot.</li>
-        </ul></p></blockquote></p>
-
-    <p>
-      You can grab <a style="margin-bottom:30px;"
-       href="https://raw.githubusercontent.com/synrc/kvs/master/src/kvs_stream.erl">kvs_stream</a>
-      and use it in your applications without importing <b>synrc/kvs</b> dependency,
-      as this module is self-containing.
-      The possible applications are: public and private feeds, FIFO queues,
-   unread messages, chat applications, blockchain, etc.<br><br>
-
-    </section>
-    <section>
-
-<h3>RECORDS</h3>
-
-<figure><figcaption>KVS CORE</figcaption><code>
-    #ok { data= [] :: term() }.
-
- #error { data= [] :: term() }.
-
-   #cur { id=   [] :: term(),
-          top=  [] :: [] | integer(),
-          bot=  [] :: [] | integer(),
-          dir=   0 ::  0 | 1,
-          reader= [] :: [] | tuple(),
-          writer= [] :: [] | tuple(),
-          left=  0 :: integer(),
-          right= 0 :: inetegr(),
-          args= [] :: term(),
-          status=[]:: add | remove | save | load
-                    | next | prev | take | drop
-                    | top | bot }.
-
-  #iter { id=   [] :: [] | integer(),
-          prev= [] :: [] | integer(),
-          next= [] :: [] | integer()).
-
-</code></figure>
-
-<p><ul>
-<li>id &mdash; Unique key of the cursor.</li>
-<li>writer &mdash; Append writes to one of the two ends of the list.</li>
-<li>reader &mdash; Reader cursor tracks distances to edges.</li>
-<li>dir=0 &mdash; Adding to top, reading up from bottom (default).</li>
-<li>dir=1 &mdash; Adding to bottom, reading down from top.</li>
-<li>top &mdash; The id of top element of the list.</li>
-<li>bot &mdash; The id of bottom element of the list.</li>
-<li>args &mdash; integer for take/drop/remove/get, tuple for add/put</li>
-<li>status &mdash; operation</li>
-<li>left &mdash; The distance to top from reader.</li>
-<li>right &mdash; The distance to bottom from reader.</li>
-</ul></p>
-
-<p><ul>
-<li>id &mdash; Unique key of the record in the list.</li>
-<li>next &mdash; The next element of the list.</li>
-<li>prev &mdash; The prev element of the list.</li>
-</ul></p>
-
-    </section>
-    <section>
-
-<h3>EXAMPLE</h3>
-
-<figure><code>
- -module(test).
- -include_lib("kvs/include/kvs.hrl").
- -include_lib("kvs/include/user.hrl").
- -compile(export_all).
-
- set(X,Y,Z) -> setelement(X,Z,Y).
-
- check() ->
-     Cur = kvs_stream:new(),
-     [A,B,C,D] = [ kvs:seq(person,1)
-              || _ <- lists:seq(1,4) ],
-     R = kvs_stream:save(
-         kvs_stream:add(set(#cur.args,#person{id=A},
-         kvs_stream:add(set(#cur.args,#person{id=B},
-         kvs_stream:add(set(#cur.args,#person{id=C},
-         kvs_stream:add(set(#cur.args,#person{id=D}, Cur ))))))))),
-     X = kvs_stream:take(
-         kvs_stream:down(
-         kvs_stream:top(R#cur{args=-1}))),
-     Y = kvs_stream:take(
-         kvs_stream:up(
-         kvs_stream:bot(R#cur{args=-1}))),
-     X = lists:reverse(Y),
-     L = length(X).
-
- > test:check().
- 4
-
- > kvs:all(cur).
- [{cur,1,{user2,1,3,[],[],[],[],[],[]},0,1,2}]
-
- > kvs_stream:take(
-   kvs_stream:top((kvs:get(cur,1))#cur{args=-1})).
-[{user2,2,[],4,[],[],[],[],[]},
- {user2,4,2,3,[],[],[],[],[]},
- {user2,3,4,1,[],[],[],[],[]},
- {user2,1,3,[],[],[],[],[],[]}]
-
-</code></figure>
-    </section>
-    <section>
-
-<h3>API</h3>
-
-<p>We prepared for you sequential steps manual.
-   Just enter one by one the erlang commands
-   in shell in right order and check the results.</p>
-
-<a name=new></a>
-<h4>new() -> #cur{}.</h4>
-
-<p>Creates a KVS cursor.</p>
-
-<figure><code>
- > kvs_stream:new().
- {cur,1,[],[],0,[],[],0,0,[],0,[]}
- > kvs:get(id_seq,"cur").
- {ok,{id_seq,"cur",1}}
- > kvs_stream:new().
- {cur,2,[],[],0,[],[],0,0,[],0,[]}
- > kvs:get(id_seq,"cur").
- {ok,{id_seq,"cur",2}}
-
-</code></figure>
-
-<a name=save></a>
-<h4>save(#cur{}) -> #cur{}.</h4>
-
-<p>Saves the cursor to database.<?p>
-
-<figure><code>
- > kvs:all(cur).
- []
- > kvs_stream:save(kvs_stream:new()).
- {cur,3,[],[],0,[],[],0,0,[],0,[]}
- > kvs:all(cur).
- [{cur,3,[],[],0,[],[],0,0,[],0,[]}]
-
-</code></figure>
-
-<a name=load></a>
-<h4>load(Id) -> #ok{data::#cur{}} | #error{}.</h4>
-
-<p>Gets a cursor from database.</p>
-
-<figure><code>
-  > S = 3, kvs_stream:load(S).
-  {cur,3,[],[],0,[],[],0,0,[],0,[]}
-
-</code></figure>
-
-<a name=add></a>
-<h4>add(#cur{args::tuple()}) -> #cur{}.</h4>
-
-<p>Adds any object to datatabase and update writer cursor.
-   Object is linked on next prev fields with existed data under cursor.
-   If cursor doesn't contain top or bottom value the additional
-   seek to the end is performed according to cursor direction.</p>
-
-<figure><code>
- > rr(kvs_stream).
- [block,container,cur,id_seq,iter,iterator,kvs,log,operation]
-
- > P = {user2,[],[],[],[],[],[],[],[]},
-   kvs_stream:save(kvs_stream:add((kvs_stream:load(S))#cur{args=P})).
- #cur{id = 3,top = 1,bot = 1,dir = 0,
-      reader = {user2,1,[],[],[],[],[],[],[]},
-      writer = {user2,1,[],[],[],[],[],[],[]},
-      left = 0,right = 0,args = [],money = 0,status = []}
-
-</code></figure>
-
-<a name=prev></a>
-<h4>prev(#cur{}) -> #cur{} | #error{}.</h4>
-
-<p>Moves cursor to prev. Consume data up from bottom.
-   Reutrn error if lists is empty, otherwise next element or last.</p>
-
-<figure><code>
- > kvs_stream:prev(kvs_stream:load(S)).
- {error,not_found}
-
-</code></figure>
-
-<a name=next></a>
-<h4>next(#cur{}) -> #cur{} | #error{}.</h4>
-
-<p>Moves cursor to next. Consume data down from top.
-   Reutrn error if lists is empty, otherwise next element or last.</p>
-
-<figure><code>
- > kvs_stream:next(kvs_stream:top(
-   kvs_stream:save(kvs_stream:add((kvs_stream:load(S))#cur{args=P})))).
-  #cur{id = 3,top = 2,bot = 1,dir = 1,
-      reader = {user2,1,[],2,[],[],[],[],[]},
-      writer = {user2,2,1,[],[],[],[],[],[]},
-      left = 0,right = 1,args = [],money = 0,status = []}
-
-</code></figure>
-
-<a name=seek></a>
-<h4>seek(#cur{}) -> #cur{} | #error{}.</h4>
-
-<p>Moves cursor to begin or end of the list depending of cursor order.
-   If cursor has no cached value then function returns error.</p>
-
-<figure><code>
- > Stream = kvs_stream:save(kvs_stream:add((kvs_stream:load(S))#cur{args=P})),
-   kvs_stream:seek(kvs_stream:up(Stream)).
- #cur{id = 3,top = 3,bot = 1,dir = 0,
-      reader = {user2,1,[],2,[],[],[],[],[]},
-      writer = {user2,3,2,[],[],[],[],[],[]},
-      left = 2,right = 0,args = [],money = 0,status = []}
-
-</code></figure>
-
-<a name=top></a>
-<h4>top(#cur{}) -> #cur{} | #error{}.</h4>
-
-<p>Moves cursor to top of the list.</p>
-
-<figure><code>
- > kvs_stream:top(kvs_stream:load(S)).
- #cur{id = 3,top = 3,bot = 1,dir = 1,
-     reader = {user2,3,2,[],[],[],[],[],[]},
-     writer = {user2,3,2,[],[],[],[],[],[]},
-     left = 2,right = 0,args = [],money = 0,status = []}
-
-</code></figure>
-
-<a name=bot></a>
-<h4>bot(#cur{}) -> #cur{} | #error{}.</h4>
-
-<p>Moves cursor to bottom of the list.</p>
-
-<figure><code>
- > kvs_stream:bot(kvs_stream:load(S)).
- #cur{id = 3,top = 3,bot = 1,dir = 0,
-      reader = {user2,1,[],2,[],[],[],[],[]},
-      writer = {user2,3,2,[],[],[],[],[],[]},
-      left = 2,right = 0,args = [],money = 0,status = []}
-
-</code></figure>
-
-<a name=take></a>
-<h4>take(#cur{args=N::integer()}) -> list().</h4>
-
-<p>Trying to consume N records from stream using its current value and direction.
-   Returns consumed data. Usually you seek to some position and then consume some data.</p>
-
-<figure><code>
- > kvs_stream:take((kvs_stream:load(S))#cur{args=-1}).
- #cur{id = 3,top = 3,bot = 1,dir = 0,
-      reader = {user2,1,[],2,[],[],[],[],[]},
-      writer = {user2,3,2,[],[],[],[],[],[]},
-      left = 2,right = 0,
-      args = [{user2,3,2,[],[],[],[],[],[]},
-              {user2,2,1,3,[],[],[],[],[]},
-              {user2,1,[],2,[],[],[],[],[]}],
-      money = 0,status = []}
-
-</code></figure>
-
-<a name=drop></a>
-<h4>drop(#cur{args=N::integer()}) -> #cur{}.</h4>
-
-<p>Drops N elements starting from reader.</p>
-
-<a name=cons></a>
-<h4>cons(#cur{args=M::#iter{}}) -> #cur{}.</h4>
-
-<p>Adds element to top.</p>
-
-<a name=snoc></a>
-<h4>snoc(#cur{args=M::#iter{}}) -> #cur{}.</h4>
-
-<p>Adds element to bottom.</p>
-
-<a name=down></a>
-<h4>down(#cur{}) -> #cur{}.</h4>
-
-<p>Changes the cursor direction.</p>
-
-<a name=up></a>
-<h4>up(#cur{}) -> #cur{}.</h4>
-
-<p>Changes the cursor direction (default).</p>
-
-<a name=remove></a>
-<h4>remove(Id,#cur{}) -> #cur{} | #error{}.</h4>
-
-<p>Removes record by id from database and unlink it from list.
-   If cursor has no cached value then function returns error.
-   Please do not use remove, keep your data immutable :-)</p>
-
-<figure><code>
- > kvs_stream:take((kvs_stream:top(kvs_stream:save(
-   kvs_stream:remove((kvs_stream:load(S))#cur{args=
-   Stream#cur.top-1}))))#cur{args=-1}).
- #cur{id = 3,top = 3,bot = 1,dir = 1,
-      reader = {user2,3,1,[],[],[],[],[],[]},
-      writer = {user2,1,[],3,[],[],[],[],[]},
-      left = 1,right = 0,
-      args = [{user2,1,[],3,[],[],[],[],[]},
-              {user2,3,1,[],[],[],[],[],[]}],
-      money = 0,status = []}
-
-</code></figure>
-
-    </section>
-    <section>
-
-<h3>CONFIG</h3>
-
-<p>In sys.config you should specify kvs backend and list of modules
-   containing <b>metainfo/0</b> exported function.</p>
-
-<figure><code>
-  [{kvs, [{dba, store_mnesia},
-          {schema, [kvs]} ]}].
-
-</code></figure>
-
-    </section>
-    <section>
-
-<p>This module may refer to:
-<a href="http://erlang.org/doc/man/mnesia.html">mnesia</a></b>,
-<a href="kvs.htm"><b>kvs</b></a>.
-</p>
-
-    </section>
-</main>
-
-<footer>
-    2005&mdash;2017 &copy; Synrc Research Center
-</footer>
-
-</body>
-</html>

+ 0 - 4
rebar.config

@@ -1,4 +0,0 @@
-{lib_dirs, [".."]}.
-{erl_opts, [nowarn_export_all]}.
-{deps_dir, ["deps"]}.
-{deps,[]}.

+ 0 - 7
src/kvs.app.src

@@ -1,7 +0,0 @@
-{application, kvs,
-   [{description, "KVS Abstract Term Database"},
-    {vsn, "6.5"},
-    {registered, []},
-    {applications, [kernel,stdlib,mnesia]},
-    {mod, { kvs_app, []}},
-    {env, []} ]}.

+ 0 - 425
src/kvs.erl

@@ -1,425 +0,0 @@
--module(kvs).
--copyright('Synrc Research Center s.r.o.').
--compile(export_all).
-%-include_lib("stdlib/include/qlc.hrl").
--include("config.hrl").
--include("metainfo.hrl").
--include("kvs.hrl").
--include("user.hrl").
--include("api.hrl").
-
-% Public Main Backend is given in sys.config and
-% could be obtained with application:get_env(kvs,dba,store_mnesia).
-
-delete(Table,Key)  -> delete  (Table, Key, #kvs{mod=?DBA}).
-remove(Table,Key)  -> remove  (Table, Key, #kvs{mod=?DBA}).
-get(Table,Key)     -> get     (Table, Key, #kvs{mod=?DBA}).
-index(Table,K,V)   -> index   (Table, K,V, #kvs{mod=?DBA}).
-change_storage(Table,Type) -> change_storage(Table,Type, #kvs{mod=?DBA}).
-entries(A,B,C)     -> entries (A,B,C, #kvs{mod=?DBA}).
-join()             -> join    ([],    #kvs{mod=?DBA}).
-join(Node)         -> join    (Node,  #kvs{mod=?DBA}).
-count(Table)       -> count   (Table, #kvs{mod=?DBA}).
-add(Record)        -> add     (Record, #kvs{mod=?DBA}).
-all(Table)         -> all     (Table, #kvs{mod=?DBA}).
-put(Record)        -> put     (Record, #kvs{mod=?DBA}).
-link(Record)       -> link    (Record, #kvs{mod=?DBA}).
-unlink(Record)     -> unlink  (Record, #kvs{mod=?DBA}).
-fold(Fun,Acc,T,S,C,D) -> fold (Fun,Acc,T,S,C,D, #kvs{mod=?DBA}).
-info(T)            -> info    (T, #kvs{mod=?DBA}).
-start()            -> start   (#kvs{mod=?DBA}).
-stop()             -> stop    (#kvs{mod=?DBA}).
-destroy()          -> destroy (#kvs{mod=?DBA}).
-version()          -> version (#kvs{mod=?DBA}).
-dir()              -> dir     (#kvs{mod=?DBA}).
-seq(Table,DX) -> next_id(Table,DX).
-next_id(Table,DX)  -> next_id(Table, DX, #kvs{mod=?DBA}).
-
-generation(Table,Key) ->
-    case Key - topleft(Table,Key) < norm(application:get_env(kvs,generation,{?MODULE,limit}),Table,Key) of
-         true -> skip;
-         false -> kvs:rotate(Table) end.
-
-norm({A,B},Table,Key) -> A:B(Table,Key);
-norm(_,Table,Key)     -> limit(Table,Key).
-
-limit(user,_Key)       -> 25000;
-limit(comment,_Key)    -> 25000;
-limit(_Table,_Key)     -> 25000.
-
-forbid(user)           -> 5;
-forbid(comment)        -> 5;
-forbid(____)           -> 5.
-
-% Implementation
-
-init(Backend, Module) ->
-    [ begin
-        Backend:create_table(T#table.name, [{attributes,T#table.fields},{T#table.copy_type, [node()]},{type,T#table.type}]),
-        [ Backend:add_table_index(T#table.name, Key) || Key <- T#table.keys ],
-        T
-    end || T <- (Module:metainfo())#schema.tables ].
-
-start(#kvs{mod=DBA}) -> DBA:start().
-stop(#kvs{mod=DBA}) -> DBA:stop().
-change_storage(Type) -> [ change_storage(Name,Type) || #table{name=Name} <- kvs:tables() ].
-change_storage(Table,Type,#kvs{mod=DBA}) -> DBA:change_storage(Table,Type).
-destroy(#kvs{mod=DBA}) -> DBA:destroy().
-join(Node,#kvs{mod=DBA}) -> R = DBA:join(Node), rotate_new(), load_partitions(), {R,load_config()}.
-version(#kvs{mod=DBA}) -> DBA:version().
-tables() -> lists:flatten([ (M:metainfo())#schema.tables || M <- modules() ]).
-table(Name) when is_atom(Name) -> lists:keyfind(rname(Name),#table.name,tables());
-table(_) -> false.
-dir(#kvs{mod=DBA}) -> DBA:dir().
-info(T,#kvs{mod=DBA}) -> DBA:info(T).
-modules() -> application:get_env(kvs,schema,[kvs_user, kvs_acl, kvs_feed, kvs_subscription ]).
-containers() ->
-    lists:flatten([ [ {T#table.name,T#table.fields}
-        || T=#table{container=true} <- (M:metainfo())#schema.tables ]
-    || M <- modules() ]).
-
-create(ContainerName) -> create(ContainerName, kvs:next_id(atom_to_list(ContainerName), 1), #kvs{mod=?DBA}).
-
-create(ContainerName, Id, Driver) ->
-    kvs:info(?MODULE,"Create: ~p",[ContainerName]),
-    Fields = proplists:get_value(ContainerName, kvs:containers()),
-    Instance = list_to_tuple([ContainerName | lists:map(fun(_) -> [] end,Fields)]),
-    Top  = setelement(#container.id,Instance,Id),
-    Top2 = setelement(#container.top,Top,[]),
-    Top3 = setelement(#container.rear,Top2,[]),
-    Top4 = setelement(#container.count,Top3,0),
-    ok = kvs:put(Top4, Driver),
-    Id.
-
-ensure_link(Record, #kvs{mod=_Store}=Driver) ->
-
-    Id    = element(2,Record),
-    Type  = rname(element(1,Record)),
-    CName = element(#iterator.container,Record),
-    Cid   = case element(#iterator.feed_id,Record) of
-                      [] -> rname(element(1,Record));
-               undefined -> rname(element(1,Record));
-                     Fid -> Fid end,
-
-    Container = case kvs:get(CName, Cid, Driver) of
-        {ok,Res} -> Res;
-        {error, _} when Cid /= undefined andalso Cid /= [] ->
-            Fields = proplists:get_value(CName, kvs:containers()),
-            NC1 = list_to_tuple([CName | lists:map(fun(_) -> [] end,Fields)]),
-            NC2 = setelement(#container.id, NC1, Cid),
-            NC3 = setelement(#container.rear, NC2, Id),
-            setelement(#container.count, NC3, 0);
-        _Error -> error end,
-
-    case Container of
-        error -> {error,no_container};
-        _ when element(#container.top, Container) == Id -> {error,just_added};
-            _ ->
-                Top = case element(#container.top, Container) of
-                    undefined -> [];
-                           [] -> [];
-                          Tid -> case kvs:get(Type, Tid, Driver) of
-                                     {error, _} -> [];
-                                     {ok,    T} -> setelement(#iterator.next, T, Id) end end,
-
-                Prev = case Top of
-                    undefined -> [];
-                           [] -> [];
-                            E -> element(#iterator.id, E) end,
-
-                Next = [],
-
-                C2 = setelement(#container.top, Container, Id),
-                C3 = setelement(#container.count, C2, element(#container.count, Container)+1),
-
-                R  = Record,
-
-                R1 = setelement(#iterator.next,    R,  Next),
-                R2 = setelement(#iterator.prev,    R1, Prev),
-                R3 = setelement(#iterator.feed_id, R2, element(#container.id, Container)),
-
-                case {kvs:put(R3, Driver),Top} of      % Iterator
-                    {ok,[]}   -> kvs:put(C3, Driver);  % Container
-                    {ok,Top}  -> kvs:put(C3, Driver),
-                                 kvs:put(Top, Driver);
-                           __ -> kvs:error(?MODULE,"Error Updating Iterator: ~p~n",
-                                             [element(#container.id,R3)]) end,
-
-                kvs:info(?MODULE,"Put: ~p~n", [element(#container.id,R3)]),
-
-                {ok, R3} end.
-
-link(Record,#kvs{mod=_Store}=Driver) ->
-    Id = element(#iterator.id, Record),
-    case kvs:get(rname(element(1,Record)), Id, Driver) of
-              {ok, Exists} -> ensure_link(Exists, Driver);
-        {error, not_found} -> {error, not_found} end.
-
-%add(Record, #kvs{mod=store_mnesia}=Driver) when is_tuple(Record) -> store_mnesia:add(Record);
-add(Record, #kvs{}=Driver) when is_tuple(Record) -> append(Record,Driver).
-
-append(Record, #kvs{mod=_Store}=Driver) when is_tuple(Record) ->
-    Id = element(#iterator.id, Record),
-    Name = rname(element(1,Record)),
-    generation(Name, Id),
-    case kvs:get(Name, Id, Driver) of
-                {error, _} -> ensure_link(Record, Driver);
-         {aborted, Reason} -> {aborted, Reason};
-                   {ok, _} -> {error, exist} end.
-
-reverse(#iterator.prev) -> #iterator.next;
-reverse(#iterator.next) -> #iterator.prev.
-
-relink(C, E, D) ->
-    kvs:warning(?MODULE,"Deprecated! Will be removed in the future, use unlink/3 instead",[]),
-    unlink(C, E, D).
-unlink(E,Driver) ->
-    case kvs:get(element(#iterator.container,E),element(#iterator.feed_id,E)) of
-        {ok,C} ->
-            unlink(C, E, Driver),
-            E2=setelement(#iterator.next, E, []),
-            E3=setelement(#iterator.prev, E2, []),
-            kvs:put(E3,Driver),
-            {ok,E};
-        _ -> {error,no_container} end.
-unlink(Container, E, Driver) ->
-    Id   = element(#iterator.id, E),
-    Next = element(#iterator.next, E),
-    Prev = element(#iterator.prev, E),
-    case kvs:get(element(1,E), Prev, Driver) of
-        {ok, PE} -> kvs:put(setelement(#iterator.next, PE, Next), Driver);
-               _ -> ok end,
-    case kvs:get(element(1,E), Next, Driver) of
-        {ok, NE} -> kvs:put(setelement(#iterator.prev, NE, Prev), Driver);
-               _ -> ok end,
-    C2 = case element(#container.top, Container) of
-        Id -> setelement(#container.top, Container, Prev);
-         _ -> Container end,
-    C3 = case element(#container.rear, C2) of
-        Id -> setelement(#container.rear, C2, Next);
-         _ -> C2 end,
-    case element(#container.top,C3) of
-        undefined -> kvs:delete(element(1,C3),element(#container.id,C3));
-               [] -> kvs:delete(element(1,C3),element(#container.id,C3));
-                _ -> kvs:put(setelement(#container.count,C3,element(#container.count,C3)-1), Driver) end.
-
-delete(Tab, Key, #kvs{mod=Mod}) ->
-    case range(Tab,Key) of
-         [] -> Mod:delete(Tab, Key);
-          T -> Mod:delete(T, Key) end.
-
-remove(Record, Id,#kvs{mod=store_mnesia}) -> store_mnesia:remove(Record,Id);
-remove(Record, Id,#kvs{}=Driver) -> takeoff(Record,Id,Driver).
-
-takeoff(Record,Id,#kvs{}=Driver) ->
-    case get(Record,Id) of
-         {error, not_found} -> kvs:error(?MODULE,"Can't remove ~p~n",[{Record,Id}]);
-                     {ok,R} -> do_remove(R,Driver) end.
-
-do_remove(E,#kvs{}=Driver) ->
-    case get(element(#iterator.container,E),element(#iterator.feed_id,E)) of
-         {ok, C} -> unlink(C,E,Driver);
-               _ -> skip end,
-    kvs:info(?MODULE,"Delete: ~p", [E]),
-    kvs:delete(element(1,E),element(2,E), Driver).
-
-fold(___,Acc,_,[],_,_,_) -> Acc;
-fold(___,Acc,_,undefined,_,_,_) -> Acc;
-fold(___,Acc,_,_,0,_,_) -> Acc;
-fold(Fun,Acc,Table,Start,Count,Direction,Driver) ->
-    %io:format("fold: ~p~n",[{Table, Start, Driver}]),
-    try
-    case kvs:get(rname(Table), Start, Driver) of
-         {ok, R} -> Prev = element(Direction, R),
-                    Count1 = case Count of C when is_integer(C) -> C - 1; _-> Count end,
-                    fold(Fun, Fun(R,Acc), Table, Prev, Count1, Direction, Driver);
-           _Error -> %kvs:error(?MODULE,"Error: ~p~n",[Error]),
-                    Acc end catch _:_ -> Acc end.
-
-entries({error,_},_,_,_)      -> [];
-entries({ok,T},N,C,Driver) ->
-    lists:reverse(fold(fun(A,Acc) -> [A|Acc] end,[],N,element(#container.top,T),C,#iterator.prev,Driver)).
-    
-
-add_seq_ids() ->
-    Init = fun(Key) ->
-           case kvs:get(id_seq, Key) of
-                {error, _} -> {Key,kvs:put(#id_seq{thing = Key, id = 0})};
-                {ok, _} -> {Key,skip} end end,
-    [ Init(atom_to_list(Name))  || {Name,_Fields} <- containers() ].
-
-
-put(Records,#kvs{mod=Mod}) when is_list(Records) -> Mod:put(Records);
-
-put(Record,#kvs{mod=Mod}) ->
-    case range(element(1,Record),element(2,Record)) of
-         [] -> Mod:put(Record);
-         Name ->  Mod:put(setelement(1,Record,Name)) end.
-
-
-get(RecordName, Key, #kvs{mod=Mod}) ->
-    case range(RecordName,Key) of
-         []   -> Mod:get(RecordName, Key);
-         Name -> case Mod:get(Name, Key) of
-                      {ok,Record} -> {ok,setelement(1,Record,kvs:last(RecordName,Key))};
-                      Else -> Else end end.
-
-count(Tab,#kvs{mod=DBA}) -> lists:foldl(fun(T,A) -> DBA:count(T) + A end, 0, rlist(Tab)).
-all(Tab,#kvs{mod=DBA}) ->
-    lists:flatten([ rnorm(rname(Tab),DBA:all(T)) || T <- rlist(Tab) ]).
-index(Tab, Key, Value,#kvs{mod=DBA}) ->
-    lists:flatten([ rnorm(rname(Tab),DBA:index(T, Key, Value)) || T <- rlist(Tab) ]).
-next_id(Tab, Incr, KVX) when is_list(Tab) -> next_id(list_to_atom(Tab), Incr, KVX);
-next_id(Tab, Incr,#kvs{mod=DBA}) ->
-    DBA:next_id(case table(Tab) of #table{} -> atom_to_list(Tab); _ -> Tab end, Incr).
-
-save_db(Path) ->
-    Data = lists:append([all(B) || B <- [list_to_atom(Name) || {table,Name} <- kvs:dir()] ]),
-    kvs:save(Path, Data).
-
-load_db(Path) ->
-    add_seq_ids(),
-    AllEntries = kvs:load(Path),
-    [kvs:put(E) || E <- lists:filter(fun(E) -> is_tuple(E) end ,AllEntries)].
-
-save(Dir, Value) ->
-    filelib:ensure_dir(Dir),
-    file:write_file(Dir, term_to_binary(Value)).
-
-load(Key) ->
-    {ok, Bin} = file:read_file(Key),
-    binary_to_term(Bin).
-
-notify(_EventPath, _Data) -> skip.
-
-config(Key)     -> config(kvs, Key, "").
-config(App,Key) -> config(App,Key, "").
-config(App, Key, Default) -> case application:get_env(App,Key) of
-                                  undefined -> Default;
-                                     {ok,V} -> V end.
-
-log_modules() -> [].
--define(ALLOWED, (config(kvs,log_modules,kvs))).
-
-log(Module, String, Args, Fun) ->
-    case lists:member(Module,?ALLOWED:log_modules()) of
-         true -> error_logger:Fun("~p:"++String, [Module|Args]);
-         false -> skip end.
-
-info(Module, String,   Args) -> log(Module, String, Args, info_msg).
-warning(Module,String, Args) -> log(Module, String, Args, warning_msg).
-error(Module, String,  Args) -> log(Module, String, Args, error_msg).
-
-
-dump() -> dump([ rlist(N) || #table{name=N} <- kvs:tables() ]).
-dump(short) ->
-    Gen = fun(T) ->
-        {S,M,C}=lists:unzip3([ dump_info(R) || R <- rlist(T) ]),
-        {lists:usort(S),lists:sum(M),lists:sum(C)}
-    end,
-    dump_format([ {T,Gen(T)} || T <- [ N || #table{name=N} <- kvs:tables() ] ]);
-dump(Table) when is_atom(Table) -> dump(rlist(Table));
-dump(Tables) ->
-    dump_format([{case nname(T) of 1 -> rname(T); _ -> T end,dump_info(T)} || T <- lists:flatten(Tables) ]).
-dump_info(T) ->
-    {mnesia:table_info(T,storage_type),
-    mnesia:table_info(T,memory) * erlang:system_info(wordsize) / 1024 / 1024,
-    mnesia:table_info(T,size)}.
-dump_format(List) ->
-    io:format("~20s ~32s ~14s ~10s~n~n",["NAME","STORAGE TYPE","MEMORY (MB)","ELEMENTS"]),
-    [ io:format("~20s ~32w ~14.2f ~10b~n",[T,S,M,C]) || {T,{S,M,C}} <- List ],
-    io:format("~nSnapshot taken: ~p~n",[calendar:now_to_datetime(os:timestamp())]).
-
-% Table Partitions
-
-range(RecordName,Id)   -> (find(kvs:config(kvs:rname(RecordName)),RecordName,Id))#block.name.
-topleft(RecordName,Id) -> (find(kvs:config(kvs:rname(RecordName)),RecordName,Id))#block.left.
-last(RecordName,Id)    -> (find(kvs:config(kvs:rname(RecordName)),RecordName,Id))#block.last.
-
-find([],_,_Id) -> #block{left=1,right=infinity,name=[],last=[]};
-find([Range|T],RecordName,Id) ->
-     case lookup(Range,Id) of
-          [] -> find(T,RecordName,Id);
-          Range -> Range end.
-
-lookup(#block{left=Left,right=Right}=I,Id) when Id =< Right, Id >= Left -> I;
-lookup(#block{},_) -> [].
-
-rotate_new() ->
-    N = [ kvs:rotate(kvs:table(T)) || {T,_} <- fold_tables(),
-        length(proplists:get_value(attributes,info(last_disc(T)),[])) /= length((table(rname(T)))#table.fields) ],
-    %io:format("Nonexistent: ~p~n",[N]),
-    N.
-rotate(#table{name=N}) ->
-    R = name(rname(N)),
-    init(setelement(#table.name,kvs:table(kvs:last_table(N)),R)),
-    update_config(rname(N),R);
-rotate(Table) ->
-    Ranges = kvs:config(Table),
-    {M,F} = application:get_env(kvs,forbidding,{?MODULE,forbid}),
-    New = lists:sublist(Ranges,M:F(Table)),
-    
-    Delete = Ranges -- New,
-    io:format("Delete: ~p~n",[Delete]),
-%    [ mnesia:change_table_copy_type(Name, node(), disc_only_copies) || #block{name=Name} <- shd(Delete) ],
-    [ mnesia:delete_table(Name) || #block{name=Name} <- Delete ],
-    rotate(kvs:table(Table)), ok.
-load_partitions()  ->
-    [ case kvs:get(config,Table) of
-        {ok,{config,_,List}} -> application:set_env(kvs,Table,List);
-        _Else -> ok end || {table,Table} <- kvs:dir() ].
-
-rnorm(Tag,List) -> [ setelement(1,R,Tag) || R <- List ].
-rlist(Table)   -> [ N || #block{name=N} <- kvs:config(Table) ]++[Table].
-shd([])        -> [];
-shd(X)         -> [hd(X)].
-wait()         -> timer:tc(fun() -> mnesia:wait_for_tables([ T#table.name || T <- kvs:tables()],infinity) end).
-stl([])        -> [];
-stl(S)         -> tl(S).
-dat(T)         -> [ mnesia:change_table_copy_type(Name, node(), disc_only_copies) || #block{name=Name} <- stl((element(2,kvs:get(config,T)))#config.value) ].
-omitone(1)     -> [];
-omitone(X)     -> X.
-limit()        -> infinity.
-load_config()  -> [ application:set_env(kvs,Key,Value) || #config{key=Key,value=Value}<- kvs:all(config) ].
-store(Table,X) -> application:set_env(kvs,Table,X), X.
-rname(Table)   -> list_to_atom(lists:filter(fun(X) -> not lists:member(X,"1234567890") end, atom_to_list(Table))).
-nname(Table)   -> list_to_integer(case lists:filter(fun(X) -> lists:member(X,"1234567890") end, atom_to_list(Table)) of [] -> "1"; E -> E end).
-fold(N)        -> kvs:fold(fun(X,A)->[X|A]end,[],process,N,-1,#iterator.next,#kvs{mod=store_mnesia}).
-top(acl)       -> id_seq(conv);
-top(i)         -> id_seq(conv);
-top(Table)     -> id_seq(Table).
-name(T)        -> list_to_atom(lists:concat([T,omitone(kvs:next_id(lists:concat([T,".tables"]),1))])).
-init(T)        -> store_mnesia:create_table(T#table.name, [{attributes,T#table.fields},{T#table.copy_type, [node()]}]),
-                [ store_mnesia:add_table_index(T#table.name, Key) || Key <- T#table.keys ].
-id_seq(Tab)    -> T = atom_to_list(Tab), case kvs:get(id_seq,T) of {ok,#id_seq{id=Id}} -> Id; _ -> kvs:next_id(T,1) end.
-last_disc(T)   -> list_to_atom(lists:concat([T,omitone(kvs:id_seq(list_to_atom(lists:concat([T,".tables"]))))])).
-last_table(T)  -> list_to_atom(lists:concat([T,omitone(lists:max(proplists:get_value(T,fold_tables(),[1])))])).
-fold_tables()  -> lists:foldl(fun(#table{name=X},Acc) ->
-                  setkey(kvs:rname(X),1,Acc,{kvs:rname(X),[kvs:nname(X)|proplists:get_value(kvs:rname(X),Acc,[])]}) end,
-                  [], kvs:tables()).
-range(L,R,Name) -> #block{left=L,right=R,name=Name,last=last_table(rname(Name))}.
-update_config(Table,Name) ->
-    Store = store(Table,case kvs:get(config,Table)  of
-                                            {error,not_found}        -> update_list(Table,[],Name);
-                                            {ok,#config{value=List}} -> update_list(Table,List,Name) end),
-%    {M,F} = application:get_env(kvs,forbidding,{?MODULE,forbid}),
-    New = Store, %lists:sublist(Store,M:F(Table)),
-    kvs:put(#config{key = Table, value = New }).
-
-update_list(Table,[],Name)                    -> [ range(top(Table)+1,limit(),Name) ];
-update_list(Table,[#block{}=CI|Tail],Name)    -> [ range(top(Table)+1,limit(),Name) ] ++
-                                                 [ CI#block{right=top(Table)}       ] ++ Tail.
-
-setkey(Name,Pos,List,New) ->
-    case lists:keyfind(Name,Pos,List) of
-        false -> [New|List];
-        _Element -> lists:keyreplace(Name,Pos,List,New) end.
-
-test() -> test(#user{}).
-
-test(Proto) ->
-    kvs:join(),
-    Table = element(1,Proto),
-    [ kvs:add(setelement(2,Proto,kvs:next_id(Table,1))) || _ <- lists:seq(1,20000) ],
-    io:format("Config: ~p~n",[kvs:all(config)]),
-    io:format("Fetch: ~p~n",[kvs:entries(kvs:get(feed,Table),Table,10)]).

+ 0 - 35
src/kvs_acl.erl

@@ -1,35 +0,0 @@
--module(kvs_acl).
--copyright('Synrc Research Center s.r.o.').
--compile(export_all).
--include("kvs.hrl").
--include("metainfo.hrl").
--include("acl.hrl").
--include("user.hrl").
-
-metainfo() ->
-    #schema{name=kvs,tables=[
-        #table{name=acl,container=true,fields=record_info(fields,acl),keys=[id,accessor]},
-        #table{name=access,container=acl,fields=record_info(fields,access)}
-    ]}.
-
-define_access(Accessor, Resource, Action) ->
-    Entry = #access{ id={Accessor, Resource}, accessor=Accessor, action=Action},
-    case kvs:add(Entry) of
-        {error, exist} -> kvs:put(Entry#access{action=Action});
-        {error, no_container} -> skip;
-        {ok, E} -> E end.
-
-check(Keys) ->
-    Acls = [Acl || {ok, Acl = #access{}} <- [kvs:get(access, Key) || Key <- Keys]],
-    case Acls of
-        [] -> none;
-        [#access{action = Action} | _] -> Action end.
-
-check_access(#user{id = Id}, Feature) ->
-    Query = [ {{user,Id},Feature} ],
-    check(Query);
-
-check_access(Id, Feature) ->
-    case kvs:get(user, Id) of
-        {ok, User} -> check_access(User, Feature);
-        E -> E end.

+ 0 - 6
src/kvs_app.erl

@@ -1,6 +0,0 @@
--module(kvs_app).
--behaviour(application).
--export([start/2, stop/1]).
-
-start(_StartType, _StartArgs) -> kvs_sup:start_link().
-stop(_State) -> ok.

+ 0 - 21
src/kvs_feed.erl

@@ -1,21 +0,0 @@
--module(kvs_feed).
--copyright('Synrc Research Center, s.r.o.').
--compile(export_all).
--include("kvs.hrl").
--include("config.hrl").
--include("entry.hrl").
--include("comment.hrl").
--include("feed.hrl").
--include("metainfo.hrl").
-
-metainfo() ->  #schema{name=kvs,tables= core() ++ feeds() }.
-
-core()     -> [ #table{name=config,fields=record_info(fields,config)},
-                #table{name=log,container=true,fields=record_info(fields,log)},
-                #table{name=cur,container=feed,fields=record_info(fields,cur)},
-                #table{name=operation,container=log,fields=record_info(fields,operation)},
-                #table{name=id_seq,fields=record_info(fields,id_seq),keys=[thing]} ].
-
-feeds()    -> [ #table{name=entry,container=true,fields=record_info(fields,entry)},
-                #table{name=comment,container=true,fields=record_info(fields,comment)},
-                #table{name=feed,container=true,fields=record_info(fields,feed)} ].

+ 0 - 120
src/kvs_stream.erl

@@ -1,120 +0,0 @@
--module(kvs_stream).
--description('KVS STREAM').
--copyrihgt('Synrc Research Center').
--author('Maxim Sokhatsky').
--license('ISC').
--include_lib("kvs/include/kvs.hrl").
--compile(export_all).
-%-include_lib("stdlib/include/assert.hrl").
-
-% section: kvs_stream prelude
-
-se(X,Y,Z)  -> setelement(X,Y,Z).
-set(X,Y,Z) -> setelement(X,Z,Y).
-e(X,Y)  -> element(X,Y).
-c0(R,V) -> se(1, R, V).
-c1(R,V) -> se(#reader.id,    R, V).
-c2(R,V) -> se(#reader.pos,   R, V).
-c3(R,V) -> se(#reader.cache, R, V).
-c4(R,V) -> se(#reader.args,  R, V).
-c5(R,V) -> se(#reader.feed,  R, V).
-c6(R,V) -> se(#reader.dir,   R, V).
-wf(R,V) -> se(#writer.first, R, V).
-sn(M,T) -> se(#iter.next, M, T).
-sp(M,T) -> se(#iter.prev, M, T).
-si(M,T) -> se(#iter.id, M, T).
-sf(M,T) -> se(#iter.feed, M, T).
-el(X,T) -> e(X, T).
-tab(T)  -> e(1, T).
-id(T)   -> e(#iter.id, T).
-en(T)   -> e(#iter.next, T).
-ep(T)   -> e(#iter.prev, T).
-pos(T)  -> e(#reader.pos, T).
-args(T) -> e(#writer.args, T).
-dir(0)  -> top;
-dir(1)  -> bot.
-acc(0)  -> next;
-acc(1)  -> prev.
-
-% section: next, prev
-
-top(#reader{feed=F}=C) -> w(kvs:get(writer,F),top,C).
-bot(#reader{feed=F}=C) -> w(kvs:get(writer,F),bot,C).
-
-next(#reader{cache=[]}) -> {error,empty};
-next(#reader{cache={T,R},pos=P}=C) -> n(kvs:get(T,R),C,P+1).
-prev(#reader{cache=[]}) -> {error,empty};
-prev(#reader{cache={T,R},pos=P}=C) -> p(kvs:get(T,R),C,P-1).
-
-n({ok,R},C,P)    -> r(kvs:get(tab(R),en(R)),C,P);
-n({error,X},_,_) -> {error,X}.
-p({ok,R},C,P)    -> r(kvs:get(tab(R),ep(R)),C,P);
-p({error,X},_,_) -> {error,X}.
-r({ok,R},C,P)    -> C#reader{cache={tab(R),id(R)},pos=P};
-r({error,X},_,_) -> {error,X}.
-w({ok,#writer{first=B}},bot,C)            -> C#reader{cache={tab(B),id(B)},pos=1};
-w({ok,#writer{cache=B,count=Size}},top,C) -> C#reader{cache={tab(B),id(B)},pos=Size};
-w({error,X},_,_)                          -> {error,X}.
-
-% section: take, drop
-
-drop(#reader{dir=D,cache=B,args=N,pos=P}=C) -> drop(acc(D),N,C,C,P,B).
-take(#reader{dir=D,cache=B,args=N,pos=P}=C) -> take(acc(D),N,C,C,[],P,B).
-
-take(_,_,{error,_},C2,R,P,B) -> C2#reader{args=lists:flatten(R),pos=P,cache=B};
-take(_,0,_,C2,R,P,B)         -> C2#reader{args=lists:flatten(R),pos=P,cache=B};
-take(A,N,#reader{cache={T,I},pos=P}=C,C2,R,_,_) ->
-    take(A,N-1,?MODULE:A(C),C2,[element(2,kvs:get(T,I))|R],P,{T,I}).
-
-drop(_,_,{error,_},C2,P,B)     -> C2#reader{pos=P,cache=B};
-drop(_,0,_,C2,P,B)             -> C2#reader{pos=P,cache=B};
-drop(A,N,#reader{cache=B,pos=P}=C,C2,_,_) ->
-    drop(A,N-1,?MODULE:A(C),C2,P,B).
-
-% new, save, load, up, down, top, bot
-
-load_writer (Id) -> case kvs:get(writer,Id) of {ok,C} -> C; E -> E end.
-load_reader (Id) -> case kvs:get(reader,Id) of {ok,C} -> C; E -> E end.
-writer (Id) -> #writer{id=Id}.
-reader (Id) ->
-    case kvs:get(writer,Id) of
-         {ok,#writer{first=[]}} -> #reader{id=kvs:next_id(reader,1),feed=Id,cache=[]};
-         {ok,#writer{first=F}}  -> #reader{id=kvs:next_id(reader,1),feed=Id,cache={tab(F),id(F)}};
-         {error,X} -> {error,X} end.
-save (C) -> NC = c4(C,[]), kvs:put(NC), NC.
-up   (C) -> C#reader{dir=0}.
-down (C) -> C#reader{dir=1}.
-
-% add
-
-add(#writer{args=M}=C) when element(2,M) == [] -> add(si(M,kvs:next_id(tab(M),1)),C);
-add(#writer{args=M}=C) -> add(M,C).
-
-add(M,#writer{cache=[]}=C) ->
-    _Id=id(M), N=sp(sn(M,[]),[]), kvs:put(N),
-    C#writer{cache=N,count=1,first=N};
-
-add(M,#writer{cache=V,count=S}=C) ->
-    N=sp(sn(M,[]),id(V)), P=sn(V,id(M)), kvs:put([N,P]),
-    C#writer{cache=N,count=S+1}.
-
-% tests
-
-check() -> test1().
-
-test1() ->
-    Id  = {p2p,1,2},
-    X   = 5,
-    _W   = kvs_stream:save(kvs_stream:writer(Id)),
-    #reader{id=R1} = kvs_stream:save(kvs_stream:reader(Id)),
-    #reader{id=R2} = kvs_stream:save(kvs_stream:reader(Id)),
-    [ kvs_stream:save(
-      kvs_stream:add((
-      kvs_stream:load_writer(Id))
-      #writer{args={user2,[],[],[],[],[],[],[],[],[]}})) || _ <- lists:seq(1,X) ],
-    Bot = kvs_stream:bot(kvs_stream:load_reader(R1)),
-    Top = kvs_stream:top(kvs_stream:load_reader(R2)),
-    #reader{args=F} = kvs_stream:take(Bot#reader{args=20,dir=0}),
-    #reader{args=B} = kvs_stream:take(Top#reader{args=20,dir=1}),
-    true = (X == length(F)),
-    true = (F == lists:reverse(B)).

+ 0 - 23
src/kvs_subscription.erl

@@ -1,23 +0,0 @@
--module(kvs_subscription).
--copyright('Synrc Research Center s.r.o.').
--include("subscription.hrl").
--include("metainfo.hrl").
--compile(export_all).
-
-metainfo() ->
-    #schema{name=kvs,tables=[
-        #table{name=subscription,fields=record_info(fields,subscription),keys=[id,whom,who]}
-    ]}.
-
-subscribe(Who, Whom) -> kvs:put(#subscription{key={Who,Whom},who = Who, whom = Whom}).
-unsubscribe(Who, Whom) ->
-    case subscribed(Who, Whom) of
-        true  -> kvs:delete(subscription, {Who, Whom});
-        false -> skip end.
-
-subscriptions(UId) -> kvs:index(subscription, who, UId).
-subscribed(Who) -> kvs:index(subscription, whom, Who).
-subscribed(Who, Whom) ->
-    case kvs:get(subscription, {Who, Whom}) of
-        {ok, _} -> true;
-        _ -> false end.

+ 0 - 6
src/kvs_sup.erl

@@ -1,6 +0,0 @@
--module(kvs_sup).
--behaviour(supervisor).
--export([init/1, start_link/0]).
-
-start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-init([])     -> {ok, { {one_for_one, 5, 10}, []} }.

+ 0 - 14
src/kvs_user.erl

@@ -1,14 +0,0 @@
--module(kvs_user).
--copyright('Synrc Research Center s.r.o.').
--include("kvs.hrl").
--include("user.hrl").
--include("group.hrl").
--include("metainfo.hrl").
--compile(export_all).
-
-metainfo() ->
-    #schema{name=kvs,tables=[
-        #table{name=group,container=feed,fields=record_info(fields,group)},
-        #table{name=user,container=feed,fields=record_info(fields,user),keys=[email]}
-    ]}.
-

+ 0 - 87
src/store/store_fs.erl

@@ -1,87 +0,0 @@
--module(store_fs).
--copyright('Synrc Research Center s.r.o.').
--include("config.hrl").
--include("metainfo.hrl").
--include_lib("stdlib/include/qlc.hrl").
--compile(export_all).
-
-start()    -> ok.
-stop()     -> ok.
-destroy()  -> ok.
-version()  -> {version,"KVS FS"}.
-dir()      -> [ {table,F} || F <- filelib:wildcard("data/*"), filelib:is_dir(F) ].
-join(_Node) -> filelib:ensure_dir("data/"). % should be rsync or smth
-change_storage(_Table,_Type) -> ok.
-
-initialize() ->
-    kvs:info(?MODULE,"fs init.~n",[]),
-    mnesia:create_schema([node()]),
-    [ kvs:init(store_fs,Module) || Module <- kvs:modules() ],
-    mnesia:wait_for_tables([ T#table.name || T <- kvs:tables()],infinity).
-
-index(_Tab,_Key,_Value) -> ok.
-get(TableName, Key) ->
-    HashKey = encode(base64:encode(crypto:hash(sha, term_to_binary(Key)))),
-    Dir = lists:concat(["data/",TableName,"/"]),
-    case file:read_file(lists:concat([Dir,HashKey])) of
-         {ok,Binary} -> {ok,binary_to_term(Binary,[safe])};
-         {error,Reason} -> {error,Reason} end.
-
-put(Records) when is_list(Records) -> lists:map(fun(Record) -> put(Record) end, Records);
-put(Record) ->
-    TableName = element(1,Record),
-    HashKey = encode(base64:encode(crypto:hash(sha, term_to_binary(element(2,Record))))),
-    BinaryValue = term_to_binary(Record),
-    Dir = lists:concat(["data/",TableName,"/"]),
-    filelib:ensure_dir(Dir),
-    File = lists:concat([Dir,HashKey]),
-    file:write_file(File,BinaryValue,[write,raw,binary,sync]).
-
-delete(_Tab, _Key) -> ok.
-count(RecordName) -> length(filelib:fold_files(lists:concat(["data/",RecordName]), "",true, fun(A,Acc)-> [A|Acc] end, [])).
-all(R) -> lists:flatten([ begin case file:read_file(File) of
-                        {ok,Binary} -> binary_to_term(Binary,[safe]);
-                        {error,_Reason} -> [] end end || File <-
-      filelib:fold_files(lists:concat(["data/",R]), "",true, fun(A,Acc)-> [A|Acc] end, []) ]).
-next_id(RecordName, Incr) -> store_mnesia:next_id(RecordName, Incr).
-create_table(Name,_Options) -> filelib:ensure_dir(lists:concat(["data/",Name,"/"])).
-add_table_index(_Record, _Field) -> ok.
-
-% URL ENCODE
-
-encode(B) when is_binary(B) -> encode(binary_to_list(B));
-encode([C | Cs]) when C >= $a, C =< $z -> [C | encode(Cs)];
-encode([C | Cs]) when C >= $A, C =< $Z -> [C | encode(Cs)];
-encode([C | Cs]) when C >= $0, C =< $9 -> [C | encode(Cs)];
-encode([C | Cs]) when C == 16#20 -> [$+ | encode(Cs)];
-
-% unreserved
-encode([C = $- | Cs]) -> [C | encode(Cs)];
-encode([C = $_ | Cs]) -> [C | encode(Cs)];
-encode([C = 46 | Cs]) -> [C | encode(Cs)];
-encode([C = $! | Cs]) -> [C | encode(Cs)];
-encode([C = $~ | Cs]) -> [C | encode(Cs)];
-encode([C = $* | Cs]) -> [C | encode(Cs)];
-encode([C = 39 | Cs]) -> [C | encode(Cs)];
-encode([C = $( | Cs]) -> [C | encode(Cs)];
-encode([C = $) | Cs]) -> [C | encode(Cs)];
-
-encode([C | Cs]) when C =< 16#7f -> escape_byte(C) ++ encode(Cs);
-encode([C | Cs]) when (C >= 16#7f) and (C =< 16#07FF) ->
-  escape_byte((C bsr 6) + 16#c0)
-  ++ escape_byte(C band 16#3f + 16#80)
-  ++ encode(Cs);
-encode([C | Cs]) when (C > 16#07FF) ->
-  escape_byte((C bsr 12) + 16#e0) % (0xe0 | C >> 12)
-  ++ escape_byte((16#3f band (C bsr 6)) + 16#80) % 0x80 | ((C >> 6) & 0x3f)
-  ++ escape_byte(C band 16#3f + 16#80) % 0x80 | (C >> 0x3f)
-  ++ encode(Cs);
-encode([C | Cs]) -> escape_byte(C) ++ encode(Cs);
-encode([]) -> [].
-
-hex_octet(N) when N =< 9 -> [$0 + N];
-hex_octet(N) when N > 15 -> hex_octet(N bsr 4) ++ hex_octet(N band 15);
-hex_octet(N) -> [N - 10 + $a].
-escape_byte(C) -> normalize(hex_octet(C)).
-normalize(H) when length(H) == 1 -> "%0" ++ H;
-normalize(H) -> "%" ++ H.

+ 0 - 71
src/store/store_kai.erl

@@ -1,71 +0,0 @@
--module(store_kai).
--author('Maxim Sokhatsky').
--copyright('Synrc Research Center s.r.o.').
--include("config.hrl").
--include("metainfo.hrl").
--include_lib("stdlib/include/qlc.hrl").
--compile(export_all).
--record(data, { key, bucket, last_modified, vector_clocks, checksum, flags, value }).
-
-start() -> kai:start(), ok.
-stop() -> kai_store:stop(), ok.
-version() -> {version,"KVS KAI"}.
-join(_Node) -> initialize(), ok.
-initialize() -> ok.
-dir() -> [{table,T}||T<-kvs:modules()].
-
-put(Records) when is_list(Records) -> lists:foreach(fun kai_put/1, Records);
-put(Record) -> kai_put(Record).
-
-kai_put(Record) ->
-    Data = #data{key = element(2,Record), bucket = table_to_num(element(1,Record)),
-        last_modified = os:timestamp(), checksum = erlang:md5(term_to_binary(Record)),
-        vector_clocks = vclock:fresh(), value = Record },
-    kai_store:put(Data).
-
-update(_Record, _Object) -> ok.
-
-get(Tab, Key) ->
-    Data = #data{key=Key,bucket=table_to_num(Tab)},
-    kai_get(Data).
-
-kai_get(Data) ->
-    case kai_store:get(Data) of
-         #data{value=Value} -> {ok,Value};
-         undefined -> {error,not_found};
-         E -> {error,E} end.
-
-delete(Tab, Key) ->
-    Data = #data{key=Key,bucket=table_to_num(Tab)},
-    kai_delete(Data).
-
-kai_delete(Data) ->
-    case kai_store:delete(Data) of
-         ok -> ok;
-         E -> {error,E} end.
-
-key_to_bin(Key) ->
-    if is_integer(Key) -> erlang:list_to_binary(integer_to_list(Key));
-       is_list(Key) -> erlang:list_to_binary(Key);
-       is_atom(Key) -> erlang:list_to_binary(erlang:atom_to_list(Key));
-       is_binary(Key) -> Key;
-       true ->  [ListKey] = io_lib:format("~p", [Key]), erlang:list_to_binary(ListKey) end.
-
-all(RecordName) ->
-    {list_of_data,List} = kai_store:list(table_to_num(RecordName)),
-    [ begin {ok,Val}=kai_get(Data),Val end || Data <- List ].
-
-all_by_index(_Tab, _IndexId, _IndexVal) -> [].
-
-table_to_num(user)         -> 10;
-table_to_num(subscription) -> 11;
-table_to_num(group)        -> 12;
-table_to_num(id_seq)       -> 13;
-table_to_num(product)      -> 14;
-table_to_num(acl)          -> 15;
-table_to_num(feed)         -> 16;
-table_to_num(entry)        -> 17;
-table_to_num(comment)      -> 18;
-table_to_num(Name) -> case kvs:config(kvs,table_to_num) of
-                        [] -> unknown_table;
-                        Module -> Module:table_to_num(Name) end.

+ 0 - 80
src/store/store_mnesia.erl

@@ -1,80 +0,0 @@
--module(store_mnesia).
--copyright('Synrc Research Center s.r.o.').
--include("config.hrl").
--include("kvs.hrl").
--include("metainfo.hrl").
--include_lib("mnesia/src/mnesia.hrl").
--include_lib("stdlib/include/qlc.hrl").
--compile(export_all).
-
-start()    -> mnesia:start().
-stop()     -> mnesia:stop().
-destroy()  -> [mnesia:delete_table(T)||{_,T}<-kvs:dir()], mnesia:delete_schema([node()]), ok.
-version()  -> {version,"KVS MNESIA"}.
-dir()      -> [{table,T}||T<-mnesia:system_info(local_tables)].
-join([])   -> mnesia:change_table_copy_type(schema, node(), disc_copies), initialize();
-join(Node) ->
-    mnesia:change_config(extra_db_nodes, [Node]),
-    mnesia:change_table_copy_type(schema, node(), disc_copies),
-    [{Tb, mnesia:add_table_copy(Tb, node(), Type)}
-     || {Tb, [{N, Type}]} <- [{T, mnesia:table_info(T, where_to_commit)}
-                               || T <- mnesia:system_info(tables)], Node==N].
-
-change_storage(Table,Type) -> mnesia:change_table_copy_type(Table, node(), Type).
-
-initialize() ->
-    kvs:info(?MODULE,"mnesia init.~n",[]),
-    mnesia:create_schema([node()]),
-    Res = [ kvs:init(store_mnesia,Module) || Module <- kvs:modules() ],
-    mnesia:wait_for_tables([ T#table.name || T <- kvs:tables()],infinity),
-    Res.
-
-index(Tab,Key,Value) ->
-    Table = kvs:table(Tab),
-    Index = string:str(Table#table.fields,[Key]),
-    lists:flatten(many(fun() -> mnesia:index_read(Tab,Value,Index+1) end)).
-
-get(RecordName, Key) -> just_one(fun() -> mnesia:read(RecordName, Key) end).
-put(Records) when is_list(Records) -> void(fun() -> lists:foreach(fun mnesia:write/1, Records) end);
-put(Record) -> put([Record]).
-delete(Tab, Key) ->
-    case mnesia:activity(context(),fun()-> mnesia:delete({Tab, Key}) end) of
-        {aborted,Reason} -> {error,Reason};
-        {atomic,_Result} -> ok;
-        X -> X end.
-count(RecordName) -> mnesia:table_info(RecordName, size).
-all(R) -> lists:flatten(many(fun() -> L= mnesia:all_keys(R), [ mnesia:read({R, G}) || G <- L ] end)).
-next_id(RecordName, Incr) -> mnesia:dirty_update_counter({id_seq, RecordName}, Incr).
-many(Fun) -> case mnesia:activity(context(),Fun) of {atomic, R} -> R; {aborted, Error} -> {error, Error}; X -> X end.
-void(Fun) -> case mnesia:activity(context(),Fun) of {atomic, ok} -> ok; {aborted, Error} -> {error, Error}; X -> X end.
-info(T) -> try mnesia:table_info(T,all) catch _:_ -> [] end.
-create_table(Name,Options) ->
-    X = mnesia:create_table(Name, Options),
-    kvs:info(?MODULE,"Create table ~p ~nOptions ~p~nReturn ~p~n",[Name, Options,X]),
-    X.
-add_table_index(Record, Field) -> mnesia:add_table_index(Record, Field).
-exec(Q) -> F = fun() -> qlc:e(Q) end, {atomic, Val} = mnesia:activity(context(),F), Val.
-just_one(Fun) ->
-    case mnesia:activity(context(),Fun) of
-        {atomic, []} -> {error, not_found};
-        {atomic, [R]} -> {ok, R};
-        {atomic, [_|_]} -> {error, duplicated};
-        [] -> {error, not_found};
-        [R] -> {ok,R};
-        [_|_] -> {error, duplicated};
-        Error -> Error end.
-
-add(Record) -> mnesia:activity(context(),fun() -> kvs:append(Record,#kvs{mod=?MODULE}) end).
-remove(Record,Id) -> mnesia:activity(context(),fun() -> kvs:takeoff(Record,Id,#kvs{mod=?MODULE}) end).
-context() -> kvs:config(kvs,mnesia_context,async_dirty).
-
-sync_indexes() ->
-    lists:map(fun sync_indexes/1, kvs:tables()).
-sync_indexes(#table{name = Table, keys = Keys}) ->
-    mnesia:wait_for_tables(Table, 10000),
-    #cstruct{attributes = Attrs} = mnesia:table_info(Table, cstruct),
-    Indexes = mnesia:table_info(Table, index),
-    IndexedKeys = [lists:nth(I-1, Attrs)|| I <- Indexes],
-    [mnesia:del_table_index(Table, Key) || Key <- IndexedKeys -- Keys],
-    [mnesia:add_table_index(Table, Key) || Key <- Keys -- IndexedKeys].
-

+ 0 - 149
src/store/store_mongo.erl

@@ -1,149 +0,0 @@
--module(store_mongo).
--author("Vitaly Shutko").
--author("Oleg Zinchenko").
--copyright('Synrc Research Center s.r.o.').
--include("metainfo.hrl").
--compile(export_all).
--define(POOL_NAME,mongo_pool).
-
-start() -> ok.
-stop() -> stopped.
-version() -> {version,"KVS MONGO"}.
-join([]) -> connect(),[kvs:init(?MODULE,M) || M <- kvs:modules()],ok;
-join(_Node) -> {error,not_implemented}.
-
-connect() ->
-  {Conn,Pool} = config(),
-  case Pool of none -> connect(Conn); _ -> connect(Pool,Conn) end.
-connect(Conn) ->
-  Spec = {?POOL_NAME,{gen_server,start_link,[{local,?POOL_NAME},mc_worker,Conn,[]]},
-          permanent,5000,worker,[kvs_sup]},
-  {ok,_} = supervisor:start_child(kvs_sup,Spec),put(no_pool,true),ok.
-connect(Pool,Conn) ->
-  Spec = poolboy:child_spec(?POOL_NAME,[{name,{local,?POOL_NAME}},{worker_module,mc_worker}]++Pool,Conn),
-  {ok,_} = supervisor:start_child(kvs_sup,Spec),ok.
-
-config() ->
-  Config = kvs:config(kvs,mongo),
-  {connection,Conn} = proplists:lookup(connection,Config),
-  P = proplists:lookup(pool,Config),
-  Pool = case P of {pool,Pl} -> Pl; _ -> none end,
-  {Conn,Pool}.
-
-transaction(Fun) ->
-  case get(no_pool) of true -> Fun(?POOL_NAME); _ -> poolboy:transaction(?POOL_NAME,Fun) end.
-
-dir() ->
-  {_,{_,{_,_,_,_,_,Colls}}} = transaction(fun (W) -> mongo:command(W,{<<"listCollections">>,1}) end),
-  [{table,binary_to_list(C)} || {_,C,_,_} <- Colls].
-
-destroy() -> transaction(fun (W) -> [mongo:command(W,{<<"drop">>,to_binary(T)}) || {_,T} <- dir()] end).
-
-next_id(_Tab,_Incr) -> mongo_id_server:object_id().
-
-to_binary({<<ObjectId:12/binary>>}) -> {ObjectId};
-to_binary({Key,Value})              -> {Key,to_binary(Value)};
-to_binary(Value)                    -> to_binary(Value,false).
-
-to_binary(V,ForceList) ->
-  if is_integer(V) -> V;
-    is_list(V) -> case io_lib:printable_unicode_list(V) of
-                    false -> lists:map(fun (X) -> to_binary(X) end, V);
-                    true -> unicode:characters_to_binary(V,utf8,utf8) end;
-    is_atom(V) -> list_to_binary(atom_to_list(V));
-    is_pid(V)  -> {pid,list_to_binary(pid_to_list(V))};
-    true       -> case ForceList of true -> [P] = io_lib:format("~p",[V]),list_to_binary(P); _ -> V end
-  end.
-
-make_document(Tab,Key,Values) ->
-  Table = kvs:table(Tab),
-  list_to_tuple(['_id',make_id(Key)|list_to_doc(tl(Table#table.fields),Values)]).
-
-list_to_doc([],[]) -> [];
-list_to_doc([F|Fields],[V|Values]) ->
-  case V of
-    undefined -> list_to_doc(Fields,Values);
-    _ -> 
-      case F of
-        feed_id -> [F,make_id(V)|list_to_doc(Fields,Values)];
-        _       -> [F,make_field(V)|list_to_doc(Fields,Values)]
-      end
-  end.
-
-make_id({<<ObjectId:12/binary>>}) -> {ObjectId};
-make_id(Term)                     -> to_binary(Term, true).
-
-make_field({geo_point, Coords}) when length(Coords) == 2; length(Coords) == 0 -> 
-  {<<"type">>, <<"Point">>, <<"coordinates">>, lists:reverse(Coords)};
-make_field({geo_polygon, Coords}) when is_list(Coords) -> 
-  {<<"type">>, <<"Polygon">>, <<"coordinates">>, [lists:reverse(Coord) || Coord <- Coords]};
-make_field(V) ->
-  if is_atom(V) -> case V of
-                     true  -> to_binary(V);
-                     false -> to_binary(V);
-                     _     -> {atom,atom_to_binary(V,utf8)} end;
-    is_pid(V)   -> {pid,list_to_binary(pid_to_list(V))};
-    is_list(V)  -> case io_lib:printable_unicode_list(V) of
-                     false -> lists:foldl(fun (X, Acc) -> [make_field(X)|Acc] end, [], V);
-                     true  -> to_binary(V) end;
-    true        -> to_binary(V) end.
-
-make_record(Tab,Doc) ->
-  Table = kvs:table(Tab),
-  DocPropList = doc_to_proplist(tuple_to_list(Doc)),
-  list_to_tuple([Tab|[proplists:get_value(atom_to_binary(F,utf8),DocPropList) || F <- Table#table.fields]]).
-
-decode_value({<<"type">>, <<"Point">>, <<"coordinates">>, Coords}) -> {geo_point, lists:reverse(Coords)};
-decode_value({<<"type">>, <<"Polygon">>, <<"coordinates">>, Coords}) -> {geo_polygon, [lists:reverse(Coord) || Coord <- Coords]};
-decode_value(<<"true">>)          -> true;
-decode_value(<<"false">>)         -> false;
-decode_value({<<"atom">>,Atom})         -> binary_to_atom(Atom,utf8);
-decode_value({<<"pid">>,Pid})           -> list_to_pid(binary_to_list(Pid));
-decode_value(V) when is_binary(V) -> unicode:characters_to_list(V,utf8);
-decode_value(V)                   -> V.
-
-decode_id({<<ObjectId:12/binary>>}) -> {ObjectId};
-decode_id(List) ->
-  {ok,Tokens,_EndLine} = erl_scan:string(lists:append(binary_to_list(List), ".")),
-  {ok,AbsForm} = erl_parse:parse_exprs(Tokens),
-  {value,Value,_Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()),
-  Value.
-
-doc_to_proplist(Doc)                 -> doc_to_proplist(Doc,[]).
-doc_to_proplist([],Acc)              -> Acc;
-doc_to_proplist([<<"_id">>,V|Doc],Acc)   -> doc_to_proplist(Doc,[{<<"id">>,decode_id(V)}|Acc]);
-doc_to_proplist([<<"feed_id">>,V|Doc],Acc) -> doc_to_proplist(Doc,[{<<"feed_id">>,decode_id(V)}|Acc]);
-doc_to_proplist([F,V|Doc],Acc)       -> doc_to_proplist(Doc,[{F,decode_value(V)}|Acc]).
-
-get(Tab,Key) ->
-  Result = transaction(fun (W) -> mongo:find_one(W,to_binary(Tab),{'_id',make_id(Key)}) end),
-  case Result of {} -> {error,not_found}; {Doc} -> {ok, make_record(Tab,Doc)} end.
-
-put(Records) when is_list(Records) ->
-  try lists:foreach(fun mongo_put/1,Records) catch error:Reason -> {error,Reason} end;
-put(Record) -> put([Record]).
-
-mongo_put(Record) ->
-  Tab          = element(1,Record),
-  Key          = element(2,Record),
-  [_,_|Values] = tuple_to_list(Record),
-  Sel          = {'_id', make_id(Key)},
-  transaction(fun (W) -> mongo:update(W,to_binary(Tab),Sel,{<<"$set">>, make_document(Tab,Key,Values)},true) end).
-
-delete(Tab,Key) ->
-  transaction(fun (W) -> mongo:delete_one(W,to_binary(Tab),{'_id',Key}) end),ok.
-
-mongo_find(Tab,Sel) ->
-  Cursor = transaction(fun (W) -> mongo:find(W,to_binary(Tab),Sel) end),
-  Result = mc_cursor:rest(Cursor),
-  mc_cursor:close(Cursor),
-  case Result of [] -> []; _ -> [make_record(Tab,Doc) || Doc <- Result] end.
-
-all(Tab) -> mongo_find(Tab,{}).
-index(Tab,Key,Value) -> mongo_find(Tab,{to_binary(Key),to_binary(Value)}).
-create_table(Tab,_Options) -> transaction(fun (W) -> mongo:command(W,{<<"create">>,to_binary(Tab)}) end).
-
-add_table_index(Tab,Key) ->
-  transaction(fun (W) -> mongo:ensure_index(W,to_binary(Tab),{key,{to_binary(Key,true),1}}) end).
-
-count(Tab) -> {_,{_,N}} = transaction(fun (W) -> mongo:command(W,{<<"count">>,to_binary(Tab)}) end),N.

+ 0 - 79
src/store/store_redis.erl

@@ -1,79 +0,0 @@
--module(store_redis).
--author('Andrey Martemyanov').
--copyright('Synrc Research Center s.r.o.').
--include("config.hrl").
--include("kvs.hrl").
--include("metainfo.hrl").
--compile(export_all).
-
-start() -> erase(eredis_pid), {ok,C}=eredis:start_link(), erlang:put(eredis_pid,C), ok.
-stop() -> P=erase(eredis_pid), eredis:stop(P), ok.
-c() -> case get(eredis_pid) of
-    P when is_pid(P) ->
-        case is_process_alive(P) of true -> P; _ -> start(), erlang:get(eredis_pid) end;
-    _ -> start(), get(eredis_pid) end.
-destroy() -> ok.
-version() -> {version,"KVS REDIS"}.
-dir() -> [{table,T}||T<-kvs:modules()].
-join(_Node) -> initialize(), ok.
-change_storage(_Table,_Type) -> ok.
-initialize() -> ok.
-
-b2i(B) -> list_to_integer(binary_to_list(B)).
-redis_table(RecordName) ->
-    list_to_binary(atom_to_list(RecordName)).
-redis_key(RecordName,Key) ->
-    <<(redis_table(RecordName))/binary,$:,(term_to_binary(Key))/binary>>.
-redis_keys(RecordName) ->
-    case eredis:q(c(), ["keys", <<(redis_table(RecordName))/binary,$:,$*>> ]) of
-        {ok,KeyList} when is_list(KeyList) -> KeyList;
-        _ -> [] end.
-redis_put(#id_seq{thing=Thing,id=Incr}) when is_integer(Incr)->
-    eredis:q(c(), ["SET", redis_key(id_seq,Thing), Incr]);
-redis_put(Record) ->
-    Key = redis_key(element(1,Record),element(2,Record)),
-    Value = term_to_binary(Record),
-    eredis:q(c(), ["SET", Key, Value]).
-redis_get(Key, Fun) ->
-    case eredis:q(c(), ["GET", Key]) of
-        {ok, undefined} -> {error,not_found};
-        {ok, <<"QUEUED">>} -> transaction;
-        {ok, Value} ->
-            if is_function(Fun) -> {ok,Fun(Value)};
-                true -> {ok,binary_to_term(Value)} end;
-        E -> {error, E} end.
-redis_get(Key) -> redis_get(Key, undefined).
-redis_transaction(Fun) ->
-    {ok, <<"OK">>} = eredis:q(c(), ["MULTI"]),
-    Fun(),
-    {ok,List} = eredis:q(c(), ["EXEC"]),
-    List.
-
-index(_RecordName,_Key,_Value) -> not_implemented.
-get(id_seq,Key) ->
-    redis_get(redis_key(id_seq,Key), fun(Value) ->
-        #id_seq{thing=Key,id=b2i(Value)} end);
-get(RecordName,Key) -> redis_get(redis_key(RecordName,Key)).
-put(Records) when is_list(Records) ->
-    redis_transaction(fun() -> lists:foreach(fun put/1, Records) end);
-put(Record) -> redis_put(Record).
-delete(RecordName,Key) ->
-    case eredis:q(c(), ["DEL", redis_key(RecordName,Key)]) of
-        {ok,<<"1">>} -> ok;
-        E -> {error, E} end.
-count(RecordName) -> length(redis_keys(RecordName)).
-all(RecordName) ->
-    Keys = redis_keys(RecordName),
-    List = redis_transaction(fun() -> [redis_get(Key) || Key <- Keys] end),
-    case RecordName of
-        id_seq ->
-            lists:zipwith(fun(K,R) ->
-                #id_seq{thing=binary_to_term(binary_part(K,7,size(K)-7)),id=b2i(R)}
-                end, Keys, List);
-        _ -> [ binary_to_term(R) || R <- List ] end.
-next_id(RecordName,Incr) ->
-    Key = redis_key(id_seq,RecordName),
-    {ok, Value} = eredis:q(c(), ["INCRBY", Key, Incr]),
-    b2i(Value).
-create_table(_Name,_Options) -> ok.
-add_table_index(_Record,_Field) -> not_implemented.

+ 0 - 167
src/store/store_riak.erl

@@ -1,167 +0,0 @@
--module(store_riak).
--author('Maxim Sokhatsky <maxim@synrc.com>').
--copyright('Synrc Research Center s.r.o.').
--include("config.hrl").
--include("user.hrl").
--include("subscription.hrl").
--include("group.hrl").
--include("comment.hrl").
--include("entry.hrl").
--include("feed.hrl").
--include("acl.hrl").
--compile(export_all).
-
-start() -> ok.
-stop() -> ok.
-version() -> {version,"KVS RIAK 2.0.2"}.
-join([]) -> ok;
-join(Ring) -> riak_core:join(Ring).
-initialize() -> riak:client_connect(node()).
-
-dir() ->
-    {ok,C}=riak:local_client(),
-    {ok,Buckets} = C:list_buckets(),
-    [{table,binary_to_list(X)}||X<-Buckets].
-
-riak_clean(Table) when is_list(Table)->
-    {ok,C}=riak:local_client(),
-    {ok,Keys}=C:list_keys(erlang:list_to_binary(Table)),
-    [ C:delete(erlang:list_to_binary(Table),Key) || Key <- Keys];
-riak_clean(Table) ->
-    {ok,C}=riak:local_client(),
-    [TableStr] = io_lib:format("~p",[Table]),
-    {ok,Keys}=C:list_keys(erlang:list_to_binary(TableStr)),
-    [ kvs:delete(Table,key_to_bin(Key)) || Key <- Keys].
-
-make_object(T) ->
-    Bucket = element(1,T),
-    Key = element(2,T),
-    Obj1 = riak_object:new(key_to_bin(Bucket), key_to_bin(Key), T),
-    Indices = make_indices(T),
-    Meta = dict:store(<<"index">>, Indices, dict:new()),
-    Obj2 = riak_object:update_metadata(Obj1, Meta),
-    kvs:info(?MODULE,"RIAK PUT IDX ~p",[Indices]),
-    Obj2.
-
-make_indices(#subscription{who=Who, whom=Whom}) -> [
-    {<<"who_bin">>, key_to_bin(Who)},
-    {<<"whom_bin">>, key_to_bin(Whom)}];
-
-make_indices(#user{id=UId,zone=Zone}) -> [
-    {<<"user_bin">>, key_to_bin(UId)},
-    {<<"zone_bin">>, key_to_bin(Zone)}];
-
-make_indices(#feed{id=UId}) -> [
-    {<<"feed_bin">>, key_to_bin(UId)}];
-
-make_indices(#comment{id={CID,EID},from=Who}) -> [
-    {<<"comment_bin">>, key_to_bin({CID,EID})},
-    {<<"author_bin">>, key_to_bin(Who)}];
-
-make_indices(#entry{id={EID,FID},entry_id=EntryId,feed_id=Feed,from=From,to=To}) -> [
-    {<<"entry_feed_bin">>, key_to_bin({EID,FID})},
-    {<<"entry_bin">>, key_to_bin(EntryId)},
-    {<<"from_bin">>, key_to_bin(From)},
-    {<<"to_bin">>, key_to_bin(To)},
-    {<<"feed_bin">>, key_to_bin(Feed)}];
-
-make_indices(Record) -> [
-    {key_to_bin(atom_to_list(element(1,Record))++"_bin"),key_to_bin(element(2,Record))}].
-
-put(Records) when is_list(Records) -> lists:foreach(fun riak_put/1, Records);
-put(Record) -> riak_put(Record).
-
-riak_put(Record) ->
-    {ok,C}=riak:local_client(),
-    Bucket = key_to_bin(element(1,Record)),
-    Key = key_to_bin(element(2,Record)),
-    Object = make_object(Record),
-    RiakAnswer = C:get(Bucket,Key),
-    case RiakAnswer of
-         {ok,O} ->
-              Obj = riak_object:update_value(riak_object:reconcile([O],false), Record),
-              C:put(Obj);
-         _ -> C:put(Object) end.
-
-get(Tab, Key) ->
-    Bucket = key_to_bin(Tab),
-    IntKey = key_to_bin(Key),
-    riak_get(Bucket, IntKey).
-
-riak_get(Bucket,Key) ->
-    {ok,C} = riak:local_client(),
-    RiakAnswer = C:get(Bucket,Key),
-    case RiakAnswer of
-        {ok, O} ->
-            % kvs:info(?MODULE,"Value Count: ~p~n",[riak_object:value_count(O)]),
-            {ok,riak_object:get_value(riak_object:reconcile([O],false))};
-        {error, notfound} -> {error, not_found};
-        X -> X end.
-
-delete(Tab, Key) ->
-    {ok,C}=riak:local_client(),
-    Bucket = key_to_bin(Tab),
-    IntKey = key_to_bin(Key),
-    C:delete(Bucket, IntKey).
-
-delete_by_index(Tab, IndexId, IndexVal) ->
-    {ok,C}=riak:local_client(),
-    Bucket = key_to_bin(Tab),
-    {ok, Keys} = riak_client:get_index(Bucket, {eq, IndexId, key_to_bin(IndexVal)}),
-    [C:delete(Bucket, Key) || Key <- Keys].
-
-key_to_bin(Key) ->
-    if is_integer(Key) -> erlang:list_to_binary(integer_to_list(Key));
-       is_list(Key) -> erlang:list_to_binary(Key);
-       is_atom(Key) -> erlang:list_to_binary(erlang:atom_to_list(Key));
-       is_binary(Key) -> Key;
-       true ->  [ListKey] = io_lib:format("~p", [Key]), erlang:list_to_binary(ListKey) end.
-
-all(RecordName) ->
-    {ok,C}=riak:local_client(),
-    RecordBin = key_to_bin(RecordName),
-    {ok,Keys} = C:list_keys(RecordBin),
-    Results = [ riak_get_raw({RecordBin, Key, C}) || Key <- Keys ],
-    [ Object || Object <- Results, Object =/= failure ].
-
-all_by_index(Tab, IndexId, IndexVal) ->
-    {ok,C}=riak:local_client(),
-    Bucket = key_to_bin(Tab),
-    {ok, Keys} = C:get_index(Bucket, {eq, IndexId, key_to_bin(IndexVal)}),
-    lists:foldl(fun(Key, Acc) ->
-        case C:get(Bucket, Key) of
-            {ok, O} -> {ok,riak_object:get_value(riak_object:reconcile([O],false))};
-            {error, notfound} -> Acc end end, [], Keys).
-
-riak_get_raw({RecordBin, Key, C}) ->
-    case C:get(RecordBin, Key) of
-        {ok, O} -> riak_object:get_value(riak_object:reconcile([O],false));
-        _ -> failure end.
-
-next_id(CounterId) -> next_id(CounterId, 1).
-next_id(CounterId, Incr) -> next_id(CounterId, 0, Incr).
-next_id(CounterId, Default, Incr) ->
-    {ok,C}=riak:local_client(),
-    CounterBin = key_to_bin(CounterId),
-    {Object, Value, _Options} =
-        case C:get(key_to_bin(id_seq), CounterBin) of
-            {ok, CurObj} ->
-                R = #id_seq{id = CurVal} = riak_object:get_value(CurObj),
-                NewVal = CurVal + Incr,
-                Obj = riak_object:update_value(CurObj, R#id_seq{id = NewVal}),
-                {Obj, NewVal, [if_not_modified]};
-            {error, notfound} ->
-                NewVal = Default + Incr,
-                Obj = riak_object:new(key_to_bin(id_seq), CounterBin, #id_seq{thing = CounterId, id = NewVal}),
-                {Obj, NewVal, [if_none_match]} end,
-    case C:put(Object) of
-        ok -> Value;
-        {error, _} -> next_id(CounterId, Incr) end.
-
-% index funs
-
-subscriptions(UId) -> all_by_index(subsciption, <<"subs_who_bin">>, list_to_binary(UId)).
-subscribed(Who) -> all_by_index(subscription, <<"subs_whom_bin">>, list_to_binary(Who)).
-author_comments(Who) ->
-    EIDs = [E || #comment{entry_id=E} <- all_by_index(comment,<<"author_bin">>, Who) ],
-    lists:flatten([ all_by_index(entry,<<"entry_bin">>,EID) || EID <- EIDs]).

+ 0 - 338
src/store/store_sql.erl

@@ -1,338 +0,0 @@
--module(store_sql).
--author('Daniil Churikov').
--author('Max Davidenko').
--compile(export_all).
--include_lib("kvs/include/sql.hrl").
--include_lib("kvs/include/metainfo.hrl").
-
--define(SETTINGS, #sql_settings{placeholder = fun (_, Pos, _) -> PosBin = integer_to_binary(Pos), <<"$", PosBin/binary>> end}).
-
-start() -> ok.
-stop() -> ok.
-
-version() -> {version,"KVS SQL 1.0.0"}.
-join() -> ok.
-join(_) -> ok.
-initialize() -> ok.
-
-insert(E, Table, S) ->
-    SkipFun = fun(#column{ro = RO}, V) -> RO orelse V == '$skip' end,
-    Q = prepare_insert(skip(SkipFun, Table#table.columns, e2l(E)), Table, S),
-    if
-        Q#query.values /= [] ->
-            Query = build(Q),
-            BinFold = fun(Elem, Acc) ->
-                <<Acc/binary, Elem/binary>>
-            end,
-            NewBody = lists:foldl(BinFold, <<>>, lists:flatten(Query#query.body)),
-            NewVals = lists:map(fun convertBindingVal/1, Query#query.values),
-            {ok, Query#query{body = NewBody, values = NewVals}};
-        true ->
-            {error, empty_insert}
-    end.
-
-select_pk(E, Table, S) ->
-    SkipFun = fun(#column{key = Key}, _) -> not Key end,
-    Q = prepare_select(skip(SkipFun, Table#table.columns, e2l(E)), Table, S),
-    if
-        Q#query.values /= [] ->
-            Query = build(Q),
-            BinFold = fun(Elem, Acc) ->
-                <<Acc/binary, Elem/binary>>
-            end,
-            NewBody = lists:foldl(BinFold, <<>>, lists:flatten(Query#query.body)),
-            NewVals = lists:map(fun convertBindingVal/1, Query#query.values),
-            {ok, Query#query{body = NewBody, values = NewVals}};
-    true -> {error, pk_miss} end.
-
-select(E, Table, S) ->
-    SkipFun = fun(_, V) -> V == '$skip' end,
-    Query = build(prepare_select(skip(SkipFun, Table#table.columns, e2l(E)), Table, S)),
-    BinFold = fun(Elem, Acc) ->
-        <<Acc/binary, Elem/binary>>
-    end,
-    NewBody = lists:foldl(BinFold, <<>>, lists:flatten(Query#query.body)),
-    NewVals = lists:map(fun convertBindingVal/1, Query#query.values),
-    {ok, Query#query{body = NewBody, values = NewVals}}.
-
-update_pk(E, Table = #table{columns = MekaoCols}, S) ->
-    SetSkipFun = fun(#column{ro = RO}, V) -> RO orelse V == '$skip' end,
-    WhereSkipFun = fun(#column{key = Key}, _) -> not Key end,
-    Vals = e2l(E),
-    Q = prepare_update(skip(SetSkipFun, MekaoCols, Vals),
-                       skip(WhereSkipFun, MekaoCols, Vals),Table, S),
-    if (Q#query.body)#sql_update.set == [] -> {error, empty_update};
-       (Q#query.body)#sql_update.where == [] -> {error, pk_miss};
-       true ->
-           Query = build(Q),
-           BinFold = fun(Elem, Acc) ->
-               <<Acc/binary, Elem/binary>>
-           end,
-           NewBody = lists:foldl(BinFold, <<>>, lists:flatten(Query#query.body)),
-           NewVals = lists:map(fun convertBindingVal/1, Query#query.values),
-           {ok, Query#query{body = NewBody, values = NewVals}}
-    end.
-
-update_pk_diff(E1, E2, Table = #table{columns = MekaoCols}, S) ->
-    Vals1 = e2l(E1),
-    Vals2 = e2l(E2),
-    DiffVals = map2(
-        fun (V, V) -> '$skip';
-            (_, V2) -> V2 end, Vals1, Vals2),
-    SetSkipFun = fun(#column{ro = RO}, V) -> RO orelse V == '$skip' end,
-    WhereSkipFun = fun(#column{key = Key}, _) -> not Key end,
-    Q = prepare_update(skip(SetSkipFun, MekaoCols, DiffVals),
-                       skip(WhereSkipFun, MekaoCols, Vals1),Table, S),
-    if (Q#query.body)#sql_update.set == [] -> {error, empty_update};
-       (Q#query.body)#sql_update.where == [] -> {error, pk_miss};
-        true ->
-            Query = build(Q),
-            BinFold = fun(Elem, Acc) ->
-                <<Acc/binary, Elem/binary>>
-            end,
-            NewBody = lists:foldl(BinFold, <<>>, lists:flatten(Query#query.body)),
-            NewVals = lists:map(fun convertBindingVal/1, Query#query.values),
-            {ok, Query#query{body = NewBody, values = NewVals}}
-    end.
-
-update(E, Selector, Table = #table{columns = MekaoCols}, S) ->
-    SetSkipFun = fun(#column{ro = RO}, V) -> RO orelse V == '$skip' end,
-    WhereSkipFun = fun(_, V) -> V == '$skip' end,
-    Q = prepare_update(skip(SetSkipFun, MekaoCols, e2l(E)),
-                       skip(WhereSkipFun, MekaoCols, e2l(Selector)),Table, S),
-    if (Q#query.body)#sql_update.set == [] -> {error, empty_update};
-        true ->
-            Query = build(Q),
-            BinFold = fun(Elem, Acc) ->
-                <<Acc/binary, Elem/binary>>
-            end,
-            NewBody = lists:foldl(BinFold, <<>>, lists:flatten(Query#query.body)),
-            NewVals = lists:map(fun convertBindingVal/1, Query#query.values),
-            {ok, Query#query{body = NewBody, values = NewVals}}
-    end.
-
-delete_pk(E, Table, S) ->
-    SkipFun = fun(#column{key = Key}, _) -> not Key end,
-    Q = prepare_delete(skip(SkipFun, Table#table.columns, e2l(E)), Table, S),
-    if Q#query.values /= [] ->
-        Query = build(Q),
-        BinFold = fun(Elem, Acc) ->
-            <<Acc/binary, Elem/binary>>
-        end,
-        NewBody = lists:foldl(BinFold, <<>>, lists:flatten(Query#query.body)),
-        NewVals = lists:map(fun convertBindingVal/1, Query#query.values),
-        {ok, Query#query{body = NewBody, values = NewVals}};
-       true -> {error, pk_miss} end.
-
-delete(Selector, Table, S) ->
-    SkipFun = fun(_, V) -> V == '$skip' end,
-    Q = prepare_delete(skip(SkipFun, Table#table.columns, e2l(Selector)), Table, S),
-    Query = build(Q),
-    BinFold = fun(Elem, Acc) ->
-        <<Acc/binary, Elem/binary>>
-    end,
-    NewBody = lists:foldl(BinFold, <<>>, lists:flatten(Query#query.body)),
-    NewVals = lists:map(fun convertBindingVal/1, Query#query.values),
-    {ok, Query#query{body = NewBody, values = NewVals}}.
-
-prepare_insert(E, Table, S) ->
-    {Cols, PHs, Types, Vals} = qdata(1, e2l(E), Table#table.columns, S),
-    Q = #sql_insert{table=atom_to_binary(Table#table.name, utf8),columns=intersperse(Cols, <<", ">>),
-                   values=intersperse(PHs, <<", ">>),returning=returning(insert, Table, S)},
-    #query{body=Q,types=Types,values=Vals,next_ph_num=length(PHs) + 1}.
-
-prepare_select(E, Table, S) ->
-    #table{columns = MekaoCols,order_by = OrderBy} = Table,
-    {Where, {_, PHs, Types, Vals}} = where(qdata(1, e2l(E), MekaoCols, S), S),
-    AllCols = intersperse(MekaoCols, <<", ">>, fun(#column{name = Name}) -> Name end),
-    Q = #sql_select{table=atom_to_binary(Table#table.name, utf8),columns=AllCols,where=Where,order_by=order_by(OrderBy)},
-    #query{body=Q,types=Types,values=Vals,next_ph_num = length(PHs) + 1}.
-
-prepare_update(SetE, WhereE, Table = #table{columns = MekaoCols}, S) ->
-    {SetCols, SetPHs, SetTypes, SetVals} = qdata(1, e2l(SetE), MekaoCols, S),
-    SetPHsLen = length(SetPHs),
-    {Where, {_, WherePHs, WhereTypes, WhereVals}} = where(qdata(SetPHsLen + 1, e2l(WhereE), MekaoCols, S), S),
-    WherePHsLen = length(WherePHs),
-    Set = intersperse2(fun (C, PH) -> [C, <<" = ">>, PH] end,<<", ">>, SetCols, SetPHs),
-    Q = #sql_update{table=atom_to_binary(Table#table.name, utf8),set=Set,where=Where,returning=returning(update, Table, S)},
-    #query{body=Q,types=SetTypes++WhereTypes,values=SetVals++WhereVals,next_ph_num = SetPHsLen + WherePHsLen + 1}.
-
-prepare_delete(E, Table, S) ->
-    {Where, {_, PHs, Types, Vals}} = where(qdata(1, e2l(E), Table#table.columns, S), S),
-    Q = #sql_delete{table=atom_to_binary(Table#table.name, utf8),where=Where,returning=returning(delete, Table, S)},
-    #query{body=Q,types=Types,values=Vals,next_ph_num = length(PHs) + 1}.
-
-build(Q = #query{body = Select}) when is_record(Select, sql_select) ->
-    #sql_select{columns=Columns,table=Table,where=Where,order_by=OrderBy} = Select,
-    Q#query{body = [<<"SELECT ">>, Columns, <<" FROM ">>, Table, build_where(Where),build_order_by(OrderBy)]};
-build(Q = #query{body = Insert}) when is_record(Insert, sql_insert) ->
-    #sql_insert{table=Table,columns=Columns,values=Values,returning=Return} = Insert,
-    Q#query{body = [<<"INSERT INTO ">>, Table, <<" (">>, Columns, <<") VALUES (">>,Values, <<")">>, build_return(Return)]};
-build(Q = #query{body = Update}) when is_record(Update, sql_update) ->
-    #sql_update{table=Table,set=Set,where=Where,returning=Return} = Update,
-    Q#query{body = [<<"UPDATE ">>, Table, <<" SET ">>, Set,build_where(Where), build_return(Return)]};
-build(Q = #query{body = Delete}) when is_record(Delete, sql_delete) ->
-    #sql_delete{table=Table,where=Where,returning=Return} = Delete,
-    Q#query{body = [<<"DELETE FROM ">>, Table, build_where(Where),build_return(Return)]}.
-
-e2l(Vals) when is_list(Vals) -> Vals;
-e2l(E) when is_tuple(E) -> [_EntityName | Vals] = tuple_to_list(E), Vals.
-
-skip(SkipFun, Cols, Vals) -> map2(fun(C, V) -> Skip = SkipFun(C, V),if Skip -> '$skip'; true -> V end end, Cols, Vals).
-
-qdata(_, [], [], _) -> {[], [], [], []};
-qdata(Num, ['$skip' | Vals], [_Col | Cols], S) -> qdata(Num, Vals, Cols, S);
-qdata(Num, [Pred | Vals], [Col | Cols], S) ->
-    #column{type = T, name = CName, transform = TrFun} = Col,
-    V = predicate_val(Pred),
-    NewV = if TrFun == undefined -> V;
-                            true -> TrFun(V) end,
-    PH = (S#sql_settings.placeholder)(Col, Num, NewV),
-    NewPred = set_predicate_val(Pred, NewV), {ResCols, ResPHs, ResTypes, ResVals} = qdata(Num + 1, Vals, Cols, S),
-    {[CName | ResCols], [PH | ResPHs], [T | ResTypes], [NewPred | ResVals]}.
-
-returning(_QType, _Table, #sql_settings{returning = undefined}) -> [];
-returning(QType, Table, #sql_settings{returning = RetFun}) -> RetFun(QType, Table).
-
-where(QData = {[], [], [], []}, _S) -> {[], QData};
-where({[C], [PH], [T], [V]}, S) ->
-    {W, {NewC, NewPH, NewT, NewV}} = predicate({C, PH, T, V}, S),
-    {[W], {[NewC], [NewPH], [NewT], [NewV]}};
-where({[C | Cs], [PH | PHs], [T | Types], [V | Vals]}, S) ->
-    {W, {NewC, NewPH, NewT, NewV}} = predicate({C, PH, T, V}, S),
-    {Ws, {NewCs, NewPHs, NewTypes, NewVals}} = where({Cs, PHs, Types, Vals}, S),
-    {[W, <<" AND ">> | Ws], {[NewC | NewCs], [NewPH | NewPHs], [NewT | NewTypes], [NewV | NewVals]}}.
-
-%% TODO: add NOT, IN, ANY, ALL, BETWEEN, LIKE handling
-predicate({C, PH, T, {'$predicate', Op, V}}, S) when Op == '='; Op == '<>' ->
-    IsNull = (S#sql_settings.is_null)(V),
-    if not IsNull -> {[C, op_to_bin(Op), PH], {C, PH, T, V}};
-    Op == '=' -> {[C, <<" IS NULL">>], {C, PH, T, V}};
-    Op == '<>' -> {[C, <<" IS NOT NULL">>], {C, PH, T, V}} end;
-predicate({C, PH, T, {'$predicate', OP, V}},  _S) -> {[C, op_to_bin(OP), PH],  {C, PH, T, V}};
-predicate({C, PH, T, V}, S) -> predicate({C, PH, T, {'$predicate', '=', V}}, S).
-
-op_to_bin('=')  -> <<" = ">>;
-op_to_bin('<>') -> <<" <> ">>;
-op_to_bin('>')  -> <<" > ">>;
-op_to_bin('>=') -> <<" >= ">>;
-op_to_bin('<')  -> <<" < ">>;
-op_to_bin('<=') -> <<" <= ">>.
-
-order_by(undefined) -> [];
-order_by([]) -> [];
-order_by([O]) -> [order_by_1(O)];
-order_by([O | OrderBy]) -> [order_by_1(O), <<", ">> | order_by(OrderBy)].
-order_by_1(E) when not is_tuple(E) -> order_by_1({E, {default, default}});
-order_by_1({Pos, Opts}) when is_integer(Pos) -> order_by_1({integer_to_list(Pos - 1), Opts});
-order_by_1({Expr, Opts}) when is_list(Expr); is_binary(Expr) -> [Expr, order_by_opts(Opts)].
-order_by_opts({Ordering, Nulls}) ->
-    O = case Ordering of
-        default -> <<"">>;
-        asc -> <<" ASC">>;
-        desc -> <<" DESC">> end,
-    case Nulls of
-        default -> O;
-        nulls_first -> <<O/binary," NULLS FIRST">>;
-        nulls_last -> <<O/binary, " NULLS LAST">> end.
-
-build_return([]) -> <<>>;
-build_return(Return) -> [<<" ">> | Return].
-build_where([]) -> <<>>;
-build_where(Where) -> [<<" WHERE ">> | Where].
-build_order_by([]) -> <<>>;
-build_order_by(OrderBy) -> [<<" ORDER BY ">>, OrderBy].
-
-predicate_val({'$predicate', _, V}) -> V;
-predicate_val(V) -> V.
-
-set_predicate_val({'$predicate', Op, _}, NewV) -> {'$predicate', Op, NewV};
-set_predicate_val(_, NewV) -> NewV.
-
-map2(_Fun, [], []) -> [];
-map2(Fun, [V1 | L1], [V2 | L2]) -> [Fun(V1, V2) | map2(Fun, L1, L2)].
-
-intersperse(List, Sep) -> intersperse(List, Sep, fun (X) -> X end).
-intersperse([], _, _) -> [];
-intersperse([Item], _, Fun) -> [Fun(Item)];
-intersperse([Item | Items], Sep, Fun) -> [Fun(Item), Sep | intersperse(Items, Sep, Fun)].
-
-intersperse2(_Fun, _Sep, [], []) -> [];
-intersperse2(Fun, _Sep, [I1], [I2]) -> [Fun(I1, I2)];
-intersperse2(Fun, Sep, [I1 | I1s], [I2 | I2s]) -> [Fun(I1, I2), Sep | intersperse2(Fun, Sep, I1s, I2s)].
-
-put(Records) when is_list(Records) -> lists:foreach(fun sql_put/1, Records);
-put(Record) -> sql_put(Record).
-
-sql_put(Record) ->
-    Table = kvs:table(element(1, Record)),
-    io:format("Trying to put ~p ~n", [Record]),
-    {ok, Query} = insert(Record, Table, ?SETTINGS),
-    io:format("Query [~p], vals [~p] ~n", [Query#query.body, Query#query.values]),
-    PutRes = extendedQuery(Query#query.body, Query#query.values),
-    case PutRes of
-        {ok, _} -> ok;
-        _ -> PutRes end.
-
-get(Table, Key) ->
-    io:format("Trying to get from ~p by ~p ~n", [Table, Key]),
-    TableSpec = kvs:table(Table),
-    SkipVals = lists:duplicate(length(TableSpec#table.fields) - 1, '$skip'),
-    RecList = [Table | [Key | SkipVals]],
-    Record = list_to_tuple(RecList),
-    sql_get(TableSpec, Record).
-
-sql_get(TableSpec, Record) ->
-    io:format("Record is: ~p ~n", [Record]),
-    {ok, Query} = select(Record, TableSpec, ?SETTINGS),
-    io:format("Query is: ~p ~n", [Query#query.body]),
-    QueryRes = extendedQuery(Query#query.body, Query#query.values),
-    case QueryRes of
-        {ok, []} -> {error, not_found};
-        {ok, Rows} -> [Row | _] = Rows, {ok, proplistToRecord(element(1, Record), Row)};
-        {ok, _Count, Rows} -> [Row | _] = Rows,{ok, proplistToRecord(element(1, Record), Row)};
-        _ -> QueryRes end.
-
-proplistToRecord(Tag, Proplist) ->
-    ValsExt = fun(Elem) ->
-        {_, Val} = Elem,
-        Val
-    end,
-    Vals = lists:map(ValsExt, Proplist),
-    RecList = [Tag | Vals],
-    list_to_tuple(RecList).
-
-convertBindingVal(Val) when is_integer(Val) -> Val;
-convertBindingVal(Val) when is_float(Val) -> Val;
-convertBindingVal(Val) -> term_to_binary(Val).
-
-mapResult([], _Rows) -> [];
-mapResult(_Columns, []) -> [];
-mapResult(Columns, Rows) ->
-  ColumnsExtractor = fun(Elem) -> Elem#column.name end,
-  ColsNames = lists:map(ColumnsExtractor, Columns),
-  ResultsMapper = fun(Row) -> lists:zip(ColsNames, tuple_to_list(Row)) end,
-  lists:map(ResultsMapper, Rows).
-
-extendedQuery(SQL, Params) ->
-  QueryRes = case Params of
-    [] -> pgsql:equery(connection(), SQL);
-     _ -> pgsql:equery(connection(), SQL, Params) end,
-  _Reply = case QueryRes of
-    {ok, Columns, Rows} -> {ok, mapResult(Columns, Rows)};
-    {ok, Count, Columns, Rows} -> {ok, Count, mapResult(Columns, Rows)};
-    _ -> QueryRes end.
-
-connection() ->
-  case wf:cache(pgsql_conn) of
-       unefined ->  Host = kvs:config(pgsql,host,"localhost"),
-                    Port = kvs:config(pgsql,port, 5432),
-                    User = kvs:config(pgsql,user, "user"),
-                    Pass = kvs:config(pgsql,pass, "pass"),
-                    Db   = kvs:config(pgsql,db, "test"),
-                    Timeout = kvs:config(pgsql,timeout,5000),
-                    {ok, Conn} = pgsql:connect(Host, User, Pass,
-                              [{database, Db}, {port, Port}, {timeout, Timeout}]),
-                    wf:cache(pgsql_conn,Conn),
-                    Conn;
-      Connection -> Connection end.

+ 0 - 9
sys.config

@@ -1,9 +0,0 @@
-[
- {kvs, [{dba,store_mnesia},
-        {user,[{interval,1,2,user},
-               {interval,3,100000,user2}]},
-        {schema, [kvs_user,
-                  kvs_acl,
-                  kvs_feed,
-                  kvs_subscription ]} ]}
-].

+ 0 - 45
test/kvs_SUITE.erl

@@ -1,45 +0,0 @@
--module(kvs_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include_lib("kvs/include/entry.hrl").
--compile(export_all).
-
-suite() -> [{timetrap,{seconds,30}}].
-all() -> [{group, feed},{group,acl}].
-groups() -> [{feed,[entry,comment,user]},
-             {acl,[access]}].
-
-init_per_suite(Config) ->
-    application:start(mnesia),
-    application:start(kvs),
-    application:set_env(kvs, schema, [kvs_user, kvs_acl, kvs_feed, kvs_subscription]),
-    application:set_env(kvs, dba, store_mnesia),
-    kvs:join(),
-    kvs:init_db(),
-    ct:log("-> Dir ~p~n",[kvs:dir()]),
-    Config.
-
-end_per_suite(Config) ->
-    kvs:destroy(),
-    application:stop(kvs),
-    ok.
-
-init_per_group(_Name, _Config) ->
-    ok.
-end_per_group(_Name, _Config) ->
-    ok.
-
-access(Config) -> ok.
-comment(Config) -> ok.
-user(Config) -> ok.
-entry(Config) ->
-    Fid = 1,
-    kvs:add(#entry{id={1,Fid},feed_id=Fid}),
-    kvs:add(#entry{id={2,Fid},feed_id=Fid}),
-    L = kvs:entries(kvs:get(feed,Fid),entry,undefined),
-    List = [ Key || #entry{id=Key} <- L ],
-    Length = length(List),
-    2 == Length,
-    List == [{1,1},{2,1}],
-    ct:log("-> List ~p~n", [List]),
-    ok.