11
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
14
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15
Place, Suite 330, Boston, MA 02111-1307 USA
17
17
*****************************************************************************/
62
62
/** Set these in order ot enable debug printout. */
64
/** Log the outcome of each row_merge_cmp() call, comparing records. */
65
64
static ibool row_merge_print_cmp;
66
/** Log each record read from temporary file. */
67
65
static ibool row_merge_print_read;
68
/** Log each record write to temporary file. */
69
66
static ibool row_merge_print_write;
70
/** Log each row_merge_blocks() call, merging two blocks of records to
72
static ibool row_merge_print_block;
73
/** Log each block read from temporary file. */
74
static ibool row_merge_print_block_read;
75
/** Log each block read from temporary file. */
76
static ibool row_merge_print_block_write;
78
68
#endif /* UNIV_DEBUG */
434
rec_offs_init(offsets_);
444
436
/* Convert the tuple to a record and then to MySQL format. */
445
heap = mem_heap_create((1 + REC_OFFS_HEADER_SIZE + n_fields)
449
buf = mem_heap_alloc(heap, sizeof *buf);
451
438
tuple = dtuple_from_fields(&tuple_store, entry, n_fields);
452
439
n_ext = dict_index_is_clust(index) ? dtuple_get_n_ext(tuple) : 0;
454
rec = rec_convert_dtuple_to_rec(*buf, index, tuple, n_ext);
455
offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap);
441
rec = rec_convert_dtuple_to_rec(buf, index, tuple, n_ext);
442
offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED,
457
445
innobase_rec_to_mysql(dup->table, rec, index, offsets);
447
if (UNIV_LIKELY_NULL(heap)) {
462
452
/*************************************************************//**
629
619
/******************************************************//**
630
Create a memory heap and allocate space for row_merge_rec_offsets()
620
Create a memory heap and allocate space for row_merge_rec_offsets().
632
621
@return memory heap */
635
624
row_merge_heap_create(
636
625
/*==================*/
637
626
const dict_index_t* index, /*!< in: record descriptor */
638
mrec_buf_t** buf, /*!< out: 3 buffers */
639
627
ulint** offsets1, /*!< out: offsets */
640
628
ulint** offsets2) /*!< out: offsets */
642
630
ulint i = 1 + REC_OFFS_HEADER_SIZE
643
631
+ dict_index_get_n_fields(index);
644
mem_heap_t* heap = mem_heap_create(2 * i * sizeof **offsets1
632
mem_heap_t* heap = mem_heap_create(2 * i * sizeof *offsets1);
647
*buf = mem_heap_alloc(heap, 3 * sizeof **buf);
648
*offsets1 = mem_heap_alloc(heap, i * sizeof **offsets1);
649
*offsets2 = mem_heap_alloc(heap, i * sizeof **offsets2);
634
*offsets1 = mem_heap_alloc(heap, i * sizeof *offsets1);
635
*offsets2 = mem_heap_alloc(heap, i * sizeof *offsets2);
651
637
(*offsets1)[0] = (*offsets2)[0] = i;
652
638
(*offsets1)[1] = (*offsets2)[1] = dict_index_get_n_fields(index);
720
699
/********************************************************************//**
721
Write a merge block to the file system.
700
Read a merge block from the file system.
722
701
@return TRUE if request was successful, FALSE if fail */
727
706
int fd, /*!< in: file descriptor */
728
ulint offset, /*!< in: offset where to read
729
in number of row_merge_block_t
707
ulint offset, /*!< in: offset where to write */
731
708
const void* buf) /*!< in: data */
733
710
ib_uint64_t ofs = ((ib_uint64_t) offset)
734
711
* sizeof(row_merge_block_t);
737
if (row_merge_print_block_write) {
738
fprintf(stderr, "row_merge_write fd=%d ofs=%lu\n",
741
#endif /* UNIV_DEBUG */
743
713
return(UNIV_LIKELY(os_file_write("(merge)", OS_FILE_FROM_FD(fd), buf,
744
714
(ulint) (ofs & 0xFFFFFFFF),
745
715
(ulint) (ofs >> 32),
1078
1048
record to be compared */
1079
1049
const ulint* offsets1, /*!< in: first record offsets */
1080
1050
const ulint* offsets2, /*!< in: second record offsets */
1081
const dict_index_t* index, /*!< in: index */
1082
ibool* null_eq) /*!< out: set to TRUE if
1083
found matching null values */
1051
const dict_index_t* index) /*!< in: index */
1087
cmp = cmp_rec_rec_simple(mrec1, mrec2, offsets1, offsets2, index,
1055
cmp = cmp_rec_rec_simple(mrec1, mrec2, offsets1, offsets2, index);
1090
1057
#ifdef UNIV_DEBUG
1091
1058
if (row_merge_print_cmp) {
1308
1269
if (!row_merge_write(file->fd, file->offset++,
1310
1271
err = DB_OUT_OF_FILE_SPACE;
1311
trx->error_key_num = i;
1315
1275
UNIV_MEM_INVALID(block[0], sizeof block[0]);
1316
1276
merge_buf[i] = row_merge_buf_empty(buf);
1318
if (UNIV_LIKELY(row != NULL)) {
1319
/* Try writing the record again, now
1320
that the buffer has been written out
1324
(!row_merge_buf_add(buf, row, ext))) {
1325
/* An empty buffer should have enough
1326
room for at least one record. */
1278
/* Try writing the record again, now that
1279
the buffer has been written out and emptied. */
1282
(row && !row_merge_buf_add(buf, row, ext))) {
1283
/* An empty buffer should have enough
1284
room for at least one record. */
1384
1339
/*************************************************************//**
1385
Merge two blocks of records on disk and write a bigger block.
1340
Merge two blocks of linked lists on disk and write a bigger block.
1386
1341
@return DB_SUCCESS or error code */
1389
1344
row_merge_blocks(
1390
1345
/*=============*/
1391
1346
const dict_index_t* index, /*!< in: index being created */
1392
const merge_file_t* file, /*!< in: file containing
1347
merge_file_t* file, /*!< in/out: file containing
1393
1348
index entries */
1394
1349
row_merge_block_t* block, /*!< in/out: 3 buffers */
1395
1350
ulint* foffs0, /*!< in/out: offset of first
1413
1367
ulint* offsets0;/* offsets of mrec0 */
1414
1368
ulint* offsets1;/* offsets of mrec1 */
1417
if (row_merge_print_block) {
1419
"row_merge_blocks fd=%d ofs=%lu + fd=%d ofs=%lu"
1420
" = fd=%d ofs=%lu\n",
1421
file->fd, (ulong) *foffs0,
1422
file->fd, (ulong) *foffs1,
1423
of->fd, (ulong) of->offset);
1425
#endif /* UNIV_DEBUG */
1427
heap = row_merge_heap_create(index, &buf, &offsets0, &offsets1);
1429
buf = mem_heap_alloc(heap, sizeof(mrec_buf_t) * 3);
1370
heap = row_merge_heap_create(index, &offsets0, &offsets1);
1431
1372
/* Write a record and read the next record. Split the output
1432
1373
file in two halves, which can be merged on the following pass. */
1502
1441
/*************************************************************//**
1503
Copy a block of index entries.
1504
@return TRUE on success, FALSE on failure */
1505
static __attribute__((nonnull))
1507
row_merge_blocks_copy(
1508
/*==================*/
1509
const dict_index_t* index, /*!< in: index being created */
1510
const merge_file_t* file, /*!< in: input file */
1511
row_merge_block_t* block, /*!< in/out: 3 buffers */
1512
ulint* foffs0, /*!< in/out: input file offset */
1513
merge_file_t* of) /*!< in/out: output file */
1515
mem_heap_t* heap; /*!< memory heap for offsets0, offsets1 */
1517
mrec_buf_t* buf; /*!< buffer for handling
1518
split mrec in block[] */
1519
const byte* b0; /*!< pointer to block[0] */
1520
byte* b2; /*!< pointer to block[2] */
1521
const mrec_t* mrec0; /*!< merge rec, points to block[0] */
1522
ulint* offsets0;/* offsets of mrec0 */
1523
ulint* offsets1;/* dummy offsets */
1526
if (row_merge_print_block) {
1528
"row_merge_blocks_copy fd=%d ofs=%lu"
1529
" = fd=%d ofs=%lu\n",
1530
file->fd, (ulong) foffs0,
1531
of->fd, (ulong) of->offset);
1533
#endif /* UNIV_DEBUG */
1535
heap = row_merge_heap_create(index, &buf, &offsets0, &offsets1);
1537
buf = mem_heap_alloc(heap, sizeof(mrec_buf_t) * 3);
1539
/* Write a record and read the next record. Split the output
1540
file in two halves, which can be merged on the following pass. */
1542
if (!row_merge_read(file->fd, *foffs0, &block[0])) {
1544
mem_heap_free(heap);
1551
b0 = row_merge_read_rec(&block[0], &buf[0], b0, index, file->fd,
1552
foffs0, &mrec0, offsets0);
1553
if (UNIV_UNLIKELY(!b0 && mrec0)) {
1559
/* append all mrec0 to output */
1561
ROW_MERGE_WRITE_GET_NEXT(0, goto done0);
1566
/* The file offset points to the beginning of the last page
1567
that has been read. Update it to point to the next block. */
1570
mem_heap_free(heap);
1571
return(row_merge_write_eof(&block[2], b2, of->fd, &of->offset)
1575
/*************************************************************//**
1576
1442
Merge disk files.
1577
1443
@return DB_SUCCESS or error code */
1578
static __attribute__((nonnull))
1582
trx_t* trx, /*!< in: transaction */
1583
1448
const dict_index_t* index, /*!< in: index being created */
1584
1449
merge_file_t* file, /*!< in/out: file containing
1585
1450
index entries */
1586
ulint* half, /*!< in/out: half the file */
1451
ulint half, /*!< in: half the file */
1587
1452
row_merge_block_t* block, /*!< in/out: 3 buffers */
1588
1453
int* tmpfd, /*!< in/out: temporary file handle */
1589
1454
TABLE* table) /*!< in/out: MySQL table, for
1594
1459
ulint foffs1; /*!< second input offset */
1595
1460
ulint error; /*!< error code */
1596
1461
merge_file_t of; /*!< output file */
1597
const ulint ihalf = *half;
1598
/*!< half the input file */
1599
ulint ohalf; /*!< half the output file */
1601
1463
UNIV_MEM_ASSERT_W(block[0], 3 * sizeof block[0]);
1602
ut_ad(ihalf < file->offset);
1604
1466
of.fd = *tmpfd;
1608
1469
/* Merge blocks to the output file. */
1613
for (; foffs0 < ihalf && foffs1 < file->offset; foffs0++, foffs1++) {
1614
ulint ahalf; /*!< arithmetic half the input file */
1616
if (UNIV_UNLIKELY(trx_is_interrupted(trx))) {
1617
return(DB_INTERRUPTED);
1473
for (; foffs0 < half && foffs1 < file->offset; foffs0++, foffs1++) {
1620
1474
error = row_merge_blocks(index, file, block,
1621
1475
&foffs0, &foffs1, &of, table);
1623
1477
if (error != DB_SUCCESS) {
1627
/* Record the offset of the output file when
1628
approximately half the output has been generated. In
1629
this way, the next invocation of row_merge() will
1630
spend most of the time in this loop. The initial
1631
estimate is ohalf==0. */
1632
ahalf = file->offset / 2;
1633
ut_ad(ohalf <= of.offset);
1635
/* Improve the estimate until reaching half the input
1636
file size, or we can not get any closer to it. All
1637
comparands should be non-negative when !(ohalf < ahalf)
1638
because ohalf <= of.offset. */
1639
if (ohalf < ahalf || of.offset - ahalf < ohalf - ahalf) {
1644
/* Copy the last blocks, if there are any. */
1646
while (foffs0 < ihalf) {
1647
if (UNIV_UNLIKELY(trx_is_interrupted(trx))) {
1648
return(DB_INTERRUPTED);
1651
if (!row_merge_blocks_copy(index, file, block, &foffs0, &of)) {
1482
/* Copy the last block, if there is one. */
1483
while (foffs0 < half) {
1484
if (!row_merge_read(file->fd, foffs0++, block)
1485
|| !row_merge_write(of.fd, of.offset++, block)) {
1652
1486
return(DB_CORRUPTION);
1656
ut_ad(foffs0 == ihalf);
1658
1489
while (foffs1 < file->offset) {
1659
if (UNIV_UNLIKELY(trx_is_interrupted(trx))) {
1660
return(DB_INTERRUPTED);
1663
if (!row_merge_blocks_copy(index, file, block, &foffs1, &of)) {
1490
if (!row_merge_read(file->fd, foffs1++, block)
1491
|| !row_merge_write(of.fd, of.offset++, block)) {
1664
1492
return(DB_CORRUPTION);
1668
ut_ad(foffs1 == file->offset);
1670
if (UNIV_UNLIKELY(of.n_rec != file->n_rec)) {
1671
return(DB_CORRUPTION);
1674
1496
/* Swap file descriptors for the next pass. */
1675
1497
*tmpfd = file->fd;
1679
1500
UNIV_MEM_INVALID(block[0], 3 * sizeof block[0]);
1698
1518
reporting erroneous key value
1699
1519
if applicable */
1701
ulint half = file->offset / 2;
1703
/* The file should always contain at least one byte (the end
1704
of file marker). Thus, it must be at least one block. */
1705
ut_ad(file->offset > 0);
1521
ulint blksz; /*!< block size */
1523
for (blksz = 1; blksz < file->offset; blksz *= 2) {
1710
error = row_merge(trx, index, file, &half,
1711
block, tmpfd, table);
1527
ut_ad(ut_is_2pow(blksz));
1528
half = ut_2pow_round((file->offset + (blksz - 1)) / 2, blksz);
1529
error = row_merge(index, file, half, block, tmpfd, table);
1713
1531
if (error != DB_SUCCESS) {
1717
/* half > 0 should hold except when the file consists
1718
of one block. No need to merge further then. */
1719
ut_ad(half > 0 || file->offset == 1);
1720
} while (half < file->offset && half > 0);
1722
1536
return(DB_SUCCESS);
1816
1631
if (!row_merge_read(fd, foffs, block)) {
1817
1632
error = DB_CORRUPTION;
1819
mrec_buf_t* buf = mem_heap_alloc(graph_heap, sizeof *buf);
1822
1635
const mrec_t* mrec;
1823
1636
dtuple_t* dtuple;
1826
b = row_merge_read_rec(block, buf, b, index,
1639
b = row_merge_read_rec(block, &buf, b, index,
1827
1640
fd, &foffs, &mrec, offsets);
1828
1641
if (UNIV_UNLIKELY(!b)) {
1829
1642
/* End of list, or I/O error */
1985
1798
static const char str1[] =
1986
1799
"PROCEDURE DROP_INDEX_PROC () IS\n"
1988
/* Rename the index, so that it will be dropped by
1989
row_merge_drop_temp_indexes() at crash recovery
1990
if the server crashes before this trx is committed. */
1991
"UPDATE SYS_INDEXES SET NAME=CONCAT('"
1992
TEMP_INDEX_PREFIX_STR "', NAME) WHERE ID = :indexid;\n"
1994
/* Drop the field definitions of the index. */
1995
1801
"DELETE FROM SYS_FIELDS WHERE INDEX_ID = :indexid;\n"
1996
/* Drop the index definition and the B-tree. */
1997
"DELETE FROM SYS_INDEXES WHERE ID = :indexid;\n"
1802
"DELETE FROM SYS_INDEXES WHERE ID = :indexid\n"
1803
" AND TABLE_ID = :tableid;\n"
2000
1806
ut_ad(index && table && trx);
2002
1808
pars_info_add_dulint_literal(info, "indexid", index->id);
1809
pars_info_add_dulint_literal(info, "tableid", table->id);
2004
1811
trx_start_if_not_started(trx);
2005
1812
trx->op_info = "dropping index";
2048
1855
/*=============================*/
2054
/* Load the table definitions that contain partially defined
2055
indexes, so that the data dictionary information can be checked
2056
when accessing the tablename.ibd files. */
1860
/* We use the private SQL parser of Innobase to generate the
1861
query graphs needed in deleting the dictionary data from system
1862
tables in Innobase. Deleting a row from SYS_INDEXES table also
1863
frees the file segments of the B-tree associated with the index. */
1864
static const char drop_temp_indexes[] =
1865
"PROCEDURE DROP_TEMP_INDEXES_PROC () IS\n"
1867
"DECLARE CURSOR c IS SELECT ID FROM SYS_INDEXES\n"
1868
"WHERE SUBSTR(NAME,0,1)='" TEMP_INDEX_PREFIX_STR "';\n"
1871
"\tWHILE 1=1 LOOP\n"
1872
"\t\tFETCH c INTO indexid;\n"
1873
"\t\tIF (SQL % NOTFOUND) THEN\n"
1876
"\t\tDELETE FROM SYS_FIELDS WHERE INDEX_ID = indexid;\n"
1877
"\t\tDELETE FROM SYS_INDEXES WHERE ID = indexid;\n"
2058
1883
trx = trx_allocate_for_background();
2059
1884
trx->op_info = "dropping partially created indexes";
2060
1885
row_mysql_lock_data_dictionary(trx);
2064
btr_pcur_open_at_index_side(
2066
dict_table_get_first_index(dict_sys->sys_indexes),
2067
BTR_SEARCH_LEAF, &pcur, TRUE, &mtr);
2074
dict_table_t* table;
2076
btr_pcur_move_to_next_user_rec(&pcur, &mtr);
2078
if (!btr_pcur_is_on_user_rec(&pcur)) {
2082
rec = btr_pcur_get_rec(&pcur);
2083
field = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_NAME_FIELD,
2085
if (len == UNIV_SQL_NULL || len == 0
2086
|| (char) *field != TEMP_INDEX_PREFIX) {
2090
/* This is a temporary index. */
2092
field = rec_get_nth_field_old(rec, 0/*TABLE_ID*/, &len);
2094
/* Corrupted TABLE_ID */
2098
table_id = mach_read_from_8(field);
2100
btr_pcur_store_position(&pcur, &mtr);
2101
btr_pcur_commit_specify_mtr(&pcur, &mtr);
2103
table = dict_table_get_on_id_low(table_id);
2106
dict_index_t* index;
2107
dict_index_t* next_index;
2109
for (index = dict_table_get_first_index(table);
2110
index; index = next_index) {
2112
next_index = dict_table_get_next_index(index);
2114
if (*index->name == TEMP_INDEX_PREFIX) {
2115
row_merge_drop_index(index, table, trx);
2116
trx_commit_for_mysql(trx);
2122
btr_pcur_restore_position(BTR_SEARCH_LEAF,
2126
btr_pcur_close(&pcur);
1887
/* Incomplete transactions may be holding some locks on the
1888
data dictionary tables. However, they should never have been
1889
able to lock the records corresponding to the partially
1890
created indexes that we are attempting to delete, because the
1891
table was locked when the indexes were being created. We will
1892
drop the partially created indexes before the rollback of
1893
incomplete transactions is initiated. Thus, this should not
1894
interfere with the incomplete transactions. */
1895
trx->isolation_level = TRX_ISO_READ_UNCOMMITTED;
1896
pars_info_t *info = pars_info_create();
1897
err = que_eval_sql(info, drop_temp_indexes, FALSE, trx);
1898
ut_a(err == DB_SUCCESS);
2128
1900
row_mysql_unlock_data_dictionary(trx);
2129
1901
trx_free_for_background(trx);