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