1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2017 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#pragma once
19
20#include "item.h"
21
22#include <memcached/dockey.h>
23#include <memcached/engine_common.h>
24#include <platform/sized_buffer.h>
25
26#include <memory>
27#include <string>
28#include <unordered_map>
29
30class SystemEventMessage;
31
32namespace Collections {
33
34class Filter;
35
36namespace VB {
37
38class Manifest;
39
40/**
41 * The VB filter is used to decide if keys on a DCP stream should be sent
42 * or dropped.
43 *
44 * A filter is built from the Collections::Filter that was established when
45 * the producer was opened. During the time the producer was opened and a
46 * stream is requested, filtered collections may have been deleted, so the
47 * ::VB::Filter becomes the intersection of the producer's filter and the
48 * open collections within the manifest.
49 *
50 * Note: There is no locking on a VB::Filter as at the moment it is constructed
51 * and then is not mutable.
52 */
53
54class Filter {
55public:
56    /**
57     * Construct a Collections::VB::Filter using the producer's
58     * Collections::Filter and the vbucket's collections manifest.
59     *
60     * If the producer's filter is configured to filter collections then the
61     * resulting object will filter the intersection filter:manifest
62     * collections. The constructor will log when it finds it must drop a
63     * collection
64     *
65     * If the producer's filter is effectively a passthrough
66     * (allowAllCollections returns true) then so will the resulting VB filter.
67     *
68     * @param filter The producer's filter that the client configured.
69     * @param manifest The vbucket's collection manifest.
70     */
71    Filter(const ::Collections::Filter& filter,
72           const ::Collections::VB::Manifest& manifest);
73
74    /**
75     * Check the item and if required, update the filter.
76     * If the item represents a collection deletion and this filter matches the
77     * collection, we must update the filter so that no more matching items
78     * would be allowed.
79     *
80     * @param item an Item to be processed.
81     * @return if the Item is allowed to be sent on the DcpStream
82     */
83    bool checkAndUpdate(const Item& item) {
84        // passthrough, everything is allowed.
85        if (passthrough) {
86            return true;
87        }
88
89        // The presence of $default is a simple check against defaultAllowed
90        if (item.getKey().getDocNamespace() ==
91                    DocNamespace::DefaultCollection &&
92            defaultAllowed) {
93            return true;
94        }
95        // More complex checks needed...
96        return checkAndUpdateSlow(item);
97    }
98
99    /**
100     * @return if the filter is empty
101     */
102    bool empty() const;
103
104    /**
105     * Add statistics for this filter, currently just depicts the object's state
106     */
107    void addStats(ADD_STAT add_stat,
108                  const void* c,
109                  const std::string& prefix,
110                  uint16_t vb) const;
111
112    /**
113     * Dump this to std::cerr
114     */
115    void dump() const;
116
117protected:
118    /**
119     * Does the filter allow the system event? I.e. a "meat,dairy" filter
120     * shouldn't allow delete events for the "fruit" collection.
121     *
122     * @param item a SystemEventMessage to check
123     * @param return true if the filter says this event should be allowed
124     */
125    bool allowSystemEvent(const Item& item) const;
126
127    /// Non-inline, slow path of checkAndUpdate().
128    bool checkAndUpdateSlow(const Item& item);
129
130    /**
131     * Remove the collection of the item from the filter
132     */
133    void remove(const Item& item);
134
135    using Container = ::std::unordered_map<cb::const_char_buffer,
136                                           std::unique_ptr<std::string>>;
137    Container filter;
138    bool defaultAllowed;
139    bool passthrough;
140    bool systemEventsAllowed;
141    std::string separator;
142
143    friend std::ostream& operator<<(std::ostream& os, const Filter& filter);
144};
145
146std::ostream& operator<<(std::ostream& os, const Filter& filter);
147
148} // end namespace VB
149} // end namespace Collections
150