From 0d294e1b64745051c14f17d017f44a9ea005f3ab Mon Sep 17 00:00:00 2001 From: "venu@myvenu.com" <> Date: Sat, 11 Jan 2003 00:36:13 -0800 Subject: [PATCH 1/2] Fix to make the tables re-inited for every execute call --- sql/sql_prepare.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 092f7c7a497..3907995676f 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -628,7 +628,7 @@ static bool parse_prepare_query(PREP_STMT *stmt, Initialize parameter items in statement */ -static bool init_param_items( PREP_STMT *stmt) +static bool init_param_items(PREP_STMT *stmt) { List<Item> ¶ms= stmt->thd->lex.param_list; Item_param **to; @@ -642,6 +642,24 @@ static bool init_param_items( PREP_STMT *stmt) return 0; } +/* + Initialize stmt execution +*/ + +static void init_stmt_execute(PREP_STMT *stmt) +{ + THD *thd= stmt->thd; + TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select_lex.table_list.first; + + /* + TODO: When the new table structure is ready, then have a status bit + to indicate the table is altered, and re-do the setup_* + and open the tables back. + */ + if (tables) + tables->table=0; //safety - nasty init +} + /* Parse the query and send the total number of parameters and resultset metadata information back to client (if any), @@ -722,6 +740,8 @@ void mysql_stmt_execute(THD *thd, char *packet) DBUG_VOID_RETURN; } + init_stmt_execute(stmt); + if (stmt->param_count && setup_params_data(stmt)) DBUG_VOID_RETURN; From 8e6a4c6201ab14414837dcba79cfb4180b92e18d Mon Sep 17 00:00:00 2001 From: "heikki@hundin.mysql.fi" <> Date: Sun, 12 Jan 2003 23:58:56 +0200 Subject: [PATCH 2/2] ut0mem.c, row0sel.c, row0mysql.c, ut0mem.h, row0sel.h, row0mysql.h: Test allocation of memory beforehand if we are trying to return a > 2 MB BLOB; normally InnoDB asserts if memory allocation fails ha_innodb.cc: Do not fetch all columns if change_active_index() is called during a query; a sum(a), max(a) query seemed to do that, doing unnecessary copying (the change actually made in the previous bk ci) Free BLOB heap of handle when MySQL calls some ::extra()'s --- innobase/include/row0mysql.h | 8 ++++++++ innobase/include/row0sel.h | 3 ++- innobase/include/ut0mem.h | 10 ++++++++++ innobase/row/row0mysql.c | 13 +++++++++++++ innobase/row/row0sel.c | 37 +++++++++++++++++++++++++++++++----- innobase/ut/ut0mem.c | 37 +++++++++++++++++++++++++++++++++++- sql/ha_innodb.cc | 34 ++++++++++++++++++++++----------- 7 files changed, 124 insertions(+), 18 deletions(-) diff --git a/innobase/include/row0mysql.h b/innobase/include/row0mysql.h index 25d2ab77007..972fabc74cf 100644 --- a/innobase/include/row0mysql.h +++ b/innobase/include/row0mysql.h @@ -52,6 +52,14 @@ row_mysql_read_var_ref_noninline( ulint* len, /* out: variable-length field length */ byte* field); /* in: field */ /*********************************************************************** +Frees the blob heap in prebuilt when no longer needed. */ + +void +row_mysql_prebuilt_free_blob_heap( +/*==============================*/ + row_prebuilt_t* prebuilt); /* in: prebuilt struct of a + ha_innobase:: table handle */ +/*********************************************************************** Stores a reference to a BLOB in the MySQL format. */ void diff --git a/innobase/include/row0sel.h b/innobase/include/row0sel.h index aa2da6fe5f6..cfc30852b87 100644 --- a/innobase/include/row0sel.h +++ b/innobase/include/row0sel.h @@ -115,7 +115,8 @@ row_search_for_mysql( /*=================*/ /* out: DB_SUCCESS, DB_RECORD_NOT_FOUND, - DB_END_OF_INDEX, or DB_DEADLOCK */ + DB_END_OF_INDEX, DB_DEADLOCK, + or DB_TOO_BIG_RECORD */ byte* buf, /* in/out: buffer for the fetched row in the MySQL format */ ulint mode, /* in: search mode PAGE_CUR_L, ... */ diff --git a/innobase/include/ut0mem.h b/innobase/include/ut0mem.h index 09e0d800685..d3d04d58596 100644 --- a/innobase/include/ut0mem.h +++ b/innobase/include/ut0mem.h @@ -50,6 +50,16 @@ ut_malloc( /* out, own: allocated memory */ ulint n); /* in: number of bytes to allocate */ /************************************************************************** +Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs +out. It cannot be used if we want to return an error message. Prints to +stderr a message if fails. */ + +ibool +ut_test_malloc( +/*===========*/ + /* out: TRUE if succeeded */ + ulint n); /* in: try to allocate this many bytes */ +/************************************************************************** Frees a memory bloock allocated with ut_malloc. */ void diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index ba56b3071cd..7cef63d1337 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -58,6 +58,19 @@ row_mysql_read_var_ref_noninline( return(row_mysql_read_var_ref(len, field)); } +/*********************************************************************** +Frees the blob heap in prebuilt when no longer needed. */ + +void +row_mysql_prebuilt_free_blob_heap( +/*==============================*/ + row_prebuilt_t* prebuilt) /* in: prebuilt struct of a + ha_innobase:: table handle */ +{ + mem_heap_free(prebuilt->blob_heap); + prebuilt->blob_heap = NULL; +} + /*********************************************************************** Stores a reference to a BLOB in the MySQL format. */ diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index ea5b3020c08..34f951b0c8a 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2039,9 +2039,12 @@ Note that the template in prebuilt may advise us to copy only a few columns to mysql_rec, other columns are left blank. All columns may not be needed in the query. */ static -void +ibool row_sel_store_mysql_rec( /*====================*/ + /* out: TRUE if success, FALSE + if could not allocate memory for a + BLOB */ byte* mysql_rec, /* out: row in the MySQL format */ row_prebuilt_t* prebuilt, /* in: prebuilt struct */ rec_t* rec) /* in: Innobase record in the index @@ -2092,7 +2095,19 @@ row_sel_store_mysql_rec( if (templ->type == DATA_BLOB) { ut_a(prebuilt->templ_contains_blob); - + + /* A heuristic test that we can allocate + the memory for a big BLOB. We have a safety + margin of 1000000 bytes. Since the test + takes some CPU time, we do not use for small + BLOBs. */ + + if (len > 2000000 + && !ut_test_malloc(len + 1000000)) { + + return(FALSE); + } + /* Copy the BLOB data to the BLOB heap of prebuilt */ @@ -2142,6 +2157,8 @@ row_sel_store_mysql_rec( } } } + + return(TRUE); } /************************************************************************* @@ -2526,7 +2543,8 @@ row_search_for_mysql( /*=================*/ /* out: DB_SUCCESS, DB_RECORD_NOT_FOUND, - DB_END_OF_INDEX, or DB_DEADLOCK */ + DB_END_OF_INDEX, DB_DEADLOCK, + or DB_TOO_BIG_RECORD */ byte* buf, /* in/out: buffer for the fetched row in the MySQL format */ ulint mode, /* in: search mode PAGE_CUR_L, ... */ @@ -2758,7 +2776,12 @@ row_search_for_mysql( #ifdef UNIV_SEARCH_DEBUG ut_a(0 == cmp_dtuple_rec(search_tuple, rec)); #endif - row_sel_store_mysql_rec(buf, prebuilt, rec); + if (!row_sel_store_mysql_rec(buf, prebuilt, + rec)) { + err = DB_TOO_BIG_RECORD; + + goto lock_wait_or_error; + } mtr_commit(&mtr); @@ -3200,7 +3223,11 @@ rec_loop: rec_get_size(rec)); mach_write_to_4(buf, rec_get_extra_size(rec) + 4); } else { - row_sel_store_mysql_rec(buf, prebuilt, rec); + if (!row_sel_store_mysql_rec(buf, prebuilt, rec)) { + err = DB_TOO_BIG_RECORD; + + goto lock_wait_or_error; + } } if (prebuilt->clust_index_was_generated) { diff --git a/innobase/ut/ut0mem.c b/innobase/ut/ut0mem.c index 03f15031fdf..a5991d5683d 100644 --- a/innobase/ut/ut0mem.c +++ b/innobase/ut/ut0mem.c @@ -77,8 +77,9 @@ ut_malloc_low( ret = malloc(n + sizeof(ut_mem_block_t)); if (ret == NULL) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Fatal error: cannot allocate %lu bytes of\n" + " InnoDB: Fatal error: cannot allocate %lu bytes of\n" "InnoDB: memory with malloc! Total allocated memory\n" "InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n" "InnoDB: Cannot continue operation!\n" @@ -134,6 +135,40 @@ ut_malloc( return(ut_malloc_low(n, TRUE)); } +/************************************************************************** +Tests if malloc of n bytes would succeed. ut_malloc() asserts if memory runs +out. It cannot be used if we want to return an error message. Prints to +stderr a message if fails. */ + +ibool +ut_test_malloc( +/*===========*/ + /* out: TRUE if succeeded */ + ulint n) /* in: try to allocate this many bytes */ +{ + void* ret; + + ret = malloc(n); + + if (ret == NULL) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Error: cannot allocate %lu bytes of memory for\n" + "InnoDB: a BLOB with malloc! Total allocated memory\n" + "InnoDB: by InnoDB %lu bytes. Operating system errno: %d\n" + "InnoDB: Check if you should increase the swap file or\n" + "InnoDB: ulimits of your operating system.\n" + "InnoDB: On FreeBSD check you have compiled the OS with\n" + "InnoDB: a big enough maximum process size.\n", + n, ut_total_allocated_memory, errno); + return(FALSE); + } + + free(ret); + + return(TRUE); +} + /************************************************************************** Frees a memory block allocated with ut_malloc. */ diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 0d56f216b3b..cfec8282e42 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -1624,6 +1624,12 @@ build_template( } if (prebuilt->select_lock_type == LOCK_X) { + /* In versions < 3.23.50 we always retrieved the clustered + index record if prebuilt->select_lock_type == LOCK_S, + but there is really not need for that, and in some cases + performance could be seriously degraded because the MySQL + optimizer did not know about our convention! */ + /* We always retrieve the whole clustered index record if we use exclusive row level locks, for example, if the read is done in an UPDATE statement. */ @@ -1632,12 +1638,6 @@ build_template( } if (templ_type == ROW_MYSQL_REC_FIELDS) { - /* In versions < 3.23.50 we always retrieved the clustered - index record if prebuilt->select_lock_type == LOCK_S, - but there is really not need for that, and in some cases - performance could be seriously degraded because the MySQL - optimizer did not know about our convention! */ - index = prebuilt->index; } else { index = clust_index; @@ -2506,11 +2506,13 @@ ha_innobase::change_active_index( dict_index_copy_types(prebuilt->search_tuple, prebuilt->index, prebuilt->index->n_fields); - /* Maybe MySQL changes the active index for a handle also - during some queries, we do not know: then it is safest to build - the template such that all columns will be fetched */ + /* MySQL changes the active index for a handle also during some + queries, for example SELECT MAX(a), SUM(a) first retrieves the MAX() + and then calculates te sum. Previously we played safe and used + the flag ROW_MYSQL_WHOLE_ROW below, but that caused unnecessary + copying. Starting from MySQL-4.1 we use a more efficient flag here. */ - build_template(prebuilt, user_thd, table, ROW_MYSQL_WHOLE_ROW); + build_template(prebuilt, user_thd, table, ROW_MYSQL_REC_FIELDS); DBUG_RETURN(0); } @@ -3742,8 +3744,18 @@ ha_innobase::extra( obsolete! */ switch (operation) { + case HA_EXTRA_FLUSH: + if (prebuilt->blob_heap) { + row_mysql_prebuilt_free_blob_heap(prebuilt); + } + break; case HA_EXTRA_RESET: - case HA_EXTRA_RESET_STATE: + if (prebuilt->blob_heap) { + row_mysql_prebuilt_free_blob_heap(prebuilt); + } + prebuilt->read_just_key = 0; + break; + case HA_EXTRA_RESET_STATE: prebuilt->read_just_key = 0; break; case HA_EXTRA_NO_KEYREAD: