1 /* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #ifndef DD__PROPERTIES_IMPL_INCLUDED
24 #define DD__PROPERTIES_IMPL_INCLUDED
25 
26 #include <map>
27 #include <memory>
28 #include <set>
29 #include <string>
30 #include <utility>
31 
32 #include "lex_string.h"
33 #include "my_dbug.h"
34 #include "my_inttypes.h"
35 #include "sql/dd/properties.h"   // dd::Properties
36 #include "sql/dd/string_type.h"  // dd::String_type
37 
38 namespace dd {
39 
40 ///////////////////////////////////////////////////////////////////////////
41 
42 /**
43   The Properties_impl class implements the Properties interface.
44 
45   The key=value pairs are stored in a std::map. An instance can be created
46   either by means of the default constructor, which creates an object
47   with an empty map, or alternatively, it can be created by means of the
48   static parse_properties function with a String_type argument. The string
49   is supposed to contain a semicolon separated list of key=value pairs,
50   where the characters '=' and ';' also may be part of key or value by
51   escaping using the '\' as an escape character. The escape character
52   itself must also be escaped if being part of key or value. All characters
53   between '=' and ';' are considered part of key or value, whitespace is
54   not ignored.
55 
56   Escaping is removed during parsing so the strings in the map are not
57   escaped. Escaping is only relevant in the context of raw strings that
58   are to be parsed, and raw strings that are returned containing all
59   key=value pairs.
60 
61   Example (note \\ due to escaping of C string literals):
62     parse_properties("a=b;b = c")     -> ("a", "b"), ("b ", " c")
63     parse_properties("a\\==b;b=\\;c") -> ("a=", "b"), ("b", ";c")
64 
65     get("a=") == "b"
66     get("b")  == ";c"
67 
68   Additional key=value pairs may be added by means of the set function,
69   which takes a string argument that is assumed to be unescaped.
70 
71   Please also refer to the comments in the file properties.h where the
72   interface is defined; the functions in the interface are commented there.
73 */
74 
75 class Properties_impl : public Properties {
76  private:
77   /* Map containing the actual key-value pairs. */
78   Properties::Map m_map;
79 
80   /* Set containing the valid keys. An empty set means any key is valid. */
81   std::set<String_type> m_keys;
82 
83  public:
84   Properties_impl() = default;
85 
86   /* Constructor accepting a set of valid keys. */
Properties_impl(const std::set<String_type> & keys)87   Properties_impl(const std::set<String_type> &keys) : m_keys(keys) {}
88 
impl()89   virtual const Properties_impl *impl() const { return this; }
90 
begin()91   virtual iterator begin() { return m_map.begin(); }
92 
begin()93   virtual const_iterator begin() const { return m_map.begin(); }
94 
end()95   virtual iterator end() { return m_map.end(); }
96 
end()97   virtual const_iterator end() const { return m_map.end(); }
98 
size()99   virtual size_type size() const { return m_map.size(); }
100 
empty()101   virtual bool empty() const { return m_map.empty(); }
102 
clear()103   virtual void clear() { return m_map.clear(); }
104 
valid_key(const String_type & key)105   virtual bool valid_key(const String_type &key) const {
106     return (m_keys.empty() || m_keys.find(key) != m_keys.end());
107   }
108 
exists(const String_type & key)109   virtual bool exists(const String_type &key) const {
110     return m_map.find(key) != m_map.end();
111   }
112 
remove(const String_type & key)113   virtual bool remove(const String_type &key) {
114     iterator it = m_map.find(key);
115 
116     if (it == m_map.end()) return true;
117 
118     m_map.erase(it);
119     return false;
120   }
121 
122   /**
123     Iterate over all entries in the private hash table. For each
124     key value pair, escape both key and value, and append the strings
125     to the result. Use '=' to separate key and value, and use ';'
126     to separate pairs.
127 
128     Invalid keys are not included in the output. However, there should
129     never be a situation where invalid keys are present, so we just assert
130     that the keys are valid.
131 
132     @return string containing all escaped key value pairs
133   */
134   virtual const String_type raw_string() const;
135 
136   /**
137     Get the string value for a given key.
138 
139     Return true if the operation fails, i.e., if the key does not exist
140     or if the key is invalid. Assert that the key exists in debug builds.
141 
142     @param      key   key to lookup the value for
143     @param[out] value string value
144     @return           Operation outcome, false if success, otherwise true
145   */
146   virtual bool get(const String_type &key, String_type *value) const;
147 
148   /**
149     Set the key/value. If the key is invalid, a warning is written
150     to the error log. Assert that the key exists in debug builds.
151 
152     @param  key    Key to set.
153     @param  value  Value to set.
154     @return        Operation outcome, false if success, otherwise true
155   */
156   virtual bool set(const String_type &key, const String_type &value);
157 
158   /**
159     Insert key/value pairs from a different property object.
160 
161     The set of valid keys is not copied, instead, the existing
162     set in the destination object is used to ignore all invalid
163     keys.
164 
165     @param properties  Source object.
166 
167     @retval  Operation outcome, false if no error, otherwise true.
168   */
169   virtual bool insert_values(const Properties &properties);
170 
171   /**
172     Insert key/value pairs from a string.
173 
174     Parse the string and add key/value pairs to this object.
175     The existing set of valid keys in the destination object
176     is used to ignore all invalid keys.
177 
178     @param  raw_string  String to be parsed.
179 
180     @retval  Operation outcome, false if no error, otherwise true.
181   */
182   virtual bool insert_values(const String_type &raw_string);
183 
184 #ifdef EXTRA_CODE_FOR_UNIT_TESTING
185   /**
186     Extend the set of valid keys after the property object is
187     created. This can be used e.g. for the SE private data.
188 
189     @pre     There must be a set of valid keys already, or the
190              map of key-value pairs must be empty. Otherwise,
191              we risk making existing keys invalid, thus hiding
192              their values.
193 
194     @param   keys    Set of additional keys to insert into
195                      the set of valid keys.
196   */
add_valid_keys(const std::set<String_type> & keys)197   void add_valid_keys(const std::set<String_type> &keys) {
198     DBUG_ASSERT(!m_keys.empty() || m_map.empty());
199     m_keys.insert(keys.begin(), keys.end());
200   }
201 
202   /**
203     Remove the set of valid keys after the property object is
204     created. Convenience method used by unit tests.
205   */
clear_valid_keys()206   void clear_valid_keys() { m_keys.clear(); }
207 
208   /**
209     Get valid key at a certain index.
210 
211     If the key set is empty, return a string representation of
212     the index is returned. If the index is out of bounds, return
213     the last key.
214 
215     @note    This is needed by unit tests to fill in
216              random key/value pairs without breaking the
217              check for valid keys.
218 
219     @param   index  Index at which to get the valid key.
220 
221     @retval  Key at the given index, a string containing the index,
222              or the last key.
223   */
valid_key_at(size_t index)224   const String_type valid_key_at(size_t index) const {
225     if (m_keys.empty()) {
226       Stringstream_type ostream;
227       ostream << index;
228       return ostream.str();
229     }
230     if (m_keys.size() <= index) {
231       return *std::next(m_keys.begin(), m_keys.size() - 1);
232     }
233     return *std::next(m_keys.begin(), index);
234   }
235 #endif
236 };
237 
238 ///////////////////////////////////////////////////////////////////////////
239 
240 }  // namespace dd
241 
242 #endif  // DD__PROPERTIES_IMPL_INCLUDED
243