@@ -127,3 +127,242 @@ static inline dberr_t row_search_for_mysql(byte *buf, page_cur_mode_t mode,
127127 return (row_search_no_mvcc(buf, mode, prebuilt, match_mode, direction));
128128 }
129129}
130+
131+ /** Stores a non-SQL-NULL field in the MySQL format. The counterpart of this
132+ function is row_mysql_store_col_in_innobase_format() in row0mysql.cc.
133+ @param[in,out] dest buffer where to store; NOTE
134+ that BLOBs are not in themselves stored
135+ here: the caller must allocate and copy
136+ the BLOB into buffer before, and pass
137+ the pointer to the BLOB in 'data'
138+ @param[in] templ MySQL column template. Its following fields
139+ are referenced: type, is_unsigned,
140+ mysql_col_len, mbminlen, mbmaxlen
141+ @param[in] index InnoDB index
142+ @param[in] field_no templ->rec_field_no or templ->clust_rec_field_no
143+ or templ->icp_rec_field_no
144+ @param[in] data data to store
145+ @param[in] len length of the data
146+ @param[in] compress_heap heap for decompression
147+ @param[in] sec_field secondary index field no if the secondary index
148+ record but the prebuilt template is in
149+ clustered index format and used only for end
150+ range comparison. */
151+ inline MY_ATTRIBUTE((always_inline)) void row_sel_field_store_in_mysql_format_func(
152+ byte *dest, const mysql_row_templ_t *templ, const dict_index_t *index,
153+ IF_DEBUG(ulint field_no, ) const byte *data, ulint len,
154+ mem_heap_t **compress_heap IF_DEBUG(, ulint sec_field)) {
155+ byte *ptr;
156+ #ifdef UNIV_DEBUG
157+ const dict_field_t *field =
158+ templ->is_virtual ? nullptr : index->get_field(field_no);
159+
160+ bool clust_templ_for_sec = (sec_field != ULINT_UNDEFINED);
161+ #endif /* UNIV_DEBUG */
162+
163+ if (templ->is_multi_val) {
164+ ib::fatal(UT_LOCATION_HERE, ER_CONVERT_MULTI_VALUE)
165+ << "Table name: " << index->table->name
166+ << " Index name: " << index->name;
167+ }
168+
169+ auto const mysql_col_len = templ->mysql_col_len;
170+
171+ ut_ad(rec_field_not_null_not_add_col_def(len));
172+ UNIV_MEM_ASSERT_RW(data, len);
173+ UNIV_MEM_ASSERT_W(dest, mysql_col_len);
174+ UNIV_MEM_INVALID(dest, mysql_col_len);
175+
176+ switch (templ->type) {
177+ const byte *field_end;
178+ byte *pad;
179+ case DATA_INT:
180+ /* Convert integer data from Innobase to a little-endian
181+ format, sign bit restored to normal */
182+
183+ ptr = dest + len;
184+
185+ for (;;) {
186+ ptr--;
187+ *ptr = *data;
188+ if (ptr == dest) {
189+ break;
190+ }
191+ data++;
192+ }
193+
194+ if (!templ->is_unsigned) {
195+ dest[len - 1] = (byte)(dest[len - 1] ^ 128);
196+ }
197+
198+ ut_ad(mysql_col_len == len);
199+
200+ break;
201+
202+ case DATA_VARCHAR:
203+ case DATA_VARMYSQL:
204+ case DATA_BINARY:
205+ field_end = dest + mysql_col_len;
206+
207+ if (templ->mysql_type == DATA_MYSQL_TRUE_VARCHAR) {
208+ /* If this is a compressed column,
209+ decompress it first */
210+ if (templ->compressed)
211+ data = row_decompress_column(
212+ data, &len,
213+ reinterpret_cast<const byte *>(templ->zip_dict_data.str),
214+ templ->zip_dict_data.length, compress_heap);
215+
216+ /* This is a >= 5.0.3 type true VARCHAR. Store the
217+ length of the data to the first byte or the first
218+ two bytes of dest. */
219+
220+ dest =
221+ row_mysql_store_true_var_len(dest, len, templ->mysql_length_bytes);
222+ /* Copy the actual data. Leave the rest of the
223+ buffer uninitialized. */
224+ memcpy(dest, data, len);
225+ break;
226+ }
227+
228+ /* Copy the actual data */
229+ ut_memcpy(dest, data, len);
230+
231+ /* Pad with trailing spaces. */
232+
233+ pad = dest + len;
234+
235+ ut_ad(templ->mbminlen <= templ->mbmaxlen);
236+
237+ /* We treat some Unicode charset strings specially. */
238+ switch (templ->mbminlen) {
239+ case 4:
240+ /* InnoDB should never have stripped partial
241+ UTF-32 characters. */
242+ ut_a(!(len & 3));
243+ break;
244+ case 2:
245+ /* A space char is two bytes,
246+ 0x0020 in UCS2 and UTF-16 */
247+
248+ if (UNIV_UNLIKELY(len & 1)) {
249+ /* A 0x20 has been stripped from the column.
250+ Pad it back. */
251+
252+ if (pad < field_end) {
253+ *pad++ = 0x20;
254+ }
255+ }
256+ }
257+
258+ row_mysql_pad_col(templ->mbminlen, pad, field_end - pad);
259+ break;
260+
261+ case DATA_BLOB:
262+ /* Store a pointer to the BLOB buffer to dest: the BLOB was
263+ already copied to the buffer in row_sel_store_mysql_rec */
264+
265+ row_mysql_store_blob_ref(
266+ dest, mysql_col_len, data, len, templ->compressed,
267+ reinterpret_cast<const byte *>(templ->zip_dict_data.str),
268+ templ->zip_dict_data.length, compress_heap);
269+ break;
270+
271+ case DATA_POINT:
272+ case DATA_VAR_POINT:
273+ case DATA_GEOMETRY:
274+ /* We store all geometry data as BLOB data at server layer. */
275+ row_mysql_store_geometry(dest, mysql_col_len, data, len);
276+ break;
277+
278+ case DATA_MYSQL:
279+ memcpy(dest, data, len);
280+
281+ ut_ad(mysql_col_len >= len);
282+ ut_ad(templ->mbmaxlen >= templ->mbminlen);
283+
284+ /* If field_no equals to templ->icp_rec_field_no, we are examining a row
285+ pointed by "icp_rec_field_no". There is possibility that icp_rec_field_no
286+ refers to a field in a secondary index while templ->rec_field_no points
287+ to field in a primary index. The length should still be equal, unless the
288+ field pointed by icp_rec_field_no has a prefix or this is a virtual
289+ column.
290+ For end range condition check of secondary index with cluster index
291+ template (clust_templ_for_sec), the index column data length (len)
292+ could be smaller than the actual column length (mysql_col_len) if index
293+ is on column prefix. This is not a real issue because the end range check
294+ would only need the prefix part. The length check assert is relaxed for
295+ clust_templ_for_sec. */
296+ ut_ad(templ->is_virtual || templ->mbmaxlen > templ->mbminlen ||
297+ mysql_col_len == len || clust_templ_for_sec ||
298+ (field_no == templ->icp_rec_field_no && field->prefix_len > 0));
299+
300+ /* The following assertion would fail for old tables
301+ containing UTF-8 ENUM columns due to Bug #9526. */
302+ ut_ad(!templ->mbmaxlen || !(mysql_col_len % templ->mbmaxlen));
303+ /* Length of the record will be less in case of
304+ clust_templ_for_sec is true or if it is fetched
305+ from prefix virtual column in virtual index. */
306+ ut_ad(templ->is_virtual || clust_templ_for_sec ||
307+ len * templ->mbmaxlen >= mysql_col_len ||
308+ index->has_row_versions() ||
309+ (field_no == templ->icp_rec_field_no && field->prefix_len > 0) ||
310+ templ->rec_field_is_prefix);
311+ ut_ad(templ->is_virtual || !(field->prefix_len % templ->mbmaxlen));
312+
313+ /* Pad with spaces. This undoes the stripping
314+ done in row0mysql.cc, function
315+ row_mysql_store_col_in_innobase_format(). */
316+ if ((templ->mbminlen == 1 && templ->mbmaxlen != 1) ||
317+ (templ->is_virtual && mysql_col_len > len)) {
318+ /* NOTE: This comment is for the second condition:
319+ This probably comes from a prefix virtual index, where no complete
320+ value can be got because the full virtual column can only be
321+ calculated in server layer for now. Since server now assumes the
322+ returned value should always have padding spaces, thus the fixup.
323+ However, a proper and more efficient solution is that server does
324+ not depend on the trailing spaces to check the terminal of the CHAR
325+ string, because at least in this case,server should know it's a prefix
326+ index search and no complete value would be got. */
327+ memset(dest + len, 0x20, mysql_col_len - len);
328+ }
329+ break;
330+
331+ default:
332+ #ifdef UNIV_DEBUG
333+ case DATA_SYS_CHILD:
334+ case DATA_SYS:
335+ /* These column types should never be shipped to MySQL. */
336+ ut_d(ut_error);
337+ [[fallthrough]];
338+
339+ case DATA_CHAR:
340+ case DATA_FIXBINARY:
341+ case DATA_FLOAT:
342+ case DATA_DOUBLE:
343+ case DATA_DECIMAL:
344+ /* Above are the valid column types for MySQL data. */
345+ #endif /* UNIV_DEBUG */
346+
347+ /* If sec_field value is present then mapping of
348+ secondary index records to clustered index template
349+ happens for end range comparison. So length can
350+ vary according to secondary index record length. */
351+ ut_ad((templ->is_virtual && !field) ||
352+ ((field && field->prefix_len)
353+ ? field->prefix_len == len
354+ : (clust_templ_for_sec || mysql_col_len == len)));
355+
356+ memcpy(dest, data, len);
357+ }
358+ }
359+
360+ /** Convert a non-SQL-NULL field from Innobase format to MySQL format. */
361+ inline MY_ATTRIBUTE((always_inline)) void row_sel_field_store_in_mysql_format(
362+ byte *dest, const mysql_row_templ_t *templ, const dict_index_t *idx,
363+ ulint field, const byte *src, ulint len, mem_heap_t **compress_heap,
364+ ulint sec) {
365+ row_sel_field_store_in_mysql_format_func(dest, templ, idx,
366+ IF_DEBUG(field, ) src, len,
367+ compress_heap IF_DEBUG(, sec));
368+ }
0 commit comments