diff options
author | unknown <ramil@mysql.com> | 2005-10-24 11:36:29 +0500 |
---|---|---|
committer | unknown <ramil@mysql.com> | 2005-10-24 11:36:29 +0500 |
commit | 39af58dbd46636bf4d5bcdda56a090905f740ad7 (patch) | |
tree | 619517758503f7aaf39c2b8b7a147627ef05ce4a /innobase | |
parent | 811c6d216ec224a019dad5a99e356720677b6d22 (diff) | |
parent | d29ecd5e91adf6eaeb4f2afef0db30648fe08065 (diff) | |
download | mariadb-git-39af58dbd46636bf4d5bcdda56a090905f740ad7.tar.gz |
Merge rkalimullin@bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/usr/home/ram/work/5.0.b10303
mysql-test/r/query_cache.result:
Auto merged
mysql-test/t/query_cache.test:
Auto merged
sql/sql_cache.cc:
Auto merged
sql/sql_select.cc:
Auto merged
Diffstat (limited to 'innobase')
31 files changed, 444 insertions, 123 deletions
diff --git a/innobase/buf/buf0buf.c b/innobase/buf/buf0buf.c index aa3aef7f97c..3a3b64dd51b 100644 --- a/innobase/buf/buf0buf.c +++ b/innobase/buf/buf0buf.c @@ -321,7 +321,9 @@ buf_page_is_corrupted( fprintf(stderr, " InnoDB: Error: page %lu log sequence number %lu %lu\n" "InnoDB: is in the future! Current system log sequence number %lu %lu.\n" -"InnoDB: Your database may be corrupt.\n", +"InnoDB: Your database may be corrupt or you may have copied the InnoDB\n" +"InnoDB: tablespace but not the InnoDB log files. See\n" +"http://dev.mysql.com/doc/mysql/en/backing-up.html for more information.\n", (ulong) mach_read_from_4(read_buf + FIL_PAGE_OFFSET), (ulong) ut_dulint_get_high( mach_read_from_8(read_buf + FIL_PAGE_LSN)), diff --git a/innobase/buf/buf0flu.c b/innobase/buf/buf0flu.c index ffb16790b2d..e39d1ae0a71 100644 --- a/innobase/buf/buf0flu.c +++ b/innobase/buf/buf0flu.c @@ -230,7 +230,7 @@ buf_flush_buffered_writes(void) ulint len2; ulint i; - if (trx_doublewrite == NULL) { + if (!srv_use_doublewrite_buf || trx_doublewrite == NULL) { os_aio_simulated_wake_handler_threads(); return; @@ -503,7 +503,7 @@ buf_flush_write_block_low( #endif buf_flush_init_for_writing(block->frame, block->newest_modification, block->space, block->offset); - if (!trx_doublewrite) { + if (!srv_use_doublewrite_buf || !trx_doublewrite) { fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER, FALSE, block->space, block->offset, 0, UNIV_PAGE_SIZE, (void*)block->frame, (void*)block); diff --git a/innobase/data/data0data.c b/innobase/data/data0data.c index 194213a04e1..19304a7a8e1 100644 --- a/innobase/data/data0data.c +++ b/innobase/data/data0data.c @@ -561,12 +561,12 @@ dtuple_convert_big_rec( } /* We do not store externally fields which are smaller than - DICT_MAX_COL_PREFIX_LEN */ + DICT_MAX_INDEX_COL_LEN */ - ut_a(DICT_MAX_COL_PREFIX_LEN > REC_1BYTE_OFFS_LIMIT); + ut_a(DICT_MAX_INDEX_COL_LEN > REC_1BYTE_OFFS_LIMIT); if (longest < BTR_EXTERN_FIELD_REF_SIZE + 10 - + DICT_MAX_COL_PREFIX_LEN) { + + DICT_MAX_INDEX_COL_LEN) { /* Cannot shorten more */ mem_heap_free(heap); @@ -588,10 +588,10 @@ dtuple_convert_big_rec( dfield = dtuple_get_nth_field(entry, longest_i); vector->fields[n_fields].field_no = longest_i; - ut_a(dfield->len > DICT_MAX_COL_PREFIX_LEN); + ut_a(dfield->len > DICT_MAX_INDEX_COL_LEN); vector->fields[n_fields].len = dfield->len - - DICT_MAX_COL_PREFIX_LEN; + - DICT_MAX_INDEX_COL_LEN; vector->fields[n_fields].data = mem_heap_alloc(heap, vector->fields[n_fields].len); diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index 9580a80e7e7..fb95ffbd80c 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -1625,7 +1625,7 @@ dict_index_add_col( variable-length fields, so that the extern flag can be embedded in the length word. */ - if (field->fixed_len > DICT_MAX_COL_PREFIX_LEN) { + if (field->fixed_len > DICT_MAX_INDEX_COL_LEN) { field->fixed_len = 0; } @@ -2189,7 +2189,7 @@ dict_foreign_error_report( dict_foreign_error_report_low(file, fk->foreign_table_name); fputs(msg, file); fputs(" Constraint:\n", file); - dict_print_info_on_foreign_key_in_create_format(file, NULL, fk); + dict_print_info_on_foreign_key_in_create_format(file, NULL, fk, TRUE); if (fk->foreign_index) { fputs("\nThe index in the foreign key in table is ", file); ut_print_name(file, NULL, fk->foreign_index->name); @@ -2871,8 +2871,12 @@ dict_create_foreign_constraints_low( table2 can be written also with the database name before it: test.table2; the default database is the database of parameter name */ - const char* name) /* in: table full name in the normalized form + const char* name, /* in: table full name in the normalized form database_name/table_name */ + ibool reject_fks) + /* in: if TRUE, fail with error code + DB_CANNOT_ADD_CONSTRAINT if any foreign + keys are found. */ { dict_table_t* table; dict_table_t* referenced_table; @@ -2994,6 +2998,18 @@ loop: } if (*ptr == '\0') { + /* The proper way to reject foreign keys for temporary + tables would be to split the lexing and syntactical + analysis of foreign key clauses from the actual adding + of them, so that ha_innodb.cc could first parse the SQL + command, determine if there are any foreign keys, and + if so, immediately reject the command if the table is a + temporary one. For now, this kludge will work. */ + if (reject_fks && (UT_LIST_GET_LEN(table->foreign_list) > 0)) + { + return DB_CANNOT_ADD_CONSTRAINT; + } + /**********************************************************/ /* The following call adds the foreign key constraints to the data dictionary system tables on disk */ @@ -3417,9 +3433,12 @@ dict_create_foreign_constraints( name before it: test.table2; the default database id the database of parameter name */ - const char* name) /* in: table full name in the + const char* name, /* in: table full name in the normalized form database_name/table_name */ + ibool reject_fks) /* in: if TRUE, fail with error + code DB_CANNOT_ADD_CONSTRAINT if + any foreign keys are found. */ { char* str; ulint err; @@ -3428,7 +3447,8 @@ dict_create_foreign_constraints( str = dict_strip_comments(sql_string); heap = mem_heap_create(10000); - err = dict_create_foreign_constraints_low(trx, heap, str, name); + err = dict_create_foreign_constraints_low(trx, heap, str, name, + reject_fks); mem_heap_free(heap); mem_free(str); @@ -4310,9 +4330,10 @@ CREATE TABLE. */ void dict_print_info_on_foreign_key_in_create_format( /*============================================*/ - FILE* file, /* in: file where to print */ - trx_t* trx, /* in: transaction */ - dict_foreign_t* foreign)/* in: foreign key constraint */ + FILE* file, /* in: file where to print */ + trx_t* trx, /* in: transaction */ + dict_foreign_t* foreign, /* in: foreign key constraint */ + ibool add_newline) /* in: whether to add a newline */ { const char* stripped_id; ulint i; @@ -4325,7 +4346,16 @@ dict_print_info_on_foreign_key_in_create_format( stripped_id = foreign->id; } - fputs(",\n CONSTRAINT ", file); + putc(',', file); + + if (add_newline) { + /* SHOW CREATE TABLE wants constraints each printed nicely + on its own line, while error messages want no newlines + inserted. */ + fputs("\n ", file); + } + + fputs(" CONSTRAINT ", file); ut_print_name(file, trx, stripped_id); fputs(" FOREIGN KEY (", file); @@ -4427,7 +4457,7 @@ dict_print_info_on_foreign_keys( while (foreign != NULL) { if (create_table_format) { dict_print_info_on_foreign_key_in_create_format( - file, trx, foreign); + file, trx, foreign, TRUE); } else { ulint i; fputs("; (", file); diff --git a/innobase/include/btr0btr.h b/innobase/include/btr0btr.h index 1f3a32fa70c..d28b0b129a1 100644 --- a/innobase/include/btr0btr.h +++ b/innobase/include/btr0btr.h @@ -23,16 +23,6 @@ special big record storage structure */ #define BTR_PAGE_MAX_REC_SIZE (UNIV_PAGE_SIZE / 2 - 200) -/* Maximum key size in a B-tree: the records on non-leaf levels must be -shorter than this */ - -#define BTR_PAGE_MAX_KEY_SIZE 1024 - -/* If data in page drops below this limit, we try to compress it. -NOTE! The value has to be > 2 * BTR_MAX_KEY_SIZE */ - -#define BTR_COMPRESS_LIMIT (UNIV_PAGE_SIZE / 4 + 1); - /* Latching modes for the search function (in btr0cur.*) */ #define BTR_SEARCH_LEAF RW_S_LATCH #define BTR_MODIFY_LEAF RW_X_LATCH diff --git a/innobase/include/data0type.ic b/innobase/include/data0type.ic index 06d45dd5501..d4a7b3c64b8 100644 --- a/innobase/include/data0type.ic +++ b/innobase/include/data0type.ic @@ -420,7 +420,7 @@ dtype_get_fixed_size( } /*************************************************************************** -Returns the size of a fixed size data type, 0 if not a fixed size type. */ +Returns the minimum size of a data type. */ UNIV_INLINE ulint dtype_get_min_size( diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h index d9cda402bac..5215d51cabe 100644 --- a/innobase/include/dict0dict.h +++ b/innobase/include/dict0dict.h @@ -228,9 +228,12 @@ dict_create_foreign_constraints( name before it: test.table2; the default database id the database of parameter name */ - const char* name); /* in: table full name in the + const char* name, /* in: table full name in the normalized form database_name/table_name */ + ibool reject_fks); /* in: if TRUE, fail with error + code DB_CANNOT_ADD_CONSTRAINT if + any foreign keys are found. */ /************************************************************************** Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */ @@ -372,9 +375,10 @@ CREATE TABLE. */ void dict_print_info_on_foreign_key_in_create_format( /*============================================*/ - FILE* file, /* in: file where to print */ - trx_t* trx, /* in: transaction */ - dict_foreign_t* foreign);/* in: foreign key constraint */ + FILE* file, /* in: file where to print */ + trx_t* trx, /* in: transaction */ + dict_foreign_t* foreign, /* in: foreign key constraint */ + ibool add_newline); /* in: whether to add a newline */ /************************************************************************ Displays the names of the index and the table. */ void diff --git a/innobase/include/dict0mem.h b/innobase/include/dict0mem.h index ff6c4ec9b28..7eec86d0bcb 100644 --- a/innobase/include/dict0mem.h +++ b/innobase/include/dict0mem.h @@ -152,12 +152,12 @@ struct dict_col_struct{ in some of the functions below */ }; -/* DICT_MAX_COL_PREFIX_LEN is measured in bytes. Starting from 4.1.6, we -set max col prefix len to < 3 * 256, so that one can create a column prefix -index on 255 characters of a TEXT field also in the UTF-8 charset. In that -charset, a character may take at most 3 bytes. */ +/* DICT_MAX_INDEX_COL_LEN is measured in bytes and is the max index column +length + 1. Starting from 4.1.6, we set it to < 3 * 256, so that one can +create a column prefix index on 255 characters of a TEXT field also in the +UTF-8 charset. In that charset, a character may take at most 3 bytes. */ -#define DICT_MAX_COL_PREFIX_LEN 768 +#define DICT_MAX_INDEX_COL_LEN 768 /* Data structure for a field in an index */ struct dict_field_struct{ @@ -169,12 +169,12 @@ struct dict_field_struct{ prefix in bytes in a MySQL index of type, e.g., INDEX (textcol(25)); must be smaller than - DICT_MAX_COL_PREFIX_LEN; NOTE that + DICT_MAX_INDEX_COL_LEN; NOTE that in the UTF-8 charset, MySQL sets this to 3 * the prefix len in UTF-8 chars */ ulint fixed_len; /* 0 or the fixed length of the column if smaller than - DICT_MAX_COL_PREFIX_LEN */ + DICT_MAX_INDEX_COL_LEN */ ulint fixed_offs; /* offset to the field, or ULINT_UNDEFINED if it is not fixed within the record (due to preceding diff --git a/innobase/include/mem0mem.h b/innobase/include/mem0mem.h index 87afdb8f91c..3768e93c03e 100644 --- a/innobase/include/mem0mem.h +++ b/innobase/include/mem0mem.h @@ -99,8 +99,7 @@ heap freeing. */ /********************************************************************* NOTE: Use the corresponding macros instead of this function. Creates a memory heap which allocates memory from dynamic space. For debugging -purposes, takes also the file name and line as argument in the debug -version. */ +purposes, takes also the file name and line as argument. */ UNIV_INLINE mem_heap_t* mem_heap_create_func( diff --git a/innobase/include/mem0mem.ic b/innobase/include/mem0mem.ic index 8c87c884d78..28562f7c9f8 100644 --- a/innobase/include/mem0mem.ic +++ b/innobase/include/mem0mem.ic @@ -371,8 +371,7 @@ mem_heap_free_top( /********************************************************************* NOTE: Use the corresponding macros instead of this function. Creates a memory heap which allocates memory from dynamic space. For debugging -purposes, takes also the file name and line as argument in the debug -version. */ +purposes, takes also the file name and line as argument. */ UNIV_INLINE mem_heap_t* mem_heap_create_func( diff --git a/innobase/include/os0file.h b/innobase/include/os0file.h index adbc4afafd2..224fd59a76b 100644 --- a/innobase/include/os0file.h +++ b/innobase/include/os0file.h @@ -432,6 +432,17 @@ os_file_read( offset */ ulint n); /* in: number of bytes to read */ /*********************************************************************** +Rewind file to its start, read at most size - 1 bytes from it to str, and +NUL-terminate str. All errors are silently ignored. This function is +mostly meant to be used with temporary files. */ + +void +os_file_read_string( +/*================*/ + FILE* file, /* in: file to read from */ + char* str, /* in: buffer where to read */ + ulint size); /* in: size of buffer */ +/*********************************************************************** Requests a synchronous positioned read operation. This function does not do any error handling. In case of error it returns FALSE. */ diff --git a/innobase/include/page0page.ic b/innobase/include/page0page.ic index fd5281fdbec..655ff245aa8 100644 --- a/innobase/include/page0page.ic +++ b/innobase/include/page0page.ic @@ -175,6 +175,19 @@ page_rec_is_comp( /* out: nonzero if in compact format */ const rec_t* rec) /* in: record */ { +#ifdef UNIV_RELEASE_NOT_YET_STABLE + if (UNIV_UNLIKELY((ulint)rec < (ulint)(buf_pool->frame_zero)) + || UNIV_UNLIKELY((ulint)rec >= (ulint)(buf_pool->high_end))) { + + ut_print_timestamp(stderr); + fprintf(stderr, +"InnoDB: Error: trying to read a stray page rec %p\n" +"InnoDB: buf pool start is at %p, end at %p\n", + rec, buf_pool->frame_zero, + buf_pool->high_end); + ut_error; + } +#endif return(page_is_comp(ut_align_down((rec_t*) rec, UNIV_PAGE_SIZE))); } diff --git a/innobase/include/rem0rec.h b/innobase/include/rem0rec.h index 1d15b8d1c77..69b397c9682 100644 --- a/innobase/include/rem0rec.h +++ b/innobase/include/rem0rec.h @@ -312,6 +312,15 @@ rec_offs_nth_extern( const ulint* offsets,/* in: array returned by rec_get_offsets() */ ulint n); /* in: nth field */ /********************************************************** +Returns nonzero if the SQL NULL bit is set in nth field of rec. */ +UNIV_INLINE +ulint +rec_offs_nth_sql_null( +/*==================*/ + /* out: nonzero if SQL NULL */ + const ulint* offsets,/* in: array returned by rec_get_offsets() */ + ulint n); /* in: nth field */ +/********************************************************** Gets the physical size of a field. */ UNIV_INLINE ulint diff --git a/innobase/include/rem0rec.ic b/innobase/include/rem0rec.ic index e2dceb6bae5..9c24f385f4f 100644 --- a/innobase/include/rem0rec.ic +++ b/innobase/include/rem0rec.ic @@ -955,6 +955,22 @@ rec_offs_nth_extern( } /********************************************************** +Returns nonzero if the SQL NULL bit is set in nth field of rec. */ +UNIV_INLINE +ulint +rec_offs_nth_sql_null( +/*==================*/ + /* out: nonzero if SQL NULL */ + const ulint* offsets,/* in: array returned by rec_get_offsets() */ + ulint n) /* in: nth field */ +{ + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + ut_ad(n < rec_offs_n_fields(offsets)); + return(UNIV_UNLIKELY(rec_offs_base(offsets)[1 + n] + & REC_OFFS_SQL_NULL)); +} + +/********************************************************** Gets the physical size of a field. */ UNIV_INLINE ulint diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h index 4e6ff73b0f8..b5da4634d98 100644 --- a/innobase/include/row0mysql.h +++ b/innobase/include/row0mysql.h @@ -335,8 +335,14 @@ int row_create_index_for_mysql( /*=======================*/ /* out: error number or DB_SUCCESS */ - dict_index_t* index, /* in: index defintion */ - trx_t* trx); /* in: transaction handle */ + dict_index_t* index, /* in: index definition */ + trx_t* trx, /* in: transaction handle */ + const ulint* field_lengths); /* in: if not NULL, must contain + dict_index_get_n_fields(index) + actual field lengths for the + index columns, which are + then checked for not being too + large. */ /************************************************************************* Scans a table create SQL string and adds to the data dictionary the foreign key constraints declared in the string. This function @@ -355,9 +361,13 @@ row_table_add_foreign_constraints( FOREIGN KEY (a, b) REFERENCES table2(c, d), table2 can be written also with the database name before it: test.table2 */ - const char* name); /* in: table full name in the + const char* name, /* in: table full name in the normalized form database_name/table_name */ + ibool reject_fks); /* in: if TRUE, fail with error + code DB_CANNOT_ADD_CONSTRAINT if + any foreign keys are found. */ + /************************************************************************* The master thread in srv0srv.c calls this regularly to drop tables which we must drop in background after queries to them have ended. Such lazy diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h index 5dbf003594f..0dc82893ad1 100644 --- a/innobase/include/trx0trx.h +++ b/innobase/include/trx0trx.h @@ -56,6 +56,22 @@ void trx_search_latch_release_if_reserved( /*=================================*/ trx_t* trx); /* in: transaction */ +/********************************************************************** +Set detailed error message for the transaction. */ +void +trx_set_detailed_error( +/*===================*/ + trx_t* trx, /* in: transaction struct */ + const char* msg); /* in: detailed error message */ +/***************************************************************** +Set detailed error message for the transaction from a file. Note that the +file is rewinded before reading from it. */ + +void +trx_set_detailed_error_from_file( +/*=============================*/ + trx_t* trx, /* in: transaction struct */ + FILE* file); /* in: file to read message from */ /******************************************************************** Retrieves the error_info field from a trx. */ @@ -205,7 +221,7 @@ trx_recover_for_mysql( XID* xid_list, /* in/out: prepared transactions */ ulint len); /* in: number of slots in xid_list */ /*********************************************************************** -This function is used to commit one X/Open XA distributed transaction +This function is used to find one X/Open XA distributed transaction which is in the prepared state */ trx_t * trx_get_trx_by_xid( @@ -649,6 +665,9 @@ struct trx_struct{ trx_undo_arr_t* undo_no_arr; /* array of undo numbers of undo log records which are currently processed by a rollback operation */ + /*------------------------------*/ + char detailed_error[256]; /* detailed error message for last + error, or empty. */ }; #define TRX_MAX_N_THREADS 32 /* maximum number of concurrent diff --git a/innobase/include/univ.i b/innobase/include/univ.i index 6849dcd9c51..15650f22ed8 100644 --- a/innobase/include/univ.i +++ b/innobase/include/univ.i @@ -80,6 +80,10 @@ memory is read outside the allocated blocks. */ /* Make a non-inline debug version */ +/* You can remove this define when the release is stable. This define adds +some consistency checks to code. They use a little CPU time. */ +#define UNIV_RELEASE_NOT_YET_STABLE + /* #define UNIV_DEBUG #define UNIV_MEM_DEBUG diff --git a/innobase/include/ut0mem.h b/innobase/include/ut0mem.h index 74357f6bf13..b9bbe0b5c92 100644 --- a/innobase/include/ut0mem.h +++ b/innobase/include/ut0mem.h @@ -119,6 +119,31 @@ int ut_strcmp(const void* str1, const void* str2); /************************************************************************** +Copies up to size - 1 characters from the NUL-terminated string src to +dst, NUL-terminating the result. Returns strlen(src), so truncation +occurred if the return value >= size. */ + +ulint +ut_strlcpy( +/*=======*/ + /* out: strlen(src) */ + char* dst, /* in: destination buffer */ + const char* src, /* in: source buffer */ + ulint size); /* in: size of destination buffer */ + +/************************************************************************** +Like ut_strlcpy, but if src doesn't fit in dst completely, copies the last +(size - 1) bytes of src, not the first. */ + +ulint +ut_strlcpy_rev( +/*===========*/ + /* out: strlen(src) */ + char* dst, /* in: destination buffer */ + const char* src, /* in: source buffer */ + ulint size); /* in: size of destination buffer */ + +/************************************************************************** Compute strlen(ut_strcpyq(str, q)). */ UNIV_INLINE ulint diff --git a/innobase/mem/mem0mem.c b/innobase/mem/mem0mem.c index 85f0119d02a..daf78008d45 100644 --- a/innobase/mem/mem0mem.c +++ b/innobase/mem/mem0mem.c @@ -187,9 +187,7 @@ mem_heap_create_block( } block->magic_n = MEM_BLOCK_MAGIC_N; - ut_memcpy(&(block->file_name), file_name + ut_strlen(file_name) - 7, - 7); - block->file_name[7]='\0'; + ut_strlcpy_rev(block->file_name, file_name, sizeof(block->file_name)); block->line = line; #ifdef MEM_PERIODIC_CHECK diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c index 9c87b59f018..20a3303d12d 100644 --- a/innobase/os/os0file.c +++ b/innobase/os/os0file.c @@ -2249,6 +2249,29 @@ error_handling: } /*********************************************************************** +Rewind file to its start, read at most size - 1 bytes from it to str, and +NUL-terminate str. All errors are silently ignored. This function is +mostly meant to be used with temporary files. */ + +void +os_file_read_string( +/*================*/ + FILE* file, /* in: file to read from */ + char* str, /* in: buffer where to read */ + ulint size) /* in: size of buffer */ +{ + size_t flen; + + if (size == 0) { + return; + } + + rewind(file); + flen = fread(str, 1, size - 1, file); + str[flen] = '\0'; +} + +/*********************************************************************** Requests a synchronous write operation. */ ibool diff --git a/innobase/os/os0proc.c b/innobase/os/os0proc.c index 167aed93de7..24bb007e504 100644 --- a/innobase/os/os0proc.c +++ b/innobase/os/os0proc.c @@ -292,6 +292,9 @@ os_awe_allocate_physical_mem( return(TRUE); #else + UT_NOT_USED(n_megabytes); + UT_NOT_USED(page_info); + return(FALSE); #endif } @@ -349,6 +352,8 @@ os_awe_allocate_virtual_mem_window( return(ptr); #else + UT_NOT_USED(size); + return(NULL); #endif } @@ -476,6 +481,10 @@ os_awe_map_physical_mem_to_window( return(TRUE); #else + UT_NOT_USED(ptr); + UT_NOT_USED(n_mem_pages); + UT_NOT_USED(page_info); + return(FALSE); #endif } diff --git a/innobase/os/os0sync.c b/innobase/os/os0sync.c index 356d7c8c163..487e8f40a39 100644 --- a/innobase/os/os0sync.c +++ b/innobase/os/os0sync.c @@ -631,7 +631,21 @@ os_fast_mutex_free( DeleteCriticalSection((LPCRITICAL_SECTION) fast_mutex); #else - ut_a(0 == pthread_mutex_destroy(fast_mutex)); + int ret; + + ret = pthread_mutex_destroy(fast_mutex); + + if (ret != 0) { + ut_print_timestamp(stderr); + fprintf(stderr, +" InnoDB: error: return value %lu when calling\n" +"InnoDB: pthread_mutex_destroy().\n", (ulint)ret); + fprintf(stderr, +"InnoDB: Byte contents of the pthread mutex at %p:\n", fast_mutex); + ut_print_buf(stderr, (const byte*)fast_mutex, + sizeof(os_fast_mutex_t)); + fprintf(stderr, "\n"); + } #endif if (os_sync_mutex_inited) { /* When freeing the last mutexes, we have diff --git a/innobase/rem/rem0rec.c b/innobase/rem/rem0rec.c index fbc33aea669..9480c978755 100644 --- a/innobase/rem/rem0rec.c +++ b/innobase/rem/rem0rec.c @@ -621,7 +621,7 @@ rec_set_nth_field_extern_bit_new( if (field->fixed_len) { /* fixed-length fields cannot be external (Fixed-length fields longer than - DICT_MAX_COL_PREFIX_LEN will be treated as + DICT_MAX_INDEX_COL_LEN will be treated as variable-length ones in dict_index_add_col().) */ ut_ad(i != ith); continue; diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 75d8117a73e..5e833372299 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -579,6 +579,32 @@ row_ins_cascade_calc_update_vec( } /************************************************************************* +Set detailed error message associated with foreign key errors for +the given transaction. */ +static +void +row_ins_set_detailed( +/*=================*/ + trx_t* trx, /* in: transaction */ + dict_foreign_t* foreign) /* in: foreign key constraint */ +{ + + FILE* tf = os_file_create_tmpfile(); + + if (tf) { + ut_print_name(tf, trx, foreign->foreign_table_name); + dict_print_info_on_foreign_key_in_create_format(tf, trx, + foreign, FALSE); + + trx_set_detailed_error_from_file(trx, tf); + + fclose(tf); + } else { + trx_set_detailed_error(trx, "temp file creation failed"); + } +} + +/************************************************************************* Reports a foreign key error associated with an update or a delete of a parent table index entry. */ static @@ -598,6 +624,8 @@ row_ins_foreign_report_err( FILE* ef = dict_foreign_err_file; trx_t* trx = thr_get_trx(thr); + row_ins_set_detailed(trx, foreign); + mutex_enter(&dict_foreign_err_mutex); rewind(ef); ut_print_timestamp(ef); @@ -607,7 +635,8 @@ row_ins_foreign_report_err( fputs("Foreign key constraint fails for table ", ef); ut_print_name(ef, trx, foreign->foreign_table_name); fputs(":\n", ef); - dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign); + dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign, + TRUE); putc('\n', ef); fputs(errstr, ef); fputs(" in parent table, in index ", ef); @@ -648,7 +677,9 @@ row_ins_foreign_report_add_err( child table */ { FILE* ef = dict_foreign_err_file; - + + row_ins_set_detailed(trx, foreign); + mutex_enter(&dict_foreign_err_mutex); rewind(ef); ut_print_timestamp(ef); @@ -657,7 +688,8 @@ row_ins_foreign_report_add_err( fputs("Foreign key constraint fails for table ", ef); ut_print_name(ef, trx, foreign->foreign_table_name); fputs(":\n", ef); - dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign); + dict_print_info_on_foreign_key_in_create_format(ef, trx, foreign, + TRUE); fputs("\nTrying to add in child table, in index ", ef); ut_print_name(ef, trx, foreign->foreign_index->name); if (entry) { @@ -1224,6 +1256,9 @@ run_again: if (check_table == NULL || check_table->ibd_file_missing) { if (check_ref) { FILE* ef = dict_foreign_err_file; + + row_ins_set_detailed(trx, foreign); + mutex_enter(&dict_foreign_err_mutex); rewind(ef); ut_print_timestamp(ef); @@ -1233,7 +1268,7 @@ run_again: ut_print_name(ef, trx, foreign->foreign_table_name); fputs(":\n", ef); dict_print_info_on_foreign_key_in_create_format(ef, - trx, foreign); + trx, foreign, TRUE); fputs("\nTrying to add to index ", ef); ut_print_name(ef, trx, foreign->foreign_index->name); fputs(" tuple:\n", ef); diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 2ac0824b331..82f7daf2ed8 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -513,14 +513,15 @@ handle_new_error: return(TRUE); - } else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT + } else if (err == DB_DEADLOCK || err == DB_LOCK_TABLE_FULL) { /* Roll back the whole transaction; this resolution was added to version 3.23.43 */ trx_general_rollback_for_mysql(trx, FALSE, NULL); - } else if (err == DB_OUT_OF_FILE_SPACE) { + } else if (err == DB_OUT_OF_FILE_SPACE + || err == DB_LOCK_WAIT_TIMEOUT) { if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ @@ -1972,13 +1973,20 @@ row_create_index_for_mysql( /*=======================*/ /* out: error number or DB_SUCCESS */ dict_index_t* index, /* in: index definition */ - trx_t* trx) /* in: transaction handle */ + trx_t* trx, /* in: transaction handle */ + const ulint* field_lengths) /* in: if not NULL, must contain + dict_index_get_n_fields(index) + actual field lengths for the + index columns, which are + then checked for not being too + large. */ { ind_node_t* node; mem_heap_t* heap; que_thr_t* thr; ulint err; ulint i, j; + ulint len; #ifdef UNIV_SYNC_DEBUG ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); @@ -2017,10 +2025,16 @@ row_create_index_for_mysql( } } - /* Check also that prefix_len < DICT_MAX_COL_PREFIX_LEN */ + /* Check also that prefix_len and actual length + < DICT_MAX_INDEX_COL_LEN */ + + len = dict_index_get_nth_field(index, i)->prefix_len; - if (dict_index_get_nth_field(index, i)->prefix_len - >= DICT_MAX_COL_PREFIX_LEN) { + if (field_lengths) { + len = ut_max(len, field_lengths[i]); + } + + if (len >= DICT_MAX_INDEX_COL_LEN) { err = DB_TOO_BIG_RECORD; goto error_handling; @@ -2087,9 +2101,12 @@ row_table_add_foreign_constraints( FOREIGN KEY (a, b) REFERENCES table2(c, d), table2 can be written also with the database name before it: test.table2 */ - const char* name) /* in: table full name in the + const char* name, /* in: table full name in the normalized form database_name/table_name */ + ibool reject_fks) /* in: if TRUE, fail with error + code DB_CANNOT_ADD_CONSTRAINT if + any foreign keys are found. */ { ulint err; @@ -2110,7 +2127,8 @@ row_table_add_foreign_constraints( trx->dict_operation = TRUE; - err = dict_create_foreign_constraints(trx, sql_string, name); + err = dict_create_foreign_constraints(trx, sql_string, name, + reject_fks); if (err == DB_SUCCESS) { /* Check that also referencing constraints are ok */ diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index a77010d939b..1b66f14f5d7 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2724,7 +2724,9 @@ row_sel_get_clust_rec_for_mysql( if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED && !lock_clust_rec_cons_read_sees(clust_rec, clust_index, *offsets, trx->read_view)) { - + + /* The following call returns 'offsets' associated with + 'old_vers' */ err = row_sel_build_prev_vers_for_mysql( trx->read_view, clust_index, prebuilt, clust_rec, @@ -3055,13 +3057,14 @@ row_search_for_mysql( cursor 'direction' should be 0. */ { dict_index_t* index = prebuilt->index; + ibool comp = index->table->comp; dtuple_t* search_tuple = prebuilt->search_tuple; btr_pcur_t* pcur = prebuilt->pcur; trx_t* trx = prebuilt->trx; dict_index_t* clust_index; que_thr_t* thr; rec_t* rec; - rec_t* index_rec; + rec_t* result_rec; rec_t* clust_rec; rec_t* old_vers; ulint err = DB_SUCCESS; @@ -3491,7 +3494,7 @@ rec_loop: /* PHASE 4: Look for matching records in a loop */ rec = btr_pcur_get_rec(pcur); - ut_ad(!!page_rec_is_comp(rec) == index->table->comp); + ut_ad(!!page_rec_is_comp(rec) == comp); #ifdef UNIV_SEARCH_DEBUG /* fputs("Using ", stderr); @@ -3544,19 +3547,23 @@ rec_loop: /* Do sanity checks in case our cursor has bumped into page corruption */ - if (page_rec_is_comp(rec)) { + if (comp) { next_offs = rec_get_next_offs(rec, TRUE); if (UNIV_UNLIKELY(next_offs < PAGE_NEW_SUPREMUM)) { + goto wrong_offs; } } else { next_offs = rec_get_next_offs(rec, FALSE); if (UNIV_UNLIKELY(next_offs < PAGE_OLD_SUPREMUM)) { + goto wrong_offs; } } + if (UNIV_UNLIKELY(next_offs >= UNIV_PAGE_SIZE - PAGE_DIR)) { - wrong_offs: + +wrong_offs: if (srv_force_recovery == 0 || moves_up == FALSE) { ut_print_timestamp(stderr); buf_page_print(buf_frame_align(rec)); @@ -3599,6 +3606,9 @@ rec_loop: goto next_rec; } } + /*-------------------------------------------------------------*/ + + /* Calculate the 'offsets' associated with 'rec' */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); @@ -3619,8 +3629,6 @@ rec_loop: } } - /*-------------------------------------------------------------*/ - /* Note that we cannot trust the up_match value in the cursor at this place because we can arrive here after moving the cursor! Thus we have to recompare rec and search_tuple to determine if they @@ -3711,7 +3719,7 @@ rec_loop: if (!set_also_gap_locks || srv_locks_unsafe_for_binlog || (unique_search && !UNIV_UNLIKELY(rec_get_deleted_flag( - rec, page_rec_is_comp(rec))))) { + rec, comp)))) { goto no_gap_lock; } else { @@ -3767,6 +3775,8 @@ no_gap_lock: && !lock_clust_rec_cons_read_sees(rec, index, offsets, trx->read_view)) { + /* The following call returns 'offsets' + associated with 'old_vers' */ err = row_sel_build_prev_vers_for_mysql( trx->read_view, clust_index, prebuilt, rec, @@ -3795,19 +3805,20 @@ no_gap_lock: is necessary, because we can only get the undo information via the clustered index record. */ - /* Get the clustered index record if needed */ - index_rec = rec; ut_ad(index != clust_index); goto requires_clust_rec; } } - if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, page_rec_is_comp(rec)))) { + /* NOTE that at this point rec can be an old version of a clustered + index record built for a consistent read. We cannot assume after this + point that rec is on a buffer pool page. Functions like + page_rec_is_comp() cannot be used! */ - /* The record is delete-marked: we can skip it if this is - not a consistent read which might see an earlier version - of a non-clustered index record */ + if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))) { + + /* The record is delete-marked: we can skip it */ if (srv_locks_unsafe_for_binlog && prebuilt->select_lock_type != LOCK_NONE) { @@ -3823,25 +3834,26 @@ no_gap_lock: goto next_rec; } - /* Get the clustered index record if needed and if we did - not do the search using the clustered index */ - - index_rec = rec; + /* Get the clustered index record if needed, if we did not do the + search using the clustered index. */ if (index != clust_index && prebuilt->need_to_access_clustered) { requires_clust_rec: - /* Before and after this "if" block, "offsets" will be - related to "rec", which may be in a secondary index "index" or - the clustered index ("clust_index"). However, after this - "if" block, "rec" may be pointing to - "clust_rec" of "clust_index". */ + /* We use a 'goto' to the preceding label if a consistent + read of a secondary index record requires us to look up old + versions of the associated clustered index record. */ + ut_ad(rec_offs_validate(rec, index, offsets)); /* It was a non-clustered index and we must fetch also the clustered index record */ mtr_has_extra_clust_latch = TRUE; + + /* The following call returns 'offsets' associated with + 'clust_rec'. Note that 'clust_rec' can be an old version + built for a consistent read. */ err = row_sel_get_clust_rec_for_mysql(prebuilt, index, rec, thr, &clust_rec, @@ -3858,8 +3870,7 @@ requires_clust_rec: goto next_rec; } - if (UNIV_UNLIKELY(rec_get_deleted_flag(clust_rec, - page_rec_is_comp(clust_rec)))) { + if (UNIV_UNLIKELY(rec_get_deleted_flag(clust_rec, comp))) { /* The record is delete marked: we can skip it */ @@ -3879,17 +3890,27 @@ requires_clust_rec: } if (prebuilt->need_to_access_clustered) { - rec = clust_rec; - ut_ad(rec_offs_validate(rec, clust_index, offsets)); + + result_rec = clust_rec; + + ut_ad(rec_offs_validate(result_rec, clust_index, + offsets)); } else { + /* We used 'offsets' for the clust rec, recalculate + them for 'rec' */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); + result_rec = rec; } + } else { + result_rec = rec; } - /* We found a qualifying row */ - ut_ad(rec_offs_validate(rec, - rec == clust_rec ? clust_index : index, + /* We found a qualifying record 'result_rec'. At this point, + 'offsets' are associated with 'result_rec'. */ + + ut_ad(rec_offs_validate(result_rec, + result_rec != rec ? clust_index : index, offsets)); if ((match_mode == ROW_SEL_EXACT @@ -3910,8 +3931,8 @@ requires_clust_rec: not cache rows because there the cursor is a scrollable cursor. */ - row_sel_push_cache_row_for_mysql(prebuilt, rec, offsets); - + row_sel_push_cache_row_for_mysql(prebuilt, result_rec, + offsets); if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) { goto got_row; @@ -3920,13 +3941,14 @@ requires_clust_rec: goto next_rec; } else { if (prebuilt->template_type == ROW_MYSQL_DUMMY_TEMPLATE) { - memcpy(buf + 4, rec - rec_offs_extra_size(offsets), + memcpy(buf + 4, result_rec + - rec_offs_extra_size(offsets), rec_offs_size(offsets)); mach_write_to_4(buf, rec_offs_extra_size(offsets) + 4); } else { if (!row_sel_store_mysql_rec(buf, prebuilt, - rec, offsets)) { + result_rec, offsets)) { err = DB_TOO_BIG_RECORD; goto lock_wait_or_error; @@ -3934,15 +3956,18 @@ requires_clust_rec: } if (prebuilt->clust_index_was_generated) { - if (rec != index_rec) { + if (result_rec != rec) { offsets = rec_get_offsets( - index_rec, index, offsets, + rec, index, offsets, ULINT_UNDEFINED, &heap); } - row_sel_store_row_id_to_prebuilt(prebuilt, index_rec, + row_sel_store_row_id_to_prebuilt(prebuilt, rec, index, offsets); } } + + /* From this point on, 'offsets' are invalid. */ + got_row: /* We have an optimization to save CPU time: if this is a consistent read on a unique condition on the clustered index, then we do not @@ -3993,7 +4018,7 @@ next_rec: if (moves_up) { if (UNIV_UNLIKELY(!btr_pcur_move_to_next(pcur, &mtr))) { - not_moved: +not_moved: btr_pcur_store_position(pcur, &mtr); if (match_mode != 0) { diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c index 4f44dbeae67..ff1ad1dfd05 100644 --- a/innobase/row/row0upd.c +++ b/innobase/row/row0upd.c @@ -395,6 +395,18 @@ row_upd_changes_field_size_or_external( old_len = rec_offs_nth_size(offsets, upd_field->field_no); + if (rec_offs_comp(offsets) + && rec_offs_nth_sql_null(offsets, upd_field->field_no)) { + /* Note that in the compact table format, for a + variable length field, an SQL NULL will use zero + bytes in the offset array at the start of the physical + record, but a zero-length value (empty string) will + use one byte! Thus, we cannot use update-in-place + if we update an SQL NULL varchar to an empty string! */ + + old_len = UNIV_SQL_NULL; + } + if (old_len != new_len) { return(TRUE); diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c index 325b0a109cf..e5151ebf631 100644 --- a/innobase/srv/srv0start.c +++ b/innobase/srv/srv0start.c @@ -1540,7 +1540,7 @@ NetWare. */ #endif sync_order_checks_on = TRUE; - if (srv_use_doublewrite_buf && trx_doublewrite == NULL) { + if (trx_doublewrite == NULL) { /* Create the doublewrite buffer to a new tablespace */ trx_sys_create_doublewrite_buf(); diff --git a/innobase/trx/trx0sys.c b/innobase/trx/trx0sys.c index bf48c30e942..23f1dc40d00 100644 --- a/innobase/trx/trx0sys.c +++ b/innobase/trx/trx0sys.c @@ -126,22 +126,6 @@ trx_doublewrite_init( } /******************************************************************** -Frees the doublewrite buffer. */ -static -void -trx_doublewrite_free(void) -/*======================*/ -{ - mutex_free(&(trx_doublewrite->mutex)); - - mem_free(trx_doublewrite->buf_block_arr); - ut_free(trx_doublewrite->write_buf_unaligned); - - mem_free(trx_doublewrite); - trx_doublewrite = NULL; -} - -/******************************************************************** Marks the trx sys header when we have successfully upgraded to the >= 4.1.x multiple tablespace format. */ @@ -529,9 +513,6 @@ trx_sys_doublewrite_init_or_restore_pages( fil_flush_file_spaces(FIL_TABLESPACE); - if (!srv_use_doublewrite_buf) - trx_doublewrite_free(); - leave_func: ut_free(unaligned_read_buf); } diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c index 078befb81d2..090057f5d46 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -52,6 +52,32 @@ trx_start_if_not_started_noninline( trx_start_if_not_started(trx); } +/***************************************************************** +Set detailed error message for the transaction. */ + +void +trx_set_detailed_error( +/*===================*/ + trx_t* trx, /* in: transaction struct */ + const char* msg) /* in: detailed error message */ +{ + ut_strlcpy(trx->detailed_error, msg, sizeof(trx->detailed_error)); +} + +/***************************************************************** +Set detailed error message for the transaction from a file. Note that the +file is rewinded before reading from it. */ + +void +trx_set_detailed_error_from_file( +/*=============================*/ + trx_t* trx, /* in: transaction struct */ + FILE* file) /* in: file to read message from */ +{ + os_file_read_string(file, trx->detailed_error, + sizeof(trx->detailed_error)); +} + /******************************************************************** Retrieves the error_info field from a trx. */ @@ -130,6 +156,7 @@ trx_create( trx->undo_no_arr = NULL; trx->error_state = DB_SUCCESS; + trx->detailed_error[0] = '\0'; trx->sess = sess; trx->que_state = TRX_QUE_RUNNING; diff --git a/innobase/ut/ut0mem.c b/innobase/ut/ut0mem.c index 3e8fd79a739..47b1e24e5e1 100644 --- a/innobase/ut/ut0mem.c +++ b/innobase/ut/ut0mem.c @@ -343,6 +343,54 @@ ut_free_all_mem(void) } /************************************************************************** +Copies up to size - 1 characters from the NUL-terminated string src to +dst, NUL-terminating the result. Returns strlen(src), so truncation +occurred if the return value >= size. */ + +ulint +ut_strlcpy( +/*=======*/ + /* out: strlen(src) */ + char* dst, /* in: destination buffer */ + const char* src, /* in: source buffer */ + ulint size) /* in: size of destination buffer */ +{ + ulint src_size = strlen(src); + + if (size != 0) { + ulint n = ut_min(src_size, size - 1); + + memcpy(dst, src, n); + dst[n] = '\0'; + } + + return(src_size); +} + +/************************************************************************** +Like ut_strlcpy, but if src doesn't fit in dst completely, copies the last +(size - 1) bytes of src, not the first. */ + +ulint +ut_strlcpy_rev( +/*===========*/ + /* out: strlen(src) */ + char* dst, /* in: destination buffer */ + const char* src, /* in: source buffer */ + ulint size) /* in: size of destination buffer */ +{ + ulint src_size = strlen(src); + + if (size != 0) { + ulint n = ut_min(src_size, size - 1); + + memcpy(dst, src + src_size - n, n + 1); + } + + return(src_size); +} + +/************************************************************************** Make a quoted copy of a NUL-terminated string. Leading and trailing quotes will not be included; only embedded quotes will be escaped. See also ut_strlenq() and ut_memcpyq(). */ |