1 /*****************************************************************************
2 
3 Copyright (c) 2005, 2019, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 
27 /** @file include/row0merge.h
28  Index build routines using a merge sort
29 
30  Created 13/06/2005 Jan Lindstrom
31  *******************************************************/
32 
33 #ifndef row0merge_h
34 #define row0merge_h
35 
36 #include "btr0types.h"
37 #include "data0data.h"
38 #include "dict0types.h"
39 #include "lock0types.h"
40 #include "mtr0mtr.h"
41 #include "que0types.h"
42 #include "rem0rec.h"
43 #include "rem0types.h"
44 #include "row0mysql.h"
45 #include "srv0srv.h"
46 #include "trx0types.h"
47 #include "univ.i"
48 #include "ut0stage.h"
49 
50 // Forward declaration
51 struct ib_sequence_t;
52 
53 /** @brief Block size for I/O operations in merge sort.
54 
55 The minimum is UNIV_PAGE_SIZE, or page_get_free_space_of_empty()
56 rounded to a power of 2.
57 
58 When not creating a PRIMARY KEY that contains column prefixes, this
59 can be set as small as UNIV_PAGE_SIZE / 2. */
60 typedef byte row_merge_block_t;
61 
62 /** @brief Secondary buffer for I/O operations of merge records.
63 
64 This buffer is used for writing or reading a record that spans two
65 row_merge_block_t.  Thus, it must be able to hold one merge record,
66 whose maximum size is the same as the minimum size of
67 row_merge_block_t. */
68 typedef byte mrec_buf_t[UNIV_PAGE_SIZE_MAX];
69 
70 /** @brief Merge record in row_merge_block_t.
71 
72 The format is the same as a record in ROW_FORMAT=COMPACT with the
73 exception that the REC_N_NEW_EXTRA_BYTES are omitted. */
74 typedef byte mrec_t;
75 
76 /** Merge record in row_merge_buf_t */
77 struct mtuple_t {
78   dfield_t *fields; /*!< data fields */
79 };
80 
81 /** Buffer for sorting in main memory. */
82 struct row_merge_buf_t {
83   mem_heap_t *heap;     /*!< memory heap where allocated */
84   dict_index_t *index;  /*!< the index the tuples belong to */
85   ulint total_size;     /*!< total amount of data bytes */
86   ulint n_tuples;       /*!< number of data tuples */
87   ulint max_tuples;     /*!< maximum number of data tuples */
88   mtuple_t *tuples;     /*!< array of data tuples */
89   mtuple_t *tmp_tuples; /*!< temporary copy of tuples,
90                         for sorting */
91 };
92 
93 /** Information about temporary files used in merge sort */
94 struct merge_file_t {
95   int fd;            /*!< file descriptor */
96   ulint offset;      /*!< file offset (end of file) */
97   ib_uint64_t n_rec; /*!< number of records in the file */
98 };
99 
100 /** Index field definition */
101 struct index_field_t {
102   ulint col_no;        /*!< column offset */
103   ulint prefix_len;    /*!< column prefix length, or 0
104                        if indexing the whole column */
105   bool is_v_col;       /*!< whether this is a virtual column */
106   bool is_multi_value; /*!< whether it has multi-value */
107   bool is_ascending;   /*!< true=ASC, false=DESC */
108 };
109 
110 /** Definition of an index being created */
111 struct index_def_t {
112   const char *name;          /*!< index name */
113   bool rebuild;              /*!< whether the table is rebuilt */
114   ulint ind_type;            /*!< 0, DICT_UNIQUE,
115                              or DICT_CLUSTERED */
116   ulint key_number;          /*!< MySQL key number,
117                              or ULINT_UNDEFINED if none */
118   ulint n_fields;            /*!< number of fields in index */
119   index_field_t *fields;     /*!< field definitions */
120   st_mysql_ftparser *parser; /*!< fulltext parser plugin */
121   bool is_ngram;             /*!< true if it's ngram parser */
122   bool srid_is_valid;        /*!< true if we want to check SRID
123                              while inserting to index */
124   uint32_t srid;             /*!< SRID obtained from dd column */
125 };
126 
127 /** Structure for reporting duplicate records. */
128 struct row_merge_dup_t {
129   dict_index_t *index;  /*!< index being sorted */
130   struct TABLE *table;  /*!< MySQL table object */
131   const ulint *col_map; /*!< mapping of column numbers
132                         in table to the rebuilt table
133                         (index->table), or NULL if not
134                         rebuilding table */
135   ulint n_dup;          /*!< number of duplicates */
136 };
137 
138 /** Report a duplicate key. */
139 void row_merge_dup_report(
140     row_merge_dup_t *dup,   /*!< in/out: for reporting duplicates */
141     const dfield_t *entry); /*!< in: duplicate index entry */
142 /** Sets an exclusive lock on a table, for the duration of creating indexes.
143  @return error code or DB_SUCCESS */
144 dberr_t row_merge_lock_table(trx_t *trx,          /*!< in/out: transaction */
145                              dict_table_t *table, /*!< in: table to lock */
146                              enum lock_mode mode) /*!< in: LOCK_X or LOCK_S */
147     MY_ATTRIBUTE((warn_unused_result));
148 /** Drop those indexes which were created before an error occurred.
149  The data dictionary must have been locked exclusively by the caller,
150  because the transaction will not be committed. */
151 void row_merge_drop_indexes(
152     trx_t *trx,          /*!< in/out: transaction */
153     dict_table_t *table, /*!< in/out: table containing the indexes */
154     ibool locked);       /*!< in: TRUE=table locked,
155                  FALSE=may need to do a lazy drop */
156 
157 /**Create temporary merge files in the given paramater path, and if
158 UNIV_PFS_IO defined, register the file descriptor with Performance Schema.
159 @param[in]	path	location for creating temporary merge files.
160 @return File descriptor */
161 int row_merge_file_create_low(const char *path)
162     MY_ATTRIBUTE((warn_unused_result));
163 
164 /** Destroy a merge file. And de-register the file from Performance Schema
165  if UNIV_PFS_IO is defined. */
166 void row_merge_file_destroy_low(int fd); /*!< in: merge file descriptor */
167 
168 /** Provide a new pathname for a table that is being renamed if it belongs to
169  a file-per-table tablespace.  The caller is responsible for freeing the
170  memory allocated for the return value.
171  @return new pathname of tablespace file, or NULL if space = 0 */
172 char *row_make_new_pathname(dict_table_t *table, /*!< in: table to be renamed */
173                             const char *new_name); /*!< in: new name */
174 /** Rename the tables in the data dictionary.  The data dictionary must
175  have been locked exclusively by the caller, because the transaction
176  will not be committed.
177  @return error code or DB_SUCCESS */
178 dberr_t row_merge_rename_tables_dict(
179     dict_table_t *old_table, /*!< in/out: old table, renamed to
180                              tmp_name */
181     dict_table_t *new_table, /*!< in/out: new table, renamed to
182                              old_table->name */
183     const char *tmp_name,    /*!< in: new name for old_table */
184     trx_t *trx)              /*!< in/out: dictionary transaction */
185     MY_ATTRIBUTE((warn_unused_result));
186 
187 /** Create the index and load in to the dictionary.
188 @param[in,out]	trx		trx (sets error_state)
189 @param[in,out]	table		the index is on this table
190 @param[in]	index_def	the index definition
191 @param[in]	add_v		new virtual columns added along with add
192                                 index call
193 @return index, or NULL on error */
194 dict_index_t *row_merge_create_index(trx_t *trx, dict_table_t *table,
195                                      const index_def_t *index_def,
196                                      const dict_add_v_col_t *add_v);
197 
198 /** Drop a table. The caller must have ensured that the background stats
199  thread is not processing the table. This can be done by calling
200  dict_stats_wait_bg_to_stop_using_table() after locking the dictionary and
201  before calling this function.
202  @return DB_SUCCESS or error code */
203 dberr_t row_merge_drop_table(
204     trx_t *trx,           /*!< in: transaction */
205     dict_table_t *table); /*!< in: table instance to drop */
206 
207 /** Build indexes on a table by reading a clustered index, creating a temporary
208 file containing index entries, merge sorting these index entries and inserting
209 sorted index entries to indexes.
210 @param[in]	trx		transaction
211 @param[in]	old_table	table where rows are read from
212 @param[in]	new_table	table where indexes are created; identical to
213 old_table unless creating a PRIMARY KEY
214 @param[in]	online		true if creating indexes online
215 @param[in]	indexes		indexes to be created
216 @param[in]	key_numbers	MySQL key numbers
217 @param[in]	n_indexes	size of indexes[]
218 @param[in,out]	table		MySQL table, for reporting erroneous key value
219 if applicable
220 @param[in]	add_cols	default values of added columns, or NULL
221 @param[in]	col_map		mapping of old column numbers to new ones, or
222 NULL if old_table == new_table
223 @param[in]	add_autoinc	number of added AUTO_INCREMENT columns, or
224 ULINT_UNDEFINED if none is added
225 @param[in,out]	sequence	autoinc sequence
226 @param[in]	skip_pk_sort	whether the new PRIMARY KEY will follow
227 existing order
228 @param[in,out]	stage		performance schema accounting object, used by
229 ALTER TABLE. stage->begin_phase_read_pk() will be called at the beginning of
230 this function and it will be passed to other functions for further accounting.
231 @param[in]	add_v		new virtual columns added along with indexes
232 @param[in]	eval_table	mysql table used to evaluate virtual column
233                                 value, see innobase_get_computed_value().
234 @return DB_SUCCESS or error code */
235 dberr_t row_merge_build_indexes(
236     trx_t *trx, dict_table_t *old_table, dict_table_t *new_table, bool online,
237     dict_index_t **indexes, const ulint *key_numbers, ulint n_indexes,
238     struct TABLE *table, const dtuple_t *add_cols, const ulint *col_map,
239     ulint add_autoinc, ib_sequence_t &sequence, bool skip_pk_sort,
240     ut_stage_alter_t *stage, const dict_add_v_col_t *add_v,
241     struct TABLE *eval_table) MY_ATTRIBUTE((warn_unused_result));
242 
243 /** Write a buffer to a block.
244 @param[in]	buf	sorted buffer
245 @param[in]	of	output file
246 @param[out]	block	buffer for writing to file */
247 void row_merge_buf_write(const row_merge_buf_t *buf, const merge_file_t *of,
248                          row_merge_block_t *block);
249 
250 /** Sort a buffer. */
251 void row_merge_buf_sort(
252     row_merge_buf_t *buf,  /*!< in/out: sort buffer */
253     row_merge_dup_t *dup); /*!< in/out: reporter of duplicates
254                            (NULL if non-unique index) */
255 /** Write a merge block to the file system.
256  @return true if request was successful, false if fail */
257 ibool row_merge_write(int fd,           /*!< in: file descriptor */
258                       ulint offset,     /*!< in: offset where to write,
259                                         in number of row_merge_block_t elements */
260                       const void *buf); /*!< in: data */
261 /** Empty a sort buffer.
262  @return sort buffer */
263 row_merge_buf_t *row_merge_buf_empty(
264     row_merge_buf_t *buf) /*!< in,own: sort buffer */
265     MY_ATTRIBUTE((warn_unused_result));
266 
267 /** Create a merge file int the given location.
268 @param[out]	merge_file	merge file structure
269 @param[in]	path		location for creating temporary file
270 @return file descriptor, or -1 on failure */
271 int row_merge_file_create(merge_file_t *merge_file, const char *path);
272 
273 /** Merge disk files.
274 @param[in]	trx	transaction
275 @param[in]	dup	descriptor of index being created
276 @param[in,out]	file	file containing index entries
277 @param[in,out]	block	3 buffers
278 @param[in,out]	tmpfd	temporary file handle
279 @param[in,out]	stage	performance schema accounting object, used by
280 ALTER TABLE. If not NULL, stage->begin_phase_sort() will be called initially
281 and then stage->inc() will be called for each record processed.
282 @return DB_SUCCESS or error code */
283 dberr_t row_merge_sort(trx_t *trx, const row_merge_dup_t *dup,
284                        merge_file_t *file, row_merge_block_t *block, int *tmpfd,
285                        ut_stage_alter_t *stage = nullptr);
286 
287 /** Allocate a sort buffer.
288  @return own: sort buffer */
289 row_merge_buf_t *row_merge_buf_create(
290     dict_index_t *index) /*!< in: secondary index */
291     MY_ATTRIBUTE((warn_unused_result, malloc));
292 /** Deallocate a sort buffer. */
293 void row_merge_buf_free(
294     row_merge_buf_t *buf); /*!< in,own: sort buffer to be freed */
295 /** Destroy a merge file. */
296 void row_merge_file_destroy(
297     merge_file_t *merge_file); /*!< in/out: merge file structure */
298 /** Read a merge block from the file system.
299  @return true if request was successful, false if fail */
300 ibool row_merge_read(int fd,                  /*!< in: file descriptor */
301                      ulint offset,            /*!< in: offset where to read
302                                               in number of row_merge_block_t
303                                               elements */
304                      row_merge_block_t *buf); /*!< out: data */
305 /** Read a merge record.
306  @return pointer to next record, or NULL on I/O error or end of list */
307 const byte *row_merge_read_rec(
308     row_merge_block_t *block,  /*!< in/out: file buffer */
309     mrec_buf_t *buf,           /*!< in/out: secondary buffer */
310     const byte *b,             /*!< in: pointer to record */
311     const dict_index_t *index, /*!< in: index of the record */
312     int fd,                    /*!< in: file descriptor */
313     ulint *foffs,              /*!< in/out: file offset */
314     const mrec_t **mrec,       /*!< out: pointer to merge record,
315                                or NULL on end of list
316                                (non-NULL on I/O error) */
317     ulint *offsets)            /*!< out: offsets of mrec */
318     MY_ATTRIBUTE((warn_unused_result));
319 #endif /* row0merge.h */
320