1 /*
2 Copyright (c) 2014, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #ifndef ARRAY_ADAPTER_HPP
26 #define ARRAY_ADAPTER_HPP
27
28 #include <algorithm>
29 #include <assert.h>
30
31 /*
32 Utility classes to convert between C++ strings/byte arrays and the
33 internal format used for [VAR]CHAR/BINARY types.
34
35 Base class that can be used for read operations. The column type is
36 taken from the NdbRecAttr object, so only one object is needed to
37 convert from different [VAR]CHAR/BINARY types. No additional memory
38 is allocated.
39 */
40 class ReadOnlyArrayAdapter {
41 public:
ReadOnlyArrayAdapter()42 ReadOnlyArrayAdapter() {}
43
44 enum ErrorType {Success,
45 InvalidColumnType,
46 InvalidArrayType,
47 InvalidNullColumn,
48 InvalidNullAttribute,
49 InvalidNullaRef,
50 BytesOutOfRange,
51 UnknownError};
52
53 /*
54 Return a C++ string from the aRef() value of attr. This value
55 will use the column and column type from attr. The advantage is
56 for reading; the same ArrayAdapter can be used for multiple
57 columns. The disadvantage is; passing an attribute not of
58 [VAR]CHAR/BINARY type will result in a traditional exit(-1)
59 */
60 std::string get_string(const NdbRecAttr* attr,
61 ErrorType& error) const;
62
63 /* Calculate the first_byte and number of bytes in aRef for attr */
64 void get_byte_array(const NdbRecAttr* attr,
65 const char*& first_byte,
66 size_t& bytes,
67 ErrorType& error) const;
68
69 /* Check if a column is of type [VAR]BINARY */
70 bool is_binary_array_type(const NdbDictionary::Column::Type t) const;
71
72 /* Check if a column is of type [VAR]BINARY or [VAR]CHAR */
73 bool is_array_type(const NdbDictionary::Column::Type t) const;
74 private:
75 /* Disable copy constructor */
ReadOnlyArrayAdapter(const ReadOnlyArrayAdapter & a)76 ReadOnlyArrayAdapter(const ReadOnlyArrayAdapter& a) {}
77 };
78
79
80 /*
81 Extension to ReadOnlyArrayAdapter to be used together with
82 insert/write/update operations. Memory is allocated for each
83 call to make_aRef or allocate_in_bytes. The memory allocated will
84 be deallocated by the destructor. To save memory, the scope of an
85 instance of this class should not be longer than the life time of
86 the transaction. On the other hand, it must be long enough for the
87 usage of all references created
88 */
89 class ReadWriteArrayAdapter : public ReadOnlyArrayAdapter {
90 public:
ReadWriteArrayAdapter()91 ReadWriteArrayAdapter() {}
92
93 /* Destructor, the only place where memory is deallocated */
94 ~ReadWriteArrayAdapter();
95
96 /*
97 Create a binary representation of the string 's' and return a
98 pointer to it. This pointer can later be used as argument to for
99 example setValue
100 */
101 char* make_aRef(const NdbDictionary::Column* column,
102 std::string s,
103 ErrorType& error);
104
105 /*
106 Allocate a number of bytes suitable for this column type. aRef
107 can later be used as argument to for example setValue. first_byte
108 is the first byte to store data to. bytes is the number of bytes
109 to allocate
110 */
111 void allocate_in_bytes(const NdbDictionary::Column* column,
112 char*& aRef,
113 char*& first_byte,
114 size_t bytes,
115 ErrorType& error);
116
117 private:
118 /* Disable copy constructor */
ReadWriteArrayAdapter(const ReadWriteArrayAdapter & a)119 ReadWriteArrayAdapter(const ReadWriteArrayAdapter& a)
120 :ReadOnlyArrayAdapter() {}
121
122 /* Record of allocated char arrays to delete by the destructor */
123 std::vector<char*> aRef_created;
124 };
125
126
~ReadWriteArrayAdapter()127 inline ReadWriteArrayAdapter::~ReadWriteArrayAdapter()
128 {
129 for (std::vector<char*>::iterator i = aRef_created.begin();
130 i != aRef_created.end();
131 ++i) {
132 delete [] *i;
133 }
134 }
135
136
137 char*
138 ReadWriteArrayAdapter::
make_aRef(const NdbDictionary::Column * column,std::string input,ErrorType & error)139 make_aRef(const NdbDictionary::Column* column,
140 std::string input,
141 ErrorType& error)
142 {
143 char* new_ref;
144 char* data_start;
145
146 /*
147 Allocate bytes and push them into the aRef_created vector.
148 After this operation, new_ref has a complete aRef to use in insertion
149 and data_start has ptr from which data is to be written.
150 The new_aref returned is padded completely with blank spaces.
151 */
152 allocate_in_bytes(column, new_ref, data_start, input.length(), error);
153
154 if(error != Success)
155 {
156 return NULL;
157 }
158
159 /*
160 Copy the input string into aRef's data pointer
161 without affecting remaining blank spaces at end.
162 */
163 strncpy(data_start, input.c_str(), input.length());
164
165 return new_ref;
166 }
167
168
169 void
170 ReadWriteArrayAdapter::
allocate_in_bytes(const NdbDictionary::Column * column,char * & aRef,char * & first_byte,size_t bytes,ErrorType & error)171 allocate_in_bytes(const NdbDictionary::Column* column,
172 char*& aRef,
173 char*& first_byte,
174 size_t bytes,
175 ErrorType& error)
176 {
177 bool is_binary;
178 char zero_char;
179 NdbDictionary::Column::ArrayType array_type;
180 size_t max_length;
181
182 /* unless there is going to be any problem */
183 error = Success;
184
185 if (column == NULL)
186 {
187 error = InvalidNullColumn;
188 aRef = NULL;
189 first_byte = NULL;
190 return;
191 }
192
193 if (!is_array_type(column->getType()))
194 {
195 error = InvalidColumnType;
196 aRef = NULL;
197 first_byte = NULL;
198 return;
199 }
200
201 is_binary = is_binary_array_type(column->getType());
202 zero_char = (is_binary ? 0 : ' ');
203 array_type = column->getArrayType();
204 max_length = column->getLength();
205
206 if (bytes > max_length)
207 {
208 error = BytesOutOfRange;
209 aRef = NULL;
210 first_byte = NULL;
211 return;
212 }
213
214 switch (array_type) {
215 case NdbDictionary::Column::ArrayTypeFixed:
216 /* no need to store length bytes */
217 aRef = new char[max_length];
218 first_byte = aRef;
219 /* pad the complete string with blank space (or) null bytes */
220 for (size_t i=0; i < max_length; i++) {
221 aRef[i] = zero_char;
222 }
223 break;
224 case NdbDictionary::Column::ArrayTypeShortVar:
225 /* byte length stored over first byte. no padding required */
226 aRef = new char[1 + bytes];
227 first_byte = aRef + 1;
228 aRef[0] = (char)bytes;
229 break;
230 case NdbDictionary::Column::ArrayTypeMediumVar:
231 /* byte length stored over first two bytes. no padding required */
232 aRef = new char[2 + bytes];
233 first_byte = aRef + 2;
234 aRef[0] = (char)(bytes % 256);
235 aRef[1] = (char)(bytes / 256);
236 break;
237 }
238 aRef_created.push_back(aRef);
239 }
240
241
get_string(const NdbRecAttr * attr,ErrorType & error) const242 std::string ReadOnlyArrayAdapter::get_string(const NdbRecAttr* attr,
243 ErrorType& error) const
244 {
245 size_t attr_bytes= 0;
246 const char* data_ptr= NULL;
247 std::string result= "";
248
249 /* get the beginning of data and its size.. */
250 get_byte_array(attr, data_ptr, attr_bytes, error);
251
252 if(error != Success)
253 {
254 return result;
255 }
256
257 /* ..and copy the value into result */
258 result = string(data_ptr, attr_bytes);
259
260 /* special treatment for FixedArrayType to eliminate padding characters */
261 if(attr->getColumn()->getArrayType() == NdbDictionary::Column::ArrayTypeFixed)
262 {
263 char padding_char = ' ';
264 std::size_t last = result.find_last_not_of(padding_char);
265 result = result.substr(0, last+1);
266 }
267
268 return result;
269 }
270
271
272 void
273 ReadOnlyArrayAdapter::
get_byte_array(const NdbRecAttr * attr,const char * & data_ptr,size_t & bytes,ErrorType & error) const274 get_byte_array(const NdbRecAttr* attr,
275 const char*& data_ptr,
276 size_t& bytes,
277 ErrorType& error) const
278 {
279 /* unless there is a problem */
280 error= Success;
281
282 if (attr == NULL)
283 {
284 error = InvalidNullAttribute;
285 return;
286 }
287
288 if (!is_array_type(attr->getType()))
289 {
290 error = InvalidColumnType;
291 return;
292 }
293
294 const NdbDictionary::Column::ArrayType array_type =
295 attr->getColumn()->getArrayType();
296 const size_t attr_bytes = attr->get_size_in_bytes();
297 const char* aRef = attr->aRef();
298
299 if(aRef == NULL)
300 {
301 error= InvalidNullaRef;
302 return;
303 }
304
305 switch (array_type) {
306 case NdbDictionary::Column::ArrayTypeFixed:
307 /* no length bytes stored with aRef */
308 data_ptr = aRef;
309 bytes = attr_bytes;
310 break;
311 case NdbDictionary::Column::ArrayTypeShortVar:
312 /* first byte of aRef has length of the data */
313 data_ptr = aRef + 1;
314 bytes = (size_t)(aRef[0]);
315 break;
316 case NdbDictionary::Column::ArrayTypeMediumVar:
317 /* first two bytes of aRef has length of the data */
318 data_ptr = aRef + 2;
319 bytes = (size_t)(aRef[1]) * 256 + (size_t)(aRef[0]);
320 break;
321 default:
322 /* should never reach here */
323 data_ptr = NULL;
324 bytes = 0;
325 error = InvalidArrayType;
326 break;
327 }
328 }
329
330
331 bool
332 ReadOnlyArrayAdapter::
is_binary_array_type(const NdbDictionary::Column::Type t) const333 is_binary_array_type(const NdbDictionary::Column::Type t) const
334 {
335 bool is_binary;
336
337 switch (t)
338 {
339 case NdbDictionary::Column::Binary:
340 case NdbDictionary::Column::Varbinary:
341 case NdbDictionary::Column::Longvarbinary:
342 is_binary = true;
343 break;
344 default:
345 is_binary = false;
346 }
347 return is_binary;
348 }
349
350
351 bool
352 ReadOnlyArrayAdapter::
is_array_type(const NdbDictionary::Column::Type t) const353 is_array_type(const NdbDictionary::Column::Type t) const
354 {
355 bool is_array;
356
357 switch (t)
358 {
359 case NdbDictionary::Column::Binary:
360 case NdbDictionary::Column::Varbinary:
361 case NdbDictionary::Column::Longvarbinary:
362 case NdbDictionary::Column::Char:
363 case NdbDictionary::Column::Varchar:
364 case NdbDictionary::Column::Longvarchar:
365 is_array = true;
366 break;
367 default:
368 is_array = false;
369 }
370 return is_array;
371 }
372
373 #endif // #ifndef ARRAY_ADAPTER_HPP
374