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